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.
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/PreXULSkeletonUI.h"
64 #include "mozilla/Logging.h"
65 #include "mozilla/MathAlgorithms.h"
66 #include "mozilla/MiscEvents.h"
67 #include "mozilla/MouseEvents.h"
68 #include "mozilla/PresShell.h"
69 #include "mozilla/ScopeExit.h"
70 #include "mozilla/SwipeTracker.h"
71 #include "mozilla/TouchEvents.h"
72 #include "mozilla/TimeStamp.h"
74 #include "mozilla/ipc/MessageChannel.h"
79 #include "nsAppRunner.h"
90 #include <propvarutil.h>
93 #include "mozilla/Logging.h"
97 #include "mozilla/WidgetTraceEvent.h"
98 #include "nsContentUtils.h"
99 #include "nsISupportsPrimitives.h"
100 #include "nsITheme.h"
101 #include "nsIObserverService.h"
102 #include "nsIScreenManager.h"
103 #include "imgIContainer.h"
105 #include "nsIRollupListener.h"
106 #include "nsIClipboard.h"
107 #include "WinMouseScrollHandler.h"
108 #include "nsFontMetrics.h"
109 #include "nsIFontEnumerator.h"
112 #include "nsThreadUtils.h"
113 #include "nsNativeCharsetUtils.h"
114 #include "nsGkAtoms.h"
116 #include "nsAppDirectoryServiceDefs.h"
117 #include "nsWidgetsCID.h"
118 #include "nsTHashtable.h"
119 #include "nsHashKeys.h"
120 #include "nsString.h"
121 #include "mozilla/Components.h"
122 #include "nsNativeThemeWin.h"
123 #include "nsWindowsDllInterceptor.h"
124 #include "nsLayoutUtils.h"
126 #include "nsWindowGfx.h"
127 #include "gfxWindowsPlatform.h"
128 #include "gfxDWriteFonts.h"
130 #include "nsPrintfCString.h"
131 #include "mozilla/Preferences.h"
132 #include "SystemTimeConverter.h"
133 #include "WinTaskbar.h"
134 #include "WidgetUtils.h"
135 #include "WinContentSystemParameters.h"
136 #include "WinWindowOcclusionTracker.h"
137 #include "nsIWidgetListener.h"
138 #include "mozilla/dom/Document.h"
139 #include "mozilla/dom/MouseEventBinding.h"
140 #include "mozilla/dom/Touch.h"
141 #include "mozilla/gfx/2D.h"
142 #include "mozilla/gfx/GPUProcessManager.h"
143 #include "mozilla/intl/LocaleService.h"
144 #include "mozilla/layers/WebRenderLayerManager.h"
145 #include "mozilla/WindowsVersion.h"
146 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
147 #include "mozilla/TextEventDispatcherListener.h"
148 #include "mozilla/widget/nsAutoRollup.h"
149 #include "mozilla/widget/PlatformWidgetTypes.h"
150 #include "nsStyleConsts.h"
151 #include "nsBidiKeyboard.h"
152 #include "nsStyleConsts.h"
153 #include "gfxConfig.h"
154 #include "InProcessWinCompositorWidget.h"
155 #include "InputDeviceUtils.h"
156 #include "ScreenHelperWin.h"
157 #include "mozilla/StaticPrefs_apz.h"
158 #include "mozilla/StaticPrefs_dom.h"
159 #include "mozilla/StaticPrefs_gfx.h"
160 #include "mozilla/StaticPrefs_layout.h"
161 #include "mozilla/StaticPrefs_widget.h"
162 #include "nsNativeAppSupportWin.h"
164 #include "nsIGfxInfo.h"
165 #include "nsUXThemeConstants.h"
166 #include "KeyboardLayout.h"
167 #include "nsNativeDragTarget.h"
168 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
170 #include <richedit.h>
172 #if defined(ACCESSIBILITY)
175 # include "mozilla/a11y/Logging.h"
179 # include <winuser.h>
180 # include "nsAccessibilityService.h"
181 # include "mozilla/a11y/DocAccessible.h"
182 # include "mozilla/a11y/LazyInstantiator.h"
183 # include "mozilla/a11y/Platform.h"
184 # if !defined(WINABLEAPI)
185 # include <winable.h>
186 # endif // !defined(WINABLEAPI)
187 #endif // defined(ACCESSIBILITY)
189 #include "nsIWinTaskbar.h"
190 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
192 #include "WindowsUIUtils.h"
194 #include "nsWindowDefs.h"
196 #include "nsCrashOnException.h"
198 #include "nsIContent.h"
200 #include "mozilla/BackgroundHangMonitor.h"
201 #include "WinIMEHandler.h"
207 #include "InkCollector.h"
209 // ERROR from wingdi.h (below) gets undefined by some code.
211 // #define RGN_ERROR ERROR
214 #if !defined(SM_CONVERTIBLESLATEMODE)
215 # define SM_CONVERTIBLESLATEMODE 0x2003
218 #if !defined(WM_DPICHANGED)
219 # define WM_DPICHANGED 0x02E0
222 #include "mozilla/gfx/DeviceManagerDx.h"
223 #include "mozilla/layers/APZInputBridge.h"
224 #include "mozilla/layers/InputAPZContext.h"
225 #include "mozilla/layers/KnowsCompositor.h"
226 #include "InputData.h"
228 #include "mozilla/TaskController.h"
229 #include "mozilla/Telemetry.h"
230 #include "mozilla/webrender/WebRenderAPI.h"
231 #include "mozilla/layers/IAPZCTreeManager.h"
233 #include "DirectManipulationOwner.h"
235 using namespace mozilla
;
236 using namespace mozilla::dom
;
237 using namespace mozilla::gfx
;
238 using namespace mozilla::layers
;
239 using namespace mozilla::widget
;
240 using namespace mozilla::plugins
;
242 /**************************************************************
243 **************************************************************
247 ** nsWindow Class static initializations and global variables.
249 **************************************************************
250 **************************************************************/
252 /**************************************************************
254 * SECTION: nsWindow statics
256 **************************************************************/
257 static const wchar_t kUser32LibName
[] = L
"user32.dll";
259 bool nsWindow::sDropShadowEnabled
= true;
260 uint32_t nsWindow::sInstanceCount
= 0;
261 bool nsWindow::sSwitchKeyboardLayout
= false;
262 BOOL
nsWindow::sIsOleInitialized
= FALSE
;
263 nsIWidget::Cursor
nsWindow::sCurrentCursor
= {};
264 nsWindow
* nsWindow::sCurrentWindow
= nullptr;
265 bool nsWindow::sJustGotDeactivate
= false;
266 bool nsWindow::sJustGotActivate
= false;
267 bool nsWindow::sIsInMouseCapture
= false;
269 // imported in nsWidgetFactory.cpp
270 TriStateBool
nsWindow::sCanQuit
= TRI_UNKNOWN
;
272 // Hook Data Memebers for Dropdowns. sProcessHook Tells the
273 // hook methods whether they should be processing the hook
275 HHOOK
nsWindow::sMsgFilterHook
= nullptr;
276 HHOOK
nsWindow::sCallProcHook
= nullptr;
277 HHOOK
nsWindow::sCallMouseHook
= nullptr;
278 bool nsWindow::sProcessHook
= false;
279 UINT
nsWindow::sRollupMsgId
= 0;
280 HWND
nsWindow::sRollupMsgWnd
= nullptr;
281 UINT
nsWindow::sHookTimerId
= 0;
283 // Mouse Clicks - static variable definitions for figuring
285 POINT
nsWindow::sLastMousePoint
= {0};
286 POINT
nsWindow::sLastMouseMovePoint
= {0};
287 LONG
nsWindow::sLastMouseDownTime
= 0L;
288 LONG
nsWindow::sLastClickCount
= 0L;
289 BYTE
nsWindow::sLastMouseButton
= 0;
291 bool nsWindow::sHaveInitializedPrefs
= false;
292 bool nsWindow::sIsRestoringSession
= false;
294 bool nsWindow::sFirstTopLevelWindowCreated
= false;
296 TriStateBool
nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor
= TRI_UNKNOWN
;
298 bool nsWindow::sTouchInjectInitialized
= false;
299 InjectTouchInputPtr
nsWindow::sInjectTouchFuncPtr
;
301 static SystemTimeConverter
<DWORD
>& TimeConverter() {
302 static SystemTimeConverter
<DWORD
> timeConverterSingleton
;
303 return timeConverterSingleton
;
308 class CurrentWindowsTimeGetter
{
310 explicit CurrentWindowsTimeGetter(HWND aWnd
) : mWnd(aWnd
) {}
312 DWORD
GetCurrentTime() const { return ::GetTickCount(); }
314 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp
& aNow
) {
315 DWORD currentTime
= GetCurrentTime();
316 if (sBackwardsSkewStamp
&& currentTime
== sLastPostTime
) {
317 // There's already one inflight with this timestamp. Don't
321 sBackwardsSkewStamp
= Some(aNow
);
322 sLastPostTime
= currentTime
;
323 static_assert(sizeof(WPARAM
) >= sizeof(DWORD
),
324 "Can't fit a DWORD in a WPARAM");
325 ::PostMessage(mWnd
, MOZ_WM_SKEWFIX
, sLastPostTime
, 0);
328 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime
,
329 TimeStamp
* aOutSkewStamp
) {
330 if (aPostTime
!= sLastPostTime
) {
331 // The SKEWFIX message is stale; we've sent a new one since then.
335 MOZ_ASSERT(sBackwardsSkewStamp
);
336 *aOutSkewStamp
= sBackwardsSkewStamp
.value();
337 sBackwardsSkewStamp
= Nothing();
342 static Maybe
<TimeStamp
> sBackwardsSkewStamp
;
343 static DWORD sLastPostTime
;
347 Maybe
<TimeStamp
> CurrentWindowsTimeGetter::sBackwardsSkewStamp
;
348 DWORD
CurrentWindowsTimeGetter::sLastPostTime
= 0;
350 } // namespace mozilla
352 /**************************************************************
354 * SECTION: globals variables
356 **************************************************************/
358 static const char* sScreenManagerContractID
=
359 "@mozilla.org/gfx/screenmanager;1";
361 extern mozilla::LazyLogModule gWindowsLog
;
363 // True if we have sent a notification that we are suspending/sleeping.
364 static bool gIsSleepMode
= false;
366 static NS_DEFINE_CID(kCClipboardCID
, NS_CLIPBOARD_CID
);
368 // General purpose user32.dll hook object
369 static WindowsDllInterceptor sUser32Intercept
;
371 // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
372 // the default window border Windows paints. Glass will be extended inward
373 // this distance to remove the border.
374 static const int32_t kGlassMarginAdjustment
= 2;
376 // When the client area is extended out into the default window frame area,
377 // this is the minimum amount of space along the edge of resizable windows
378 // we will always display a resize cursor in, regardless of the underlying
380 static const int32_t kResizableBorderMinSize
= 3;
382 // Getting this object from the window server can be expensive. Keep it
383 // around, also get it off the main thread. (See bug 1640852)
384 StaticRefPtr
<IVirtualDesktopManager
> gVirtualDesktopManager
;
385 static bool gInitializedVirtualDesktopManager
= false;
387 // We should never really try to accelerate windows bigger than this. In some
388 // cases this might lead to no D3D9 acceleration where we could have had it
389 // but D3D9 does not reliably report when it supports bigger windows. 8192
390 // is as safe as we can get, we know at least D3D10 hardware always supports
391 // this, other hardware we expect to report correctly in D3D9.
392 #define MAX_ACCELERATED_DIMENSION 8192
394 // On window open (as well as after), Windows has an unfortunate habit of
395 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
396 // to DOM target conversions for these, we cache responses for a given
397 // coordinate this many milliseconds:
398 #define HITTEST_CACHE_LIFETIME_MS 50
400 #if defined(ACCESSIBILITY)
405 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
406 * injecting tiptsf.dll. The touchscreen process then posts registered messages
407 * to our main thread. The tiptsf hook picks up those registered messages and
408 * uses them as commands, some of which call into UIA, which then calls into
409 * MSAA, which then sends WM_GETOBJECT to us.
411 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
412 * hook. Since thread-local hooks are called ahead of global hooks, we will
413 * see these registered messages before tiptsf does. At this point we can then
414 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
415 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
416 * flag by calling TIPMessageHandler::IsA11yBlocked().
418 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
419 * function that also calls into UIA.
421 class TIPMessageHandler
{
423 ~TIPMessageHandler() {
425 ::UnhookWindowsHookEx(mHook
);
429 static void Initialize() {
430 if (!IsWin8OrLater()) {
438 sInstance
= new TIPMessageHandler();
439 ClearOnShutdown(&sInstance
);
442 static bool IsA11yBlocked() {
447 return sInstance
->mA11yBlockCount
> 0;
451 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
452 MOZ_ASSERT(NS_IsMainThread());
454 // Registered messages used by tiptsf
455 mMessages
[0] = ::RegisterWindowMessage(L
"ImmersiveFocusNotification");
456 mMessages
[1] = ::RegisterWindowMessage(L
"TipCloseMenus");
457 mMessages
[2] = ::RegisterWindowMessage(L
"TabletInputPanelOpening");
458 mMessages
[3] = ::RegisterWindowMessage(L
"IHM Pen or Touch Event noticed");
459 mMessages
[4] = ::RegisterWindowMessage(L
"ProgrammabilityCaretVisibility");
460 mMessages
[5] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPHidden");
461 mMessages
[6] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPInfo");
463 mHook
= ::SetWindowsHookEx(WH_GETMESSAGE
, &TIPHook
, nullptr,
464 ::GetCurrentThreadId());
467 // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
468 // first initialized.
469 if (!IsWin10OrLater() && GetModuleHandle(L
"tiptsf.dll") &&
470 !sProcessCaretEventsStub
) {
471 sTipTsfInterceptor
.Init("tiptsf.dll");
472 DebugOnly
<bool> ok
= sProcessCaretEventsStub
.Set(
473 sTipTsfInterceptor
, "ProcessCaretEvents", &ProcessCaretEventsHook
);
477 if (!sSendMessageTimeoutWStub
) {
478 sUser32Intercept
.Init("user32.dll");
479 DebugOnly
<bool> hooked
= sSendMessageTimeoutWStub
.Set(
480 sUser32Intercept
, "SendMessageTimeoutW", &SendMessageTimeoutWHook
);
485 class MOZ_RAII A11yInstantiationBlocker
{
487 A11yInstantiationBlocker() {
488 if (!TIPMessageHandler::sInstance
) {
491 ++TIPMessageHandler::sInstance
->mA11yBlockCount
;
494 ~A11yInstantiationBlocker() {
495 if (!TIPMessageHandler::sInstance
) {
498 MOZ_ASSERT(TIPMessageHandler::sInstance
->mA11yBlockCount
> 0);
499 --TIPMessageHandler::sInstance
->mA11yBlockCount
;
503 friend class A11yInstantiationBlocker
;
505 static LRESULT CALLBACK
TIPHook(int aCode
, WPARAM aWParam
, LPARAM aLParam
) {
506 if (aCode
< 0 || !sInstance
) {
507 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
510 MSG
* msg
= reinterpret_cast<MSG
*>(aLParam
);
511 UINT
& msgCode
= msg
->message
;
513 for (uint32_t i
= 0; i
< ArrayLength(sInstance
->mMessages
); ++i
) {
514 if (msgCode
== sInstance
->mMessages
[i
]) {
515 A11yInstantiationBlocker block
;
516 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
520 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
523 static void CALLBACK
ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook
,
524 DWORD aEvent
, HWND aHwnd
,
525 LONG aObjectId
, LONG aChildId
,
526 DWORD aGeneratingTid
,
528 A11yInstantiationBlocker block
;
529 sProcessCaretEventsStub(aWinEventHook
, aEvent
, aHwnd
, aObjectId
, aChildId
,
530 aGeneratingTid
, aEventTime
);
533 static LRESULT WINAPI
SendMessageTimeoutWHook(HWND aHwnd
, UINT aMsgCode
,
534 WPARAM aWParam
, LPARAM aLParam
,
535 UINT aFlags
, UINT aTimeout
,
536 PDWORD_PTR aMsgResult
) {
537 // We don't want to handle this unless the message is a WM_GETOBJECT that we
538 // want to block, and the aHwnd is a nsWindow that belongs to the current
540 if (!aMsgResult
|| aMsgCode
!= WM_GETOBJECT
||
541 static_cast<DWORD
>(aLParam
) != OBJID_CLIENT
||
542 !WinUtils::GetNSWindowPtr(aHwnd
) ||
543 ::GetWindowThreadProcessId(aHwnd
, nullptr) != ::GetCurrentThreadId() ||
545 return sSendMessageTimeoutWStub(aHwnd
, aMsgCode
, aWParam
, aLParam
, aFlags
,
546 aTimeout
, aMsgResult
);
549 // In this case we want to fake the result that would happen if we had
550 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
551 // off to DefWindowProc to accomplish this.
552 *aMsgResult
= static_cast<DWORD_PTR
>(
553 ::DefWindowProcW(aHwnd
, aMsgCode
, aWParam
, aLParam
));
555 return static_cast<LRESULT
>(TRUE
);
558 static WindowsDllInterceptor sTipTsfInterceptor
;
559 static WindowsDllInterceptor::FuncHookType
<WINEVENTPROC
>
560 sProcessCaretEventsStub
;
561 static WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
562 sSendMessageTimeoutWStub
;
563 static StaticAutoPtr
<TIPMessageHandler
> sInstance
;
567 uint32_t mA11yBlockCount
;
570 WindowsDllInterceptor
TIPMessageHandler::sTipTsfInterceptor
;
571 WindowsDllInterceptor::FuncHookType
<WINEVENTPROC
>
572 TIPMessageHandler::sProcessCaretEventsStub
;
573 WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
574 TIPMessageHandler::sSendMessageTimeoutWStub
;
575 StaticAutoPtr
<TIPMessageHandler
> TIPMessageHandler::sInstance
;
577 } // namespace mozilla
579 #endif // defined(ACCESSIBILITY)
583 // This task will get the VirtualDesktopManager from the generic thread pool
584 // since doing this on the main thread on startup causes performance issues.
588 // This should be fine and should not require any locking, as when the main
589 // thread will access it, if it races with this function it will either find
590 // it to be null or to have a valid value.
591 class InitializeVirtualDesktopManagerTask
: public Task
{
593 InitializeVirtualDesktopManagerTask() : Task(false, kDefaultPriorityValue
) {}
595 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
596 bool GetName(nsACString
& aName
) override
{
597 aName
.AssignLiteral("InitializeVirtualDesktopManagerTask");
602 virtual bool Run() override
{
604 if (!IsWin10OrLater()) {
608 RefPtr
<IVirtualDesktopManager
> desktopManager
;
609 HRESULT hr
= ::CoCreateInstance(
610 CLSID_VirtualDesktopManager
, NULL
, CLSCTX_INPROC_SERVER
,
611 __uuidof(IVirtualDesktopManager
), getter_AddRefs(desktopManager
));
616 gVirtualDesktopManager
= desktopManager
;
622 static BOOL
GetMouseVanishSystemPref(bool aShouldUpdate
) {
623 static bool sInitialized
= false;
624 static BOOL sMouseVanishSystemPref
= FALSE
;
626 if (!sInitialized
|| aShouldUpdate
) {
627 BOOL ok
= ::SystemParametersInfo(SPI_GETMOUSEVANISH
, 0,
628 &sMouseVanishSystemPref
, 0);
630 // Getting system pref failed so just use user pref.
631 sMouseVanishSystemPref
=
632 StaticPrefs::widget_windows_hide_cursor_when_typing();
637 return sMouseVanishSystemPref
;
640 static bool IsMouseVanishKey(WPARAM aVirtKey
) {
660 * Hide/unhide the cursor if the correct Windows and Firefox settings are set.
662 static void MaybeHideCursor(bool aShouldHide
) {
663 static bool sIsHidden
= false;
664 bool shouldHide
= aShouldHide
&&
665 StaticPrefs::widget_windows_hide_cursor_when_typing() &&
666 GetMouseVanishSystemPref(false);
668 if (shouldHide
!= sIsHidden
) {
669 [[maybe_unused
]] int count
= ::ShowCursor(aShouldHide
? FALSE
: TRUE
);
670 MOZ_ASSERT(count
== (aShouldHide
? -1 : 0));
671 sIsHidden
= shouldHide
;
675 } // namespace mozilla
677 /**************************************************************
678 **************************************************************
680 ** BLOCK: nsIWidget impl.
682 ** nsIWidget interface implementation, broken down into
685 **************************************************************
686 **************************************************************/
688 /**************************************************************
690 * SECTION: nsWindow construction and destruction
692 **************************************************************/
694 nsWindow::nsWindow(bool aIsChildWindow
)
695 : nsBaseWidget(eBorderStyle_default
),
696 mBrush(::CreateSolidBrush(NSRGB_2_COLOREF(::GetSysColor(COLOR_BTNFACE
)))),
697 mFrameState(std::in_place
, this),
698 mIsChildWindow(aIsChildWindow
),
699 mLastPaintEndTime(TimeStamp::Now()),
700 mCachedHitTestTime(TimeStamp::Now()),
701 mSizeConstraintsScale(GetDefaultScale().scale
),
702 mDesktopId("DesktopIdMutex") {
703 MOZ_ASSERT(mWindowType
== eWindowType_child
);
705 if (!gInitializedVirtualDesktopManager
) {
706 TaskController::Get()->AddTask(
707 MakeAndAddRef
<InitializeVirtualDesktopManagerTask
>());
708 gInitializedVirtualDesktopManager
= true;
711 // Global initialization
712 if (!sInstanceCount
) {
713 // Global app registration id for Win7 and up. See
714 // WinTaskbar.cpp for details.
715 // MSIX packages explicitly do not support setting the appid from within
716 // the app, as it is set in the package manifest instead.
717 if (!WinUtils::HasPackageIdentity()) {
718 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
720 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
721 #if defined(ACCESSIBILITY)
722 mozilla::TIPMessageHandler::Initialize();
723 #endif // defined(ACCESSIBILITY)
724 if (SUCCEEDED(::OleInitialize(nullptr))) {
725 sIsOleInitialized
= TRUE
;
727 NS_ASSERTION(sIsOleInitialized
, "***** OLE is not initialized!\n");
728 MouseScrollHandler::Initialize();
730 nsUXThemeData::UpdateNativeThemeInfo();
731 RedirectedKeyDownMessageManager::Forget();
732 if (mPointerEvents
.ShouldEnableInkCollector()) {
733 InkCollector::sInkCollector
= new InkCollector();
740 nsWindow::~nsWindow() {
743 // If the widget was released without calling Destroy() then the native window
744 // still exists, and we need to destroy it. Destroy() will early-return if it
745 // was already called. In any case it is important to call it before
746 // destroying mPresentLock (cf. 1156182).
749 // Free app icon resources. This must happen after `OnDestroy` (see bug
751 if (mIconSmall
) ::DestroyIcon(mIconSmall
);
753 if (mIconBig
) ::DestroyIcon(mIconBig
);
758 if (sInstanceCount
== 0) {
759 if (InkCollector::sInkCollector
) {
760 InkCollector::sInkCollector
->Shutdown();
761 InkCollector::sInkCollector
= nullptr;
763 IMEHandler::Terminate();
765 if (sIsOleInitialized
) {
766 ::OleFlushClipboard();
768 sIsOleInitialized
= FALSE
;
772 NS_IF_RELEASE(mNativeDragTarget
);
775 /**************************************************************
777 * SECTION: nsIWidget::Create, nsIWidget::Destroy
779 * Creating and destroying windows for this widget.
781 **************************************************************/
783 // Allow Derived classes to modify the height that is passed
784 // when the window is created or resized.
785 int32_t nsWindow::GetHeight(int32_t aProposedHeight
) { return aProposedHeight
; }
787 static bool ShouldCacheTitleBarInfo(nsWindowType aWindowType
,
788 nsBorderStyle aBorderStyle
) {
789 return (aWindowType
== eWindowType_toplevel
) &&
790 (aBorderStyle
== eBorderStyle_default
||
791 aBorderStyle
== eBorderStyle_all
) &&
792 (!nsUXThemeData::sTitlebarInfoPopulatedThemed
||
793 !nsUXThemeData::sTitlebarInfoPopulatedAero
);
796 void nsWindow::SendAnAPZEvent(InputData
& aEvent
) {
797 LRESULT popupHandlingResult
;
798 if (DealWithPopups(mWnd
, MOZ_WM_DMANIP
, 0, 0, &popupHandlingResult
)) {
799 // We need to consume the event after using it to roll up the popup(s).
803 if (mSwipeTracker
&& aEvent
.mInputType
== PANGESTURE_INPUT
) {
804 // Give the swipe tracker a first pass at the event. If a new pan gesture
805 // has been started since the beginning of the swipe, the swipe tracker
806 // will know to ignore the event.
807 nsEventStatus status
=
808 mSwipeTracker
->ProcessEvent(aEvent
.AsPanGestureInput());
809 if (status
== nsEventStatus_eConsumeNoDefault
) {
814 APZEventResult result
;
816 result
= mAPZC
->InputBridge()->ReceiveInputEvent(aEvent
);
818 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
822 MOZ_ASSERT(aEvent
.mInputType
== PANGESTURE_INPUT
||
823 aEvent
.mInputType
== PINCHGESTURE_INPUT
);
825 if (aEvent
.mInputType
== PANGESTURE_INPUT
) {
826 PanGestureInput
& panInput
= aEvent
.AsPanGestureInput();
827 WidgetWheelEvent event
= panInput
.ToWidgetEvent(this);
828 bool canTriggerSwipe
= SwipeTracker::CanTriggerSwipe(panInput
);
830 if (MayStartSwipeForNonAPZ(panInput
, CanTriggerSwipe
{canTriggerSwipe
})) {
834 event
= MayStartSwipeForAPZ(panInput
, result
,
835 CanTriggerSwipe
{canTriggerSwipe
});
838 ProcessUntransformedAPZEvent(&event
, result
);
843 PinchGestureInput
& pinchInput
= aEvent
.AsPinchGestureInput();
844 WidgetWheelEvent event
= pinchInput
.ToWidgetEvent(this);
845 ProcessUntransformedAPZEvent(&event
, result
);
848 void nsWindow::RecreateDirectManipulationIfNeeded() {
849 DestroyDirectManipulation();
851 if (mWindowType
!= eWindowType_toplevel
&& mWindowType
!= eWindowType_popup
) {
855 if (!(StaticPrefs::apz_allow_zooming() ||
856 StaticPrefs::apz_windows_use_direct_manipulation()) ||
857 StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
861 if (!IsWin10OrLater()) {
862 // Chrome source said the Windows Direct Manipulation implementation had
863 // important bugs until Windows 10 (although IE on Windows 8.1 seems to use
864 // Direct Manipulation).
868 mDmOwner
= MakeUnique
<DirectManipulationOwner
>(this);
870 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
871 GetHeight(mBounds
.Height()));
872 mDmOwner
->Init(bounds
);
875 void nsWindow::ResizeDirectManipulationViewport() {
877 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
878 GetHeight(mBounds
.Height()));
879 mDmOwner
->ResizeViewport(bounds
);
883 void nsWindow::DestroyDirectManipulation() {
890 // Create the proper widget
891 nsresult
nsWindow::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
892 const LayoutDeviceIntRect
& aRect
,
893 nsWidgetInitData
* aInitData
) {
894 nsWidgetInitData defaultInitData
;
895 if (!aInitData
) aInitData
= &defaultInitData
;
897 nsIWidget
* baseParent
=
898 aInitData
->mWindowType
== eWindowType_dialog
||
899 aInitData
->mWindowType
== eWindowType_toplevel
||
900 aInitData
->mWindowType
== eWindowType_invisible
904 mIsTopWidgetWindow
= (nullptr == baseParent
);
907 // Ensure that the toolkit is created.
908 nsToolkit::GetToolkit();
910 BaseCreate(baseParent
, aInitData
);
913 if (aParent
) { // has a nsIWidget parent
914 parent
= aParent
? (HWND
)aParent
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
916 } else { // has a nsNative parent
917 parent
= (HWND
)aNativeParent
;
919 aNativeParent
? WinUtils::GetNSWindowPtr((HWND
)aNativeParent
) : nullptr;
922 mIsRTL
= aInitData
->mRTL
;
923 mOpeningAnimationSuppressed
= aInitData
->mIsAnimationSuppressed
;
924 mAlwaysOnTop
= aInitData
->mAlwaysOnTop
;
925 mResizable
= aInitData
->mResizable
;
927 DWORD style
= WindowStyle();
928 DWORD extendedStyle
= WindowExStyle();
930 // When window is PiP window on Windows7, WS_EX_COMPOSITED is set to suppress
931 // flickering during resizing with hardware acceleration.
932 bool isPIPWindow
= aInitData
&& aInitData
->mPIPWindow
;
933 if (isPIPWindow
&& !IsWin8OrLater() &&
934 gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING
) &&
935 WidgetTypeSupportsAcceleration()) {
936 extendedStyle
|= WS_EX_COMPOSITED
;
939 if (mWindowType
== eWindowType_popup
) {
944 if (!IsWin8OrLater() && HasBogusPopupsDropShadowOnMultiMonitor() &&
945 ShouldUseOffMainThreadCompositing()) {
946 extendedStyle
|= WS_EX_COMPOSITED
;
949 if (aInitData
->mMouseTransparent
) {
950 // This flag makes the window transparent to mouse events
951 mMouseTransparent
= true;
952 extendedStyle
|= WS_EX_TRANSPARENT
;
954 } else if (mWindowType
== eWindowType_invisible
) {
955 // Make sure CreateWindowEx succeeds at creating a toplevel window
956 style
&= ~0x40000000; // WS_CHILDWINDOW
958 // See if the caller wants to explictly set clip children and clip siblings
959 if (aInitData
->clipChildren
) {
960 style
|= WS_CLIPCHILDREN
;
962 style
&= ~WS_CLIPCHILDREN
;
964 if (aInitData
->clipSiblings
) {
965 style
|= WS_CLIPSIBLINGS
;
969 const wchar_t* className
;
970 if (aInitData
->mDropShadow
) {
971 className
= GetWindowPopupClass();
973 className
= GetWindowClass();
976 if (aInitData
->mWindowType
== eWindowType_toplevel
&& !aParent
&&
977 !sFirstTopLevelWindowCreated
) {
978 sFirstTopLevelWindowCreated
= true;
979 mWnd
= ConsumePreXULSkeletonUIHandle();
980 auto skeletonUIError
= GetPreXULSkeletonUIErrorReason();
981 if (skeletonUIError
) {
982 nsAutoString
errorString(
983 GetPreXULSkeletonUIErrorString(skeletonUIError
.value()));
984 Telemetry::ScalarSet(
985 Telemetry::ScalarID::STARTUP_SKELETON_UI_DISABLED_REASON
,
989 MOZ_ASSERT(style
== kPreXULSkeletonUIWindowStyle
,
990 "The skeleton UI window style should match the expected "
991 "style for the first window created");
992 MOZ_ASSERT(extendedStyle
== kPreXULSkeletonUIWindowStyleEx
,
993 "The skeleton UI window extended style should match the "
994 "expected extended style for the first window created");
995 mIsShowingPreXULSkeletonUI
= true;
997 // If we successfully consumed the pre-XUL skeleton UI, just update
998 // our internal state to match what is currently being displayed.
1000 mFrameState
->ConsumePreXULSkeletonState(WasPreXULSkeletonUIMaximized());
1002 // These match the margins set in browser-tabsintitlebar.js with
1003 // default prefs on Windows. Bug 1673092 tracks lining this up with
1004 // that more correctly instead of hard-coding it.
1005 LayoutDeviceIntMargin
margins(0, 2, 2, 2);
1006 SetNonClientMargins(margins
);
1008 // Reset the WNDPROC for this window and its whole class, as we had
1009 // to use our own WNDPROC when creating the the skeleton UI window.
1010 ::SetWindowLongPtrW(mWnd
, GWLP_WNDPROC
,
1011 reinterpret_cast<LONG_PTR
>(
1012 WinUtils::NonClientDpiScalingDefWindowProcW
));
1013 ::SetClassLongPtrW(mWnd
, GCLP_WNDPROC
,
1014 reinterpret_cast<LONG_PTR
>(
1015 WinUtils::NonClientDpiScalingDefWindowProcW
));
1021 ::CreateWindowExW(extendedStyle
, className
, L
"", style
, aRect
.X(),
1022 aRect
.Y(), aRect
.Width(), GetHeight(aRect
.Height()),
1023 parent
, nullptr, nsToolkit::mDllInstance
, nullptr);
1027 NS_WARNING("nsWindow CreateWindowEx failed.");
1028 return NS_ERROR_FAILURE
;
1031 if (aInitData
->mIsPrivate
) {
1032 if (Preferences::GetBool("browser.privacySegmentation.enabled", false)) {
1033 RefPtr
<IPropertyStore
> pPropStore
;
1034 if (!FAILED(SHGetPropertyStoreForWindow(mWnd
, IID_IPropertyStore
,
1035 getter_AddRefs(pPropStore
)))) {
1038 // make sure we're using the private browsing AUMID so that taskbar
1039 // grouping works properly
1040 Unused
<< NS_WARN_IF(
1041 !mozilla::widget::WinTaskbar::GenerateAppUserModelID(aumid
, true));
1042 if (!FAILED(InitPropVariantFromString(aumid
.get(), &pv
))) {
1043 if (!FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
))) {
1044 pPropStore
->Commit();
1047 PropVariantClear(&pv
);
1050 HICON icon
= ::LoadIconW(::GetModuleHandleW(nullptr),
1051 MAKEINTRESOURCEW(IDI_PBMODE
));
1057 mDeviceNotifyHandle
= InputDeviceUtils::RegisterNotification(mWnd
);
1059 // If mDefaultScale is set before mWnd has been set, it will have the scale of
1060 // the primary monitor, rather than the monitor that the window is actually
1061 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
1062 // which resets mDefaultScale, but for popup windows we don't reset
1063 // mDefaultScale on that message. In order to ensure that popup windows
1064 // spawned on a non-primary monitor end up with the correct scale, we reset
1065 // mDefaultScale here so that it gets recomputed using the correct monitor now
1066 // that we have a mWnd.
1067 mDefaultScale
= -1.0;
1070 DWORD dwAttribute
= TRUE
;
1071 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1072 sizeof dwAttribute
);
1075 UpdateDarkModeToolbar();
1077 if (mOpeningAnimationSuppressed
) {
1078 SuppressAnimation(true);
1082 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1083 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1086 if (mWindowType
!= eWindowType_invisible
&&
1087 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
1088 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
1090 // We create two zero-sized windows as descendants of the top-level window,
1093 // Top-level window (MozillaWindowClass)
1094 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
1095 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
1097 // We need to have the middle window, otherwise the Trackpoint driver
1098 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
1099 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
1100 // window hierarchy until they are handled by nsWindow::WindowProc.
1101 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
1102 // but these do not propagate automatically, so we have the window
1103 // procedure pretend that they were dispatched to the top-level window
1106 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
1107 // is given below so that it catches the Trackpoint driver's heuristics.
1108 HWND scrollContainerWnd
= ::CreateWindowW(
1109 className
, L
"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD
| WS_VISIBLE
, 0,
1110 0, 0, 0, mWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1111 HWND scrollableWnd
= ::CreateWindowW(
1112 className
, L
"FAKETRACKPOINTSCROLLABLE",
1113 WS_CHILD
| WS_VISIBLE
| WS_VSCROLL
| WS_TABSTOP
| 0x30, 0, 0, 0, 0,
1114 scrollContainerWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1116 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
1117 // WindowProcInternal can distinguish it from the top-level window
1119 ::SetWindowLongPtrW(scrollableWnd
, GWLP_ID
, eFakeTrackPointScrollableID
);
1121 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
1122 // old window procedure in its "user data".
1123 WNDPROC oldWndProc
= (WNDPROC
)::SetWindowLongPtrW(
1124 scrollableWnd
, GWLP_WNDPROC
, (LONG_PTR
)nsWindow::WindowProc
);
1125 ::SetWindowLongPtrW(scrollableWnd
, GWLP_USERDATA
, (LONG_PTR
)oldWndProc
);
1128 SubclassWindow(TRUE
);
1130 // Starting with Windows XP, a process always runs within a terminal services
1131 // session. In order to play nicely with RDP, fast user switching, and the
1132 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
1133 // our HWND in order to receive this message.
1134 DebugOnly
<BOOL
> wtsRegistered
=
1135 ::WTSRegisterSessionNotification(mWnd
, NOTIFY_FOR_THIS_SESSION
);
1136 NS_ASSERTION(wtsRegistered
, "WTSRegisterSessionNotification failed!\n");
1138 mDefaultIMC
.Init(this);
1139 IMEHandler::InitInputContext(this, mInputContext
);
1141 // Do some initialization work, but only if (a) it hasn't already been done,
1142 // and (b) this is the hidden window (which is conveniently created before
1143 // any visible windows but after the profile has been initialized).
1144 if (!sHaveInitializedPrefs
&& mWindowType
== eWindowType_invisible
) {
1145 sSwitchKeyboardLayout
=
1146 Preferences::GetBool("intl.keyboard.per_window_layout", false);
1147 sHaveInitializedPrefs
= true;
1150 // Query for command button metric data for rendering the titlebar. We
1151 // only do this once on the first window that has an actual titlebar
1152 if (ShouldCacheTitleBarInfo(mWindowType
, mBorderStyle
)) {
1153 nsUXThemeData::UpdateTitlebarInfo(mWnd
);
1156 static bool a11yPrimed
= false;
1157 if (!a11yPrimed
&& mWindowType
== eWindowType_toplevel
) {
1159 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
1160 ::PostMessage(mWnd
, MOZ_WM_STARTA11Y
, 0, 0);
1164 RecreateDirectManipulationIfNeeded();
1169 void nsWindow::LocalesChanged() {
1170 bool isRTL
= intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1171 if (mIsRTL
!= isRTL
) {
1172 DWORD dwAttribute
= isRTL
;
1173 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1174 sizeof dwAttribute
);
1179 // Close this nsWindow
1180 void nsWindow::Destroy() {
1181 // WM_DESTROY has already fired, avoid calling it twice
1182 if (mOnDestroyCalled
) return;
1184 // Don't destroy windows that have file pickers open, we'll tear these down
1185 // later once the picker is closed.
1186 mDestroyCalled
= true;
1187 if (mPickerDisplayCount
) return;
1189 // During the destruction of all of our children, make sure we don't get
1191 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1193 DestroyDirectManipulation();
1196 * On windows the LayerManagerOGL destructor wants the widget to be around for
1197 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
1199 DestroyLayerManager();
1201 InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle
);
1202 mDeviceNotifyHandle
= nullptr;
1204 // The DestroyWindow function destroys the specified window. The function
1205 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
1206 // and remove the keyboard focus from it. The function also destroys the
1207 // window's menu, flushes the thread message queue, destroys timers, removes
1208 // clipboard ownership, and breaks the clipboard viewer chain (if the window
1209 // is at the top of the viewer chain).
1211 // If the specified window is a parent or owner window, DestroyWindow
1212 // automatically destroys the associated child or owned windows when it
1213 // destroys the parent or owner window. The function first destroys child or
1214 // owned windows, and then it destroys the parent or owner window.
1215 VERIFY(::DestroyWindow(mWnd
));
1217 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
1218 // OnDestroy() didn't get called, call it now.
1219 if (false == mOnDestroyCalled
) {
1220 MSGResult msgResult
;
1221 mWindowHook
.Notify(mWnd
, WM_DESTROY
, 0, 0, msgResult
);
1226 /**************************************************************
1228 * SECTION: Window class utilities
1230 * Utilities for calculating the proper window class name for
1233 **************************************************************/
1235 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName
,
1237 LPWSTR aIconID
) const {
1239 if (::GetClassInfoW(nsToolkit::mDllInstance
, aClassName
, &wc
)) {
1240 // already registered
1244 wc
.style
= CS_DBLCLKS
| aExtraStyle
;
1245 wc
.lpfnWndProc
= WinUtils::NonClientDpiScalingDefWindowProcW
;
1248 wc
.hInstance
= nsToolkit::mDllInstance
;
1250 aIconID
? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID
) : nullptr;
1251 wc
.hCursor
= nullptr;
1252 wc
.hbrBackground
= mBrush
;
1253 wc
.lpszMenuName
= nullptr;
1254 wc
.lpszClassName
= aClassName
;
1256 if (!::RegisterClassW(&wc
)) {
1257 // For older versions of Win32 (i.e., not XP), the registration may
1258 // fail with aExtraStyle, so we have to re-register without it.
1259 wc
.style
= CS_DBLCLKS
;
1260 ::RegisterClassW(&wc
);
1265 static LPWSTR
const gStockApplicationIcon
= MAKEINTRESOURCEW(32512);
1267 // Return the proper window class for everything except popups.
1268 const wchar_t* nsWindow::GetWindowClass() const {
1269 switch (mWindowType
) {
1270 case eWindowType_invisible
:
1271 return RegisterWindowClass(kClassNameHidden
, 0, gStockApplicationIcon
);
1272 case eWindowType_dialog
:
1273 return RegisterWindowClass(kClassNameDialog
, 0, 0);
1275 return RegisterWindowClass(GetMainWindowClass(), 0,
1276 gStockApplicationIcon
);
1280 // Return the proper popup window class
1281 const wchar_t* nsWindow::GetWindowPopupClass() const {
1282 return RegisterWindowClass(kClassNameDropShadow
, CS_XP_DROPSHADOW
,
1283 gStockApplicationIcon
);
1286 /**************************************************************
1288 * SECTION: Window styles utilities
1290 * Return the proper windows styles and extended styles.
1292 **************************************************************/
1294 // Return nsWindow styles
1295 DWORD
nsWindow::WindowStyle() {
1298 switch (mWindowType
) {
1299 case eWindowType_child
:
1300 style
= WS_OVERLAPPED
;
1303 case eWindowType_dialog
:
1304 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
| DS_3DLOOK
|
1305 DS_MODALFRAME
| WS_CLIPCHILDREN
;
1306 if (mBorderStyle
!= eBorderStyle_default
)
1307 style
|= WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
;
1310 case eWindowType_popup
:
1313 style
|= WS_OVERLAPPED
;
1318 NS_ERROR("unknown border style");
1321 case eWindowType_toplevel
:
1322 case eWindowType_invisible
:
1323 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
|
1324 WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_CLIPCHILDREN
;
1328 if (mBorderStyle
!= eBorderStyle_default
&&
1329 mBorderStyle
!= eBorderStyle_all
) {
1330 if (mBorderStyle
== eBorderStyle_none
||
1331 !(mBorderStyle
& eBorderStyle_border
))
1332 style
&= ~WS_BORDER
;
1334 if (mBorderStyle
== eBorderStyle_none
||
1335 !(mBorderStyle
& eBorderStyle_title
)) {
1336 style
&= ~WS_DLGFRAME
;
1341 if (mBorderStyle
== eBorderStyle_none
||
1342 !(mBorderStyle
& eBorderStyle_close
))
1344 // XXX The close box can only be removed by changing the window class,
1345 // as far as I know --- roc+moz@cs.cmu.edu
1347 if (mBorderStyle
== eBorderStyle_none
||
1348 !(mBorderStyle
& (eBorderStyle_menu
| eBorderStyle_close
)))
1349 style
&= ~WS_SYSMENU
;
1350 // Looks like getting rid of the system menu also does away with the
1351 // close box. So, we only get rid of the system menu if you want neither it
1352 // nor the close box. How does the Windows "Dialog" window class get just
1353 // closebox and no sysmenu? Who knows.
1355 if (mBorderStyle
== eBorderStyle_none
||
1356 !(mBorderStyle
& eBorderStyle_resizeh
))
1357 style
&= ~WS_THICKFRAME
;
1359 if (mBorderStyle
== eBorderStyle_none
||
1360 !(mBorderStyle
& eBorderStyle_minimize
))
1361 style
&= ~WS_MINIMIZEBOX
;
1363 if (mBorderStyle
== eBorderStyle_none
||
1364 !(mBorderStyle
& eBorderStyle_maximize
))
1365 style
&= ~WS_MAXIMIZEBOX
;
1367 if (IsPopupWithTitleBar()) {
1368 style
|= WS_CAPTION
;
1369 if (mBorderStyle
& eBorderStyle_close
) {
1370 style
|= WS_SYSMENU
;
1375 if (mIsChildWindow
) {
1376 style
|= WS_CLIPCHILDREN
;
1377 if (!(style
& WS_POPUP
)) {
1378 style
|= WS_CHILD
; // WS_POPUP and WS_CHILD are mutually exclusive.
1382 VERIFY_WINDOW_STYLE(style
);
1386 // Return nsWindow extended styles
1387 DWORD
nsWindow::WindowExStyle() {
1388 switch (mWindowType
) {
1389 case eWindowType_child
:
1392 case eWindowType_dialog
:
1393 return WS_EX_WINDOWEDGE
| WS_EX_DLGMODALFRAME
;
1395 case eWindowType_popup
: {
1396 DWORD extendedStyle
= WS_EX_TOOLWINDOW
;
1397 if (mPopupLevel
== ePopupLevelTop
) extendedStyle
|= WS_EX_TOPMOST
;
1398 return extendedStyle
;
1401 NS_ERROR("unknown border style");
1404 case eWindowType_toplevel
:
1405 case eWindowType_invisible
:
1406 return WS_EX_WINDOWEDGE
;
1410 /**************************************************************
1412 * SECTION: Window subclassing utilities
1414 * Set or clear window subclasses on native windows. Used in
1415 * Create and Destroy.
1417 **************************************************************/
1419 // Subclass (or remove the subclass from) this component's nsWindow
1420 void nsWindow::SubclassWindow(BOOL bState
) {
1422 if (!mWnd
|| !IsWindow(mWnd
)) {
1423 NS_ERROR("Invalid window handle");
1426 mPrevWndProc
= reinterpret_cast<WNDPROC
>(SetWindowLongPtrW(
1427 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(nsWindow::WindowProc
)));
1428 NS_ASSERTION(mPrevWndProc
, "Null standard window procedure");
1429 // connect the this pointer to the nsWindow handle
1430 WinUtils::SetNSWindowBasePtr(mWnd
, this);
1432 if (IsWindow(mWnd
)) {
1433 SetWindowLongPtrW(mWnd
, GWLP_WNDPROC
,
1434 reinterpret_cast<LONG_PTR
>(mPrevWndProc
));
1436 WinUtils::SetNSWindowBasePtr(mWnd
, nullptr);
1437 mPrevWndProc
= nullptr;
1441 /**************************************************************
1443 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1445 * Set or clear the parent widgets using window properties, and
1446 * handles calculating native parent handles.
1448 **************************************************************/
1450 // Get and set parent widgets
1451 void nsWindow::SetParent(nsIWidget
* aNewParent
) {
1452 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1453 nsIWidget
* parent
= GetParent();
1455 parent
->RemoveChild(this);
1458 mParent
= aNewParent
;
1461 ReparentNativeWidget(aNewParent
);
1462 aNewParent
->AddChild(this);
1466 // If we have no parent, SetParent should return the desktop.
1467 VERIFY(::SetParent(mWnd
, nullptr));
1468 RecreateDirectManipulationIfNeeded();
1472 void nsWindow::ReparentNativeWidget(nsIWidget
* aNewParent
) {
1473 MOZ_ASSERT(aNewParent
, "null widget");
1475 mParent
= aNewParent
;
1476 if (mWindowType
== eWindowType_popup
) {
1479 HWND newParent
= (HWND
)aNewParent
->GetNativeData(NS_NATIVE_WINDOW
);
1480 NS_ASSERTION(newParent
, "Parent widget has a null native window handle");
1481 if (newParent
&& mWnd
) {
1482 ::SetParent(mWnd
, newParent
);
1483 RecreateDirectManipulationIfNeeded();
1487 nsIWidget
* nsWindow::GetParent(void) {
1488 if (mIsTopWidgetWindow
) {
1491 if (mInDtor
|| mOnDestroyCalled
) {
1497 static int32_t RoundDown(double aDouble
) {
1498 return aDouble
> 0 ? static_cast<int32_t>(floor(aDouble
))
1499 : static_cast<int32_t>(ceil(aDouble
));
1502 float nsWindow::GetDPI() {
1504 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1506 screen
->GetDpi(&dpi
);
1511 double nsWindow::GetDefaultScaleInternal() {
1512 if (mDefaultScale
<= 0.0) {
1513 mDefaultScale
= WinUtils::LogToPhysFactor(mWnd
);
1515 return mDefaultScale
;
1518 int32_t nsWindow::LogToPhys(double aValue
) {
1519 return WinUtils::LogToPhys(
1520 ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTOPRIMARY
), aValue
);
1523 nsWindow
* nsWindow::GetParentWindow(bool aIncludeOwner
) {
1524 return static_cast<nsWindow
*>(GetParentWindowBase(aIncludeOwner
));
1527 nsWindow
* nsWindow::GetParentWindowBase(bool aIncludeOwner
) {
1528 if (mIsTopWidgetWindow
) {
1529 // Must use a flag instead of mWindowType to tell if the window is the
1530 // owned by the topmost widget, because a child window can be embedded
1531 // inside a HWND which is not associated with a nsIWidget.
1535 // If this widget has already been destroyed, pretend we have no parent.
1536 // This corresponds to code in Destroy which removes the destroyed
1537 // widget from its parent's child list.
1538 if (mInDtor
|| mOnDestroyCalled
) return nullptr;
1540 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1541 // root owner. aIncludeOwner set to false implies the search will stop at the
1542 // true parent (default).
1543 nsWindow
* widget
= nullptr;
1545 HWND parent
= nullptr;
1547 parent
= ::GetParent(mWnd
);
1549 parent
= ::GetAncestor(mWnd
, GA_PARENT
);
1552 widget
= WinUtils::GetNSWindowPtr(parent
);
1554 // If the widget is in the process of being destroyed then
1556 if (widget
->mInDtor
) {
1566 BOOL CALLBACK
nsWindow::EnumAllChildWindProc(HWND aWnd
, LPARAM aParam
) {
1567 nsWindow
* wnd
= WinUtils::GetNSWindowPtr(aWnd
);
1569 reinterpret_cast<nsTArray
<nsWindow
*>*>(aParam
)->AppendElement(wnd
);
1574 BOOL CALLBACK
nsWindow::EnumAllThreadWindowProc(HWND aWnd
, LPARAM aParam
) {
1575 nsWindow
* wnd
= WinUtils::GetNSWindowPtr(aWnd
);
1577 reinterpret_cast<nsTArray
<nsWindow
*>*>(aParam
)->AppendElement(wnd
);
1579 EnumChildWindows(aWnd
, EnumAllChildWindProc
, aParam
);
1584 nsTArray
<nsWindow
*> nsWindow::EnumAllWindows() {
1585 nsTArray
<nsWindow
*> windows
;
1586 EnumThreadWindows(GetCurrentThreadId(), EnumAllThreadWindowProc
,
1587 reinterpret_cast<LPARAM
>(&windows
));
1591 /**************************************************************
1593 * SECTION: nsIWidget::Show
1595 * Hide or show this component.
1597 **************************************************************/
1599 void nsWindow::Show(bool bState
) {
1600 if (bState
&& mIsShowingPreXULSkeletonUI
) {
1601 // The first time we decide to actually show the window is when we decide
1602 // that we've taken over the window from the skeleton UI, and we should
1603 // no longer treat resizes / moves specially.
1604 mIsShowingPreXULSkeletonUI
= false;
1605 // Initialize the UI state - this would normally happen below, but since
1606 // we're actually already showing, we won't hit it in the normal way.
1607 ::SendMessageW(mWnd
, WM_CHANGEUISTATE
,
1608 MAKEWPARAM(UIS_SET
, UISF_HIDEFOCUS
| UISF_HIDEACCEL
), 0);
1609 #if defined(ACCESSIBILITY)
1610 // If our HWND has focus and the a11y engine hasn't started yet, fire a
1611 // focus win event. Windows already did this when the skeleton UI appeared,
1612 // but a11y wouldn't have been able to start at that point even if a client
1613 // responded. Firing this now gives clients the chance to respond with
1614 // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
1615 // this if the a11y engine has already started because it has probably
1616 // already fired focus on a descendant.
1617 if (::GetFocus() == mWnd
&& !GetAccService()) {
1618 ::NotifyWinEvent(EVENT_OBJECT_FOCUS
, mWnd
, OBJID_CLIENT
, CHILDID_SELF
);
1620 #endif // defined(ACCESSIBILITY)
1623 if (mWindowType
== eWindowType_popup
) {
1624 // See bug 603793. When we try to draw D3D9/10 windows with a drop shadow
1625 // without the DWM on a secondary monitor, windows fails to composite
1626 // our windows correctly. We therefor switch off the drop shadow for
1627 // pop-up windows when the DWM is disabled and two monitors are
1629 if (HasBogusPopupsDropShadowOnMultiMonitor() &&
1630 WinUtils::GetMonitorCount() > 1 &&
1631 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
1632 if (sDropShadowEnabled
) {
1633 ::SetClassLongA(mWnd
, GCL_STYLE
, 0);
1634 sDropShadowEnabled
= false;
1637 if (!sDropShadowEnabled
) {
1638 ::SetClassLongA(mWnd
, GCL_STYLE
, CS_DROPSHADOW
);
1639 sDropShadowEnabled
= true;
1643 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1644 // some popup menus to become invisible.
1645 LONG_PTR exStyle
= ::GetWindowLongPtrW(mWnd
, GWL_EXSTYLE
);
1646 if (exStyle
& WS_EX_LAYERED
) {
1647 ::SetWindowLongPtrW(mWnd
, GWL_EXSTYLE
, exStyle
& ~WS_EX_COMPOSITED
);
1651 bool syncInvalidate
= false;
1653 bool wasVisible
= mIsVisible
;
1654 // Set the status now so that anyone asking during ShowWindow or
1655 // SetWindowPos would get the correct answer.
1656 mIsVisible
= bState
;
1658 // We may have cached an out of date visible state. This can happen
1659 // when session restore sets the full screen mode.
1661 mOldStyle
|= WS_VISIBLE
;
1663 mOldStyle
&= ~WS_VISIBLE
;
1667 if (!wasVisible
&& mWindowType
== eWindowType_toplevel
) {
1668 // speed up the initial paint after show for
1669 // top level windows:
1670 syncInvalidate
= true;
1672 // Set the cursor before showing the window to avoid the default wait
1674 SetCursor(Cursor
{eCursor_standard
});
1676 switch (mFrameState
->GetSizeMode()) {
1677 case nsSizeMode_Fullscreen
:
1678 ::ShowWindow(mWnd
, SW_SHOW
);
1680 case nsSizeMode_Maximized
:
1681 ::ShowWindow(mWnd
, SW_SHOWMAXIMIZED
);
1683 case nsSizeMode_Minimized
:
1684 ::ShowWindow(mWnd
, SW_SHOWMINIMIZED
);
1687 if (CanTakeFocus() && !mAlwaysOnTop
) {
1688 ::ShowWindow(mWnd
, SW_SHOWNORMAL
);
1690 ::ShowWindow(mWnd
, SW_SHOWNOACTIVATE
);
1691 // Don't flicker the window if we're restoring session
1692 if (!sIsRestoringSession
) {
1693 Unused
<< GetAttention(2);
1699 DWORD flags
= SWP_NOSIZE
| SWP_NOMOVE
| SWP_SHOWWINDOW
;
1700 if (wasVisible
) flags
|= SWP_NOZORDER
;
1701 if (mAlwaysOnTop
) flags
|= SWP_NOACTIVATE
;
1703 if (mWindowType
== eWindowType_popup
) {
1704 // ensure popups are the topmost of the TOPMOST
1705 // layer. Remember not to set the SWP_NOZORDER
1706 // flag as that might allow the taskbar to overlap
1708 flags
|= SWP_NOACTIVATE
;
1709 HWND owner
= ::GetWindow(mWnd
, GW_OWNER
);
1711 // ePopupLevelTop popups should be above all else. All other
1712 // types should be placed in front of their owner, without
1713 // changing the owner's z-level relative to other windows.
1714 if (PopupLevel() != ePopupLevelTop
) {
1715 ::SetWindowPos(mWnd
, owner
, 0, 0, 0, 0, flags
);
1716 ::SetWindowPos(owner
, mWnd
, 0, 0, 0, 0,
1717 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1719 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1722 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0, flags
);
1725 if (mWindowType
== eWindowType_dialog
&& !CanTakeFocus())
1726 flags
|= SWP_NOACTIVATE
;
1728 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1732 if (!wasVisible
&& (mWindowType
== eWindowType_toplevel
||
1733 mWindowType
== eWindowType_dialog
)) {
1734 // When a toplevel window or dialog is shown, initialize the UI state
1735 ::SendMessageW(mWnd
, WM_CHANGEUISTATE
,
1736 MAKEWPARAM(UIS_SET
, UISF_HIDEFOCUS
| UISF_HIDEACCEL
), 0);
1739 // Clear contents to avoid ghosting of old content if we display
1740 // this window again.
1741 if (wasVisible
&& mTransparencyMode
== eTransparencyTransparent
) {
1742 if (mCompositorWidgetDelegate
) {
1743 mCompositorWidgetDelegate
->ClearTransparentWindow();
1746 if (mWindowType
!= eWindowType_dialog
) {
1747 ::ShowWindow(mWnd
, SW_HIDE
);
1749 ::SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
1750 SWP_HIDEWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOZORDER
|
1756 if (!wasVisible
&& bState
) {
1758 if (syncInvalidate
&& !mInDtor
&& !mOnDestroyCalled
) {
1759 ::UpdateWindow(mWnd
);
1763 if (mOpeningAnimationSuppressed
) {
1764 SuppressAnimation(false);
1768 /**************************************************************
1770 * SECTION: nsIWidget::IsVisible
1772 * Returns the visibility state.
1774 **************************************************************/
1776 // Return true if the whether the component is visible, false otherwise
1777 bool nsWindow::IsVisible() const { return mIsVisible
; }
1779 /**************************************************************
1781 * SECTION: Window clipping utilities
1783 * Used in Size and Move operations for setting the proper
1784 * window clipping regions for window transparency.
1786 **************************************************************/
1788 static bool ShouldHaveRoundedMenuDropShadow(nsWindow
* aWindow
) {
1789 nsView
* view
= nsView::GetViewFor(aWindow
);
1790 return view
&& view
->GetFrame() &&
1791 view
->GetFrame()->StyleUIReset()->mWindowShadow
==
1792 StyleWindowShadow::Cliprounded
;
1795 // XP and Vista visual styles sometimes require window clipping regions to be
1796 // applied for proper transparency. These routines are called on size and move
1798 // XXX this is apparently still needed in Windows 7 and later
1799 void nsWindow::ClearThemeRegion() {
1800 if (mWindowType
== eWindowType_popup
&&
1801 (mPopupType
== ePopupTypeMenu
|| mPopupType
== ePopupTypePanel
) &&
1802 ShouldHaveRoundedMenuDropShadow(this)) {
1803 SetWindowRgn(mWnd
, nullptr, false);
1804 } else if (!HasGlass() &&
1805 (mWindowType
== eWindowType_popup
&& !IsPopupWithTitleBar() &&
1806 (mPopupType
== ePopupTypeTooltip
||
1807 mPopupType
== ePopupTypePanel
))) {
1808 SetWindowRgn(mWnd
, nullptr, false);
1812 void nsWindow::SetThemeRegion() {
1813 // Clip the window to the rounded rect area of the popup if needed.
1814 if (mWindowType
== eWindowType_popup
&&
1815 (mPopupType
== ePopupTypeMenu
|| mPopupType
== ePopupTypePanel
)) {
1816 nsView
* view
= nsView::GetViewFor(this);
1818 LayoutDeviceIntSize size
=
1819 nsLayoutUtils::GetBorderRadiusForMenuDropShadow(view
->GetFrame());
1820 if (size
.width
|| size
.height
) {
1822 NSToIntRound(size
.width
* GetDesktopToDeviceScale().scale
);
1824 NSToIntRound(size
.height
* GetDesktopToDeviceScale().scale
);
1825 HRGN region
= CreateRoundRectRgn(0, 0, mBounds
.Width() + 1,
1826 mBounds
.Height() + 1, width
, height
);
1827 if (!SetWindowRgn(mWnd
, region
, false)) {
1828 DeleteObject(region
); // region setting failed so delete the region.
1834 // Popup types that have a visual styles region applied (bug 376408). This can
1835 // be expanded for other window types as needed. The regions are applied
1836 // generically to the base window so default constants are used for part and
1837 // state. At some point we might need part and state values from
1838 // nsNativeThemeWin's GetThemePartAndState, but currently windows that change
1839 // shape based on state haven't come up.
1840 else if (!HasGlass() &&
1841 (mWindowType
== eWindowType_popup
&& !IsPopupWithTitleBar() &&
1842 (mPopupType
== ePopupTypeTooltip
||
1843 mPopupType
== ePopupTypePanel
))) {
1844 HRGN hRgn
= nullptr;
1845 RECT rect
= {0, 0, mBounds
.Width(), mBounds
.Height()};
1847 HDC dc
= ::GetDC(mWnd
);
1848 GetThemeBackgroundRegion(nsUXThemeData::GetTheme(eUXTooltip
), dc
,
1849 TTP_STANDARD
, TS_NORMAL
, &rect
, &hRgn
);
1851 if (!SetWindowRgn(mWnd
, hRgn
,
1852 false)) // do not delete or alter hRgn if accepted.
1855 ::ReleaseDC(mWnd
, dc
);
1859 /**************************************************************
1861 * SECTION: Touch and APZ-related functions
1863 **************************************************************/
1865 void nsWindow::RegisterTouchWindow() {
1866 mTouchWindow
= true;
1867 ::RegisterTouchWindow(mWnd
, TWF_WANTPALM
);
1868 ::EnumChildWindows(mWnd
, nsWindow::RegisterTouchForDescendants
, 0);
1871 BOOL CALLBACK
nsWindow::RegisterTouchForDescendants(HWND aWnd
, LPARAM aMsg
) {
1872 nsWindow
* win
= WinUtils::GetNSWindowPtr(aWnd
);
1874 ::RegisterTouchWindow(aWnd
, TWF_WANTPALM
);
1879 void nsWindow::LockAspectRatio(bool aShouldLock
) {
1881 mAspectRatio
= (float)mBounds
.Height() / (float)mBounds
.Width();
1887 /**************************************************************
1889 * SECTION: nsIWidget::SetWindowMouseTransparent
1891 * Sets whether the window should ignore mouse events.
1893 **************************************************************/
1894 void nsWindow::SetWindowMouseTransparent(bool aIsTransparent
) {
1899 LONG_PTR oldStyle
= ::GetWindowLongPtrW(mWnd
, GWL_EXSTYLE
);
1900 LONG_PTR newStyle
= aIsTransparent
? (oldStyle
| WS_EX_TRANSPARENT
)
1901 : (oldStyle
& ~WS_EX_TRANSPARENT
);
1902 ::SetWindowLongPtrW(mWnd
, GWL_EXSTYLE
, newStyle
);
1903 mMouseTransparent
= aIsTransparent
;
1906 /**************************************************************
1908 * SECTION: nsIWidget::Move, nsIWidget::Resize,
1909 * nsIWidget::Size, nsIWidget::BeginResizeDrag
1911 * Repositioning and sizing a window.
1913 **************************************************************/
1915 void nsWindow::SetSizeConstraints(const SizeConstraints
& aConstraints
) {
1916 SizeConstraints c
= aConstraints
;
1918 if (mWindowType
!= eWindowType_popup
&& mResizable
) {
1920 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK
)), c
.mMinSize
.width
);
1922 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK
)), c
.mMinSize
.height
);
1925 if (mMaxTextureSize
> 0) {
1926 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1927 // a window grow bigger as we won't be able to draw content there in
1929 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
1930 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
1933 mSizeConstraintsScale
= GetDefaultScale().scale
;
1935 nsBaseWidget::SetSizeConstraints(c
);
1938 const SizeConstraints
nsWindow::GetSizeConstraints() {
1939 double scale
= GetDefaultScale().scale
;
1940 if (mSizeConstraintsScale
== scale
|| mSizeConstraintsScale
== 0.0) {
1941 return mSizeConstraints
;
1943 scale
/= mSizeConstraintsScale
;
1944 SizeConstraints c
= mSizeConstraints
;
1945 if (c
.mMinSize
.width
!= NS_MAXSIZE
) {
1946 c
.mMinSize
.width
= NSToIntRound(c
.mMinSize
.width
* scale
);
1948 if (c
.mMinSize
.height
!= NS_MAXSIZE
) {
1949 c
.mMinSize
.height
= NSToIntRound(c
.mMinSize
.height
* scale
);
1951 if (c
.mMaxSize
.width
!= NS_MAXSIZE
) {
1952 c
.mMaxSize
.width
= NSToIntRound(c
.mMaxSize
.width
* scale
);
1954 if (c
.mMaxSize
.height
!= NS_MAXSIZE
) {
1955 c
.mMaxSize
.height
= NSToIntRound(c
.mMaxSize
.height
* scale
);
1960 // Move this component
1961 void nsWindow::Move(double aX
, double aY
) {
1962 if (mWindowType
== eWindowType_toplevel
||
1963 mWindowType
== eWindowType_dialog
) {
1964 SetSizeMode(nsSizeMode_Normal
);
1967 // for top-level windows only, convert coordinates from desktop pixels
1968 // (the "parent" coordinate space) to the window's device pixel space
1970 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1971 int32_t x
= NSToIntRound(aX
* scale
);
1972 int32_t y
= NSToIntRound(aY
* scale
);
1974 // Check to see if window needs to be moved first
1975 // to avoid a costly call to SetWindowPos. This check
1976 // can not be moved to the calling code in nsView, because
1977 // some platforms do not position child windows correctly
1979 // Only perform this check for non-popup windows, since the positioning can
1980 // in fact change even when the x/y do not. We always need to perform the
1981 // check. See bug #97805 for details.
1982 if (mWindowType
!= eWindowType_popup
&& mBounds
.IsEqualXY(x
, y
)) {
1983 // Nothing to do, since it is already positioned correctly.
1987 mBounds
.MoveTo(x
, y
);
1991 // complain if a window is moved offscreen (legal, but potentially
1993 if (mIsTopWidgetWindow
) { // only a problem for top-level windows
1994 // Make sure this window is actually on the screen before we move it
1995 // XXX: Needs multiple monitor support
1996 HDC dc
= ::GetDC(mWnd
);
1998 if (::GetDeviceCaps(dc
, TECHNOLOGY
) == DT_RASDISPLAY
) {
2000 ::SystemParametersInfo(SPI_GETWORKAREA
, 0, &workArea
, 0);
2001 // no annoying assertions. just mention the issue.
2002 if (x
< 0 || x
>= workArea
.right
|| y
< 0 || y
>= workArea
.bottom
) {
2003 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2004 ("window moved to offscreen position\n"));
2007 ::ReleaseDC(mWnd
, dc
);
2012 // Normally, when the skeleton UI is disabled, we resize+move the window
2013 // before showing it in order to ensure that it restores to the correct
2014 // position when the user un-maximizes it. However, when we are using the
2015 // skeleton UI, this results in the skeleton UI window being moved around
2016 // undesirably before being locked back into the maximized position. To
2017 // avoid this, we simply set the placement to restore to via
2018 // SetWindowPlacement. It's a little bit more of a dance, though, since we
2019 // need to convert the workspace coords that SetWindowPlacement uses to the
2020 // screen space coordinates we normally use with SetWindowPos.
2021 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2022 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2023 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2025 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2026 if (NS_WARN_IF(!monitor
)) {
2029 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2030 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2033 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2035 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2036 pl
.rcNormalPosition
.left
+= deltaX
;
2037 pl
.rcNormalPosition
.right
+= deltaX
;
2038 pl
.rcNormalPosition
.top
+= deltaY
;
2039 pl
.rcNormalPosition
.bottom
+= deltaY
;
2040 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2044 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOSIZE
;
2045 double oldScale
= mDefaultScale
;
2046 mResizeState
= IN_SIZEMOVE
;
2047 VERIFY(::SetWindowPos(mWnd
, nullptr, x
, y
, 0, 0, flags
));
2048 mResizeState
= NOT_RESIZING
;
2049 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2056 ResizeDirectManipulationViewport();
2058 NotifyRollupGeometryChange();
2061 // Resize this component
2062 void nsWindow::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
2063 // for top-level windows only, convert coordinates from desktop pixels
2064 // (the "parent" coordinate space) to the window's device pixel space
2066 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
2067 int32_t width
= NSToIntRound(aWidth
* scale
);
2068 int32_t height
= NSToIntRound(aHeight
* scale
);
2070 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
2071 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
2072 if (width
< 0 || height
< 0) {
2073 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << width
<< ", "
2074 << height
<< ") repaint: " << aRepaint
;
2077 ConstrainSize(&width
, &height
);
2079 // Avoid unnecessary resizing calls
2080 if (mBounds
.IsEqualSize(width
, height
)) {
2087 // Set cached value for lightweight and printing
2088 bool wasLocking
= mAspectRatio
!= 0.0;
2089 mBounds
.SizeTo(width
, height
);
2091 LockAspectRatio(true); // This causes us to refresh the mAspectRatio value
2095 // Refer to the comment above a similar check in nsWindow::Move
2096 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2097 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2098 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2099 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2100 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2101 mResizeState
= RESIZING
;
2102 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2103 mResizeState
= NOT_RESIZING
;
2105 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
;
2108 flags
|= SWP_NOREDRAW
;
2112 double oldScale
= mDefaultScale
;
2113 mResizeState
= RESIZING
;
2115 ::SetWindowPos(mWnd
, nullptr, 0, 0, width
, GetHeight(height
), flags
));
2117 mResizeState
= NOT_RESIZING
;
2118 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2124 ResizeDirectManipulationViewport();
2127 if (aRepaint
) Invalidate();
2129 NotifyRollupGeometryChange();
2132 // Resize this component
2133 void nsWindow::Resize(double aX
, double aY
, double aWidth
, double aHeight
,
2135 // for top-level windows only, convert coordinates from desktop pixels
2136 // (the "parent" coordinate space) to the window's device pixel space
2138 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
2139 int32_t x
= NSToIntRound(aX
* scale
);
2140 int32_t y
= NSToIntRound(aY
* scale
);
2141 int32_t width
= NSToIntRound(aWidth
* scale
);
2142 int32_t height
= NSToIntRound(aHeight
* scale
);
2144 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
2145 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
2146 if (width
< 0 || height
< 0) {
2147 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << x
<< " ," << y
2148 << ", " << width
<< ", " << height
2149 << ") repaint: " << aRepaint
;
2152 ConstrainSize(&width
, &height
);
2154 // Avoid unnecessary resizing calls
2155 if (mBounds
.IsEqualRect(x
, y
, width
, height
)) {
2162 // Set cached value for lightweight and printing
2163 mBounds
.SetRect(x
, y
, width
, height
);
2166 // Refer to the comment above a similar check in nsWindow::Move
2167 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2168 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2169 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2171 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2172 if (NS_WARN_IF(!monitor
)) {
2175 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2176 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2179 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2181 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2182 pl
.rcNormalPosition
.left
+= deltaX
;
2183 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2184 pl
.rcNormalPosition
.top
+= deltaY
;
2185 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2186 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2188 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
;
2190 flags
|= SWP_NOREDRAW
;
2195 double oldScale
= mDefaultScale
;
2196 mResizeState
= RESIZING
;
2198 ::SetWindowPos(mWnd
, nullptr, x
, y
, width
, GetHeight(height
), flags
));
2199 mResizeState
= NOT_RESIZING
;
2200 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2204 if (mTransitionWnd
) {
2205 // If we have a fullscreen transition window, we need to make
2206 // it topmost again, otherwise the taskbar may be raised by
2207 // the system unexpectedly when we leave fullscreen state.
2208 ::SetWindowPos(mTransitionWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
2209 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
2214 ResizeDirectManipulationViewport();
2217 if (aRepaint
) Invalidate();
2219 NotifyRollupGeometryChange();
2222 mozilla::Maybe
<bool> nsWindow::IsResizingNativeWidget() {
2223 if (mResizeState
== RESIZING
) {
2229 nsresult
nsWindow::BeginResizeDrag(WidgetGUIEvent
* aEvent
, int32_t aHorizontal
,
2230 int32_t aVertical
) {
2231 NS_ENSURE_ARG_POINTER(aEvent
);
2233 if (aEvent
->mClass
!= eMouseEventClass
) {
2234 // you can only begin a resize drag with a mouse event
2235 return NS_ERROR_INVALID_ARG
;
2238 if (aEvent
->AsMouseEvent()->mButton
!= MouseButton::ePrimary
) {
2239 // you can only begin a resize drag with the left mouse button
2240 return NS_ERROR_INVALID_ARG
;
2243 // work out what sizemode we're talking about
2245 if (aVertical
< 0) {
2246 if (aHorizontal
< 0) {
2247 syscommand
= SC_SIZE
| WMSZ_TOPLEFT
;
2248 } else if (aHorizontal
== 0) {
2249 syscommand
= SC_SIZE
| WMSZ_TOP
;
2251 syscommand
= SC_SIZE
| WMSZ_TOPRIGHT
;
2253 } else if (aVertical
== 0) {
2254 if (aHorizontal
< 0) {
2255 syscommand
= SC_SIZE
| WMSZ_LEFT
;
2256 } else if (aHorizontal
== 0) {
2257 return NS_ERROR_INVALID_ARG
;
2259 syscommand
= SC_SIZE
| WMSZ_RIGHT
;
2262 if (aHorizontal
< 0) {
2263 syscommand
= SC_SIZE
| WMSZ_BOTTOMLEFT
;
2264 } else if (aHorizontal
== 0) {
2265 syscommand
= SC_SIZE
| WMSZ_BOTTOM
;
2267 syscommand
= SC_SIZE
| WMSZ_BOTTOMRIGHT
;
2271 // resizing doesn't work if the mouse is already captured
2272 CaptureMouse(false);
2274 // find the top-level window
2275 HWND toplevelWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
2277 // tell Windows to start the resize
2278 ::PostMessage(toplevelWnd
, WM_SYSCOMMAND
, syscommand
,
2279 POINTTOPOINTS(aEvent
->mRefPoint
));
2284 /**************************************************************
2286 * SECTION: Window Z-order and state.
2288 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
2289 * nsIWidget::ConstrainPosition
2291 * Z-order, positioning, restore, minimize, and maximize.
2293 **************************************************************/
2295 // Position the window behind the given window
2296 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement
,
2297 nsIWidget
* aWidget
, bool aActivate
) {
2298 HWND behind
= HWND_TOP
;
2299 if (aPlacement
== eZPlacementBottom
)
2300 behind
= HWND_BOTTOM
;
2301 else if (aPlacement
== eZPlacementBelow
&& aWidget
)
2302 behind
= (HWND
)aWidget
->GetNativeData(NS_NATIVE_WINDOW
);
2303 UINT flags
= SWP_NOMOVE
| SWP_NOREPOSITION
| SWP_NOSIZE
;
2304 if (!aActivate
) flags
|= SWP_NOACTIVATE
;
2306 if (!CanTakeFocus() && behind
== HWND_TOP
) {
2307 // Can't place the window to top so place it behind the foreground window
2308 // (as long as it is not topmost)
2309 HWND wndAfter
= ::GetForegroundWindow();
2311 behind
= HWND_BOTTOM
;
2312 else if (!(GetWindowLongPtrW(wndAfter
, GWL_EXSTYLE
) & WS_EX_TOPMOST
))
2314 flags
|= SWP_NOACTIVATE
;
2317 ::SetWindowPos(mWnd
, behind
, 0, 0, 0, 0, flags
);
2320 static UINT
GetCurrentShowCmd(HWND aWnd
) {
2322 pl
.length
= sizeof(pl
);
2323 ::GetWindowPlacement(aWnd
, &pl
);
2327 // Maximize, minimize or restore the window.
2328 void nsWindow::SetSizeMode(nsSizeMode aMode
) {
2329 // If we are still displaying a maximized pre-XUL skeleton UI, ignore the
2330 // noise of sizemode changes. Once we have "shown" the window for the first
2331 // time (called nsWindow::Show(true), even though the window is already
2332 // technically displayed), we will again accept sizemode changes.
2333 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2337 mFrameState
->EnsureSizeMode(aMode
);
2340 nsSizeMode
nsWindow::SizeMode() { return mFrameState
->GetSizeMode(); }
2342 void DoGetWorkspaceID(HWND aWnd
, nsAString
* aWorkspaceID
) {
2343 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2344 if (!desktopManager
|| !aWnd
) {
2349 HRESULT hr
= desktopManager
->GetWindowDesktopId(aWnd
, &desktop
);
2354 RPC_WSTR workspaceIDStr
= nullptr;
2355 if (UuidToStringW(&desktop
, &workspaceIDStr
) == RPC_S_OK
) {
2356 aWorkspaceID
->Assign((wchar_t*)workspaceIDStr
);
2357 RpcStringFreeW(&workspaceIDStr
);
2361 void nsWindow::GetWorkspaceID(nsAString
& workspaceID
) {
2362 // If we have a value cached, use that, but also make sure it is
2363 // scheduled to be updated. If we don't yet have a value, get
2364 // one synchronously.
2365 auto desktop
= mDesktopId
.Lock();
2366 if (desktop
->mID
.IsEmpty()) {
2367 DoGetWorkspaceID(mWnd
, &desktop
->mID
);
2368 desktop
->mUpdateIsQueued
= false;
2370 AsyncUpdateWorkspaceID(*desktop
);
2373 workspaceID
= desktop
->mID
;
2376 void nsWindow::AsyncUpdateWorkspaceID(Desktop
& aDesktop
) {
2377 struct UpdateWorkspaceIdTask
: public Task
{
2378 explicit UpdateWorkspaceIdTask(nsWindow
* aSelf
)
2379 : Task(false /* mainThread */, EventQueuePriority::Normal
),
2382 bool Run() override
{
2383 auto desktop
= mSelf
->mDesktopId
.Lock();
2384 if (desktop
->mUpdateIsQueued
) {
2385 DoGetWorkspaceID(mSelf
->mWnd
, &desktop
->mID
);
2386 desktop
->mUpdateIsQueued
= false;
2391 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
2392 bool GetName(nsACString
& aName
) override
{
2393 aName
.AssignLiteral("UpdateWorkspaceIdTask");
2398 RefPtr
<nsWindow
> mSelf
;
2401 if (aDesktop
.mUpdateIsQueued
) {
2405 aDesktop
.mUpdateIsQueued
= true;
2406 TaskController::Get()->AddTask(MakeAndAddRef
<UpdateWorkspaceIdTask
>(this));
2409 void nsWindow::MoveToWorkspace(const nsAString
& workspaceID
) {
2410 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2411 if (!desktopManager
) {
2416 const nsString flat
= PromiseFlatString(workspaceID
);
2417 RPC_WSTR workspaceIDStr
= reinterpret_cast<RPC_WSTR
>((wchar_t*)flat
.get());
2418 if (UuidFromStringW(workspaceIDStr
, &desktop
) == RPC_S_OK
) {
2419 if (SUCCEEDED(desktopManager
->MoveWindowToDesktop(mWnd
, desktop
))) {
2420 auto desktop
= mDesktopId
.Lock();
2421 desktop
->mID
= workspaceID
;
2426 void nsWindow::SuppressAnimation(bool aSuppress
) {
2427 DWORD dwAttribute
= aSuppress
? TRUE
: FALSE
;
2428 DwmSetWindowAttribute(mWnd
, DWMWA_TRANSITIONS_FORCEDISABLED
, &dwAttribute
,
2429 sizeof dwAttribute
);
2432 // Constrain a potential move to fit onscreen
2433 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2434 // except when using per-monitor DPI, in which case it's device pixels.
2435 void nsWindow::ConstrainPosition(bool aAllowSlop
, int32_t* aX
, int32_t* aY
) {
2436 if (!mIsTopWidgetWindow
) // only a problem for top-level windows
2439 double dpiScale
= GetDesktopToDeviceScale().scale
;
2441 // We need to use the window size in the kind of pixels used for window-
2442 // manipulation APIs.
2444 std::max
<int32_t>(NSToIntRound(mBounds
.Width() / dpiScale
), 1);
2446 std::max
<int32_t>(NSToIntRound(mBounds
.Height() / dpiScale
), 1);
2448 /* get our playing field. use the current screen, or failing that
2449 for any reason, use device caps for the default screen. */
2452 nsCOMPtr
<nsIScreenManager
> screenmgr
=
2453 do_GetService(sScreenManagerContractID
);
2457 nsCOMPtr
<nsIScreen
> screen
;
2458 int32_t left
, top
, width
, height
;
2460 screenmgr
->ScreenForRect(*aX
, *aY
, logWidth
, logHeight
,
2461 getter_AddRefs(screen
));
2462 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
2463 // For normalized windows, use the desktop work area.
2464 nsresult rv
= screen
->GetAvailRectDisplayPix(&left
, &top
, &width
, &height
);
2465 if (NS_FAILED(rv
)) {
2469 // For full screen windows, use the desktop.
2470 nsresult rv
= screen
->GetRectDisplayPix(&left
, &top
, &width
, &height
);
2471 if (NS_FAILED(rv
)) {
2475 screenRect
.left
= left
;
2476 screenRect
.right
= left
+ width
;
2477 screenRect
.top
= top
;
2478 screenRect
.bottom
= top
+ height
;
2481 if (*aX
< screenRect
.left
- logWidth
+ kWindowPositionSlop
)
2482 *aX
= screenRect
.left
- logWidth
+ kWindowPositionSlop
;
2483 else if (*aX
>= screenRect
.right
- kWindowPositionSlop
)
2484 *aX
= screenRect
.right
- kWindowPositionSlop
;
2486 if (*aY
< screenRect
.top
- logHeight
+ kWindowPositionSlop
)
2487 *aY
= screenRect
.top
- logHeight
+ kWindowPositionSlop
;
2488 else if (*aY
>= screenRect
.bottom
- kWindowPositionSlop
)
2489 *aY
= screenRect
.bottom
- kWindowPositionSlop
;
2492 if (*aX
< screenRect
.left
)
2493 *aX
= screenRect
.left
;
2494 else if (*aX
>= screenRect
.right
- logWidth
)
2495 *aX
= screenRect
.right
- logWidth
;
2497 if (*aY
< screenRect
.top
)
2498 *aY
= screenRect
.top
;
2499 else if (*aY
>= screenRect
.bottom
- logHeight
)
2500 *aY
= screenRect
.bottom
- logHeight
;
2504 /**************************************************************
2506 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2508 * Enabling and disabling the widget.
2510 **************************************************************/
2512 // Enable/disable this component
2513 void nsWindow::Enable(bool bState
) {
2515 ::EnableWindow(mWnd
, bState
);
2519 // Return the current enable state
2520 bool nsWindow::IsEnabled() const {
2521 return !mWnd
|| (::IsWindowEnabled(mWnd
) &&
2522 ::IsWindowEnabled(::GetAncestor(mWnd
, GA_ROOT
)));
2525 /**************************************************************
2527 * SECTION: nsIWidget::SetFocus
2529 * Give the focus to this widget.
2531 **************************************************************/
2533 void nsWindow::SetFocus(Raise aRaise
, mozilla::dom::CallerType aCallerType
) {
2535 #ifdef WINSTATE_DEBUG_OUTPUT
2536 if (mWnd
== WinUtils::GetTopLevelHWND(mWnd
)) {
2537 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2538 ("*** SetFocus: [ top] raise=%d\n", aRaise
== Raise::Yes
));
2540 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2541 ("*** SetFocus: [child] raise=%d\n", aRaise
== Raise::Yes
));
2544 // Uniconify, if necessary
2545 HWND toplevelWnd
= WinUtils::GetTopLevelHWND(mWnd
);
2546 if (aRaise
== Raise::Yes
&& ::IsIconic(toplevelWnd
)) {
2547 ::ShowWindow(toplevelWnd
, SW_RESTORE
);
2553 /**************************************************************
2557 * GetBounds, GetClientBounds, GetScreenBounds,
2558 * GetRestoredBounds, GetClientOffset
2559 * SetDrawsInTitlebar, SetNonClientMargins
2561 * Bound calculations.
2563 **************************************************************/
2565 // Return the window's full dimensions in screen coordinates.
2566 // If the window has a parent, converts the origin to an offset
2567 // of the parent's screen origin.
2568 LayoutDeviceIntRect
nsWindow::GetBounds() {
2574 VERIFY(::GetWindowRect(mWnd
, &r
));
2576 LayoutDeviceIntRect rect
;
2579 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2581 // popup window bounds' are in screen coordinates, not relative to parent
2583 if (mWindowType
== eWindowType_popup
) {
2584 rect
.MoveTo(r
.left
, r
.top
);
2588 // chrome on parent:
2589 // ___ 5,5 (chrome start)
2590 // | ____ 10,10 (client start)
2591 // | | ____ 20,20 (child start)
2593 // 20,20 - 5,5 = 15,15 (??)
2594 // minus GetClientOffset:
2595 // 15,15 - 5,5 = 10,10
2597 // no chrome on parent:
2598 // ______ 10,10 (win start)
2599 // | ____ 20,20 (child start)
2601 // 20,20 - 10,10 = 10,10
2603 // walking the chain:
2604 // ___ 5,5 (chrome start)
2605 // | ___ 10,10 (client start)
2606 // | | ___ 20,20 (child start)
2607 // | | | __ 30,30 (child start)
2609 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2610 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2611 // minus GetClientOffset:
2612 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2614 // convert coordinates if parent exists
2615 HWND parent
= ::GetParent(mWnd
);
2618 VERIFY(::GetWindowRect(parent
, &pr
));
2621 // adjust for chrome
2622 nsWindow
* pWidget
= static_cast<nsWindow
*>(GetParent());
2623 if (pWidget
&& pWidget
->IsTopLevelWidget()) {
2624 LayoutDeviceIntPoint clientOffset
= pWidget
->GetClientOffset();
2625 r
.left
-= clientOffset
.x
;
2626 r
.top
-= clientOffset
.y
;
2629 rect
.MoveTo(r
.left
, r
.top
);
2633 // Get this component dimension
2634 LayoutDeviceIntRect
nsWindow::GetClientBounds() {
2636 return LayoutDeviceIntRect(0, 0, 0, 0);
2640 if (!::GetClientRect(mWnd
, &r
)) {
2641 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
2642 gfxCriticalNoteOnce
<< "GetClientRect failed " << ::GetLastError();
2646 LayoutDeviceIntRect bounds
= GetBounds();
2647 LayoutDeviceIntRect rect
;
2648 rect
.MoveTo(bounds
.TopLeft() + GetClientOffset());
2649 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2653 // Like GetBounds, but don't offset by the parent
2654 LayoutDeviceIntRect
nsWindow::GetScreenBounds() {
2660 VERIFY(::GetWindowRect(mWnd
, &r
));
2662 return LayoutDeviceIntRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2665 nsresult
nsWindow::GetRestoredBounds(LayoutDeviceIntRect
& aRect
) {
2666 if (SizeMode() == nsSizeMode_Normal
) {
2667 aRect
= GetScreenBounds();
2671 return NS_ERROR_FAILURE
;
2674 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2675 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2676 const RECT
& r
= pl
.rcNormalPosition
;
2678 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2680 return NS_ERROR_FAILURE
;
2682 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2683 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2685 aRect
.SetRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2686 aRect
.MoveBy(mi
.rcWork
.left
- mi
.rcMonitor
.left
,
2687 mi
.rcWork
.top
- mi
.rcMonitor
.top
);
2691 // Return the x,y offset of the client area from the origin of the window. If
2692 // the window is borderless returns (0,0).
2693 LayoutDeviceIntPoint
nsWindow::GetClientOffset() {
2695 return LayoutDeviceIntPoint(0, 0);
2699 GetWindowRect(mWnd
, &r1
);
2700 LayoutDeviceIntPoint pt
= WidgetToScreenOffset();
2701 return LayoutDeviceIntPoint(pt
.x
- r1
.left
, pt
.y
- r1
.top
);
2704 void nsWindow::SetDrawsInTitlebar(bool aState
) {
2705 nsWindow
* window
= GetTopLevelWindow(true);
2706 if (window
&& window
!= this) {
2707 return window
->SetDrawsInTitlebar(aState
);
2711 // top, right, bottom, left for nsIntMargin
2712 LayoutDeviceIntMargin
margins(0, -1, -1, -1);
2713 SetNonClientMargins(margins
);
2715 LayoutDeviceIntMargin
margins(-1, -1, -1, -1);
2716 SetNonClientMargins(margins
);
2720 void nsWindow::ResetLayout() {
2721 // This will trigger a frame changed event, triggering
2722 // nc calc size and a sizemode gecko event.
2723 SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
2724 SWP_FRAMECHANGED
| SWP_NOACTIVATE
| SWP_NOMOVE
|
2725 SWP_NOOWNERZORDER
| SWP_NOSIZE
| SWP_NOZORDER
);
2727 // If hidden, just send the frame changed event for now.
2728 if (!mIsVisible
) return;
2730 // Send a gecko size event to trigger reflow.
2731 RECT clientRc
= {0};
2732 GetClientRect(mWnd
, &clientRc
);
2733 OnResize(WinUtils::ToIntRect(clientRc
).Size());
2735 // Invalidate and update
2739 // Internally track the caption status via a window property. Required
2740 // due to our internal handling of WM_NCACTIVATE when custom client
2742 static const wchar_t kManageWindowInfoProperty
[] = L
"ManageWindowInfoProperty";
2743 typedef BOOL(WINAPI
* GetWindowInfoPtr
)(HWND hwnd
, PWINDOWINFO pwi
);
2744 static WindowsDllInterceptor::FuncHookType
<GetWindowInfoPtr
>
2745 sGetWindowInfoPtrStub
;
2747 BOOL WINAPI
GetWindowInfoHook(HWND hWnd
, PWINDOWINFO pwi
) {
2748 if (!sGetWindowInfoPtrStub
) {
2749 NS_ASSERTION(FALSE
, "Something is horribly wrong in GetWindowInfoHook!");
2753 reinterpret_cast<LONG_PTR
>(GetPropW(hWnd
, kManageWindowInfoProperty
));
2754 // No property set, return the default data.
2755 if (!windowStatus
) return sGetWindowInfoPtrStub(hWnd
, pwi
);
2756 // Call GetWindowInfo and update dwWindowStatus with our
2757 // internally tracked value.
2758 BOOL result
= sGetWindowInfoPtrStub(hWnd
, pwi
);
2760 pwi
->dwWindowStatus
= (windowStatus
== 1 ? 0 : WS_ACTIVECAPTION
);
2764 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption
) {
2767 sUser32Intercept
.Init("user32.dll");
2768 sGetWindowInfoPtrStub
.Set(sUser32Intercept
, "GetWindowInfo",
2769 &GetWindowInfoHook
);
2770 if (!sGetWindowInfoPtrStub
) {
2774 // Update our internally tracked caption status
2775 SetPropW(mWnd
, kManageWindowInfoProperty
,
2776 reinterpret_cast<HANDLE
>(static_cast<INT_PTR
>(aActiveCaption
) + 1));
2779 #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
2780 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
2782 void nsWindow::UpdateDarkModeToolbar() {
2783 if (!IsWin10OrLater()) {
2786 LookAndFeel::EnsureColorSchemesInitialized();
2787 BOOL dark
= LookAndFeel::ColorSchemeForChrome() == ColorScheme::Dark
;
2788 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
, &dark
,
2790 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE
, &dark
,
2795 * Called when the window layout changes: full screen mode transitions,
2796 * theme changes, and composition changes. Calculates the new non-client
2797 * margins and fires off a frame changed event, which triggers an nc calc
2798 * size windows event, kicking the changes in.
2800 * The offsets calculated here are based on the value of `mNonClientMargins`
2801 * which is specified in the "chromemargins" attribute of the window. For
2802 * each margin, the value specified has the following meaning:
2803 * -1 - leave the default frame in place
2804 * 0 - remove the frame
2805 * >0 - frame size equals min(0, (default frame size - margin value))
2807 * This function calculates and populates `mNonClientOffset`.
2808 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2809 * as (default frame size - offset). For example, if the left frame should
2810 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2813 * For maximized, fullscreen, and minimized windows, the values stored in
2814 * `mNonClientMargins` are ignored, and special processing takes place.
2816 * For non-glass windows, we only allow frames to be their default size
2817 * or removed entirely.
2819 bool nsWindow::UpdateNonClientMargins(int32_t aSizeMode
, bool aReflowWindow
) {
2820 if (!mCustomNonClient
) return false;
2822 if (aSizeMode
== -1) {
2823 aSizeMode
= mFrameState
->GetSizeMode();
2826 bool hasCaption
= (mBorderStyle
& (eBorderStyle_all
| eBorderStyle_title
|
2827 eBorderStyle_menu
| eBorderStyle_default
));
2829 float dpi
= GetDPI();
2831 // mCaptionHeight is the default size of the NC area at
2832 // the top of the window. If the window has a caption,
2833 // the size is calculated as the sum of:
2834 // SM_CYFRAME - The thickness of the sizing border
2835 // around a resizable window
2836 // SM_CXPADDEDBORDER - The amount of border padding
2837 // for captioned windows
2838 // SM_CYCAPTION - The height of the caption area
2840 // If the window does not have a caption, mCaptionHeight will be equal to
2841 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2843 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2844 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CYCAPTION
, dpi
) +
2845 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2847 if (!mUseResizeMarginOverrides
) {
2848 // mHorResizeMargin is the size of the default NC areas on the
2849 // left and right sides of our window. It is calculated as
2851 // SM_CXFRAME - The thickness of the sizing border
2852 // SM_CXPADDEDBORDER - The amount of border padding
2853 // for captioned windows
2855 // If the window does not have a caption, mHorResizeMargin will be equal to
2856 // `WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)`
2858 WinUtils::GetSystemMetricsForDpi(SM_CXFRAME
, dpi
) +
2859 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2862 // mVertResizeMargin is the size of the default NC area at the
2863 // bottom of the window. It is calculated as the sum of:
2864 // SM_CYFRAME - The thickness of the sizing border
2865 // SM_CXPADDEDBORDER - The amount of border padding
2866 // for captioned windows.
2868 // If the window does not have a caption, mVertResizeMargin will be equal to
2869 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2871 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2872 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2876 if (aSizeMode
== nsSizeMode_Minimized
) {
2877 // Use default frame size for minimized windows
2878 mNonClientOffset
.top
= 0;
2879 mNonClientOffset
.left
= 0;
2880 mNonClientOffset
.right
= 0;
2881 mNonClientOffset
.bottom
= 0;
2882 } else if (aSizeMode
== nsSizeMode_Fullscreen
) {
2883 // Remove the default frame from the top of our fullscreen window. This
2884 // makes the whole caption part of our client area, allowing us to draw
2885 // in the whole caption area. Additionally remove the default frame from
2886 // the left, right, and bottom.
2887 mNonClientOffset
.top
= mCaptionHeight
;
2888 mNonClientOffset
.bottom
= mVertResizeMargin
;
2889 mNonClientOffset
.left
= mHorResizeMargin
;
2890 mNonClientOffset
.right
= mHorResizeMargin
;
2891 } else if (aSizeMode
== nsSizeMode_Maximized
) {
2892 // Remove the default frame from the top of our maximized window. This
2893 // makes the whole caption part of our client area, allowing us to draw
2894 // in the whole caption area. Use default frame size on left, right, and
2895 // bottom. The reason this works is that, for maximized windows,
2896 // Windows positions them so that their frames fall off the screen.
2897 // This gives the illusion of windows having no frames when they are
2898 // maximized. If we try to mess with the frame sizes by setting these
2899 // offsets to positive values, our client area will fall off the screen.
2900 mNonClientOffset
.top
= mCaptionHeight
;
2901 mNonClientOffset
.bottom
= 0;
2902 mNonClientOffset
.left
= 0;
2903 mNonClientOffset
.right
= 0;
2905 APPBARDATA appBarData
;
2906 appBarData
.cbSize
= sizeof(appBarData
);
2907 UINT taskbarState
= SHAppBarMessage(ABM_GETSTATE
, &appBarData
);
2908 if (ABS_AUTOHIDE
& taskbarState
) {
2910 appBarData
.hWnd
= FindWindow(L
"Shell_TrayWnd", nullptr);
2911 if (appBarData
.hWnd
) {
2912 HMONITOR taskbarMonitor
=
2913 ::MonitorFromWindow(appBarData
.hWnd
, MONITOR_DEFAULTTOPRIMARY
);
2914 HMONITOR windowMonitor
=
2915 ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONEAREST
);
2916 if (taskbarMonitor
== windowMonitor
) {
2917 SHAppBarMessage(ABM_GETTASKBARPOS
, &appBarData
);
2918 edge
= appBarData
.uEdge
;
2922 if (ABE_LEFT
== edge
) {
2923 mNonClientOffset
.left
-= 1;
2924 } else if (ABE_RIGHT
== edge
) {
2925 mNonClientOffset
.right
-= 1;
2926 } else if (ABE_BOTTOM
== edge
|| ABE_TOP
== edge
) {
2927 mNonClientOffset
.bottom
-= 1;
2931 bool glass
= gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
2933 // We're dealing with a "normal" window (not maximized, minimized, or
2934 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2937 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2938 // frame intact. Setting it to a value greater than 0 reduces the frame
2939 // size by that amount.
2941 if (mNonClientMargins
.top
> 0 && glass
) {
2942 mNonClientOffset
.top
= std::min(mCaptionHeight
, mNonClientMargins
.top
);
2943 } else if (mNonClientMargins
.top
== 0) {
2944 mNonClientOffset
.top
= mCaptionHeight
;
2946 mNonClientOffset
.top
= 0;
2949 if (mNonClientMargins
.bottom
> 0 && glass
) {
2950 mNonClientOffset
.bottom
=
2951 std::min(mVertResizeMargin
, mNonClientMargins
.bottom
);
2952 } else if (mNonClientMargins
.bottom
== 0) {
2953 mNonClientOffset
.bottom
= mVertResizeMargin
;
2955 mNonClientOffset
.bottom
= 0;
2958 if (mNonClientMargins
.left
> 0 && glass
) {
2959 mNonClientOffset
.left
=
2960 std::min(mHorResizeMargin
, mNonClientMargins
.left
);
2961 } else if (mNonClientMargins
.left
== 0) {
2962 mNonClientOffset
.left
= mHorResizeMargin
;
2964 mNonClientOffset
.left
= 0;
2967 if (mNonClientMargins
.right
> 0 && glass
) {
2968 mNonClientOffset
.right
=
2969 std::min(mHorResizeMargin
, mNonClientMargins
.right
);
2970 } else if (mNonClientMargins
.right
== 0) {
2971 mNonClientOffset
.right
= mHorResizeMargin
;
2973 mNonClientOffset
.right
= 0;
2977 if (aReflowWindow
) {
2978 // Force a reflow of content based on the new client
2986 nsresult
nsWindow::SetNonClientMargins(LayoutDeviceIntMargin
& margins
) {
2987 if (!mIsTopWidgetWindow
|| mBorderStyle
== eBorderStyle_none
)
2988 return NS_ERROR_INVALID_ARG
;
2991 mFutureMarginsOnceChromeShows
= margins
;
2992 mFutureMarginsToUse
= true;
2995 mFutureMarginsToUse
= false;
2997 // Request for a reset
2998 if (margins
.top
== -1 && margins
.left
== -1 && margins
.right
== -1 &&
2999 margins
.bottom
== -1) {
3000 mCustomNonClient
= false;
3001 mNonClientMargins
= margins
;
3002 // Force a reflow of content based on the new client
3007 reinterpret_cast<LONG_PTR
>(GetPropW(mWnd
, kManageWindowInfoProperty
));
3009 ::SendMessageW(mWnd
, WM_NCACTIVATE
, 1 != windowStatus
, 0);
3015 if (margins
.top
< -1 || margins
.bottom
< -1 || margins
.left
< -1 ||
3017 return NS_ERROR_INVALID_ARG
;
3019 mNonClientMargins
= margins
;
3020 mCustomNonClient
= true;
3021 if (!UpdateNonClientMargins()) {
3022 NS_WARNING("UpdateNonClientMargins failed!");
3029 void nsWindow::SetResizeMargin(mozilla::LayoutDeviceIntCoord aResizeMargin
) {
3030 mUseResizeMarginOverrides
= true;
3031 mHorResizeMargin
= aResizeMargin
;
3032 mVertResizeMargin
= aResizeMargin
;
3033 UpdateNonClientMargins();
3036 void nsWindow::InvalidateNonClientRegion() {
3037 // +-+-----------------------+-+
3038 // | | app non-client chrome | |
3039 // | +-----------------------+ |
3040 // | | app client chrome | | }
3041 // | +-----------------------+ | }
3042 // | | app content | | } area we don't want to invalidate
3043 // | +-----------------------+ | }
3044 // | | app client chrome | | }
3045 // | +-----------------------+ |
3046 // +---------------------------+ <
3047 // ^ ^ windows non-client chrome
3048 // client area = app *
3050 GetWindowRect(mWnd
, &rect
);
3051 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
3052 HRGN winRgn
= CreateRectRgnIndirect(&rect
);
3054 // Subtract app client chrome and app content leaving
3055 // windows non-client chrome and app non-client chrome
3057 GetWindowRect(mWnd
, &rect
);
3058 rect
.top
+= mCaptionHeight
;
3059 rect
.right
-= mHorResizeMargin
;
3060 rect
.bottom
-= mHorResizeMargin
;
3061 rect
.left
+= mVertResizeMargin
;
3062 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
3063 HRGN clientRgn
= CreateRectRgnIndirect(&rect
);
3064 CombineRgn(winRgn
, winRgn
, clientRgn
, RGN_DIFF
);
3065 DeleteObject(clientRgn
);
3067 // triggers ncpaint and paint events for the two areas
3068 RedrawWindow(mWnd
, nullptr, winRgn
, RDW_FRAME
| RDW_INVALIDATE
);
3069 DeleteObject(winRgn
);
3072 HRGN
nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion
) {
3075 if (aRegion
== (HRGN
)1) { // undocumented value indicating a full refresh
3076 GetWindowRect(mWnd
, &rect
);
3077 rgn
= CreateRectRgnIndirect(&rect
);
3081 GetClientRect(mWnd
, &rect
);
3082 MapWindowPoints(mWnd
, nullptr, (LPPOINT
)&rect
, 2);
3083 HRGN nonClientRgn
= CreateRectRgnIndirect(&rect
);
3084 CombineRgn(rgn
, rgn
, nonClientRgn
, RGN_DIFF
);
3085 DeleteObject(nonClientRgn
);
3089 /**************************************************************
3091 * SECTION: nsIWidget::SetBackgroundColor
3093 * Sets the window background paint color.
3095 **************************************************************/
3097 void nsWindow::SetBackgroundColor(const nscolor
& aColor
) {
3098 if (mBrush
) ::DeleteObject(mBrush
);
3100 mBrush
= ::CreateSolidBrush(NSRGB_2_COLOREF(aColor
));
3101 if (mWnd
!= nullptr) {
3102 ::SetClassLongPtrW(mWnd
, GCLP_HBRBACKGROUND
, (LONG_PTR
)mBrush
);
3106 /**************************************************************
3108 * SECTION: nsIWidget::SetCursor
3110 * SetCursor and related utilities for manging cursor state.
3112 **************************************************************/
3114 // Set this component cursor
3115 static HCURSOR
CursorFor(nsCursor aCursor
) {
3117 case eCursor_select
:
3118 return ::LoadCursor(nullptr, IDC_IBEAM
);
3120 return ::LoadCursor(nullptr, IDC_WAIT
);
3121 case eCursor_hyperlink
:
3122 return ::LoadCursor(nullptr, IDC_HAND
);
3123 case eCursor_standard
:
3124 case eCursor_context_menu
: // XXX See bug 258960.
3125 return ::LoadCursor(nullptr, IDC_ARROW
);
3127 case eCursor_n_resize
:
3128 case eCursor_s_resize
:
3129 return ::LoadCursor(nullptr, IDC_SIZENS
);
3131 case eCursor_w_resize
:
3132 case eCursor_e_resize
:
3133 return ::LoadCursor(nullptr, IDC_SIZEWE
);
3135 case eCursor_nw_resize
:
3136 case eCursor_se_resize
:
3137 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
3139 case eCursor_ne_resize
:
3140 case eCursor_sw_resize
:
3141 return ::LoadCursor(nullptr, IDC_SIZENESW
);
3143 case eCursor_crosshair
:
3144 return ::LoadCursor(nullptr, IDC_CROSS
);
3147 return ::LoadCursor(nullptr, IDC_SIZEALL
);
3150 return ::LoadCursor(nullptr, IDC_HELP
);
3152 case eCursor_copy
: // CSS3
3153 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_COPY
));
3156 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ALIAS
));
3159 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_CELL
));
3161 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_GRAB
));
3163 case eCursor_grabbing
:
3164 return ::LoadCursor(nsToolkit::mDllInstance
,
3165 MAKEINTRESOURCE(IDC_GRABBING
));
3167 case eCursor_spinning
:
3168 return ::LoadCursor(nullptr, IDC_APPSTARTING
);
3170 case eCursor_zoom_in
:
3171 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ZOOMIN
));
3173 case eCursor_zoom_out
:
3174 return ::LoadCursor(nsToolkit::mDllInstance
,
3175 MAKEINTRESOURCE(IDC_ZOOMOUT
));
3177 case eCursor_not_allowed
:
3178 case eCursor_no_drop
:
3179 return ::LoadCursor(nullptr, IDC_NO
);
3181 case eCursor_col_resize
:
3182 return ::LoadCursor(nsToolkit::mDllInstance
,
3183 MAKEINTRESOURCE(IDC_COLRESIZE
));
3185 case eCursor_row_resize
:
3186 return ::LoadCursor(nsToolkit::mDllInstance
,
3187 MAKEINTRESOURCE(IDC_ROWRESIZE
));
3189 case eCursor_vertical_text
:
3190 return ::LoadCursor(nsToolkit::mDllInstance
,
3191 MAKEINTRESOURCE(IDC_VERTICALTEXT
));
3193 case eCursor_all_scroll
:
3194 // XXX not 100% appropriate perhaps
3195 return ::LoadCursor(nullptr, IDC_SIZEALL
);
3197 case eCursor_nesw_resize
:
3198 return ::LoadCursor(nullptr, IDC_SIZENESW
);
3200 case eCursor_nwse_resize
:
3201 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
3203 case eCursor_ns_resize
:
3204 return ::LoadCursor(nullptr, IDC_SIZENS
);
3206 case eCursor_ew_resize
:
3207 return ::LoadCursor(nullptr, IDC_SIZEWE
);
3210 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_NONE
));
3213 NS_ERROR("Invalid cursor type");
3218 static HCURSOR
CursorForImage(const nsIWidget::Cursor
& aCursor
,
3219 CSSToLayoutDeviceScale aScale
) {
3220 if (!aCursor
.IsCustom()) {
3224 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
3226 // Reject cursors greater than 128 pixels in either direction, to prevent
3228 // XXX ideally we should rescale. Also, we could modify the API to
3229 // allow trusted content to set larger cursors.
3230 if (size
.width
> 128 || size
.height
> 128) {
3234 LayoutDeviceIntSize layoutSize
=
3235 RoundedToInt(CSSIntSize(size
.width
, size
.height
) * aScale
);
3236 LayoutDeviceIntPoint hotspot
=
3237 RoundedToInt(CSSIntPoint(aCursor
.mHotspotX
, aCursor
.mHotspotY
) * aScale
);
3239 nsresult rv
= nsWindowGfx::CreateIcon(aCursor
.mContainer
, true, hotspot
,
3240 layoutSize
, &cursor
);
3241 if (NS_FAILED(rv
)) {
3248 void nsWindow::SetCursor(const Cursor
& aCursor
) {
3249 static HCURSOR sCurrentHCursor
= nullptr;
3250 static bool sCurrentHCursorIsCustom
= false;
3254 if (sCurrentCursor
== aCursor
&& sCurrentHCursor
&& !mUpdateCursor
) {
3255 // Cursors in windows are global, so even if our mUpdateCursor flag is
3256 // false we always need to make sure the Windows cursor is up-to-date,
3257 // since stuff like native drag and drop / resizers code can mutate it
3258 // outside of this method.
3259 ::SetCursor(sCurrentHCursor
);
3263 mUpdateCursor
= false;
3265 if (sCurrentHCursorIsCustom
) {
3266 ::DestroyIcon(sCurrentHCursor
);
3268 sCurrentHCursor
= nullptr;
3269 sCurrentHCursorIsCustom
= false;
3270 sCurrentCursor
= aCursor
;
3272 HCURSOR cursor
= CursorForImage(aCursor
, GetDefaultScale());
3273 bool custom
= false;
3277 cursor
= CursorFor(aCursor
.mDefaultCursor
);
3284 sCurrentHCursor
= cursor
;
3285 sCurrentHCursorIsCustom
= custom
;
3286 ::SetCursor(cursor
);
3289 /**************************************************************
3291 * SECTION: nsIWidget::Get/SetTransparencyMode
3293 * Manage the transparency mode of the window containing this
3294 * widget. Only works for popup and dialog windows when the
3295 * Desktop Window Manager compositor is not enabled.
3297 **************************************************************/
3299 nsTransparencyMode
nsWindow::GetTransparencyMode() {
3300 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
3303 void nsWindow::SetTransparencyMode(nsTransparencyMode aMode
) {
3304 nsWindow
* window
= GetTopLevelWindow(true);
3307 if (!window
|| window
->DestroyCalled()) {
3311 if (nsWindowType::eWindowType_toplevel
== window
->mWindowType
&&
3312 mTransparencyMode
!= aMode
&&
3313 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3314 NS_WARNING("Cannot set transparency mode on top-level windows.");
3318 window
->SetWindowTranslucencyInner(aMode
);
3321 void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion
& aOpaqueRegion
) {
3322 if (!HasGlass() || GetParent()) return;
3324 // If there is no opaque region or hidechrome=true, set margins
3325 // to support a full sheet of glass. Comments in MSDN indicate
3326 // all values must be set to -1 to get a full sheet of glass.
3327 MARGINS margins
= {-1, -1, -1, -1};
3328 if (!aOpaqueRegion
.IsEmpty()) {
3329 LayoutDeviceIntRect clientBounds
= GetClientBounds();
3330 // Find the largest rectangle and use that to calculate the inset.
3331 LayoutDeviceIntRect largest
= aOpaqueRegion
.GetLargestRectangle();
3332 margins
.cxLeftWidth
= largest
.X();
3333 margins
.cxRightWidth
= clientBounds
.Width() - largest
.XMost();
3334 margins
.cyBottomHeight
= clientBounds
.Height() - largest
.YMost();
3335 if (mCustomNonClient
) {
3336 // The minimum glass height must be the caption buttons height,
3337 // otherwise the buttons are drawn incorrectly.
3338 largest
.MoveToY(std::max
<uint32_t>(
3339 largest
.Y(), nsUXThemeData::GetCommandButtonBoxMetrics().cy
));
3341 margins
.cyTopHeight
= largest
.Y();
3344 // Only update glass area if there are changes
3345 if (memcmp(&mGlassMargins
, &margins
, sizeof mGlassMargins
)) {
3346 mGlassMargins
= margins
;
3351 /**************************************************************
3353 * SECTION: nsIWidget::UpdateWindowDraggingRegion
3355 * For setting the draggable titlebar region from CSS
3356 * with -moz-window-dragging: drag.
3358 **************************************************************/
3360 void nsWindow::UpdateWindowDraggingRegion(
3361 const LayoutDeviceIntRegion
& aRegion
) {
3362 if (mDraggableRegion
!= aRegion
) {
3363 mDraggableRegion
= aRegion
;
3367 void nsWindow::UpdateGlass() {
3368 MARGINS margins
= mGlassMargins
;
3370 // DWMNCRP_USEWINDOWSTYLE - The non-client rendering area is
3371 // rendered based on the window style.
3372 // DWMNCRP_ENABLED - The non-client area rendering is
3373 // enabled; the window style is ignored.
3374 DWMNCRENDERINGPOLICY policy
= DWMNCRP_USEWINDOWSTYLE
;
3375 switch (mTransparencyMode
) {
3376 case eTransparencyBorderlessGlass
:
3377 // Only adjust if there is some opaque rectangle
3378 if (margins
.cxLeftWidth
>= 0) {
3379 margins
.cxLeftWidth
+= kGlassMarginAdjustment
;
3380 margins
.cyTopHeight
+= kGlassMarginAdjustment
;
3381 margins
.cxRightWidth
+= kGlassMarginAdjustment
;
3382 margins
.cyBottomHeight
+= kGlassMarginAdjustment
;
3385 case eTransparencyGlass
:
3386 policy
= DWMNCRP_ENABLED
;
3392 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3393 ("glass margins: left:%d top:%d right:%d bottom:%d\n",
3394 margins
.cxLeftWidth
, margins
.cyTopHeight
, margins
.cxRightWidth
,
3395 margins
.cyBottomHeight
));
3397 // Extends the window frame behind the client area
3398 if (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3399 DwmExtendFrameIntoClientArea(mWnd
, &margins
);
3400 DwmSetWindowAttribute(mWnd
, DWMWA_NCRENDERING_POLICY
, &policy
,
3405 /**************************************************************
3407 * SECTION: nsIWidget::HideWindowChrome
3409 * Show or hide window chrome.
3411 **************************************************************/
3413 void nsWindow::HideWindowChrome(bool aShouldHide
) {
3414 HWND hwnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
3415 if (!WinUtils::GetNSWindowPtr(hwnd
)) {
3416 NS_WARNING("Trying to hide window decorations in an embedded context");
3420 if (mHideChrome
== aShouldHide
) return;
3422 DWORD_PTR style
, exStyle
;
3423 mHideChrome
= aShouldHide
;
3425 DWORD_PTR tempStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3426 DWORD_PTR tempExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3428 style
= tempStyle
& ~(WS_CAPTION
| WS_THICKFRAME
);
3429 exStyle
= tempExStyle
& ~(WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
|
3430 WS_EX_CLIENTEDGE
| WS_EX_STATICEDGE
);
3432 mOldStyle
= tempStyle
;
3433 mOldExStyle
= tempExStyle
;
3435 if (!mOldStyle
|| !mOldExStyle
) {
3436 mOldStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3437 mOldExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3441 exStyle
= mOldExStyle
;
3442 if (mFutureMarginsToUse
) {
3443 SetNonClientMargins(mFutureMarginsOnceChromeShows
);
3447 VERIFY_WINDOW_STYLE(style
);
3448 ::SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
3449 ::SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exStyle
);
3452 /**************************************************************
3454 * SECTION: nsWindow::Invalidate
3456 * Invalidate an area of the client for painting.
3458 **************************************************************/
3460 // Invalidate this component visible area
3461 void nsWindow::Invalidate(bool aEraseBackground
, bool aUpdateNCArea
,
3462 bool aIncludeChildren
) {
3467 #ifdef WIDGET_DEBUG_OUTPUT
3468 debug_DumpInvalidate(stdout
, this, nullptr, "noname", (int32_t)mWnd
);
3469 #endif // WIDGET_DEBUG_OUTPUT
3471 DWORD flags
= RDW_INVALIDATE
;
3472 if (aEraseBackground
) {
3475 if (aUpdateNCArea
) {
3478 if (aIncludeChildren
) {
3479 flags
|= RDW_ALLCHILDREN
;
3482 VERIFY(::RedrawWindow(mWnd
, nullptr, nullptr, flags
));
3485 // Invalidate this component visible area
3486 void nsWindow::Invalidate(const LayoutDeviceIntRect
& aRect
) {
3488 #ifdef WIDGET_DEBUG_OUTPUT
3489 debug_DumpInvalidate(stdout
, this, &aRect
, "noname", (int32_t)mWnd
);
3490 #endif // WIDGET_DEBUG_OUTPUT
3494 rect
.left
= aRect
.X();
3495 rect
.top
= aRect
.Y();
3496 rect
.right
= aRect
.XMost();
3497 rect
.bottom
= aRect
.YMost();
3499 VERIFY(::InvalidateRect(mWnd
, &rect
, FALSE
));
3503 static LRESULT CALLBACK
FullscreenTransitionWindowProc(HWND hWnd
, UINT uMsg
,
3507 case WM_FULLSCREEN_TRANSITION_BEFORE
:
3508 case WM_FULLSCREEN_TRANSITION_AFTER
: {
3509 DWORD duration
= (DWORD
)lParam
;
3510 DWORD flags
= AW_BLEND
;
3511 if (uMsg
== WM_FULLSCREEN_TRANSITION_AFTER
) {
3514 ::AnimateWindow(hWnd
, duration
, flags
);
3515 // The message sender should have added ref for us.
3516 NS_DispatchToMainThread(
3517 already_AddRefed
<nsIRunnable
>((nsIRunnable
*)wParam
));
3521 ::PostQuitMessage(0);
3524 return ::DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3529 struct FullscreenTransitionInitData
{
3535 FullscreenTransitionInitData()
3536 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3538 ~FullscreenTransitionInitData() {
3540 ::CloseHandle(mSemaphore
);
3543 ::CloseHandle(mThread
);
3548 static DWORD WINAPI
FullscreenTransitionThreadProc(LPVOID lpParam
) {
3549 // Initialize window class
3550 static bool sInitialized
= false;
3551 if (!sInitialized
) {
3553 wc
.lpfnWndProc
= ::FullscreenTransitionWindowProc
;
3554 wc
.hInstance
= nsToolkit::mDllInstance
;
3555 wc
.hbrBackground
= ::CreateSolidBrush(RGB(0, 0, 0));
3556 wc
.lpszClassName
= kClassNameTransition
;
3557 ::RegisterClassW(&wc
);
3558 sInitialized
= true;
3561 auto data
= static_cast<FullscreenTransitionInitData
*>(lpParam
);
3562 HWND wnd
= ::CreateWindowW(kClassNameTransition
, L
"", 0, 0, 0, 0, 0, nullptr,
3563 nullptr, nsToolkit::mDllInstance
, nullptr);
3565 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3569 // Since AnimateWindow blocks the thread of the transition window,
3570 // we need to hide the cursor for that window, otherwise the system
3571 // would show the busy pointer to the user.
3572 ::ShowCursor(false);
3573 ::SetWindowLongW(wnd
, GWL_STYLE
, 0);
3576 WS_EX_LAYERED
| WS_EX_TRANSPARENT
| WS_EX_TOOLWINDOW
| WS_EX_NOACTIVATE
);
3577 ::SetWindowPos(wnd
, HWND_TOPMOST
, data
->mBounds
.X(), data
->mBounds
.Y(),
3578 data
->mBounds
.Width(), data
->mBounds
.Height(), 0);
3580 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3581 // The initialization data may no longer be valid
3582 // after we release the semaphore.
3586 while (::GetMessageW(&msg
, nullptr, 0, 0)) {
3587 ::TranslateMessage(&msg
);
3588 ::DispatchMessage(&msg
);
3591 ::DestroyWindow(wnd
);
3595 class FullscreenTransitionData final
: public nsISupports
{
3599 explicit FullscreenTransitionData(HWND aWnd
) : mWnd(aWnd
) {
3600 MOZ_ASSERT(NS_IsMainThread(),
3601 "FullscreenTransitionData "
3602 "should be constructed in the main thread");
3608 ~FullscreenTransitionData() {
3609 MOZ_ASSERT(NS_IsMainThread(),
3610 "FullscreenTransitionData "
3611 "should be deconstructed in the main thread");
3612 ::PostMessageW(mWnd
, WM_DESTROY
, 0, 0);
3616 NS_IMPL_ISUPPORTS0(FullscreenTransitionData
)
3619 bool nsWindow::PrepareForFullscreenTransition(nsISupports
** aData
) {
3620 // We don't support fullscreen transition when composition is not
3621 // enabled, which could make the transition broken and annoying.
3623 if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3627 FullscreenTransitionInitData initData
;
3628 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
3629 int32_t x
, y
, width
, height
;
3630 screen
->GetRectDisplayPix(&x
, &y
, &width
, &height
);
3631 MOZ_ASSERT(BoundsUseDesktopPixels(),
3632 "Should only be called on top-level window");
3633 double scale
= GetDesktopToDeviceScale().scale
; // XXX or GetDefaultScale() ?
3634 initData
.mBounds
.SetRect(NSToIntRound(x
* scale
), NSToIntRound(y
* scale
),
3635 NSToIntRound(width
* scale
),
3636 NSToIntRound(height
* scale
));
3638 // Create a semaphore for synchronizing the window handle which will
3639 // be created by the transition thread and used by the main thread for
3640 // posting the transition messages.
3641 initData
.mSemaphore
= ::CreateSemaphore(nullptr, 0, 1, nullptr);
3642 if (initData
.mSemaphore
) {
3643 initData
.mThread
= ::CreateThread(
3644 nullptr, 0, FullscreenTransitionThreadProc
, &initData
, 0, nullptr);
3645 if (initData
.mThread
) {
3646 ::WaitForSingleObject(initData
.mSemaphore
, INFINITE
);
3649 if (!initData
.mWnd
) {
3653 mTransitionWnd
= initData
.mWnd
;
3655 auto data
= new FullscreenTransitionData(initData
.mWnd
);
3662 void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage
,
3665 nsIRunnable
* aCallback
) {
3666 auto data
= static_cast<FullscreenTransitionData
*>(aData
);
3667 nsCOMPtr
<nsIRunnable
> callback
= aCallback
;
3668 UINT msg
= aStage
== eBeforeFullscreenToggle
? WM_FULLSCREEN_TRANSITION_BEFORE
3669 : WM_FULLSCREEN_TRANSITION_AFTER
;
3670 WPARAM wparam
= (WPARAM
)callback
.forget().take();
3671 ::PostMessage(data
->mWnd
, msg
, wparam
, (LPARAM
)aDuration
);
3675 void nsWindow::CleanupFullscreenTransition() {
3676 MOZ_ASSERT(NS_IsMainThread(),
3677 "CleanupFullscreenTransition "
3678 "should only run on the main thread");
3680 mTransitionWnd
= nullptr;
3683 void nsWindow::OnFullscreenWillChange(bool aFullScreen
) {
3684 if (mWidgetListener
) {
3685 mWidgetListener
->FullscreenWillChange(aFullScreen
);
3689 void nsWindow::OnFullscreenChanged(bool aFullScreen
) {
3690 // taskbarInfo will be nullptr pre Windows 7 until Bug 680227 is resolved.
3691 nsCOMPtr
<nsIWinTaskbar
> taskbarInfo
= do_GetService(NS_TASKBAR_CONTRACTID
);
3693 // Notify the taskbar that we will be entering full screen mode.
3694 if (aFullScreen
&& taskbarInfo
) {
3695 taskbarInfo
->PrepareFullScreenHWND(mWnd
, TRUE
);
3698 // If we are going fullscreen, the window size continues to change
3699 // and the window will be reflow again then.
3700 UpdateNonClientMargins(mFrameState
->GetSizeMode(), /* Reflow */ !aFullScreen
);
3702 // Will call hide chrome, reposition window. Note this will
3703 // also cache dimensions for restoration, so it should only
3704 // be called once per fullscreen request.
3705 nsBaseWidget::InfallibleMakeFullScreen(aFullScreen
);
3707 if (mIsVisible
&& !aFullScreen
&&
3708 mFrameState
->GetSizeMode() == nsSizeMode_Normal
) {
3709 // Ensure the window exiting fullscreen get activated. Window
3710 // activation might be bypassed in SetSizeMode.
3711 DispatchFocusToTopLevelWindow(true);
3714 // Notify the taskbar that we have exited full screen mode.
3715 if (!aFullScreen
&& taskbarInfo
) {
3716 taskbarInfo
->PrepareFullScreenHWND(mWnd
, FALSE
);
3719 OnSizeModeChange(mFrameState
->GetSizeMode());
3721 if (mWidgetListener
) {
3722 mWidgetListener
->FullscreenChanged(aFullScreen
);
3726 nsresult
nsWindow::MakeFullScreen(bool aFullScreen
) {
3727 mFrameState
->EnsureFullscreenMode(aFullScreen
);
3731 /**************************************************************
3733 * SECTION: Native data storage
3735 * nsIWidget::GetNativeData
3736 * nsIWidget::FreeNativeData
3738 * Set or clear native data based on a constant.
3740 **************************************************************/
3742 // Return some native data according to aDataType
3743 void* nsWindow::GetNativeData(uint32_t aDataType
) {
3744 switch (aDataType
) {
3745 case NS_NATIVE_TMP_WINDOW
:
3746 return (void*)::CreateWindowExW(
3747 mIsRTL
? WS_EX_LAYOUTRTL
: 0, GetWindowClass(), L
"", WS_CHILD
,
3748 CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, CW_USEDEFAULT
, mWnd
,
3749 nullptr, nsToolkit::mDllInstance
, nullptr);
3750 case NS_NATIVE_WIDGET
:
3751 case NS_NATIVE_WINDOW
:
3752 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID
:
3754 case NS_NATIVE_GRAPHIC
:
3755 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3757 case NS_RAW_NATIVE_IME_CONTEXT
: {
3758 void* pseudoIMEContext
= GetPseudoIMEContext();
3759 if (pseudoIMEContext
) {
3760 return pseudoIMEContext
;
3764 case NS_NATIVE_TSF_THREAD_MGR
:
3765 case NS_NATIVE_TSF_CATEGORY_MGR
:
3766 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR
:
3767 return IMEHandler::GetNativeData(this, aDataType
);
3776 void nsWindow::SetNativeData(uint32_t aDataType
, uintptr_t aVal
) {
3777 NS_ERROR("SetNativeData called with unsupported data type.");
3780 // Free some native data according to aDataType
3781 void nsWindow::FreeNativeData(void* data
, uint32_t aDataType
) {
3782 switch (aDataType
) {
3783 case NS_NATIVE_GRAPHIC
:
3784 case NS_NATIVE_WIDGET
:
3785 case NS_NATIVE_WINDOW
:
3792 /**************************************************************
3794 * SECTION: nsIWidget::SetTitle
3796 * Set the main windows title text.
3798 **************************************************************/
3800 nsresult
nsWindow::SetTitle(const nsAString
& aTitle
) {
3801 const nsString
& strTitle
= PromiseFlatString(aTitle
);
3802 AutoRestore
<bool> sendingText(mSendingSetText
);
3803 mSendingSetText
= true;
3804 ::SendMessageW(mWnd
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)(LPCWSTR
)strTitle
.get());
3808 /**************************************************************
3810 * SECTION: nsIWidget::SetIcon
3812 * Set the main windows icon.
3814 **************************************************************/
3816 void nsWindow::SetBigIcon(HICON aIcon
) {
3818 (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)aIcon
);
3820 ::DestroyIcon(icon
);
3826 void nsWindow::SetSmallIcon(HICON aIcon
) {
3827 HICON icon
= (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_SMALL
,
3830 ::DestroyIcon(icon
);
3836 void nsWindow::SetIcon(const nsAString
& aIconSpec
) {
3837 // Assume the given string is a local identifier for an icon file.
3839 nsCOMPtr
<nsIFile
> iconFile
;
3840 ResolveIconName(aIconSpec
, u
".ico"_ns
, getter_AddRefs(iconFile
));
3841 if (!iconFile
) return;
3843 nsAutoString iconPath
;
3844 iconFile
->GetPath(iconPath
);
3846 // XXX this should use MZLU (see bug 239279)
3851 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3852 ::GetSystemMetrics(SM_CXICON
),
3853 ::GetSystemMetrics(SM_CYICON
), LR_LOADFROMFILE
);
3855 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3856 ::GetSystemMetrics(SM_CXSMICON
),
3857 ::GetSystemMetrics(SM_CYSMICON
), LR_LOADFROMFILE
);
3860 SetBigIcon(bigIcon
);
3862 #ifdef DEBUG_SetIcon
3864 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3865 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3866 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3871 SetSmallIcon(smallIcon
);
3873 #ifdef DEBUG_SetIcon
3875 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3876 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3877 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3883 void nsWindow::SetBigIconNoData() {
3885 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3886 SetBigIcon(bigIcon
);
3889 void nsWindow::SetSmallIconNoData() {
3891 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3892 SetSmallIcon(smallIcon
);
3895 /**************************************************************
3897 * SECTION: nsIWidget::WidgetToScreenOffset
3899 * Return this widget's origin in screen coordinates.
3901 **************************************************************/
3903 LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset() {
3907 ::ClientToScreen(mWnd
, &point
);
3908 return LayoutDeviceIntPoint(point
.x
, point
.y
);
3911 LayoutDeviceIntSize
nsWindow::ClientToWindowSize(
3912 const LayoutDeviceIntSize
& aClientSize
) {
3913 if (mWindowType
== eWindowType_popup
&& !IsPopupWithTitleBar())
3916 // just use (200, 200) as the position
3920 r
.right
= 200 + aClientSize
.width
;
3921 r
.bottom
= 200 + aClientSize
.height
;
3922 ::AdjustWindowRectEx(&r
, WindowStyle(), false, WindowExStyle());
3924 return LayoutDeviceIntSize(r
.right
- r
.left
, r
.bottom
- r
.top
);
3927 /**************************************************************
3929 * SECTION: nsIWidget::EnableDragDrop
3931 * Enables/Disables drag and drop of files on this widget.
3933 **************************************************************/
3935 void nsWindow::EnableDragDrop(bool aEnable
) {
3937 // Return early if the window already closed
3942 if (!mNativeDragTarget
) {
3943 mNativeDragTarget
= new nsNativeDragTarget(this);
3944 mNativeDragTarget
->AddRef();
3945 if (SUCCEEDED(::CoLockObjectExternal((LPUNKNOWN
)mNativeDragTarget
, TRUE
,
3947 ::RegisterDragDrop(mWnd
, (LPDROPTARGET
)mNativeDragTarget
);
3951 if (mWnd
&& mNativeDragTarget
) {
3952 ::RevokeDragDrop(mWnd
);
3953 ::CoLockObjectExternal((LPUNKNOWN
)mNativeDragTarget
, FALSE
, TRUE
);
3954 mNativeDragTarget
->DragCancel();
3955 NS_RELEASE(mNativeDragTarget
);
3960 /**************************************************************
3962 * SECTION: nsIWidget::CaptureMouse
3964 * Enables/Disables system mouse capture.
3966 **************************************************************/
3968 void nsWindow::CaptureMouse(bool aCapture
) {
3969 TRACKMOUSEEVENT mTrack
;
3970 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3971 mTrack
.dwHoverTime
= 0;
3972 mTrack
.hwndTrack
= mWnd
;
3974 mTrack
.dwFlags
= TME_CANCEL
| TME_LEAVE
;
3977 mTrack
.dwFlags
= TME_LEAVE
;
3980 sIsInMouseCapture
= aCapture
;
3981 TrackMouseEvent(&mTrack
);
3984 /**************************************************************
3986 * SECTION: nsIWidget::CaptureRollupEvents
3988 * Dealing with event rollup on destroy for popups. Enables &
3989 * Disables system capture of any and all events that would
3990 * cause a dropdown to be rolled up.
3992 **************************************************************/
3994 void nsWindow::CaptureRollupEvents(nsIRollupListener
* aListener
,
3997 gRollupListener
= aListener
;
3998 if (!sMsgFilterHook
&& !sCallProcHook
&& !sCallMouseHook
) {
3999 RegisterSpecialDropdownHooks();
4001 sProcessHook
= true;
4003 gRollupListener
= nullptr;
4004 sProcessHook
= false;
4005 UnregisterSpecialDropdownHooks();
4009 /**************************************************************
4011 * SECTION: nsIWidget::GetAttention
4013 * Bring this window to the user's attention.
4015 **************************************************************/
4017 // Draw user's attention to this window until it comes to foreground.
4018 nsresult
nsWindow::GetAttention(int32_t aCycleCount
) {
4020 if (!mWnd
) return NS_ERROR_NOT_INITIALIZED
;
4022 HWND flashWnd
= WinUtils::GetTopLevelHWND(mWnd
, false, false);
4023 HWND fgWnd
= ::GetForegroundWindow();
4024 // Don't flash if the flash count is 0 or if the foreground window is our
4025 // window handle or that of our owned-most window.
4026 if (aCycleCount
== 0 || flashWnd
== fgWnd
||
4027 flashWnd
== WinUtils::GetTopLevelHWND(fgWnd
, false, false)) {
4031 DWORD defaultCycleCount
= 0;
4032 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT
, 0, &defaultCycleCount
, 0);
4034 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_ALL
,
4035 aCycleCount
> 0 ? aCycleCount
: defaultCycleCount
, 0};
4036 ::FlashWindowEx(&flashInfo
);
4041 void nsWindow::StopFlashing() {
4042 HWND flashWnd
= mWnd
;
4043 while (HWND ownerWnd
= ::GetWindow(flashWnd
, GW_OWNER
)) {
4044 flashWnd
= ownerWnd
;
4047 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_STOP
, 0, 0};
4048 ::FlashWindowEx(&flashInfo
);
4051 /**************************************************************
4053 * SECTION: nsIWidget::HasPendingInputEvent
4055 * Ask whether there user input events pending. All input events are
4056 * included, including those not targeted at this nsIwidget instance.
4058 **************************************************************/
4060 bool nsWindow::HasPendingInputEvent() {
4061 // If there is pending input or the user is currently
4062 // moving the window then return true.
4063 // Note: When the user is moving the window WIN32 spins
4064 // a separate event loop and input events are not
4065 // reported to the application.
4066 if (HIWORD(GetQueueStatus(QS_INPUT
))) return true;
4067 GUITHREADINFO guiInfo
;
4068 guiInfo
.cbSize
= sizeof(GUITHREADINFO
);
4069 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo
)) return false;
4070 return GUI_INMOVESIZE
== (guiInfo
.flags
& GUI_INMOVESIZE
);
4073 /**************************************************************
4075 * SECTION: nsIWidget::GetWindowRenderer
4077 * Get the window renderer associated with this widget.
4079 **************************************************************/
4081 WindowRenderer
* nsWindow::GetWindowRenderer() {
4082 if (mWindowRenderer
) {
4083 return mWindowRenderer
;
4086 if (!mLocalesChangedObserver
) {
4087 mLocalesChangedObserver
= new LocalesChangedObserver(this);
4091 if (!mWindowRenderer
&& ShouldUseOffMainThreadCompositing()) {
4092 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
4096 if (!mWindowRenderer
) {
4097 MOZ_ASSERT(!mCompositorSession
&& !mCompositorBridgeChild
);
4098 MOZ_ASSERT(!mCompositorWidgetDelegate
);
4100 // Ensure we have a widget proxy even if we're not using the compositor,
4101 // since all our transparent window handling lives there.
4102 WinCompositorWidgetInitData
initData(
4103 reinterpret_cast<uintptr_t>(mWnd
),
4104 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
4105 mTransparencyMode
, mFrameState
->GetSizeMode());
4106 // If we're not using the compositor, the options don't actually matter.
4107 CompositorOptions
options(false, false);
4108 mBasicLayersSurface
=
4109 new InProcessWinCompositorWidget(initData
, options
, this);
4110 mCompositorWidgetDelegate
= mBasicLayersSurface
;
4111 mWindowRenderer
= CreateFallbackRenderer();
4114 NS_ASSERTION(mWindowRenderer
, "Couldn't provide a valid window renderer.");
4116 if (mWindowRenderer
) {
4117 // Update the size constraints now that the layer manager has been
4119 KnowsCompositor
* knowsCompositor
= mWindowRenderer
->AsKnowsCompositor();
4120 if (knowsCompositor
) {
4121 SizeConstraints c
= mSizeConstraints
;
4122 mMaxTextureSize
= knowsCompositor
->GetMaxTextureSize();
4123 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
4124 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
4125 nsBaseWidget::SetSizeConstraints(c
);
4129 return mWindowRenderer
;
4132 /**************************************************************
4134 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
4136 * Called to connect the nsWindow to the delegate providing
4137 * platform compositing API access.
4139 **************************************************************/
4141 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate
* delegate
) {
4143 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
4144 MOZ_ASSERT(mCompositorWidgetDelegate
,
4145 "nsWindow::SetCompositorWidgetDelegate called with a "
4146 "non-PlatformCompositorWidgetDelegate");
4148 mCompositorWidgetDelegate
= nullptr;
4152 /**************************************************************
4154 * SECTION: nsIWidget::OnDefaultButtonLoaded
4156 * Called after the dialog is loaded and it has a default button.
4158 **************************************************************/
4160 nsresult
nsWindow::OnDefaultButtonLoaded(
4161 const LayoutDeviceIntRect
& aButtonRect
) {
4162 if (aButtonRect
.IsEmpty()) return NS_OK
;
4164 // Don't snap when we are not active.
4165 HWND activeWnd
= ::GetActiveWindow();
4166 if (activeWnd
!= ::GetForegroundWindow() ||
4167 WinUtils::GetTopLevelHWND(mWnd
, true) !=
4168 WinUtils::GetTopLevelHWND(activeWnd
, true)) {
4172 bool isAlwaysSnapCursor
=
4173 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
4175 if (!isAlwaysSnapCursor
) {
4176 BOOL snapDefaultButton
;
4177 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON
, 0, &snapDefaultButton
,
4183 LayoutDeviceIntRect widgetRect
= GetScreenBounds();
4184 LayoutDeviceIntRect
buttonRect(aButtonRect
+ widgetRect
.TopLeft());
4186 LayoutDeviceIntPoint
centerOfButton(buttonRect
.X() + buttonRect
.Width() / 2,
4187 buttonRect
.Y() + buttonRect
.Height() / 2);
4188 // The center of the button can be outside of the widget.
4189 // E.g., it could be hidden by scrolling.
4190 if (!widgetRect
.Contains(centerOfButton
)) {
4194 if (!::SetCursorPos(centerOfButton
.x
, centerOfButton
.y
)) {
4195 NS_ERROR("SetCursorPos failed");
4196 return NS_ERROR_FAILURE
;
4201 void nsWindow::UpdateThemeGeometries(
4202 const nsTArray
<ThemeGeometry
>& aThemeGeometries
) {
4203 RefPtr
<WebRenderLayerManager
> layerManager
=
4204 GetWindowRenderer() ? GetWindowRenderer()->AsWebRender() : nullptr;
4205 if (!layerManager
) {
4210 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
4214 mWindowButtonsRect
= Nothing();
4216 if (!IsWin10OrLater()) {
4217 for (size_t i
= 0; i
< aThemeGeometries
.Length(); i
++) {
4218 if (aThemeGeometries
[i
].mType
==
4219 nsNativeThemeWin::eThemeGeometryTypeWindowButtons
) {
4220 LayoutDeviceIntRect bounds
= aThemeGeometries
[i
].mRect
;
4221 // Extend the bounds by one pixel to the right, because that's how much
4222 // the actual window button shape extends past the client area of the
4223 // window (and overlaps the right window frame).
4224 bounds
.SetWidth(bounds
.Width() + 1);
4225 if (!mWindowButtonsRect
) {
4226 mWindowButtonsRect
= Some(bounds
);
4233 void nsWindow::AddWindowOverlayWebRenderCommands(
4234 layers::WebRenderBridgeChild
* aWrBridge
, wr::DisplayListBuilder
& aBuilder
,
4235 wr::IpcResourceUpdateQueue
& aResources
) {
4236 if (mWindowButtonsRect
) {
4237 wr::LayoutRect rect
= wr::ToLayoutRect(*mWindowButtonsRect
);
4238 aBuilder
.PushClearRect(rect
);
4242 uint32_t nsWindow::GetMaxTouchPoints() const {
4243 return WinUtils::GetMaxTouchPoints();
4246 void nsWindow::SetWindowClass(const nsAString
& xulWinType
) {
4247 mIsEarlyBlankWindow
= xulWinType
.EqualsLiteral("navigator:blank");
4250 /**************************************************************
4251 **************************************************************
4253 ** BLOCK: Moz Events
4255 ** Moz GUI event management.
4257 **************************************************************
4258 **************************************************************/
4260 /**************************************************************
4262 * SECTION: Mozilla event initialization
4264 * Helpers for initializing moz events.
4266 **************************************************************/
4268 // Event initialization
4269 void nsWindow::InitEvent(WidgetGUIEvent
& event
, LayoutDeviceIntPoint
* aPoint
) {
4270 if (nullptr == aPoint
) { // use the point from the event
4271 // get the message position in client coordinates
4272 if (mWnd
!= nullptr) {
4273 DWORD pos
= ::GetMessagePos();
4276 cpos
.x
= GET_X_LPARAM(pos
);
4277 cpos
.y
= GET_Y_LPARAM(pos
);
4279 ::ScreenToClient(mWnd
, &cpos
);
4280 event
.mRefPoint
= LayoutDeviceIntPoint(cpos
.x
, cpos
.y
);
4282 event
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
4285 // use the point override if provided
4286 event
.mRefPoint
= *aPoint
;
4289 event
.AssignEventTime(CurrentMessageWidgetEventTime());
4292 WidgetEventTime
nsWindow::CurrentMessageWidgetEventTime() const {
4293 LONG messageTime
= ::GetMessageTime();
4294 return WidgetEventTime(messageTime
, GetMessageTimeStamp(messageTime
));
4297 /**************************************************************
4299 * SECTION: Moz event dispatch helpers
4301 * Helpers for dispatching different types of moz events.
4303 **************************************************************/
4305 // Main event dispatch. Invokes callback and ProcessEvent method on
4306 // Event Listener object. Part of nsIWidget.
4307 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* event
,
4308 nsEventStatus
& aStatus
) {
4309 #ifdef WIDGET_DEBUG_OUTPUT
4310 debug_DumpEvent(stdout
, event
->mWidget
, event
, "something", (int32_t)mWnd
);
4311 #endif // WIDGET_DEBUG_OUTPUT
4313 aStatus
= nsEventStatus_eIgnore
;
4315 // Top level windows can have a view attached which requires events be sent
4316 // to the underlying base window and the view. Added when we combined the
4317 // base chrome window with the main content child for nc client area (title
4319 if (mAttachedWidgetListener
) {
4320 aStatus
= mAttachedWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4321 } else if (mWidgetListener
) {
4322 aStatus
= mWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4325 // the window can be destroyed during processing of seemingly innocuous events
4326 // like, say, mousedowns due to the magic of scripting. mousedowns will return
4327 // nsEventStatus_eIgnore, which causes problems with the deleted window.
4329 if (mOnDestroyCalled
) aStatus
= nsEventStatus_eConsumeNoDefault
;
4333 bool nsWindow::DispatchStandardEvent(EventMessage aMsg
) {
4334 WidgetGUIEvent
event(true, aMsg
, this);
4337 bool result
= DispatchWindowEvent(event
);
4341 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent
* event
) {
4342 nsEventStatus status
= DispatchInputEvent(event
).mContentStatus
;
4343 return ConvertStatus(status
);
4346 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent
* aEvent
) {
4347 nsEventStatus status
;
4348 DispatchEvent(aEvent
, status
);
4349 return ConvertStatus(status
);
4352 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent
* aEvent
) {
4353 nsEventStatus status
=
4354 DispatchInputEvent(aEvent
->AsInputEvent()).mContentStatus
;
4355 return ConvertStatus(status
);
4358 // Recursively dispatch synchronous paints for nsIWidget
4359 // descendants with invalidated rectangles.
4360 BOOL CALLBACK
nsWindow::DispatchStarvedPaints(HWND aWnd
, LPARAM aMsg
) {
4361 LONG_PTR proc
= ::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
4362 if (proc
== (LONG_PTR
)&nsWindow::WindowProc
) {
4363 // its one of our windows so check to see if it has a
4364 // invalidated rect. If it does. Dispatch a synchronous
4366 if (GetUpdateRect(aWnd
, nullptr, FALSE
)) VERIFY(::UpdateWindow(aWnd
));
4371 // Check for pending paints and dispatch any pending paint
4372 // messages for any nsIWidget which is a descendant of the
4373 // top-level window that *this* window is embedded within.
4375 // Note: We do not dispatch pending paint messages for non
4376 // nsIWidget managed windows.
4377 void nsWindow::DispatchPendingEvents() {
4380 "We were asked to dispatch pending events during painting, "
4381 "denying since that's unsafe.");
4385 // We need to ensure that reflow events do not get starved.
4386 // At the same time, we don't want to recurse through here
4387 // as that would prevent us from dispatching starved paints.
4388 static int recursionBlocker
= 0;
4389 if (recursionBlocker
++ == 0) {
4390 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4394 // Quickly check to see if there are any paint events pending,
4395 // but only dispatch them if it has been long enough since the
4396 // last paint completed.
4397 if (::GetQueueStatus(QS_PAINT
) &&
4398 ((TimeStamp::Now() - mLastPaintEndTime
).ToMilliseconds() >= 50)) {
4399 // Find the top level window.
4400 HWND topWnd
= WinUtils::GetTopLevelHWND(mWnd
);
4402 // Dispatch pending paints for topWnd and all its descendant windows.
4403 // Note: EnumChildWindows enumerates all descendant windows not just
4404 // the children (but not the window itself).
4405 nsWindow::DispatchStarvedPaints(topWnd
, 0);
4406 ::EnumChildWindows(topWnd
, nsWindow::DispatchStarvedPaints
, 0);
4410 void nsWindow::DispatchCustomEvent(const nsString
& eventName
) {
4411 if (Document
* doc
= GetDocument()) {
4412 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
4413 win
->DispatchCustomEvent(eventName
, ChromeOnlyDispatch::eYes
);
4418 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage
,
4419 LayoutDeviceIntPoint aEventPoint
) {
4420 // Allow users to start dragging by double-tapping.
4421 if (aEventMessage
== eMouseDoubleClick
) {
4425 // In chrome UI, allow touchdownstartsdrag attributes
4426 // to cause any touchdown event to trigger a drag.
4427 if (aEventMessage
== eMouseDown
) {
4428 WidgetMouseEvent
hittest(true, eMouseHitTest
, this,
4429 WidgetMouseEvent::eReal
);
4430 hittest
.mRefPoint
= aEventPoint
;
4431 hittest
.mIgnoreRootScrollFrame
= true;
4432 hittest
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
4433 DispatchInputEvent(&hittest
);
4435 if (EventTarget
* target
= hittest
.GetDOMEventTarget()) {
4436 if (nsIContent
* content
= nsIContent::FromEventTarget(target
)) {
4437 // Check if the element or any parent element has the
4438 // attribute we're looking for.
4439 for (Element
* element
= content
->GetAsElementOrParentElement(); element
;
4440 element
= element
->GetParentElement()) {
4441 nsAutoString startDrag
;
4442 element
->GetAttribute(u
"touchdownstartsdrag"_ns
, startDrag
);
4443 if (!startDrag
.IsEmpty()) {
4454 // Deal with all sort of mouse event
4455 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage
, WPARAM wParam
,
4456 LPARAM lParam
, bool aIsContextMenuKey
,
4457 int16_t aButton
, uint16_t aInputSource
,
4458 WinPointerInfo
* aPointerInfo
,
4460 bool result
= false;
4464 if (!mWidgetListener
) {
4468 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
4469 LayoutDeviceIntPoint mpScreen
= eventPoint
+ WidgetToScreenOffset();
4471 // Suppress mouse moves caused by widget creation. Make sure to do this early
4472 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4474 if (aEventMessage
== eMouseMove
) {
4475 if ((sLastMouseMovePoint
.x
== mpScreen
.x
) &&
4476 (sLastMouseMovePoint
.y
== mpScreen
.y
)) {
4479 sLastMouseMovePoint
.x
= mpScreen
.x
;
4480 sLastMouseMovePoint
.y
= mpScreen
.y
;
4483 if (!aIgnoreAPZ
&& WinUtils::GetIsMouseFromTouch(aEventMessage
)) {
4485 // If mTouchWindow is true, then we must have APZ enabled and be
4486 // feeding it raw touch events. In that case we only want to
4487 // send touch-generated mouse events to content if they should
4488 // start a touch-based drag-and-drop gesture, such as on
4489 // double-tapping or when tapping elements marked with the
4490 // touchdownstartsdrag attribute in chrome UI.
4492 if (TouchEventShouldStartDrag(aEventMessage
, eventPoint
)) {
4493 aEventMessage
= eMouseTouchDrag
;
4500 uint32_t pointerId
=
4501 aPointerInfo
? aPointerInfo
->pointerId
: MOUSE_POINTERID();
4503 // Since it is unclear whether a user will use the digitizer,
4504 // Postpone initialization until first PEN message will be found.
4505 if (MouseEvent_Binding::MOZ_SOURCE_PEN
== aInputSource
4506 // Messages should be only at topLevel window.
4507 && nsWindowType::eWindowType_toplevel
== mWindowType
4508 // Currently this scheme is used only when pointer events is enabled.
4509 && InkCollector::sInkCollector
) {
4510 InkCollector::sInkCollector
->SetTarget(mWnd
);
4511 InkCollector::sInkCollector
->SetPointerId(pointerId
);
4514 switch (aEventMessage
) {
4519 // eMouseMove and eMouseExitFromWidget are here because we need to make
4520 // sure capture flag isn't left on after a drag where we wouldn't see a
4521 // button up message (see bug 324131).
4524 case eMouseExitFromWidget
:
4525 if (!(wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) &&
4527 CaptureMouse(false);
4535 WidgetMouseEvent
event(true, aEventMessage
, this, WidgetMouseEvent::eReal
,
4536 aIsContextMenuKey
? WidgetMouseEvent::eContextMenuKey
4537 : WidgetMouseEvent::eNormal
);
4538 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
) {
4539 LayoutDeviceIntPoint
zero(0, 0);
4540 InitEvent(event
, &zero
);
4542 InitEvent(event
, &eventPoint
);
4545 ModifierKeyState modifierKeyState
;
4546 modifierKeyState
.InitInputEvent(event
);
4548 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4549 // event in the web content for blocking web content to prevent its default.
4550 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4551 // this should not block web page to prevent its default. I.e., it should
4552 // behave same as ContextMenu key without Shift key.
4553 // XXX Should we allow to block web page to prevent its default with
4554 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4555 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
&& event
.IsShift() &&
4556 NativeKey::LastKeyOrCharMSG().message
== WM_SYSKEYDOWN
&&
4557 NativeKey::LastKeyOrCharMSG().wParam
== VK_F10
) {
4558 event
.mModifiers
&= ~MODIFIER_SHIFT
;
4561 event
.mButton
= aButton
;
4562 event
.mInputSource
= aInputSource
;
4564 // Mouse events from Windows WM_POINTER*. Fill more information in
4565 // WidgetMouseEvent.
4566 event
.AssignPointerHelperData(*aPointerInfo
);
4567 event
.mPressure
= aPointerInfo
->mPressure
;
4568 event
.mButtons
= aPointerInfo
->mButtons
;
4570 // If we get here the mouse events must be from non-touch sources, so
4571 // convert it to pointer events as well
4572 event
.convertToPointer
= true;
4573 event
.pointerId
= pointerId
;
4576 bool insideMovementThreshold
=
4577 (DeprecatedAbs(sLastMousePoint
.x
- eventPoint
.x
) <
4578 (short)::GetSystemMetrics(SM_CXDOUBLECLK
)) &&
4579 (DeprecatedAbs(sLastMousePoint
.y
- eventPoint
.y
) <
4580 (short)::GetSystemMetrics(SM_CYDOUBLECLK
));
4584 case MouseButton::ePrimary
:
4585 eventButton
= VK_LBUTTON
;
4587 case MouseButton::eMiddle
:
4588 eventButton
= VK_MBUTTON
;
4590 case MouseButton::eSecondary
:
4591 eventButton
= VK_RBUTTON
;
4598 // Doubleclicks are used to set the click count, then changed to mousedowns
4599 // We're going to time double-clicks from mouse *up* to next mouse *down*
4600 LONG curMsgTime
= ::GetMessageTime();
4602 switch (aEventMessage
) {
4603 case eMouseDoubleClick
:
4604 event
.mMessage
= eMouseDown
;
4605 event
.mButton
= aButton
;
4606 sLastClickCount
= 2;
4607 sLastMouseDownTime
= curMsgTime
;
4610 // remember when this happened for the next mouse down
4611 sLastMousePoint
.x
= eventPoint
.x
;
4612 sLastMousePoint
.y
= eventPoint
.y
;
4613 sLastMouseButton
= eventButton
;
4616 // now look to see if we want to convert this to a double- or triple-click
4617 if (((curMsgTime
- sLastMouseDownTime
) < (LONG
)::GetDoubleClickTime()) &&
4618 insideMovementThreshold
&& eventButton
== sLastMouseButton
) {
4621 // reset the click count, to count *this* click
4622 sLastClickCount
= 1;
4624 // Set last Click time on MouseDown only
4625 sLastMouseDownTime
= curMsgTime
;
4628 if (!insideMovementThreshold
) {
4629 sLastClickCount
= 0;
4632 case eMouseExitFromWidget
:
4634 Some(IsTopLevelMouseExit(mWnd
) ? WidgetMouseEvent::ePlatformTopLevel
4635 : WidgetMouseEvent::ePlatformChild
);
4640 event
.mClickCount
= sLastClickCount
;
4643 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
4644 ("Msg Time: %d Click Count: %d\n", curMsgTime
, event
.mClickCount
));
4647 // call the event callback
4648 if (mWidgetListener
) {
4649 if (aEventMessage
== eMouseMove
) {
4650 LayoutDeviceIntRect rect
= GetBounds();
4653 if (rect
.Contains(event
.mRefPoint
)) {
4654 if (sCurrentWindow
== nullptr || sCurrentWindow
!= this) {
4655 if ((nullptr != sCurrentWindow
) && (!sCurrentWindow
->mInDtor
)) {
4656 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4657 sCurrentWindow
->DispatchMouseEvent(
4658 eMouseExitFromWidget
, wParam
, pos
, false, MouseButton::ePrimary
,
4659 aInputSource
, aPointerInfo
);
4661 sCurrentWindow
= this;
4663 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4664 sCurrentWindow
->DispatchMouseEvent(
4665 eMouseEnterIntoWidget
, wParam
, pos
, false,
4666 MouseButton::ePrimary
, aInputSource
, aPointerInfo
);
4670 } else if (aEventMessage
== eMouseExitFromWidget
) {
4671 if (sCurrentWindow
== this) {
4672 sCurrentWindow
= nullptr;
4676 result
= ConvertStatus(DispatchInputEvent(&event
).mContentStatus
);
4678 // Release the widget with NS_IF_RELEASE() just in case
4679 // the context menu key code in EventListenerManager::HandleEvent()
4680 // released it already.
4687 HWND
nsWindow::GetTopLevelForFocus(HWND aCurWnd
) {
4688 // retrieve the toplevel window or dialogue
4689 HWND toplevelWnd
= nullptr;
4691 toplevelWnd
= aCurWnd
;
4692 nsWindow
* win
= WinUtils::GetNSWindowPtr(aCurWnd
);
4694 if (win
->mWindowType
== eWindowType_toplevel
||
4695 win
->mWindowType
== eWindowType_dialog
) {
4700 aCurWnd
= ::GetParent(aCurWnd
); // Parent or owner (if has no parent)
4705 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate
) {
4707 sJustGotActivate
= false;
4709 sJustGotDeactivate
= false;
4710 mLastKillFocusWindow
= nullptr;
4712 HWND toplevelWnd
= GetTopLevelForFocus(mWnd
);
4715 nsWindow
* win
= WinUtils::GetNSWindowPtr(toplevelWnd
);
4716 if (win
&& win
->mWidgetListener
) {
4718 win
->mWidgetListener
->WindowActivated();
4720 win
->mWidgetListener
->WindowDeactivated();
4726 HWND
nsWindow::WindowAtMouse() {
4727 DWORD pos
= ::GetMessagePos();
4729 mp
.x
= GET_X_LPARAM(pos
);
4730 mp
.y
= GET_Y_LPARAM(pos
);
4731 return ::WindowFromPoint(mp
);
4734 bool nsWindow::IsTopLevelMouseExit(HWND aWnd
) {
4735 HWND mouseWnd
= WindowAtMouse();
4737 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4738 // (which includes the non-client area). If the mouse has moved into
4739 // the non-client area, we should treat it as a top-level exit.
4740 HWND mouseTopLevel
= WinUtils::GetTopLevelHWND(mouseWnd
);
4741 if (mouseWnd
== mouseTopLevel
) return true;
4743 return WinUtils::GetTopLevelHWND(aWnd
) != mouseTopLevel
;
4746 /**************************************************************
4750 * IPC related helpers.
4752 **************************************************************/
4755 bool nsWindow::IsAsyncResponseEvent(UINT aMsg
, LRESULT
& aResult
) {
4760 case WM_WINDOWPOSCHANGING
:
4761 case WM_WINDOWPOSCHANGED
:
4762 case WM_PARENTNOTIFY
:
4763 case WM_ACTIVATEAPP
:
4766 case WM_CHILDACTIVATE
:
4767 case WM_IME_SETCONTEXT
:
4771 case WM_MOUSEACTIVATE
:
4772 case WM_CONTEXTMENU
:
4776 case WM_SETTINGCHANGE
:
4784 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4792 void nsWindow::IPCWindowProcHandler(UINT
& msg
, WPARAM
& wParam
, LPARAM
& lParam
) {
4794 msg
!= WM_GETOBJECT
,
4795 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4796 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4798 // Modal UI being displayed in windowless plugins.
4799 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4800 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4802 if (IsAsyncResponseEvent(msg
, res
)) {
4808 // Handle certain sync plugin events sent to the parent which
4809 // trigger ipc calls that result in deadlocks.
4812 bool handled
= false;
4815 // Windowless flash sending WM_ACTIVATE events to the main window
4816 // via calls to ShowWindow.
4818 if (lParam
!= 0 && LOWORD(wParam
) == WA_ACTIVE
&&
4819 IsWindow((HWND
)lParam
)) {
4820 // Check for Adobe Reader X sync activate message from their
4821 // helper window and ignore. Fixes an annoying focus problem.
4822 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) ==
4824 wchar_t szClass
[10];
4825 HWND focusWnd
= (HWND
)lParam
;
4826 if (IsWindowVisible(focusWnd
) &&
4827 GetClassNameW(focusWnd
, szClass
,
4828 sizeof(szClass
) / sizeof(char16_t
)) &&
4829 !wcscmp(szClass
, L
"Edit") &&
4830 !WinUtils::IsOurProcessWindow(focusWnd
)) {
4837 // Plugins taking or losing focus triggering focus app messages.
4840 // Windowed plugins that pass sys key events to defwndproc generate
4841 // WM_SYSCOMMAND events to the main window.
4843 // Windowed plugins that fire context menu selection events to parent
4845 case WM_CONTEXTMENU
:
4846 // IME events fired as a result of synchronous focus changes
4847 case WM_IME_SETCONTEXT
:
4853 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4854 ReplyMessage(dwResult
);
4858 /**************************************************************
4859 **************************************************************
4861 ** BLOCK: Native events
4863 ** Main Windows message handlers and OnXXX handlers for
4864 ** Windows event handling.
4866 **************************************************************
4867 **************************************************************/
4869 /**************************************************************
4871 * SECTION: Wind proc.
4873 * The main Windows event procedures and associated
4874 * message processing methods.
4876 **************************************************************/
4878 static bool DisplaySystemMenu(HWND hWnd
, nsSizeMode sizeMode
, bool isRtl
,
4879 int32_t x
, int32_t y
) {
4880 HMENU hMenu
= GetSystemMenu(hWnd
, FALSE
);
4883 mii
.cbSize
= sizeof(MENUITEMINFO
);
4884 mii
.fMask
= MIIM_STATE
;
4887 // update the options
4888 mii
.fState
= MF_ENABLED
;
4889 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4890 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4891 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4892 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4893 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4895 mii
.fState
= MF_GRAYED
;
4897 case nsSizeMode_Fullscreen
:
4898 // intentional fall through
4899 case nsSizeMode_Maximized
:
4900 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4901 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4902 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4904 case nsSizeMode_Minimized
:
4905 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4907 case nsSizeMode_Normal
:
4908 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4910 case nsSizeMode_Invalid
:
4911 NS_ASSERTION(false, "Did the argument come from invalid IPC?");
4914 MOZ_ASSERT_UNREACHABLE("Unhnalded nsSizeMode value detected");
4917 LPARAM cmd
= TrackPopupMenu(
4919 (TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
| TPM_TOPALIGN
|
4920 (isRtl
? TPM_RIGHTALIGN
: TPM_LEFTALIGN
)),
4921 x
, y
, 0, hWnd
, nullptr);
4923 PostMessage(hWnd
, WM_SYSCOMMAND
, cmd
, 0);
4930 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4931 // exceptions and passes the real work to WindowProcInternal. See bug 587406
4932 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
4933 LRESULT CALLBACK
nsWindow::WindowProc(HWND hWnd
, UINT msg
, WPARAM wParam
,
4935 mozilla::ipc::CancelCPOWs();
4937 BackgroundHangMonitor().NotifyActivity();
4939 return mozilla::CallWindowProcCrashProtected(WindowProcInternal
, hWnd
, msg
,
4943 LRESULT CALLBACK
nsWindow::WindowProcInternal(HWND hWnd
, UINT msg
,
4944 WPARAM wParam
, LPARAM lParam
) {
4945 if (::GetWindowLongPtrW(hWnd
, GWLP_ID
) == eFakeTrackPointScrollableID
) {
4946 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4947 if (msg
== WM_HSCROLL
) {
4948 // Route WM_HSCROLL messages to the main window.
4949 hWnd
= ::GetParent(::GetParent(hWnd
));
4951 // Handle all other messages with its original window procedure.
4952 WNDPROC prevWindowProc
= (WNDPROC
)::GetWindowLongPtr(hWnd
, GWLP_USERDATA
);
4953 return ::CallWindowProcW(prevWindowProc
, hWnd
, msg
, wParam
, lParam
);
4957 if (msg
== MOZ_WM_TRACE
) {
4958 // This is a tracer event for measuring event loop latency.
4959 // See WidgetTraceEvent.cpp for more details.
4960 mozilla::SignalTracerThread();
4964 // Get the window which caused the event and ask it to process the message
4965 nsWindow
* targetWindow
= WinUtils::GetNSWindowPtr(hWnd
);
4966 NS_ASSERTION(targetWindow
, "nsWindow* is null!");
4967 if (!targetWindow
) return ::DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4969 // Hold the window for the life of this method, in case it gets
4970 // destroyed during processing, unless we're in the dtor already.
4971 nsCOMPtr
<nsIWidget
> kungFuDeathGrip
;
4972 if (!targetWindow
->mInDtor
) kungFuDeathGrip
= targetWindow
;
4974 targetWindow
->IPCWindowProcHandler(msg
, wParam
, lParam
);
4976 // Create this here so that we store the last rolled up popup until after
4977 // the event has been processed.
4978 nsAutoRollup autoRollup
;
4980 LRESULT popupHandlingResult
;
4981 if (DealWithPopups(hWnd
, msg
, wParam
, lParam
, &popupHandlingResult
))
4982 return popupHandlingResult
;
4984 // Call ProcessMessage
4986 if (targetWindow
->ProcessMessage(msg
, wParam
, lParam
, &retValue
)) {
4990 LRESULT res
= ::CallWindowProcW(targetWindow
->GetPrevWindowProc(), hWnd
, msg
,
4996 const char16_t
* GetQuitType() {
4997 if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART
, false)) {
4998 DWORD cchCmdLine
= 0;
4999 HRESULT rc
= ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
5000 &cchCmdLine
, nullptr);
5002 return u
"os-restart";
5008 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage
, WPARAM
& aWParam
,
5010 MSGResult
& aResult
) {
5011 if (mWindowHook
.Notify(mWnd
, aMessage
, aWParam
, aLParam
, aResult
)) {
5015 if (IMEHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
, aResult
)) {
5019 if (MouseScrollHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
,
5027 // The main windows message processing method. Wraps ProcessMessageInternal so
5028 // we can log aRetValue.
5029 bool nsWindow::ProcessMessage(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
5030 LRESULT
* aRetValue
) {
5031 bool result
= ProcessMessageInternal(msg
, wParam
, lParam
, aRetValue
);
5033 // SHOW_REPEAT_EVENTS indicates whether to show all (repeating) events,
5034 // SHOW_MOUSEMOVE_EVENTS indicates whether to show mouse move events.
5035 // See nsWindowDbg for details.
5036 PrintEvent(msg
, wParam
, lParam
, *aRetValue
, result
, SHOW_REPEAT_EVENTS
,
5037 SHOW_MOUSEMOVE_EVENTS
);
5042 // The main windows message processing method. Called by ProcessMessage.
5043 bool nsWindow::ProcessMessageInternal(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
5044 LRESULT
* aRetValue
) {
5045 MSGResult
msgResult(aRetValue
);
5046 if (ExternalHandlerProcessMessage(msg
, wParam
, lParam
, msgResult
)) {
5047 return (msgResult
.mConsumed
|| !mWnd
);
5050 bool result
= false; // call the default nsWindow proc
5053 // Glass hit testing w/custom transparent margins
5054 LRESULT dwmHitResult
;
5055 if (mCustomNonClient
&&
5056 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled() &&
5057 /* We don't do this for win10 glass with a custom titlebar,
5058 * in order to avoid the caption buttons breaking. */
5059 !(IsWin10OrLater() && HasGlass()) &&
5060 DwmDefWindowProc(mWnd
, msg
, wParam
, lParam
, &dwmHitResult
)) {
5061 *aRetValue
= dwmHitResult
;
5065 // (Large blocks of code should be broken out into OnEvent handlers.)
5067 // WM_QUERYENDSESSION must be handled by all windows.
5068 // Otherwise Windows thinks the window can just be killed at will.
5069 case WM_QUERYENDSESSION
:
5070 if (sCanQuit
== TRI_UNKNOWN
) {
5071 // Ask if it's ok to quit, and store the answer until we
5072 // get WM_ENDSESSION signaling the round is complete.
5073 nsCOMPtr
<nsIObserverService
> obsServ
=
5074 mozilla::services::GetObserverService();
5075 nsCOMPtr
<nsISupportsPRBool
> cancelQuit
=
5076 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
5077 cancelQuit
->SetData(false);
5079 const char16_t
* quitType
= GetQuitType();
5080 obsServ
->NotifyObservers(cancelQuit
, "quit-application-requested",
5084 cancelQuit
->GetData(&abortQuit
);
5085 sCanQuit
= abortQuit
? TRI_FALSE
: TRI_TRUE
;
5087 *aRetValue
= sCanQuit
? TRUE
: FALSE
;
5091 case MOZ_WM_STARTA11Y
:
5092 #if defined(ACCESSIBILITY)
5093 Unused
<< GetAccessible();
5101 case MOZ_WM_APP_QUIT
:
5102 if (msg
== MOZ_WM_APP_QUIT
|| (wParam
== TRUE
&& sCanQuit
== TRI_TRUE
)) {
5103 // Let's fake a shutdown sequence without actually closing windows etc.
5104 // to avoid Windows killing us in the middle. A proper shutdown would
5105 // require having a chance to pump some messages. Unfortunately
5106 // Windows won't let us do that. Bug 212316.
5107 nsCOMPtr
<nsIObserverService
> obsServ
=
5108 mozilla::services::GetObserverService();
5109 const char16_t
* syncShutdown
= u
"syncShutdown";
5110 const char16_t
* quitType
= GetQuitType();
5112 AppShutdown::Init(AppShutdownMode::Normal
, 0);
5114 obsServ
->NotifyObservers(nullptr, "quit-application-granted",
5116 obsServ
->NotifyObservers(nullptr, "quit-application-forced", nullptr);
5118 AppShutdown::OnShutdownConfirmed();
5120 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed
,
5122 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown
,
5124 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown
,
5126 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown
, nullptr);
5127 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM
,
5129 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry
,
5132 AppShutdown::DoImmediateExit();
5134 sCanQuit
= TRI_UNKNOWN
;
5138 case WM_SYSCOLORCHANGE
:
5139 // No need to invalidate layout for system color changes, but we need to
5140 // invalidate style.
5141 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
5144 case WM_THEMECHANGED
: {
5145 // Before anything else, push updates to child processes
5146 WinContentSystemParameters::GetSingleton()->OnThemeChanged();
5148 // Update non-client margin offsets
5149 UpdateNonClientMargins();
5150 nsUXThemeData::UpdateNativeThemeInfo();
5152 // We assume pretty much everything could've changed here.
5153 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
5155 UpdateDarkModeToolbar();
5157 // Invalidate the window so that the repaint will
5158 // pick up the new theme.
5159 Invalidate(true, true, true);
5162 case WM_WTSSESSION_CHANGE
: {
5164 case WTS_CONSOLE_CONNECT
:
5165 case WTS_REMOTE_CONNECT
:
5166 case WTS_SESSION_UNLOCK
:
5167 // When a session becomes visible, we should invalidate.
5168 Invalidate(true, true, true);
5175 case WM_FONTCHANGE
: {
5176 // We only handle this message for the hidden window,
5177 // as we only need to update the (global) font list once
5178 // for any given change, not once per window!
5179 if (mWindowType
!= eWindowType_invisible
) {
5184 bool didChange
= false;
5186 // update the global font list
5187 nsCOMPtr
<nsIFontEnumerator
> fontEnum
=
5188 do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv
);
5189 if (NS_SUCCEEDED(rv
)) {
5190 fontEnum
->UpdateFontList(&didChange
);
5192 gfxPlatform::ForceGlobalReflow(gfxPlatform::NeedsReframe::Yes
);
5194 } // if (NS_SUCCEEDED(rv))
5197 case WM_SETTINGCHANGE
: {
5198 if (wParam
== SPI_SETCLIENTAREAANIMATION
||
5199 // CaretBlinkTime is cached in nsLookAndFeel
5200 wParam
== SPI_SETKEYBOARDDELAY
) {
5201 // This only affects reduced motion settings and and carent blink time,
5202 // so no need to invalidate style / layout.
5203 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5206 if (wParam
== SPI_SETFONTSMOOTHING
||
5207 wParam
== SPI_SETFONTSMOOTHINGTYPE
) {
5208 gfxDWriteFont::UpdateSystemTextVars();
5211 if (auto lParamString
= reinterpret_cast<const wchar_t*>(lParam
)) {
5212 if (!wcscmp(lParamString
, L
"ImmersiveColorSet")) {
5213 // This affects system colors (-moz-win-accentcolor), so gotta pass
5215 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
5219 // UserInteractionMode, ConvertibleSlateMode, SystemDockMode may cause
5220 // @media(pointer) queries to change, which layout needs to know about
5222 // (WM_SETTINGCHANGE will be sent to all top-level windows, so we
5223 // only respond to the hidden top-level window to avoid hammering
5224 // layout with a bunch of NotifyThemeChanged() calls)
5226 if (mWindowType
== eWindowType_invisible
) {
5227 if (!wcscmp(lParamString
, L
"UserInteractionMode") ||
5228 !wcscmp(lParamString
, L
"ConvertibleSlateMode") ||
5229 !wcscmp(lParamString
, L
"SystemDockMode")) {
5230 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5231 WindowsUIUtils::UpdateInTabletMode();
5236 // SPI_GETMOUSEVANISH sends WM_SETTINGCHANGE when changed but does
5237 // not include identifiers in the parameters. WM_SETTINGCHANGE docs
5238 // recommend updating all cached settings when this message is received
5240 GetMouseVanishSystemPref(true);
5243 case WM_DEVICECHANGE
: {
5244 if (wParam
== DBT_DEVICEARRIVAL
|| wParam
== DBT_DEVICEREMOVECOMPLETE
) {
5245 DEV_BROADCAST_HDR
* hdr
= reinterpret_cast<DEV_BROADCAST_HDR
*>(lParam
);
5246 // Check dbch_devicetype explicitly since we will get other device types
5247 // (e.g. DBT_DEVTYP_VOLUME) for some reasons even if we specify
5248 // DBT_DEVTYP_DEVICEINTERFACE in the filter for
5249 // RegisterDeviceNotification.
5250 if (hdr
->dbch_devicetype
== DBT_DEVTYP_DEVICEINTERFACE
) {
5251 // This can only change media queries (any-hover/any-pointer).
5252 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5257 case WM_NCCALCSIZE
: {
5258 // NOTE: the following block is mirrored in PreXULSkeletonUI.cpp, and
5259 // will need to be kept in sync.
5260 if (mCustomNonClient
) {
5261 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5262 // the proposed window rectangle for our window. During our
5263 // processing of the `WM_NCCALCSIZE` message, we are expected to
5264 // modify the `RECT` that `lParam` points to, so that its value upon
5265 // our return is the new client area. We must return 0 if `wParam`
5268 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5269 // struct. This struct contains an array of 3 `RECT`s, the first of
5270 // which has the exact same meaning as the `RECT` that is pointed to
5271 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5272 // conjunction with our return value, can
5273 // be used to specify portions of the source and destination window
5274 // rectangles that are valid and should be preserved. We opt not to
5275 // implement an elaborate client-area preservation technique, and
5276 // simply return 0, which means "preserve the entire old client area
5277 // and align it with the upper-left corner of our new client area".
5279 wParam
? &(reinterpret_cast<NCCALCSIZE_PARAMS
*>(lParam
))->rgrc
[0]
5280 : (reinterpret_cast<RECT
*>(lParam
));
5281 clientRect
->top
+= mCaptionHeight
- mNonClientOffset
.top
;
5282 clientRect
->left
+= mHorResizeMargin
- mNonClientOffset
.left
;
5283 clientRect
->right
-= mHorResizeMargin
- mNonClientOffset
.right
;
5284 clientRect
->bottom
-= mVertResizeMargin
- mNonClientOffset
.bottom
;
5285 // Make client rect's width and height more than 0 to
5286 // avoid problems of webrender and angle.
5287 clientRect
->right
= std::max(clientRect
->right
, clientRect
->left
+ 1);
5288 clientRect
->bottom
= std::max(clientRect
->bottom
, clientRect
->top
+ 1);
5296 case WM_NCHITTEST
: {
5297 if (mMouseTransparent
) {
5298 // Treat this window as transparent.
5299 *aRetValue
= HTTRANSPARENT
;
5305 * If an nc client area margin has been moved, we are responsible
5306 * for calculating where the resize margins are and returning the
5307 * appropriate set of hit test constants. DwmDefWindowProc (above)
5308 * will handle hit testing on it's command buttons if we are on a
5309 * composited desktop.
5312 if (!mCustomNonClient
) break;
5315 ClientMarginHitTestPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5322 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5323 * custom titlebar we paint ourselves, or if we're the ones
5324 * sending the message with an updated title
5327 if ((mSendingSetText
&&
5328 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) ||
5329 !mCustomNonClient
|| mNonClientMargins
.top
== -1)
5333 // From msdn, the way around this is to disable the visible state
5334 // temporarily. We need the text to be set but we don't want the
5335 // redraw to occur. However, we need to make sure that we don't
5336 // do this at the same time that a Present is happening.
5338 // To do this we take mPresentLock in nsWindow::PreRender and
5339 // if that lock is taken we wait before doing WM_SETTEXT
5340 if (mCompositorWidgetDelegate
) {
5341 mCompositorWidgetDelegate
->EnterPresentLock();
5343 DWORD style
= GetWindowLong(mWnd
, GWL_STYLE
);
5344 SetWindowLong(mWnd
, GWL_STYLE
, style
& ~WS_VISIBLE
);
5346 CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
, wParam
, lParam
);
5347 SetWindowLong(mWnd
, GWL_STYLE
, style
);
5348 if (mCompositorWidgetDelegate
) {
5349 mCompositorWidgetDelegate
->LeavePresentLock();
5355 case WM_NCACTIVATE
: {
5357 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5358 * through WM_NCPAINT via InvalidateNonClientRegion.
5360 UpdateGetWindowInfoCaptionStatus(FALSE
!= wParam
);
5362 if (!mCustomNonClient
) break;
5364 // There is a case that rendered result is not kept. Bug 1237617
5365 if (wParam
== TRUE
&& !gfxEnv::DisableForcePresent() &&
5366 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
5367 NS_DispatchToMainThread(NewRunnableMethod(
5368 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent
));
5371 // let the dwm handle nc painting on glass
5372 // Never allow native painting if we are on fullscreen
5373 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
&&
5374 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled())
5377 if (wParam
== TRUE
) {
5379 *aRetValue
= FALSE
; // ignored
5381 // invalidate to trigger a paint
5382 InvalidateNonClientRegion();
5386 *aRetValue
= TRUE
; // go ahead and deactive
5388 // invalidate to trigger a paint
5389 InvalidateNonClientRegion();
5396 * ClearType changes often don't send a WM_SETTINGCHANGE message. But they
5397 * do seem to always send a WM_NCPAINT message, so let's update on that.
5399 gfxDWriteFont::UpdateSystemTextVars();
5402 * Reset the non-client paint region so that it excludes the
5403 * non-client areas we paint manually. Then call defwndproc
5404 * to do the actual painting.
5407 if (!mCustomNonClient
) break;
5409 // let the dwm handle nc painting on glass
5410 if (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) break;
5412 HRGN paintRgn
= ExcludeNonClientFromPaintRegion((HRGN
)wParam
);
5413 LRESULT res
= CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
,
5414 (WPARAM
)paintRgn
, lParam
);
5415 if (paintRgn
!= (HRGN
)wParam
) DeleteObject(paintRgn
);
5420 case WM_POWERBROADCAST
:
5422 case PBT_APMSUSPEND
:
5423 PostSleepWakeNotification(true);
5425 case PBT_APMRESUMEAUTOMATIC
:
5426 case PBT_APMRESUMECRITICAL
:
5427 case PBT_APMRESUMESUSPEND
:
5428 PostSleepWakeNotification(false);
5433 case WM_CLOSE
: // close request
5434 if (mWidgetListener
) mWidgetListener
->RequestWindowClose(this);
5435 result
= true; // abort window closure
5440 DestroyLayerManager();
5446 *aRetValue
= (int)OnPaint(nullptr, 0);
5450 case WM_PRINTCLIENT
:
5451 result
= OnPaint((HDC
)wParam
, 0);
5455 result
= OnHotKey(wParam
, lParam
);
5460 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5461 result
= ProcessCharMessage(nativeMsg
, nullptr);
5462 DispatchPendingEvents();
5467 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5468 nativeMsg
.time
= ::GetMessageTime();
5469 result
= ProcessKeyUpMessage(nativeMsg
, nullptr);
5470 DispatchPendingEvents();
5475 if (IsMouseVanishKey(wParam
)) {
5476 MaybeHideCursor(true);
5479 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5480 result
= ProcessKeyDownMessage(nativeMsg
, nullptr);
5481 DispatchPendingEvents();
5484 // say we've dealt with erase background if widget does
5485 // not need auto-erasing
5487 if (!AutoErase((HDC
)wParam
)) {
5493 case WM_MOUSEMOVE
: {
5494 MaybeHideCursor(false);
5496 LPARAM lParamScreen
= lParamToScreen(lParam
);
5497 mSimulatedClientArea
= IsSimulatedClientArea(GET_X_LPARAM(lParamScreen
),
5498 GET_Y_LPARAM(lParamScreen
));
5500 if (!mMousePresent
&& !sIsInMouseCapture
) {
5501 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5502 TRACKMOUSEEVENT mTrack
;
5503 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5504 mTrack
.dwFlags
= TME_LEAVE
;
5505 mTrack
.dwHoverTime
= 0;
5506 mTrack
.hwndTrack
= mWnd
;
5507 TrackMouseEvent(&mTrack
);
5509 mMousePresent
= true;
5511 // Suppress dispatch of pending events
5512 // when mouse moves are generated by widget
5513 // creation instead of user input.
5515 mp
.x
= GET_X_LPARAM(lParamScreen
);
5516 mp
.y
= GET_Y_LPARAM(lParamScreen
);
5517 bool userMovedMouse
= false;
5518 if ((sLastMouseMovePoint
.x
!= mp
.x
) || (sLastMouseMovePoint
.y
!= mp
.y
)) {
5519 userMovedMouse
= true;
5523 DispatchMouseEvent(eMouseMove
, wParam
, lParam
, false,
5524 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5525 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5526 if (userMovedMouse
) {
5527 DispatchPendingEvents();
5531 case WM_NCMOUSEMOVE
: {
5532 MaybeHideCursor(false);
5534 LPARAM lParamClient
= lParamToClient(lParam
);
5535 if (IsSimulatedClientArea(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
))) {
5536 if (!sIsInMouseCapture
) {
5537 TRACKMOUSEEVENT mTrack
;
5538 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5539 mTrack
.dwFlags
= TME_LEAVE
| TME_NONCLIENT
;
5540 mTrack
.dwHoverTime
= 0;
5541 mTrack
.hwndTrack
= mWnd
;
5542 TrackMouseEvent(&mTrack
);
5544 // If we noticed the mouse moving in our draggable region, forward the
5545 // message as a normal WM_MOUSEMOVE.
5546 SendMessage(mWnd
, WM_MOUSEMOVE
, 0, lParamClient
);
5548 // We've transitioned from a draggable area to somewhere else within
5549 // the non-client area - perhaps one of the edges of the window for
5551 mSimulatedClientArea
= false;
5554 if (mMousePresent
&& !sIsInMouseCapture
&& !mSimulatedClientArea
) {
5555 SendMessage(mWnd
, WM_MOUSELEAVE
, 0, 0);
5559 case WM_LBUTTONDOWN
: {
5560 MaybeHideCursor(false);
5563 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5564 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5565 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5566 DispatchPendingEvents();
5569 case WM_LBUTTONUP
: {
5571 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5572 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5573 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5574 DispatchPendingEvents();
5577 case WM_NCMOUSELEAVE
: {
5578 mSimulatedClientArea
= false;
5580 if (EventIsInsideWindow(this)) {
5581 // If we're handling WM_NCMOUSELEAVE and the mouse is still over the
5582 // window, then by process of elimination, the mouse has moved from the
5583 // non-client to client area, so no need to fall-through to the
5584 // WM_MOUSELEAVE handler. We also need to re-register for the
5585 // WM_MOUSELEAVE message, since according to the documentation at [1],
5586 // all tracking requested via TrackMouseEvent is cleared once
5587 // WM_NCMOUSELEAVE or WM_MOUSELEAVE fires.
5589 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-trackmouseevent
5590 TRACKMOUSEEVENT mTrack
;
5591 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5592 mTrack
.dwFlags
= TME_LEAVE
;
5593 mTrack
.dwHoverTime
= 0;
5594 mTrack
.hwndTrack
= mWnd
;
5595 TrackMouseEvent(&mTrack
);
5598 // We've transitioned from non-client to outside of the window, so
5599 // fall-through to the WM_MOUSELEAVE handler.
5601 case WM_MOUSELEAVE
: {
5602 if (!mMousePresent
) break;
5603 if (mSimulatedClientArea
) break;
5604 mMousePresent
= false;
5606 // Check if the mouse is over the fullscreen transition window, if so
5607 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5608 // transition window disappears will not be ignored, even if the mouse
5610 if (mTransitionWnd
&& WindowAtMouse() == mTransitionWnd
) {
5611 sLastMouseMovePoint
= {0};
5614 // We need to check mouse button states and put them in for
5616 WPARAM mouseState
= (GetKeyState(VK_LBUTTON
) ? MK_LBUTTON
: 0) |
5617 (GetKeyState(VK_MBUTTON
) ? MK_MBUTTON
: 0) |
5618 (GetKeyState(VK_RBUTTON
) ? MK_RBUTTON
: 0);
5619 // Synthesize an event position because we don't get one from
5621 LPARAM pos
= lParamToClient(::GetMessagePos());
5622 DispatchMouseEvent(eMouseExitFromWidget
, mouseState
, pos
, false,
5623 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5626 case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER
: {
5627 LPARAM pos
= lParamToClient(::GetMessagePos());
5628 MOZ_ASSERT(InkCollector::sInkCollector
);
5629 uint16_t pointerId
= InkCollector::sInkCollector
->GetPointerId();
5630 if (pointerId
!= 0) {
5631 WinPointerInfo pointerInfo
;
5632 pointerInfo
.pointerId
= pointerId
;
5633 DispatchMouseEvent(eMouseExitFromWidget
, wParam
, pos
, false,
5634 MouseButton::ePrimary
,
5635 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
5636 InkCollector::sInkCollector
->ClearTarget();
5637 InkCollector::sInkCollector
->ClearPointerId();
5641 case WM_CONTEXTMENU
: {
5642 // If the context menu is brought up by a touch long-press, then
5643 // the APZ code is responsible for dealing with this, so we don't
5644 // need to do anything.
5646 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
5647 MOZ_ASSERT(mAPZC
); // since mTouchWindow is true, APZ must be enabled
5652 // if the context menu is brought up from the keyboard, |lParam|
5655 bool contextMenukey
= false;
5657 contextMenukey
= true;
5658 pos
= lParamToClient(GetMessagePos());
5660 pos
= lParamToClient(lParam
);
5663 result
= DispatchMouseEvent(
5664 eContextMenu
, wParam
, pos
, contextMenukey
,
5665 contextMenukey
? MouseButton::ePrimary
: MouseButton::eSecondary
,
5666 MOUSE_INPUT_SOURCE());
5667 if (lParam
!= -1 && !result
&& mCustomNonClient
&&
5668 mDraggableRegion
.Contains(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
))) {
5669 // Blank area hit, throw up the system menu.
5670 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5671 GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5676 case WM_POINTERLEAVE
:
5677 case WM_POINTERDOWN
:
5679 case WM_POINTERUPDATE
:
5680 MaybeHideCursor(false);
5681 result
= OnPointerEvents(msg
, wParam
, lParam
);
5683 DispatchPendingEvents();
5687 case DM_POINTERHITTEST
:
5689 UINT contactId
= GET_POINTERID_WPARAM(wParam
);
5690 POINTER_INPUT_TYPE pointerType
;
5691 if (mPointerEvents
.GetPointerType(contactId
, &pointerType
) &&
5692 pointerType
== PT_TOUCHPAD
) {
5693 mDmOwner
->SetContact(contactId
);
5698 case WM_LBUTTONDBLCLK
:
5699 MaybeHideCursor(false);
5700 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5701 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5702 DispatchPendingEvents();
5705 case WM_MBUTTONDOWN
:
5706 MaybeHideCursor(false);
5707 result
= DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5708 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5709 DispatchPendingEvents();
5713 result
= DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5714 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5715 DispatchPendingEvents();
5718 case WM_MBUTTONDBLCLK
:
5719 MaybeHideCursor(false);
5720 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5721 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5722 DispatchPendingEvents();
5725 case WM_NCMBUTTONDOWN
:
5726 MaybeHideCursor(false);
5727 result
= DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5728 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5729 DispatchPendingEvents();
5732 case WM_NCMBUTTONUP
:
5733 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5734 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5735 DispatchPendingEvents();
5738 case WM_NCMBUTTONDBLCLK
:
5739 MaybeHideCursor(false);
5741 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5742 false, MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5743 DispatchPendingEvents();
5746 case WM_RBUTTONDOWN
:
5747 MaybeHideCursor(false);
5749 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5750 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5751 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5752 DispatchPendingEvents();
5757 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5758 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5759 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5760 DispatchPendingEvents();
5763 case WM_RBUTTONDBLCLK
:
5764 MaybeHideCursor(false);
5766 DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5767 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5768 DispatchPendingEvents();
5771 case WM_NCRBUTTONDOWN
:
5772 MaybeHideCursor(false);
5774 DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5775 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5776 DispatchPendingEvents();
5779 case WM_NCRBUTTONUP
:
5781 DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5782 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5783 DispatchPendingEvents();
5786 case WM_NCRBUTTONDBLCLK
:
5787 MaybeHideCursor(false);
5788 result
= DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5789 false, MouseButton::eSecondary
,
5790 MOUSE_INPUT_SOURCE());
5791 DispatchPendingEvents();
5794 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5795 // of mouse. If 5-button mouse works with standard mouse deriver of
5796 // Windows, users cannot disable 4th button (browser back) nor 5th button
5797 // (browser forward). We should allow to do it with our prefs since we can
5798 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5799 // messages are not sent to DefWindowProc.
5800 case WM_XBUTTONDOWN
:
5802 case WM_NCXBUTTONDOWN
:
5803 case WM_NCXBUTTONUP
:
5804 MaybeHideCursor(false);
5807 switch (GET_XBUTTON_WPARAM(wParam
)) {
5809 result
= !Preferences::GetBool("mousebutton.4th.enabled", true);
5812 result
= !Preferences::GetBool("mousebutton.5th.enabled", true);
5820 if (mAspectRatio
> 0) {
5821 LPRECT rect
= (LPRECT
)lParam
;
5822 int32_t newWidth
, newHeight
;
5824 // The following conditions and switch statement borrow heavily from the
5825 // Chromium source code from
5826 // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
5827 if (wParam
== WMSZ_LEFT
|| wParam
== WMSZ_RIGHT
||
5828 wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_BOTTOMLEFT
) {
5829 newWidth
= rect
->right
- rect
->left
;
5830 newHeight
= newWidth
* mAspectRatio
;
5831 if (newHeight
< mSizeConstraints
.mMinSize
.height
) {
5832 newHeight
= mSizeConstraints
.mMinSize
.height
;
5833 newWidth
= newHeight
/ mAspectRatio
;
5834 } else if (newHeight
> mSizeConstraints
.mMaxSize
.height
) {
5835 newHeight
= mSizeConstraints
.mMaxSize
.height
;
5836 newWidth
= newHeight
/ mAspectRatio
;
5839 newHeight
= rect
->bottom
- rect
->top
;
5840 newWidth
= newHeight
/ mAspectRatio
;
5841 if (newWidth
< mSizeConstraints
.mMinSize
.width
) {
5842 newWidth
= mSizeConstraints
.mMinSize
.width
;
5843 newHeight
= newWidth
* mAspectRatio
;
5844 } else if (newWidth
> mSizeConstraints
.mMaxSize
.width
) {
5845 newWidth
= mSizeConstraints
.mMaxSize
.width
;
5846 newHeight
= newWidth
* mAspectRatio
;
5853 rect
->right
= newWidth
+ rect
->left
;
5854 rect
->bottom
= rect
->top
+ newHeight
;
5857 rect
->right
= newWidth
+ rect
->left
;
5858 rect
->top
= rect
->bottom
- newHeight
;
5862 rect
->left
= rect
->right
- newWidth
;
5863 rect
->top
= rect
->bottom
- newHeight
;
5866 rect
->right
= rect
->left
+ newWidth
;
5867 rect
->top
= rect
->bottom
- newHeight
;
5869 case WMSZ_BOTTOMLEFT
:
5870 rect
->left
= rect
->right
- newWidth
;
5871 rect
->bottom
= rect
->top
+ newHeight
;
5873 case WMSZ_BOTTOMRIGHT
:
5874 rect
->right
= rect
->left
+ newWidth
;
5875 rect
->bottom
= rect
->top
+ newHeight
;
5880 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5881 // resize or move event. Instead we wait for first VM_SIZING message
5882 // within a ENTERSIZEMOVE to consider this a live resize event.
5883 if (mResizeState
== IN_SIZEMOVE
) {
5884 mResizeState
= RESIZING
;
5885 NotifyLiveResizeStarted();
5891 FinishLiveResizing(MOVING
);
5892 if (WinUtils::IsPerMonitorDPIAware()) {
5893 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5894 // a window around. Therefore, call ChangedDPI and ResetLayout here
5895 // if it appears that the window's scaling is not what we expect.
5896 // This causes the prescontext and appshell window management code to
5897 // check the appUnitsPerDevPixel value and current widget size, and
5898 // refresh them if necessary. If nothing has changed, these calls will
5899 // return without actually triggering any extra reflow or painting.
5900 if (WinUtils::LogToPhysFactor(mWnd
) != mDefaultScale
) {
5903 if (mWidgetListener
) {
5904 mWidgetListener
->UIResolutionChanged();
5910 case WM_ENTERSIZEMOVE
: {
5911 if (mResizeState
== NOT_RESIZING
) {
5912 mResizeState
= IN_SIZEMOVE
;
5917 case WM_EXITSIZEMOVE
: {
5918 FinishLiveResizing(NOT_RESIZING
);
5920 if (!sIsInMouseCapture
) {
5921 NotifySizeMoveDone();
5927 case WM_DISPLAYCHANGE
: {
5928 ScreenHelperWin::RefreshScreens();
5929 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
5931 gfxInfo
->RefreshMonitors();
5933 if (mWidgetListener
) {
5934 mWidgetListener
->UIResolutionChanged();
5939 case WM_NCLBUTTONDBLCLK
:
5940 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
), false,
5941 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5942 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5943 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5944 DispatchPendingEvents();
5947 case WM_NCLBUTTONDOWN
: {
5948 // Dispatch a custom event when this happens in the draggable region, so
5949 // that non-popup-based panels can react to it. This doesn't send an
5950 // actual mousedown event because that would break dragging or interfere
5951 // with other mousedown handling in the caption area.
5952 if (ClientMarginHitTestPoint(GET_X_LPARAM(lParam
),
5953 GET_Y_LPARAM(lParam
)) == HTCAPTION
) {
5954 DispatchCustomEvent(u
"draggableregionleftmousedown"_ns
);
5957 if (IsWindowButton(wParam
) && mCustomNonClient
&& !mWindowButtonsRect
) {
5958 DispatchMouseEvent(eMouseDown
, wParamFromGlobalMouseState(),
5959 lParamToClient(lParam
), false, MouseButton::ePrimary
,
5960 MOUSE_INPUT_SOURCE(), nullptr, true);
5961 DispatchPendingEvents();
5967 case WM_APPCOMMAND
: {
5968 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5969 result
= HandleAppCommandMsg(nativeMsg
, aRetValue
);
5973 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5974 // and the loword of wParam specifies which. But we don't want to tell
5975 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5976 // events are fired. Instead, set either the sJustGotActivate or
5977 // gJustGotDeactivate flags and activate/deactivate once the focus
5980 int32_t fActive
= LOWORD(wParam
);
5982 MaybeHideCursor(false);
5985 if (mWidgetListener
) {
5986 if (WA_INACTIVE
== fActive
) {
5987 // when minimizing a window, the deactivation and focus events will
5988 // be fired in the reverse order. Instead, just deactivate right away.
5989 // This can also happen when a modal system dialog is opened, so check
5990 // if the last window to receive the WM_KILLFOCUS message was this one
5991 // or a child of this one.
5992 if (HIWORD(wParam
) ||
5993 (mLastKillFocusWindow
&&
5994 (GetTopLevelForFocus(mLastKillFocusWindow
) == mWnd
))) {
5995 DispatchFocusToTopLevelWindow(false);
5997 sJustGotDeactivate
= true;
5999 if (mIsTopWidgetWindow
) {
6000 mLastKeyboardLayout
= KeyboardLayout::GetInstance()->GetLayout();
6005 sJustGotActivate
= true;
6006 WidgetMouseEvent
event(true, eMouseActivate
, this,
6007 WidgetMouseEvent::eReal
);
6009 ModifierKeyState modifierKeyState
;
6010 modifierKeyState
.InitInputEvent(event
);
6011 DispatchInputEvent(&event
);
6012 if (sSwitchKeyboardLayout
&& mLastKeyboardLayout
)
6013 ActivateKeyboardLayout(mLastKeyboardLayout
, 0);
6018 case WM_MOUSEACTIVATE
:
6019 // A popup with a parent owner should not be activated when clicked but
6020 // should still allow the mouse event to be fired, so the return value
6021 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
6022 // just use default processing so that the window is activated.
6023 if (IsPopup() && IsOwnerForegroundWindow()) {
6024 *aRetValue
= MA_NOACTIVATE
;
6029 case WM_WINDOWPOSCHANGING
: {
6030 LPWINDOWPOS info
= (LPWINDOWPOS
)lParam
;
6031 OnWindowPosChanging(info
);
6035 case WM_GETMINMAXINFO
: {
6036 MINMAXINFO
* mmi
= (MINMAXINFO
*)lParam
;
6037 // Set the constraints. The minimum size should also be constrained to the
6038 // default window maximum size so that it fits on screen.
6039 mmi
->ptMinTrackSize
.x
=
6040 std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
6041 std::max((int32_t)mmi
->ptMinTrackSize
.x
,
6042 mSizeConstraints
.mMinSize
.width
));
6043 mmi
->ptMinTrackSize
.y
=
6044 std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
6045 std::max((int32_t)mmi
->ptMinTrackSize
.y
,
6046 mSizeConstraints
.mMinSize
.height
));
6047 mmi
->ptMaxTrackSize
.x
= std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
6048 mSizeConstraints
.mMaxSize
.width
);
6049 mmi
->ptMaxTrackSize
.y
= std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
6050 mSizeConstraints
.mMaxSize
.height
);
6054 // If previous focused window isn't ours, it must have received the
6055 // redirected message. So, we should forget it.
6056 if (!WinUtils::IsOurProcessWindow(HWND(wParam
))) {
6057 RedirectedKeyDownMessageManager::Forget();
6059 if (sJustGotActivate
) {
6060 DispatchFocusToTopLevelWindow(true);
6065 if (sJustGotDeactivate
) {
6066 DispatchFocusToTopLevelWindow(false);
6068 mLastKillFocusWindow
= mWnd
;
6072 case WM_WINDOWPOSCHANGED
: {
6073 WINDOWPOS
* wp
= (LPWINDOWPOS
)lParam
;
6074 OnWindowPosChanged(wp
);
6078 case WM_INPUTLANGCHANGEREQUEST
:
6083 case WM_INPUTLANGCHANGE
:
6084 KeyboardLayout::GetInstance()->OnLayoutChange(
6085 reinterpret_cast<HKL
>(lParam
));
6086 nsBidiKeyboard::OnLayoutChange();
6087 result
= false; // always pass to child window
6090 case WM_DESTROYCLIPBOARD
: {
6091 nsIClipboard
* clipboard
;
6092 nsresult rv
= CallGetService(kCClipboardCID
, &clipboard
);
6093 if (NS_SUCCEEDED(rv
)) {
6094 clipboard
->EmptyClipboard(nsIClipboard::kGlobalClipboard
);
6095 NS_RELEASE(clipboard
);
6099 #ifdef ACCESSIBILITY
6100 case WM_GETOBJECT
: {
6102 // Do explicit casting to make it working on 64bit systems (see bug 649236
6104 int32_t objId
= static_cast<DWORD
>(lParam
);
6105 if (objId
== OBJID_CLIENT
) { // oleacc.dll will be loaded dynamically
6106 RefPtr
<IAccessible
> root(
6107 a11y::LazyInstantiator::GetRootAccessible(mWnd
));
6109 *aRetValue
= LresultFromObject(IID_IAccessible
, wParam
, root
);
6110 a11y::LazyInstantiator::EnableBlindAggregation(mWnd
);
6117 case WM_SYSCOMMAND
: {
6118 WPARAM filteredWParam
= (wParam
& 0xFFF0);
6119 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6120 filteredWParam
== SC_RESTORE
&&
6121 GetCurrentShowCmd(mWnd
) != SW_SHOWMINIMIZED
) {
6122 mFrameState
->EnsureFullscreenMode(false);
6126 // Handle the system menu manually when we're in full screen mode
6127 // so we can set the appropriate options.
6128 if (filteredWParam
== SC_KEYMENU
&& lParam
== VK_SPACE
&&
6129 mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
) {
6130 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
6131 MOZ_SYSCONTEXT_X_POS
, MOZ_SYSCONTEXT_Y_POS
);
6136 case WM_DWMCOMPOSITIONCHANGED
:
6137 // Every window will get this message, but gfxVars only broadcasts
6138 // updates when the value actually changes
6139 if (XRE_IsParentProcess()) {
6140 BOOL dwmEnabled
= FALSE
;
6141 if (FAILED(::DwmIsCompositionEnabled(&dwmEnabled
)) || !dwmEnabled
) {
6142 gfxVars::SetDwmCompositionEnabled(false);
6144 gfxVars::SetDwmCompositionEnabled(true);
6148 UpdateNonClientMargins();
6149 BroadcastMsg(mWnd
, WM_DWMCOMPOSITIONCHANGED
);
6150 // TODO: Why is NotifyThemeChanged needed, what does it affect? And can we
6151 // make it more granular by tweaking the ChangeKind we pass?
6152 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
6154 Invalidate(true, true, true);
6157 case WM_DPICHANGED
: {
6158 LPRECT rect
= (LPRECT
)lParam
;
6159 OnDPIChanged(rect
->left
, rect
->top
, rect
->right
- rect
->left
,
6160 rect
->bottom
- rect
->top
);
6164 case WM_UPDATEUISTATE
: {
6165 // If the UI state has changed, fire an event so the UI updates the
6166 // keyboard cues based on the system setting and how the window was
6167 // opened. For example, a dialog opened via a keyboard press on a button
6168 // should enable cues, whereas the same dialog opened via a mouse click of
6169 // the button should not.
6170 if (mWindowType
== eWindowType_toplevel
||
6171 mWindowType
== eWindowType_dialog
) {
6172 int32_t action
= LOWORD(wParam
);
6173 if (action
== UIS_SET
|| action
== UIS_CLEAR
) {
6174 int32_t flags
= HIWORD(wParam
);
6175 UIStateChangeType showFocusRings
= UIStateChangeType_NoChange
;
6176 if (flags
& UISF_HIDEFOCUS
) {
6177 showFocusRings
= (action
== UIS_SET
) ? UIStateChangeType_Clear
6178 : UIStateChangeType_Set
;
6180 NotifyUIStateChanged(showFocusRings
);
6187 /* Gesture support events */
6188 case WM_TABLET_QUERYSYSTEMGESTURESTATUS
:
6189 // According to MS samples, this must be handled to enable
6190 // rotational support in multi-touch drivers.
6192 *aRetValue
= TABLET_ROTATE_GESTURE_ENABLE
;
6196 result
= OnTouch(wParam
, lParam
);
6203 result
= OnGesture(wParam
, lParam
);
6206 case WM_GESTURENOTIFY
: {
6207 if (mWindowType
!= eWindowType_invisible
) {
6208 // A GestureNotify event is dispatched to decide which single-finger
6209 // panning direction should be active (including none) and if pan
6210 // feedback should be displayed. Java and plugin windows can make their
6213 GESTURENOTIFYSTRUCT
* gestureinfo
= (GESTURENOTIFYSTRUCT
*)lParam
;
6214 nsPointWin touchPoint
;
6215 touchPoint
= gestureinfo
->ptsLocation
;
6216 touchPoint
.ScreenToClient(mWnd
);
6217 WidgetGestureNotifyEvent
gestureNotifyEvent(true, eGestureNotify
, this);
6218 gestureNotifyEvent
.mRefPoint
=
6219 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint
);
6220 nsEventStatus status
;
6221 DispatchEvent(&gestureNotifyEvent
, status
);
6222 mDisplayPanFeedback
= gestureNotifyEvent
.mDisplayPanFeedback
;
6224 mGesture
.SetWinGestureSupport(mWnd
, gestureNotifyEvent
.mPanDirection
);
6226 result
= false; // should always bubble to DefWindowProc
6230 WidgetContentCommandEvent
command(true, eContentCommandDelete
, this);
6231 DispatchWindowEvent(command
);
6236 WidgetContentCommandEvent
command(true, eContentCommandCut
, this);
6237 DispatchWindowEvent(command
);
6242 WidgetContentCommandEvent
command(true, eContentCommandCopy
, this);
6243 DispatchWindowEvent(command
);
6248 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this);
6249 DispatchWindowEvent(command
);
6254 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this);
6255 DispatchWindowEvent(command
);
6256 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6261 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this);
6262 DispatchWindowEvent(command
);
6263 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6268 // Support EM_CANPASTE message only when wParam isn't specified or
6269 // is plain text format.
6270 if (wParam
== 0 || wParam
== CF_TEXT
|| wParam
== CF_UNICODETEXT
) {
6271 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this,
6273 DispatchWindowEvent(command
);
6274 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6280 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this, true);
6281 DispatchWindowEvent(command
);
6282 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6287 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this, true);
6288 DispatchWindowEvent(command
);
6289 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6293 case MOZ_WM_SKEWFIX
: {
6294 TimeStamp skewStamp
;
6295 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam
,
6297 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
6303 if (msg
== nsAppShell::GetTaskbarButtonCreatedMessage()) {
6304 SetHasTaskbarIconBeenCreated();
6309 //*aRetValue = result;
6313 // Events which caused mWnd destruction and aren't consumed
6314 // will crash during the Windows default processing.
6319 void nsWindow::FinishLiveResizing(ResizeState aNewState
) {
6320 if (mResizeState
== RESIZING
) {
6321 NotifyLiveResizeStopped();
6323 mResizeState
= aNewState
;
6327 /**************************************************************
6329 * SECTION: Broadcast messaging
6331 * Broadcast messages to all windows.
6333 **************************************************************/
6335 // Enumerate all child windows sending aMsg to each of them
6336 BOOL CALLBACK
nsWindow::BroadcastMsgToChildren(HWND aWnd
, LPARAM aMsg
) {
6337 WNDPROC winProc
= (WNDPROC
)::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
6338 if (winProc
== &nsWindow::WindowProc
) {
6339 // it's one of our windows so go ahead and send a message to it
6340 ::CallWindowProcW(winProc
, aWnd
, aMsg
, 0, 0);
6345 // Enumerate all top level windows specifying that the children of each
6346 // top level window should be enumerated. Do *not* send the message to
6347 // each top level window since it is assumed that the toolkit will send
6348 // aMsg to them directly.
6349 BOOL CALLBACK
nsWindow::BroadcastMsg(HWND aTopWindow
, LPARAM aMsg
) {
6350 // Iterate each of aTopWindows child windows sending the aMsg
6352 ::EnumChildWindows(aTopWindow
, nsWindow::BroadcastMsgToChildren
, aMsg
);
6356 /**************************************************************
6358 * SECTION: Event processing helpers
6360 * Special processing for certain event types and
6361 * synthesized events.
6363 **************************************************************/
6365 int32_t nsWindow::ClientMarginHitTestPoint(int32_t mx
, int32_t my
) {
6366 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
||
6367 mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
) {
6371 // Calculations are done in screen coords
6373 GetWindowRect(mWnd
, &winRect
);
6375 // hit return constants:
6376 // HTBORDER - non-resizable border
6377 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
6378 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
6379 // HTTOPLEFT, HTTOPRIGHT - resizable corner
6380 // HTCAPTION - general title bar area
6381 // HTCLIENT - area considered the client
6382 // HTCLOSE - hovering over the close button
6383 // HTMAXBUTTON - maximize button
6384 // HTMINBUTTON - minimize button
6386 int32_t testResult
= HTCLIENT
;
6388 bool isResizable
= (mBorderStyle
& (eBorderStyle_all
| eBorderStyle_resizeh
|
6389 eBorderStyle_default
)) > 0
6392 if (mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) isResizable
= false;
6394 // Ensure being accessible to borders of window. Even if contents are in
6395 // this area, the area must behave as border.
6396 nsIntMargin
nonClientSize(
6397 std::max(mCaptionHeight
- mNonClientOffset
.top
, kResizableBorderMinSize
),
6398 std::max(mHorResizeMargin
- mNonClientOffset
.right
,
6399 kResizableBorderMinSize
),
6400 std::max(mVertResizeMargin
- mNonClientOffset
.bottom
,
6401 kResizableBorderMinSize
),
6402 std::max(mHorResizeMargin
- mNonClientOffset
.left
,
6403 kResizableBorderMinSize
));
6405 bool allowContentOverride
=
6406 mFrameState
->GetSizeMode() == nsSizeMode_Maximized
||
6407 (mx
>= winRect
.left
+ nonClientSize
.left
&&
6408 mx
<= winRect
.right
- nonClientSize
.right
&&
6409 my
>= winRect
.top
+ nonClientSize
.top
&&
6410 my
<= winRect
.bottom
- nonClientSize
.bottom
);
6412 // The border size. If there is no content under mouse cursor, the border
6413 // size should be larger than the values in system settings. Otherwise,
6414 // contents under the mouse cursor should be able to override the behavior.
6415 // E.g., user must expect that Firefox button always opens the popup menu
6416 // even when the user clicks on the above edge of it.
6417 nsIntMargin
borderSize(std::max(nonClientSize
.top
, mVertResizeMargin
),
6418 std::max(nonClientSize
.right
, mHorResizeMargin
),
6419 std::max(nonClientSize
.bottom
, mVertResizeMargin
),
6420 std::max(nonClientSize
.left
, mHorResizeMargin
));
6423 bool bottom
= false;
6427 if (my
>= winRect
.top
&& my
< winRect
.top
+ borderSize
.top
) {
6429 } else if (my
<= winRect
.bottom
&& my
> winRect
.bottom
- borderSize
.bottom
) {
6433 // (the 2x case here doubles the resize area for corners)
6434 int multiplier
= (top
|| bottom
) ? 2 : 1;
6435 if (mx
>= winRect
.left
&&
6436 mx
< winRect
.left
+ (multiplier
* borderSize
.left
)) {
6438 } else if (mx
<= winRect
.right
&&
6439 mx
> winRect
.right
- (multiplier
* borderSize
.right
)) {
6443 bool inResizeRegion
= false;
6448 testResult
= HTTOPLEFT
;
6450 testResult
= HTTOPRIGHT
;
6451 } else if (bottom
) {
6452 testResult
= HTBOTTOM
;
6454 testResult
= HTBOTTOMLEFT
;
6456 testResult
= HTBOTTOMRIGHT
;
6458 if (left
) testResult
= HTLEFT
;
6459 if (right
) testResult
= HTRIGHT
;
6461 inResizeRegion
= (testResult
!= HTCLIENT
);
6464 testResult
= HTCAPTION
;
6465 else if (bottom
|| left
|| right
)
6466 testResult
= HTBORDER
;
6469 if (!sIsInMouseCapture
&& allowContentOverride
) {
6470 POINT pt
= {mx
, my
};
6471 ::ScreenToClient(mWnd
, &pt
);
6473 if (pt
.x
== mCachedHitTestPoint
.x
&& pt
.y
== mCachedHitTestPoint
.y
&&
6474 TimeStamp::Now() - mCachedHitTestTime
<
6475 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS
)) {
6476 return mCachedHitTestResult
;
6479 mCachedHitTestPoint
= {pt
.x
, pt
.y
};
6480 mCachedHitTestTime
= TimeStamp::Now();
6482 if (mWindowBtnRect
[WindowButtonType::Minimize
].Contains(pt
.x
, pt
.y
)) {
6483 testResult
= HTMINBUTTON
;
6484 } else if (mWindowBtnRect
[WindowButtonType::Maximize
].Contains(pt
.x
,
6486 testResult
= HTMAXBUTTON
;
6487 } else if (mWindowBtnRect
[WindowButtonType::Close
].Contains(pt
.x
, pt
.y
)) {
6488 testResult
= HTCLOSE
;
6489 } else if (!inResizeRegion
) {
6490 // If we're in the resize region, avoid overriding that with either a
6491 // drag or a client result; resize takes priority over either (but not
6492 // over the window controls, which is why we check this after those).
6493 if (mDraggableRegion
.Contains(pt
.x
, pt
.y
)) {
6494 testResult
= HTCAPTION
;
6496 testResult
= HTCLIENT
;
6500 mCachedHitTestResult
= testResult
;
6506 bool nsWindow::IsSimulatedClientArea(int32_t screenX
, int32_t screenY
) {
6507 int32_t testResult
= ClientMarginHitTestPoint(screenX
, screenY
);
6508 return testResult
== HTCAPTION
|| IsWindowButton(testResult
);
6511 bool nsWindow::IsWindowButton(int32_t hitTestResult
) {
6512 return hitTestResult
== HTMINBUTTON
|| hitTestResult
== HTMAXBUTTON
||
6513 hitTestResult
== HTCLOSE
;
6516 TimeStamp
nsWindow::GetMessageTimeStamp(LONG aEventTime
) const {
6517 CurrentWindowsTimeGetter
getCurrentTime(mWnd
);
6518 return TimeConverter().GetTimeStampFromSystemTime(aEventTime
, getCurrentTime
);
6521 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode
) {
6522 if (aIsSleepMode
== gIsSleepMode
) return;
6524 gIsSleepMode
= aIsSleepMode
;
6526 nsCOMPtr
<nsIObserverService
> observerService
=
6527 mozilla::services::GetObserverService();
6528 if (observerService
)
6529 observerService
->NotifyObservers(nullptr,
6531 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6532 : NS_WIDGET_WAKE_OBSERVER_TOPIC
,
6536 LRESULT
nsWindow::ProcessCharMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6537 if (IMEHandler::IsComposingOn(this)) {
6538 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION
);
6540 // These must be checked here too as a lone WM_CHAR could be received
6541 // if a child window didn't handle it (for example Alt+Space in a content
6543 ModifierKeyState modKeyState
;
6544 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6545 return static_cast<LRESULT
>(nativeKey
.HandleCharMessage(aEventDispatched
));
6548 LRESULT
nsWindow::ProcessKeyUpMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6549 ModifierKeyState modKeyState
;
6550 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6551 bool result
= nativeKey
.HandleKeyUpMessage(aEventDispatched
);
6552 if (aMsg
.wParam
== VK_F10
) {
6553 // Bug 1382199: Windows default behavior will trigger the System menu bar
6554 // when F10 is released. Among other things, this causes the System menu bar
6555 // to appear when a web page overrides the contextmenu event. We *never*
6556 // want this default behavior, so eat this key (never pass it to Windows).
6562 LRESULT
nsWindow::ProcessKeyDownMessage(const MSG
& aMsg
,
6563 bool* aEventDispatched
) {
6564 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6565 // must clean up the redirected message information itself. For more
6566 // information, see above comment of
6567 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6568 // KeyboardLayout.h.
6569 RedirectedKeyDownMessageManager::AutoFlusher
redirectedMsgFlusher(this, aMsg
);
6571 ModifierKeyState modKeyState
;
6573 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6575 static_cast<LRESULT
>(nativeKey
.HandleKeyDownMessage(aEventDispatched
));
6576 // HandleKeyDownMessage cleaned up the redirected message information
6577 // itself, so, we should do nothing.
6578 redirectedMsgFlusher
.Cancel();
6580 if (aMsg
.wParam
== VK_MENU
||
6581 (aMsg
.wParam
== VK_F10
&& !modKeyState
.IsShift())) {
6582 // We need to let Windows handle this keypress,
6583 // by returning false, if there's a native menu
6584 // bar somewhere in our containing window hierarchy.
6585 // Otherwise we handle the keypress and don't pass
6586 // it on to Windows, by returning true.
6587 bool hasNativeMenu
= false;
6590 if (::GetMenu(hWnd
)) {
6591 hasNativeMenu
= true;
6594 hWnd
= ::GetParent(hWnd
);
6596 result
= !hasNativeMenu
;
6602 nsresult
nsWindow::SynthesizeNativeKeyEvent(
6603 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
6604 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
6605 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
6606 AutoObserverNotifier
notifier(aObserver
, "keyevent");
6608 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
6609 return keyboardLayout
->SynthesizeNativeKeyEvent(
6610 this, aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
, aCharacters
,
6611 aUnmodifiedCharacters
);
6614 nsresult
nsWindow::SynthesizeNativeMouseEvent(
6615 LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
6616 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
6617 nsIObserver
* aObserver
) {
6618 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
6621 memset(&input
, 0, sizeof(input
));
6623 // TODO (bug 1693240):
6624 // Now, we synthesize native mouse events asynchronously since we want to
6625 // synthesize the event on the front window at the point. However, Windows
6626 // does not provide a way to set modifier only while a mouse message is
6627 // being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
6628 // need a trick for handling it.
6630 switch (aNativeMessage
) {
6631 case NativeMouseMessage::Move
:
6632 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
;
6633 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6634 // to the position it's already at, we still dispatch a mousemove
6635 // event, because the callers of this function expect that.
6636 sLastMouseMovePoint
= {0};
6638 case NativeMouseMessage::ButtonDown
:
6639 case NativeMouseMessage::ButtonUp
: {
6640 const bool isDown
= aNativeMessage
== NativeMouseMessage::ButtonDown
;
6642 case MouseButton::ePrimary
:
6643 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
6645 case MouseButton::eMiddle
:
6647 isDown
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
6649 case MouseButton::eSecondary
:
6651 isDown
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
6653 case MouseButton::eX1
:
6654 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6655 input
.mi
.mouseData
= XBUTTON1
;
6657 case MouseButton::eX2
:
6658 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6659 input
.mi
.mouseData
= XBUTTON2
;
6662 return NS_ERROR_INVALID_ARG
;
6666 case NativeMouseMessage::EnterWindow
:
6667 case NativeMouseMessage::LeaveWindow
:
6668 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Windows");
6669 return NS_ERROR_INVALID_ARG
;
6672 input
.type
= INPUT_MOUSE
;
6673 ::SetCursorPos(aPoint
.x
, aPoint
.y
);
6674 ::SendInput(1, &input
, sizeof(INPUT
));
6679 nsresult
nsWindow::SynthesizeNativeMouseScrollEvent(
6680 LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
, double aDeltaX
,
6681 double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
6682 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
6683 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
6684 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6685 this, aPoint
, aNativeMessage
,
6686 (aNativeMessage
== WM_MOUSEWHEEL
|| aNativeMessage
== WM_VSCROLL
)
6687 ? static_cast<int32_t>(aDeltaY
)
6688 : static_cast<int32_t>(aDeltaX
),
6689 aModifierFlags
, aAdditionalFlags
);
6692 nsresult
nsWindow::SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase
,
6693 LayoutDeviceIntPoint aPoint
,
6694 double aDeltaX
, double aDeltaY
,
6695 int32_t aModifierFlags
) {
6696 DirectManipulationOwner::SynthesizeNativeTouchpadPan(
6697 this, aEventPhase
, aPoint
, aDeltaX
, aDeltaY
, aModifierFlags
);
6701 static void MaybeLogSizeMode(nsSizeMode aMode
) {
6702 #ifdef WINSTATE_DEBUG_OUTPUT
6704 case nsSizeMode_Normal
:
6705 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6706 ("*** SizeMode: nsSizeMode_Normal\n"));
6708 case nsSizeMode_Minimized
:
6709 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6710 ("*** SizeMode: nsSizeMode_Minimized\n"));
6712 case nsSizeMode_Maximized
:
6713 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6714 ("*** SizeMode: nsSizeMode_Maximized\n"));
6717 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** SizeMode: ??????\n"));
6723 static void MaybeLogPosChanged(HWND aWnd
, WINDOWPOS
* wp
) {
6724 #ifdef WINSTATE_DEBUG_OUTPUT
6725 if (aWnd
== WinUtils::GetTopLevelHWND(aWnd
)) {
6726 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [ top] "));
6728 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [child] "));
6730 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("WINDOWPOS flags:"));
6731 if (wp
->flags
& SWP_FRAMECHANGED
) {
6732 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_FRAMECHANGED "));
6734 if (wp
->flags
& SWP_SHOWWINDOW
) {
6735 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_SHOWWINDOW "));
6737 if (wp
->flags
& SWP_NOSIZE
) {
6738 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOSIZE "));
6740 if (wp
->flags
& SWP_HIDEWINDOW
) {
6741 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_HIDEWINDOW "));
6743 if (wp
->flags
& SWP_NOZORDER
) {
6744 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOZORDER "));
6746 if (wp
->flags
& SWP_NOACTIVATE
) {
6747 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOACTIVATE "));
6749 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("\n"));
6753 /**************************************************************
6755 * SECTION: OnXXX message handlers
6757 * For message handlers that need to be broken out or
6758 * implemented in specific platform code.
6760 **************************************************************/
6762 void nsWindow::OnWindowPosChanged(WINDOWPOS
* wp
) {
6763 if (wp
== nullptr) return;
6765 MaybeLogPosChanged(mWnd
, wp
);
6767 // Handle window size mode changes
6768 if (wp
->flags
& SWP_FRAMECHANGED
) {
6769 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6770 // windows when fullscreen games disable desktop composition. If we're
6771 // minimized and not being activated, ignore the event and let windows
6773 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
&&
6774 (wp
->flags
& SWP_NOACTIVATE
)) {
6778 mFrameState
->OnFrameChanged();
6780 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
6781 // Skip window size change events below on minimization.
6786 // Notify visibility change when window is activated.
6787 if (!(wp
->flags
& SWP_NOACTIVATE
) && NeedsToTrackWindowOcclusionState()) {
6788 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
6789 this, mFrameState
->GetSizeMode() != nsSizeMode_Minimized
);
6792 // Handle window position changes
6793 if (!(wp
->flags
& SWP_NOMOVE
)) {
6794 mBounds
.MoveTo(wp
->x
, wp
->y
);
6795 NotifyWindowMoved(wp
->x
, wp
->y
);
6798 // Handle window size changes
6799 if (!(wp
->flags
& SWP_NOSIZE
)) {
6801 int32_t newWidth
, newHeight
;
6803 ::GetWindowRect(mWnd
, &r
);
6805 newWidth
= r
.right
- r
.left
;
6806 newHeight
= r
.bottom
- r
.top
;
6808 if (newWidth
> mLastSize
.width
) {
6812 drect
.left
= wp
->x
+ mLastSize
.width
;
6814 drect
.right
= drect
.left
+ (newWidth
- mLastSize
.width
);
6815 drect
.bottom
= drect
.top
+ newHeight
;
6817 ::RedrawWindow(mWnd
, &drect
, nullptr,
6818 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6819 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6821 if (newHeight
> mLastSize
.height
) {
6826 drect
.top
= wp
->y
+ mLastSize
.height
;
6827 drect
.right
= drect
.left
+ newWidth
;
6828 drect
.bottom
= drect
.top
+ (newHeight
- mLastSize
.height
);
6830 ::RedrawWindow(mWnd
, &drect
, nullptr,
6831 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6832 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6835 mBounds
.SizeTo(newWidth
, newHeight
);
6836 mLastSize
.width
= newWidth
;
6837 mLastSize
.height
= newHeight
;
6839 #ifdef WINSTATE_DEBUG_OUTPUT
6840 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6841 ("*** Resize window: %d x %d x %d x %d\n", wp
->x
, wp
->y
, newWidth
,
6845 if (mAspectRatio
> 0) {
6846 // It's possible (via Windows Aero Snap) that the size of the window
6847 // has changed such that it violates the aspect ratio constraint. If so,
6848 // queue up an event to enforce the aspect ratio constraint and repaint.
6849 // When resized with Windows Aero Snap, we are in the NOT_RESIZING state.
6850 float newAspectRatio
= (float)newHeight
/ newWidth
;
6851 if (mResizeState
== NOT_RESIZING
&& mAspectRatio
!= newAspectRatio
) {
6852 // Hold a reference to self alive and pass it into the lambda to make
6853 // sure this nsIWidget stays alive long enough to run this function.
6854 nsCOMPtr
<nsIWidget
> self(this);
6855 NS_DispatchToMainThread(NS_NewRunnableFunction(
6856 "EnforceAspectRatio", [self
, this, newWidth
]() -> void {
6858 Resize(newWidth
, newWidth
* mAspectRatio
, true);
6864 // If a maximized window is resized, recalculate the non-client margins.
6865 if (mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
6866 if (UpdateNonClientMargins(nsSizeMode_Maximized
, true)) {
6867 // gecko resize event already sent by UpdateNonClientMargins.
6873 // Notify the widget listener for size change of client area for gecko
6874 // events. This needs to be done when either window size is changed,
6875 // or window frame is changed. They may not happen together.
6876 // However, we don't invoke that for popup when window frame changes,
6877 // because popups may trigger frame change before size change via
6878 // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
6879 // code below call OnResize with a wrong client size first, which can
6880 // lead to flickerling for some popups.
6881 if (!(wp
->flags
& SWP_NOSIZE
) ||
6882 ((wp
->flags
& SWP_FRAMECHANGED
) && !IsPopup())) {
6884 LayoutDeviceIntSize clientSize
;
6885 if (::GetClientRect(mWnd
, &r
)) {
6886 clientSize
= WinUtils::ToIntRect(r
).Size();
6888 clientSize
= mBounds
.Size();
6890 // Send a gecko resize event
6891 OnResize(clientSize
);
6895 void nsWindow::OnWindowPosChanging(LPWINDOWPOS
& info
) {
6896 // Update non-client margins if the frame size is changing, and let the
6897 // browser know we are changing size modes, so alternative css can kick in.
6898 // If we're going into fullscreen mode, ignore this, since it'll reset
6899 // margins to normal mode.
6900 if (info
->flags
& SWP_FRAMECHANGED
&& !(info
->flags
& SWP_NOSIZE
)) {
6901 mFrameState
->OnFrameChanging();
6904 // Force fullscreen. This works around a bug in Windows 10 1809 where
6905 // using fullscreen when a window is "snapped" causes a spurious resize
6906 // smaller than the full screen, see bug 1482920.
6907 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6908 !(info
->flags
& SWP_NOMOVE
) && !(info
->flags
& SWP_NOSIZE
)) {
6909 nsCOMPtr
<nsIScreenManager
> screenmgr
=
6910 do_GetService(sScreenManagerContractID
);
6912 LayoutDeviceIntRect
bounds(info
->x
, info
->y
, info
->cx
, info
->cy
);
6913 DesktopIntRect deskBounds
=
6914 RoundedToInt(bounds
/ GetDesktopToDeviceScale());
6915 nsCOMPtr
<nsIScreen
> screen
;
6916 screenmgr
->ScreenForRect(deskBounds
.X(), deskBounds
.Y(),
6917 deskBounds
.Width(), deskBounds
.Height(),
6918 getter_AddRefs(screen
));
6921 int32_t x
, y
, width
, height
;
6922 screen
->GetRect(&x
, &y
, &width
, &height
);
6932 // enforce local z-order rules
6933 if (!(info
->flags
& SWP_NOZORDER
)) {
6934 HWND hwndAfter
= info
->hwndInsertAfter
;
6936 nsWindow
* aboveWindow
= 0;
6937 nsWindowZ placement
;
6939 if (hwndAfter
== HWND_BOTTOM
)
6940 placement
= nsWindowZBottom
;
6941 else if (hwndAfter
== HWND_TOP
|| hwndAfter
== HWND_TOPMOST
||
6942 hwndAfter
== HWND_NOTOPMOST
)
6943 placement
= nsWindowZTop
;
6945 placement
= nsWindowZRelative
;
6946 aboveWindow
= WinUtils::GetNSWindowPtr(hwndAfter
);
6949 if (mWidgetListener
) {
6950 nsCOMPtr
<nsIWidget
> actualBelow
= nullptr;
6951 if (mWidgetListener
->ZLevelChanged(false, &placement
, aboveWindow
,
6952 getter_AddRefs(actualBelow
))) {
6953 if (placement
== nsWindowZBottom
)
6954 info
->hwndInsertAfter
= HWND_BOTTOM
;
6955 else if (placement
== nsWindowZTop
)
6956 info
->hwndInsertAfter
= HWND_TOP
;
6958 info
->hwndInsertAfter
=
6959 (HWND
)actualBelow
->GetNativeData(NS_NATIVE_WINDOW
);
6964 // prevent rude external programs from making hidden window visible
6965 if (mWindowType
== eWindowType_invisible
) info
->flags
&= ~SWP_SHOWWINDOW
;
6967 // When waking from sleep or switching out of tablet mode, Windows 10
6968 // Version 1809 will reopen popup windows that should be hidden. Detect
6969 // this case and refuse to show the window.
6970 static bool sDWMUnhidesPopups
= IsWin10Sep2018UpdateOrLater();
6971 if (sDWMUnhidesPopups
&& (info
->flags
& SWP_SHOWWINDOW
) &&
6972 mWindowType
== eWindowType_popup
&& mWidgetListener
&&
6973 mWidgetListener
->ShouldNotBeVisible()) {
6974 info
->flags
&= ~SWP_SHOWWINDOW
;
6978 void nsWindow::UserActivity() {
6979 // Check if we have the idle service, if not we try to get it.
6980 if (!mIdleService
) {
6981 mIdleService
= do_GetService("@mozilla.org/widget/useridleservice;1");
6984 // Check that we now have the idle service.
6986 mIdleService
->ResetIdleTimeOut(0);
6990 // Helper function for TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT,
6992 static bool TouchDeviceNeedsPanGestureConversion(HANDLE aSource
) {
6993 std::string deviceName
;
6995 // The first call just queries how long the name string will be.
6996 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, nullptr, &dataSize
);
6997 if (!dataSize
|| dataSize
> 0x10000) {
7000 deviceName
.resize(dataSize
);
7001 // The second call actually populates the string.
7002 UINT result
= GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, &deviceName
[0],
7004 if (result
== UINT_MAX
) {
7007 // The affected device name is "\\?\VIRTUAL_DIGITIZER", but each backslash
7008 // needs to be escaped with another one.
7009 std::string expectedDeviceName
= "\\\\?\\VIRTUAL_DIGITIZER";
7010 // For some reason, the dataSize returned by the first call is double the
7011 // actual length of the device name (as if it were returning the size of a
7012 // wide-character string in bytes) even though we are using the narrow
7013 // version of the API. For the comparison against the expected device name
7014 // to pass, we truncate the buffer to be no longer tha the expected device
7016 if (deviceName
.substr(0, expectedDeviceName
.length()) != expectedDeviceName
) {
7020 RID_DEVICE_INFO deviceInfo
;
7021 deviceInfo
.cbSize
= sizeof(deviceInfo
);
7022 dataSize
= sizeof(deviceInfo
);
7024 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICEINFO
, &deviceInfo
, &dataSize
);
7025 if (result
== UINT_MAX
) {
7028 // The device identifiers that we check for here come from bug 1355162
7029 // comment 1 (see also bug 1511901 comment 35).
7030 return deviceInfo
.dwType
== RIM_TYPEHID
&& deviceInfo
.hid
.dwVendorId
== 0 &&
7031 deviceInfo
.hid
.dwProductId
== 0 &&
7032 deviceInfo
.hid
.dwVersionNumber
== 1 &&
7033 deviceInfo
.hid
.usUsagePage
== 13 && deviceInfo
.hid
.usUsage
== 4;
7036 // Determine if the touch device that originated |aOSEvent| needs to have
7037 // touch events representing a two-finger gesture converted to pan
7039 // We only do this for touch devices with a specific name and identifiers.
7040 static bool TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent
,
7041 uint32_t aTouchCount
) {
7042 if (!StaticPrefs::apz_windows_check_for_pan_gesture_conversion()) {
7045 if (aTouchCount
== 0) {
7048 HANDLE source
= aOSEvent
[0].hSource
;
7050 // Cache the result of this computation for each touch device.
7051 // Touch devices are identified by the HANDLE stored in the hSource
7052 // field of TOUCHINPUT.
7053 static std::map
<HANDLE
, bool> sResultCache
;
7054 auto [iter
, inserted
] = sResultCache
.emplace(source
, false);
7056 iter
->second
= TouchDeviceNeedsPanGestureConversion(source
);
7058 return iter
->second
;
7061 Maybe
<PanGestureInput
> nsWindow::ConvertTouchToPanGesture(
7062 const MultiTouchInput
& aTouchInput
, PTOUCHINPUT aOSEvent
) {
7063 // Checks if the touch device that originated the touch event is one
7064 // for which we want to convert the touch events to pang gesture events.
7065 bool shouldConvert
= TouchDeviceNeedsPanGestureConversion(
7066 aOSEvent
, aTouchInput
.mTouches
.Length());
7067 if (!shouldConvert
) {
7071 // Only two-finger gestures need conversion.
7072 if (aTouchInput
.mTouches
.Length() != 2) {
7076 PanGestureInput::PanGestureType eventType
= PanGestureInput::PANGESTURE_PAN
;
7077 if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_START
) {
7078 eventType
= PanGestureInput::PANGESTURE_START
;
7079 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_END
) {
7080 eventType
= PanGestureInput::PANGESTURE_END
;
7081 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_CANCEL
) {
7082 eventType
= PanGestureInput::PANGESTURE_CANCELLED
;
7085 // Use the midpoint of the two touches as the start point of the pan gesture.
7086 ScreenPoint focusPoint
= (aTouchInput
.mTouches
[0].mScreenPoint
+
7087 aTouchInput
.mTouches
[1].mScreenPoint
) /
7089 // To compute the displacement of the pan gesture, we keep track of the
7090 // location of the previous event.
7091 ScreenPoint displacement
= (eventType
== PanGestureInput::PANGESTURE_START
)
7093 : (focusPoint
- mLastPanGestureFocus
);
7094 mLastPanGestureFocus
= focusPoint
;
7096 // We need to negate the displacement because for a touch event, moving the
7097 // fingers down results in scrolling up, but for a touchpad gesture, we want
7098 // moving the fingers down to result in scrolling down.
7099 PanGestureInput
result(eventType
, aTouchInput
.mTime
, aTouchInput
.mTimeStamp
,
7100 focusPoint
, -displacement
, aTouchInput
.modifiers
);
7101 result
.mSimulateMomentum
= true;
7103 return Some(result
);
7106 // Dispatch an event that originated as an OS touch event.
7107 // Usually, we want to dispatch it as a touch event, but some touchpads
7108 // produce touch events for two-finger scrolling, which need to be converted
7109 // to pan gesture events for correct behaviour.
7110 void nsWindow::DispatchTouchOrPanGestureInput(MultiTouchInput
& aTouchInput
,
7111 PTOUCHINPUT aOSEvent
) {
7112 if (Maybe
<PanGestureInput
> panInput
=
7113 ConvertTouchToPanGesture(aTouchInput
, aOSEvent
)) {
7114 DispatchPanGestureInput(*panInput
);
7118 DispatchTouchInput(aTouchInput
);
7121 bool nsWindow::OnTouch(WPARAM wParam
, LPARAM lParam
) {
7122 uint32_t cInputs
= LOWORD(wParam
);
7123 PTOUCHINPUT pInputs
= new TOUCHINPUT
[cInputs
];
7125 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, pInputs
,
7126 sizeof(TOUCHINPUT
))) {
7127 MultiTouchInput touchInput
, touchEndInput
;
7129 // Walk across the touch point array processing each contact point.
7130 for (uint32_t i
= 0; i
< cInputs
; i
++) {
7131 bool addToEvent
= false, addToEndEvent
= false;
7133 // N.B.: According with MS documentation
7134 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
7135 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
7136 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
7137 // TOUCHEVENTF_UP can be combined together.
7139 if (pInputs
[i
].dwFlags
& (TOUCHEVENTF_DOWN
| TOUCHEVENTF_MOVE
)) {
7140 if (touchInput
.mTimeStamp
.IsNull()) {
7141 // Initialize a touch event to send.
7142 touchInput
.mType
= MultiTouchInput::MULTITOUCH_MOVE
;
7143 touchInput
.mTime
= ::GetMessageTime();
7144 touchInput
.mTimeStamp
= GetMessageTimeStamp(touchInput
.mTime
);
7145 ModifierKeyState modifierKeyState
;
7146 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
7148 // Pres shell expects this event to be a eTouchStart
7149 // if any new contact points have been added since the last event sent.
7150 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_DOWN
) {
7151 touchInput
.mType
= MultiTouchInput::MULTITOUCH_START
;
7155 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_UP
) {
7156 // Pres shell expects removed contacts points to be delivered in a
7157 // separate eTouchEnd event containing only the contact points that were
7159 if (touchEndInput
.mTimeStamp
.IsNull()) {
7160 // Initialize a touch event to send.
7161 touchEndInput
.mType
= MultiTouchInput::MULTITOUCH_END
;
7162 touchEndInput
.mTime
= ::GetMessageTime();
7163 touchEndInput
.mTimeStamp
= GetMessageTimeStamp(touchEndInput
.mTime
);
7164 ModifierKeyState modifierKeyState
;
7165 touchEndInput
.modifiers
= modifierKeyState
.GetModifiers();
7167 addToEndEvent
= true;
7169 if (!addToEvent
&& !addToEndEvent
) {
7170 // Filter out spurious Windows events we don't understand, like palm
7175 // Setup the touch point we'll append to the touch event array.
7176 nsPointWin touchPoint
;
7177 touchPoint
.x
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].x
);
7178 touchPoint
.y
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].y
);
7179 touchPoint
.ScreenToClient(mWnd
);
7181 // Initialize the touch data.
7182 SingleTouchData
touchData(
7183 pInputs
[i
].dwID
, // aIdentifier
7184 ScreenIntPoint::FromUnknownPoint(touchPoint
), // aScreenPoint
7185 // The contact area info cannot be trusted even when
7186 // TOUCHINPUTMASKF_CONTACTAREA is set when the input source is pen,
7187 // which somehow violates the API docs. (bug 1710509) Ultimately the
7188 // dwFlags check will become redundant since we want to migrate to
7189 // WM_POINTER for pens. (bug 1707075)
7190 (pInputs
[i
].dwMask
& TOUCHINPUTMASKF_CONTACTAREA
) &&
7191 !(pInputs
[i
].dwFlags
& TOUCHEVENTF_PEN
)
7192 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs
[i
].cxContact
) / 2,
7193 TOUCH_COORD_TO_PIXEL(pInputs
[i
].cyContact
) / 2)
7194 : ScreenSize(1, 1), // aRadius
7195 0.0f
, // aRotationAngle
7198 // Append touch data to the appropriate event.
7200 touchInput
.mTouches
.AppendElement(touchData
);
7202 if (addToEndEvent
) {
7203 touchEndInput
.mTouches
.AppendElement(touchData
);
7207 // Dispatch touch start and touch move event if we have one.
7208 if (!touchInput
.mTimeStamp
.IsNull()) {
7209 DispatchTouchOrPanGestureInput(touchInput
, pInputs
);
7211 // Dispatch touch end event if we have one.
7212 if (!touchEndInput
.mTimeStamp
.IsNull()) {
7213 DispatchTouchOrPanGestureInput(touchEndInput
, pInputs
);
7218 CloseTouchInputHandle((HTOUCHINPUT
)lParam
);
7222 // Gesture event processing. Handles WM_GESTURE events.
7223 bool nsWindow::OnGesture(WPARAM wParam
, LPARAM lParam
) {
7224 // Treatment for pan events which translate into scroll events:
7225 if (mGesture
.IsPanEvent(lParam
)) {
7226 if (!mGesture
.ProcessPanMessage(mWnd
, wParam
, lParam
))
7227 return false; // ignore
7229 nsEventStatus status
;
7231 WidgetWheelEvent
wheelEvent(true, eWheel
, this);
7233 ModifierKeyState modifierKeyState
;
7234 modifierKeyState
.InitInputEvent(wheelEvent
);
7236 wheelEvent
.mButton
= 0;
7237 wheelEvent
.mTime
= ::GetMessageTime();
7238 wheelEvent
.mTimeStamp
= GetMessageTimeStamp(wheelEvent
.mTime
);
7239 wheelEvent
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
7241 bool endFeedback
= true;
7243 if (mGesture
.PanDeltaToPixelScroll(wheelEvent
)) {
7244 DispatchEvent(&wheelEvent
, status
);
7247 if (mDisplayPanFeedback
) {
7248 mGesture
.UpdatePanFeedbackX(
7249 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaX
)),
7251 mGesture
.UpdatePanFeedbackY(
7252 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaY
)),
7254 mGesture
.PanFeedbackFinalize(mWnd
, endFeedback
);
7257 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
7262 // Other gestures translate into simple gesture events:
7263 WidgetSimpleGestureEvent
event(true, eVoidEvent
, this);
7264 if (!mGesture
.ProcessGestureMessage(mWnd
, wParam
, lParam
, event
)) {
7265 return false; // fall through to DefWndProc
7268 // Polish up and send off the new event
7269 ModifierKeyState modifierKeyState
;
7270 modifierKeyState
.InitInputEvent(event
);
7272 event
.mTime
= ::GetMessageTime();
7273 event
.mTimeStamp
= GetMessageTimeStamp(event
.mTime
);
7274 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
7276 nsEventStatus status
;
7277 DispatchEvent(&event
, status
);
7278 if (status
== nsEventStatus_eIgnore
) {
7279 return false; // Ignored, fall through
7282 // Only close this if we process and return true.
7283 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
7285 return true; // Handled
7288 // WM_DESTROY event handler
7289 void nsWindow::OnDestroy() {
7290 mOnDestroyCalled
= true;
7292 // Make sure we don't get destroyed in the process of tearing down.
7293 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
7295 // Dispatch the destroy notification.
7296 if (!mInDtor
) NotifyWindowDestroyed();
7298 // Prevent the widget from sending additional events.
7299 mWidgetListener
= nullptr;
7300 mAttachedWidgetListener
= nullptr;
7302 DestroyDirectManipulation();
7304 if (mWnd
== mLastKillFocusWindow
) {
7305 mLastKillFocusWindow
= nullptr;
7307 // Unregister notifications from terminal services
7308 ::WTSUnRegisterSessionNotification(mWnd
);
7310 // Free our subclass and clear |this| stored in the window props. We will no
7311 // longer receive events from Windows after this point.
7312 SubclassWindow(FALSE
);
7314 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
7315 // can be cleared. (It's used in tracking windows for mouse events.)
7316 if (sCurrentWindow
== this) sCurrentWindow
= nullptr;
7318 // Disconnects us from our parent, will call our GetParent().
7319 nsBaseWidget::Destroy();
7321 // Release references to children, device context, toolkit, and app shell.
7322 nsBaseWidget::OnDestroy();
7324 // Clear our native parent handle.
7325 // XXX Windows will take care of this in the proper order, and
7326 // SetParent(nullptr)'s remove child on the parent already took place in
7327 // nsBaseWidget's Destroy call above.
7328 // SetParent(nullptr);
7331 // We have to destroy the native drag target before we null out our window
7333 EnableDragDrop(false);
7335 // If we're going away and for some reason we're still the rollup widget,
7336 // rollup and turn off capture.
7337 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7338 nsCOMPtr
<nsIWidget
> rollupWidget
;
7339 if (rollupListener
) {
7340 rollupWidget
= rollupListener
->GetRollupWidget();
7342 if (this == rollupWidget
) {
7343 if (rollupListener
) rollupListener
->Rollup(0, false, nullptr, nullptr);
7344 CaptureRollupEvents(nullptr, false);
7347 IMEHandler::OnDestroyWindow(this);
7349 // Free GDI window class objects
7351 VERIFY(::DeleteObject(mBrush
));
7355 // Destroy any custom cursor resources.
7356 if (mCursor
.IsCustom()) {
7357 SetCursor(Cursor
{eCursor_standard
});
7360 if (mCompositorWidgetDelegate
) {
7361 mCompositorWidgetDelegate
->OnDestroyWindow();
7363 mBasicLayersSurface
= nullptr;
7365 // Finalize panning feedback to possibly restore window displacement
7366 mGesture
.PanFeedbackFinalize(mWnd
, true);
7368 // Clear the main HWND.
7372 // Send a resize message to the listener
7373 bool nsWindow::OnResize(const LayoutDeviceIntSize
& aSize
) {
7374 if (mCompositorWidgetDelegate
&&
7375 !mCompositorWidgetDelegate
->OnWindowResize(aSize
)) {
7379 bool result
= false;
7380 if (mWidgetListener
) {
7381 result
= mWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
7384 // If there is an attached view, inform it as well as the normal widget
7386 if (mAttachedWidgetListener
) {
7387 return mAttachedWidgetListener
->WindowResized(this, aSize
.width
,
7394 void nsWindow::OnSizeModeChange(nsSizeMode aSizeMode
) {
7395 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7396 ("nsWindow::OnSizeModeChange() aSizeMode %d", aSizeMode
));
7398 if (NeedsToTrackWindowOcclusionState()) {
7399 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
7400 this, aSizeMode
!= nsSizeMode_Minimized
);
7402 wr::DebugFlags flags
{0};
7403 flags
.bits
= gfx::gfxVars::WebRenderDebugFlags();
7404 bool debugEnabled
= bool(flags
& wr::DebugFlags::WINDOW_VISIBILITY_DBG
);
7405 if (debugEnabled
&& mCompositorWidgetDelegate
) {
7406 mCompositorWidgetDelegate
->NotifyVisibilityUpdated(aSizeMode
,
7411 if (mCompositorWidgetDelegate
) {
7412 mCompositorWidgetDelegate
->OnWindowModeChange(aSizeMode
);
7415 if (mWidgetListener
) {
7416 mWidgetListener
->SizeModeChanged(aSizeMode
);
7420 bool nsWindow::OnHotKey(WPARAM wParam
, LPARAM lParam
) { return true; }
7422 // Can be overriden. Controls auto-erase of background.
7423 bool nsWindow::AutoErase(HDC dc
) { return false; }
7425 bool nsWindow::IsPopup() { return mWindowType
== eWindowType_popup
; }
7427 bool nsWindow::ShouldUseOffMainThreadCompositing() {
7428 if (mWindowType
== eWindowType_popup
&& mPopupType
== ePopupTypeTooltip
) {
7432 // Content rendering of popup is always done by child window.
7433 // See nsDocumentViewer::ShouldAttachToTopLevel().
7434 if (mWindowType
== eWindowType_popup
&& !mIsChildWindow
) {
7435 MOZ_ASSERT(!mParent
);
7439 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
7442 void nsWindow::WindowUsesOMTC() {
7443 ULONG_PTR style
= ::GetClassLongPtr(mWnd
, GCL_STYLE
);
7445 NS_WARNING("Could not get window class style");
7448 style
|= CS_HREDRAW
| CS_VREDRAW
;
7449 DebugOnly
<ULONG_PTR
> result
= ::SetClassLongPtr(mWnd
, GCL_STYLE
, style
);
7450 NS_WARNING_ASSERTION(result
, "Could not reset window class style");
7453 bool nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
7454 if (sHasBogusPopupsDropShadowOnMultiMonitor
== TRI_UNKNOWN
) {
7455 // Since any change in the preferences requires a restart, this can be
7457 // Check for Direct2D first.
7458 sHasBogusPopupsDropShadowOnMultiMonitor
=
7459 gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ? TRI_TRUE
7461 if (!sHasBogusPopupsDropShadowOnMultiMonitor
) {
7462 // Otherwise check if Direct3D 9 may be used.
7463 if (gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING
) &&
7464 !gfxConfig::IsEnabled(gfx::Feature::OPENGL_COMPOSITING
)) {
7465 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= components::GfxInfo::Service();
7468 nsCString discardFailureId
;
7469 if (NS_SUCCEEDED(gfxInfo
->GetFeatureStatus(
7470 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS
, discardFailureId
,
7472 if (status
== nsIGfxInfo::FEATURE_STATUS_OK
||
7473 gfxConfig::IsForcedOnByUser(gfx::Feature::HW_COMPOSITING
)) {
7474 sHasBogusPopupsDropShadowOnMultiMonitor
= TRI_TRUE
;
7481 return !!sHasBogusPopupsDropShadowOnMultiMonitor
;
7484 void nsWindow::OnDPIChanged(int32_t x
, int32_t y
, int32_t width
,
7486 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
7487 // they remain tied to their original parent's resolution.
7488 if (mWindowType
== eWindowType_popup
) {
7491 if (StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
7494 mDefaultScale
= -1.0; // force recomputation of scale factor
7496 if (mResizeState
!= RESIZING
&&
7497 mFrameState
->GetSizeMode() == nsSizeMode_Normal
) {
7498 // Limit the position (if not in the middle of a drag-move) & size,
7499 // if it would overflow the destination screen
7500 nsCOMPtr
<nsIScreenManager
> sm
= do_GetService(sScreenManagerContractID
);
7502 nsCOMPtr
<nsIScreen
> screen
;
7503 sm
->ScreenForRect(x
, y
, width
, height
, getter_AddRefs(screen
));
7505 int32_t availLeft
, availTop
, availWidth
, availHeight
;
7506 screen
->GetAvailRect(&availLeft
, &availTop
, &availWidth
, &availHeight
);
7507 if (mResizeState
!= MOVING
) {
7508 x
= std::max(x
, availLeft
);
7509 y
= std::max(y
, availTop
);
7511 width
= std::min(width
, availWidth
);
7512 height
= std::min(height
, availHeight
);
7516 Resize(x
, y
, width
, height
, true);
7518 UpdateNonClientMargins();
7523 /**************************************************************
7524 **************************************************************
7526 ** BLOCK: IME management and accessibility
7528 ** Handles managing IME input and accessibility.
7530 **************************************************************
7531 **************************************************************/
7533 void nsWindow::SetInputContext(const InputContext
& aContext
,
7534 const InputContextAction
& aAction
) {
7535 InputContext newInputContext
= aContext
;
7536 IMEHandler::SetInputContext(this, newInputContext
, aAction
);
7537 mInputContext
= newInputContext
;
7540 InputContext
nsWindow::GetInputContext() {
7541 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7542 if (WinUtils::IsIMEEnabled(mInputContext
) && IMEHandler::GetOpenState(this)) {
7543 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN
;
7545 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7547 return mInputContext
;
7550 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
7551 return IMEHandler::GetNativeTextEventDispatcherListener();
7554 #ifdef ACCESSIBILITY
7556 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7557 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7559 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: " \
7561 aHwnd, ::GetParent(aHwnd), aWnd); \
7562 printf(" acc: %p", aAcc); \
7564 nsAutoString name; \
7566 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7572 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7575 a11y::LocalAccessible
* nsWindow::GetAccessible() {
7576 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7577 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled
)
7580 if (mInDtor
|| mOnDestroyCalled
|| mWindowType
== eWindowType_invisible
) {
7584 // In case of popup window return a popup accessible.
7585 nsView
* view
= nsView::GetViewFor(this);
7587 nsIFrame
* frame
= view
->GetFrame();
7588 if (frame
&& nsLayoutUtils::IsPopup(frame
)) {
7589 nsAccessibilityService
* accService
= GetOrCreateAccService();
7591 a11y::DocAccessible
* docAcc
=
7592 GetAccService()->GetDocAccessible(frame
->PresShell());
7596 docAcc
->GetAccessibleOrDescendant(frame
->GetContent()));
7597 return docAcc
->GetAccessibleOrDescendant(frame
->GetContent());
7603 // otherwise root document accessible.
7604 NS_LOG_WMGETOBJECT(this, mWnd
, GetRootAccessible());
7605 return GetRootAccessible();
7609 /**************************************************************
7610 **************************************************************
7612 ** BLOCK: Transparency
7614 ** Window transparency helpers.
7616 **************************************************************
7617 **************************************************************/
7619 void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode
) {
7620 if (aMode
== mTransparencyMode
) return;
7622 // stop on dialogs and popups!
7623 HWND hWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
7624 nsWindow
* parent
= WinUtils::GetNSWindowPtr(hWnd
);
7627 NS_WARNING("Trying to use transparent chrome in an embedded context");
7631 if (parent
!= this) {
7633 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7636 if (aMode
== eTransparencyTransparent
) {
7637 // If we're switching to the use of a transparent window, hide the chrome
7639 HideWindowChrome(true);
7640 } else if (mHideChrome
&& mTransparencyMode
== eTransparencyTransparent
) {
7641 // if we're switching out of transparent, re-enable our parent's chrome.
7642 HideWindowChrome(false);
7645 LONG_PTR style
= ::GetWindowLongPtrW(hWnd
, GWL_STYLE
),
7646 exStyle
= ::GetWindowLongPtr(hWnd
, GWL_EXSTYLE
);
7648 if (parent
->mIsVisible
) {
7649 style
|= WS_VISIBLE
;
7650 if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
7651 style
|= WS_MAXIMIZE
;
7652 } else if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
7653 style
|= WS_MINIMIZE
;
7657 if (aMode
== eTransparencyTransparent
)
7658 exStyle
|= WS_EX_LAYERED
;
7660 exStyle
&= ~WS_EX_LAYERED
;
7662 VERIFY_WINDOW_STYLE(style
);
7663 ::SetWindowLongPtrW(hWnd
, GWL_STYLE
, style
);
7664 ::SetWindowLongPtrW(hWnd
, GWL_EXSTYLE
, exStyle
);
7666 if (HasGlass()) memset(&mGlassMargins
, 0, sizeof mGlassMargins
);
7667 mTransparencyMode
= aMode
;
7669 if (mCompositorWidgetDelegate
) {
7670 mCompositorWidgetDelegate
->UpdateTransparency(aMode
);
7674 // Clear window by transparent black when compositor window is used in GPU
7675 // process and non-client area rendering by DWM is enabled.
7676 // It is for showing non-client area rendering. See nsWindow::UpdateGlass().
7677 if (HasGlass() && GetWindowRenderer()->AsKnowsCompositor() &&
7678 GetWindowRenderer()->AsKnowsCompositor()->GetUseCompositorWnd()) {
7681 hdc
= ::GetWindowDC(mWnd
);
7682 ::GetWindowRect(mWnd
, &rect
);
7683 ::MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
7684 ::FillRect(hdc
, &rect
,
7685 reinterpret_cast<HBRUSH
>(GetStockObject(BLACK_BRUSH
)));
7686 ReleaseDC(mWnd
, hdc
);
7689 // Disable double buffering with D3D compositor for disabling compositor
7691 if (HasGlass() && !gfxVars::UseWebRender() &&
7692 gfxVars::UseDoubleBufferingWithCompositor()) {
7693 gfxVars::SetUseDoubleBufferingWithCompositor(false);
7694 GPUProcessManager::Get()->ResetCompositors();
7698 /**************************************************************
7699 **************************************************************
7701 ** BLOCK: Popup rollup hooks
7703 ** Deals with CaptureRollup on popup windows.
7705 **************************************************************
7706 **************************************************************/
7708 // Schedules a timer for a window, so we can rollup after processing the hook
7710 void nsWindow::ScheduleHookTimer(HWND aWnd
, UINT aMsgId
) {
7711 // In some cases multiple hooks may be scheduled
7712 // so ignore any other requests once one timer is scheduled
7713 if (sHookTimerId
== 0) {
7714 // Remember the window handle and the message ID to be used later
7715 sRollupMsgId
= aMsgId
;
7716 sRollupMsgWnd
= aWnd
;
7717 // Schedule native timer for doing the rollup after
7718 // this event is done being processed
7719 sHookTimerId
= ::SetTimer(nullptr, 0, 0, (TIMERPROC
)HookTimerForPopups
);
7720 NS_ASSERTION(sHookTimerId
, "Timer couldn't be created.");
7724 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7725 int gLastMsgCode
= 0;
7726 extern MSGFEventMsgInfo gMSGFEvents
[];
7729 // Process Menu messages, rollup when popup is clicked.
7730 LRESULT CALLBACK
nsWindow::MozSpecialMsgFilter(int code
, WPARAM wParam
,
7732 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7734 MSG
* pMsg
= (MSG
*)lParam
;
7737 while (gMSGFEvents
[inx
].mId
!= code
&& gMSGFEvents
[inx
].mStr
!= nullptr) {
7740 if (code
!= gLastMsgCode
) {
7741 if (gMSGFEvents
[inx
].mId
== code
) {
7743 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7744 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code
,
7745 gMSGFEvents
[inx
].mStr
, pMsg
->hwnd
));
7749 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7750 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code
,
7751 gMSGFEvents
[inx
].mId
, pMsg
->hwnd
));
7754 gLastMsgCode
= code
;
7756 PrintEvent(pMsg
->message
, FALSE
, FALSE
);
7758 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7760 if (sProcessHook
&& code
== MSGF_MENU
) {
7761 MSG
* pMsg
= (MSG
*)lParam
;
7762 ScheduleHookTimer(pMsg
->hwnd
, pMsg
->message
);
7765 return ::CallNextHookEx(sMsgFilterHook
, code
, wParam
, lParam
);
7768 // Process all mouse messages. Roll up when a click is in a native window
7769 // that doesn't have an nsIWidget.
7770 LRESULT CALLBACK
nsWindow::MozSpecialMouseProc(int code
, WPARAM wParam
,
7773 switch (WinUtils::GetNativeMessage(wParam
)) {
7774 case WM_LBUTTONDOWN
:
7775 case WM_RBUTTONDOWN
:
7776 case WM_MBUTTONDOWN
:
7778 case WM_MOUSEHWHEEL
: {
7779 MOUSEHOOKSTRUCT
* ms
= (MOUSEHOOKSTRUCT
*)lParam
;
7780 nsIWidget
* mozWin
= WinUtils::GetNSWindowPtr(ms
->hwnd
);
7782 ScheduleHookTimer(ms
->hwnd
, (UINT
)wParam
);
7788 return ::CallNextHookEx(sCallMouseHook
, code
, wParam
, lParam
);
7791 // Process all messages. Roll up when the window is moving, or
7792 // is resizing or when maximized or mininized.
7793 LRESULT CALLBACK
nsWindow::MozSpecialWndProc(int code
, WPARAM wParam
,
7795 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7797 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7798 PrintEvent(cwpt
->message
, FALSE
, FALSE
);
7803 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7804 if (cwpt
->message
== WM_MOVING
|| cwpt
->message
== WM_SIZING
||
7805 cwpt
->message
== WM_GETMINMAXINFO
) {
7806 ScheduleHookTimer(cwpt
->hwnd
, (UINT
)cwpt
->message
);
7810 return ::CallNextHookEx(sCallProcHook
, code
, wParam
, lParam
);
7813 // Register the special "hooks" for dropdown processing.
7814 void nsWindow::RegisterSpecialDropdownHooks() {
7815 NS_ASSERTION(!sMsgFilterHook
, "sMsgFilterHook must be NULL!");
7816 NS_ASSERTION(!sCallProcHook
, "sCallProcHook must be NULL!");
7818 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7820 // Install msg hook for moving the window and resizing
7821 if (!sMsgFilterHook
) {
7822 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7823 sMsgFilterHook
= SetWindowsHookEx(WH_MSGFILTER
, MozSpecialMsgFilter
,
7824 nullptr, GetCurrentThreadId());
7825 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7826 if (!sMsgFilterHook
) {
7827 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7828 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7833 // Install msg hook for menus
7834 if (!sCallProcHook
) {
7835 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7836 sCallProcHook
= SetWindowsHookEx(WH_CALLWNDPROC
, MozSpecialWndProc
, nullptr,
7837 GetCurrentThreadId());
7838 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7839 if (!sCallProcHook
) {
7841 gWindowsLog
, LogLevel::Info
,
7842 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7847 // Install msg hook for the mouse
7848 if (!sCallMouseHook
) {
7849 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7850 sCallMouseHook
= SetWindowsHookEx(WH_MOUSE
, MozSpecialMouseProc
, nullptr,
7851 GetCurrentThreadId());
7852 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7853 if (!sCallMouseHook
) {
7854 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7855 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7861 // Unhook special message hooks for dropdowns.
7862 void nsWindow::UnregisterSpecialDropdownHooks() {
7864 "***************** De-installing Msg Hooks ***************\n");
7866 if (sCallProcHook
) {
7867 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7868 if (!::UnhookWindowsHookEx(sCallProcHook
)) {
7869 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7871 sCallProcHook
= nullptr;
7874 if (sMsgFilterHook
) {
7875 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7876 if (!::UnhookWindowsHookEx(sMsgFilterHook
)) {
7877 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7879 sMsgFilterHook
= nullptr;
7882 if (sCallMouseHook
) {
7883 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7884 if (!::UnhookWindowsHookEx(sCallMouseHook
)) {
7885 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7887 sCallMouseHook
= nullptr;
7891 // This timer is designed to only fire one time at most each time a "hook"
7892 // function is used to rollup the dropdown. In some cases, the timer may be
7893 // scheduled from the hook, but that hook event or a subsequent event may roll
7894 // up the dropdown before this timer function is executed.
7896 // For example, if an MFC control takes focus, the combobox will lose focus and
7897 // rollup before this function fires.
7898 VOID CALLBACK
nsWindow::HookTimerForPopups(HWND hwnd
, UINT uMsg
, UINT idEvent
,
7900 if (sHookTimerId
!= 0) {
7901 // if the window is nullptr then we need to use the ID to kill the timer
7902 DebugOnly
<BOOL
> status
= ::KillTimer(nullptr, sHookTimerId
);
7903 NS_ASSERTION(status
, "Hook Timer was not killed.");
7907 if (sRollupMsgId
!= 0) {
7908 // Note: DealWithPopups does the check to make sure that the rollup widget
7910 LRESULT popupHandlingResult
;
7911 nsAutoRollup autoRollup
;
7912 DealWithPopups(sRollupMsgWnd
, sRollupMsgId
, 0, 0, &popupHandlingResult
);
7914 sRollupMsgWnd
= nullptr;
7918 static bool IsDifferentThreadWindow(HWND aWnd
) {
7919 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd
, nullptr);
7923 bool nsWindow::EventIsInsideWindow(nsWindow
* aWindow
,
7924 Maybe
<POINT
> aEventPoint
) {
7926 ::GetWindowRect(aWindow
->mWnd
, &r
);
7931 DWORD pos
= ::GetMessagePos();
7932 mp
.x
= GET_X_LPARAM(pos
);
7933 mp
.y
= GET_Y_LPARAM(pos
);
7936 // was the event inside this window?
7937 return static_cast<bool>(::PtInRect(&r
, mp
));
7941 bool nsWindow::GetPopupsToRollup(nsIRollupListener
* aRollupListener
,
7942 uint32_t* aPopupsToRollup
,
7943 Maybe
<POINT
> aEventPoint
) {
7944 // If we're dealing with menus, we probably have submenus and we don't want
7945 // to rollup some of them if the click is in a parent menu of the current
7947 *aPopupsToRollup
= UINT32_MAX
;
7948 AutoTArray
<nsIWidget
*, 5> widgetChain
;
7949 uint32_t sameTypeCount
= aRollupListener
->GetSubmenuWidgetChain(&widgetChain
);
7950 for (uint32_t i
= 0; i
< widgetChain
.Length(); ++i
) {
7951 nsIWidget
* widget
= widgetChain
[i
];
7952 if (EventIsInsideWindow(static_cast<nsWindow
*>(widget
), aEventPoint
)) {
7953 // Don't roll up if the mouse event occurred within a menu of the
7954 // same type. If the mouse event occurred in a menu higher than that,
7955 // roll up, but pass the number of popups to Rollup so that only those
7956 // of the same type close up.
7957 if (i
< sameTypeCount
) {
7961 *aPopupsToRollup
= sameTypeCount
;
7969 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd
) {
7970 // While popup is open, popup window might be activated by other application.
7971 // At this time, we need to take back focus to the previous window but it
7972 // causes flickering its nonclient area because WM_NCACTIVATE comes before
7973 // WM_ACTIVATE and we cannot know which window will take focus at receiving
7974 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
7976 // If non-popup window receives WM_NCACTIVATE at deactivating, default
7977 // wndproc shouldn't handle it as deactivating. Instead, at receiving
7978 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
7979 // This returns true if the window needs to handle WM_NCACTIVATE later.
7981 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7982 return window
&& !window
->IsPopup();
7985 static bool IsTouchSupportEnabled(HWND aWnd
) {
7986 nsWindow
* topWindow
=
7987 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd
, true));
7988 return topWindow
? topWindow
->IsTouchWindow() : false;
7991 static Maybe
<POINT
> GetSingleTouch(WPARAM wParam
, LPARAM lParam
) {
7993 uint32_t cInputs
= LOWORD(wParam
);
7998 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, &input
,
7999 sizeof(TOUCHINPUT
))) {
8001 ret
->x
= TOUCH_COORD_TO_PIXEL(input
.x
);
8002 ret
->y
= TOUCH_COORD_TO_PIXEL(input
.y
);
8004 // Note that we don't call CloseTouchInputHandle here because we need
8005 // to read the touch input info again in OnTouch later.
8010 bool nsWindow::DealWithPopups(HWND aWnd
, UINT aMessage
, WPARAM aWParam
,
8011 LPARAM aLParam
, LRESULT
* aResult
) {
8012 NS_ASSERTION(aResult
, "Bad outResult");
8014 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
8015 *aResult
= MA_NOACTIVATE
;
8017 if (!::IsWindowVisible(aWnd
)) {
8021 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
8022 NS_ENSURE_TRUE(rollupListener
, false);
8024 nsCOMPtr
<nsIWidget
> popup
= rollupListener
->GetRollupWidget();
8029 static bool sSendingNCACTIVATE
= false;
8030 static bool sPendingNCACTIVATE
= false;
8031 uint32_t popupsToRollup
= UINT32_MAX
;
8033 bool consumeRollupEvent
= false;
8034 Maybe
<POINT
> touchPoint
; // In screen coords.
8036 nsWindow
* popupWindow
= static_cast<nsWindow
*>(popup
.get());
8037 UINT nativeMessage
= WinUtils::GetNativeMessage(aMessage
);
8038 switch (nativeMessage
) {
8040 if (!IsTouchSupportEnabled(aWnd
)) {
8041 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
8042 // compatibility mouse events will do it instead.
8045 touchPoint
= GetSingleTouch(aWParam
, aLParam
);
8050 case WM_LBUTTONDOWN
:
8051 case WM_RBUTTONDOWN
:
8052 case WM_MBUTTONDOWN
:
8053 case WM_NCLBUTTONDOWN
:
8054 case WM_NCRBUTTONDOWN
:
8055 case WM_NCMBUTTONDOWN
:
8056 if (nativeMessage
!= WM_TOUCH
&& IsTouchSupportEnabled(aWnd
) &&
8057 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
8058 // If any of these mouse events are really compatibility events that
8059 // Windows is sending for touch inputs, then don't allow them to dismiss
8060 // popups when APZ is enabled (instead we do the dismissing as part of
8061 // WM_TOUCH handling which is more correct).
8062 // If we don't do this, then when the user lifts their finger after a
8063 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
8064 // us will dismiss the contextmenu popup that we displayed as part of
8065 // handling the long-tap-up.
8068 if (!EventIsInsideWindow(popupWindow
, touchPoint
) &&
8069 GetPopupsToRollup(rollupListener
, &popupsToRollup
, touchPoint
)) {
8073 case WM_POINTERDOWN
: {
8074 WinPointerEvents pointerEvents
;
8075 if (!pointerEvents
.ShouldRollupOnPointerEvent(nativeMessage
, aWParam
)) {
8079 pt
.x
= GET_X_LPARAM(aLParam
);
8080 pt
.y
= GET_Y_LPARAM(aLParam
);
8081 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
8084 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
8085 // Don't roll up if the event is inside the popup window.
8089 case MOZ_WM_DMANIP
: {
8091 ::GetCursorPos(&pt
);
8092 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
8095 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
8096 // Don't roll up if the event is inside the popup window
8101 case WM_MOUSEHWHEEL
:
8102 // We need to check if the popup thinks that it should cause closing
8103 // itself when mouse wheel events are fired outside the rollup widget.
8104 if (!EventIsInsideWindow(popupWindow
)) {
8105 // Check if we should consume this event even if we don't roll-up:
8106 consumeRollupEvent
= rollupListener
->ShouldConsumeOnMouseWheelEvent();
8107 *aResult
= MA_ACTIVATE
;
8108 if (rollupListener
->ShouldRollupOnMouseWheelEvent() &&
8109 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8113 return consumeRollupEvent
;
8115 case WM_ACTIVATEAPP
:
8119 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
8120 // because we cannot distinguish it's caused by mouse or not.
8121 if (LOWORD(aWParam
) == WA_ACTIVE
&& aLParam
) {
8122 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
8123 if (window
&& window
->IsPopup()) {
8124 // Cancel notifying widget listeners of deactivating the previous
8125 // active window (see WM_KILLFOCUS case in ProcessMessage()).
8126 sJustGotDeactivate
= false;
8127 // Reactivate the window later.
8128 ::PostMessageW(aWnd
, MOZ_WM_REACTIVATE
, aWParam
, aLParam
);
8131 // Don't rollup the popup when focus moves back to the parent window
8132 // from a popup because such case is caused by strange mouse drivers.
8133 nsWindow
* prevWindow
=
8134 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
8135 if (prevWindow
&& prevWindow
->IsPopup()) {
8136 // Consume this message here since previous window must not have
8137 // been inactivated since we've already stopped accepting the
8138 // inactivation below.
8141 } else if (LOWORD(aWParam
) == WA_INACTIVE
) {
8142 nsWindow
* activeWindow
=
8143 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
8144 if (sPendingNCACTIVATE
&& NeedsToHandleNCActivateDelayed(aWnd
)) {
8145 // If focus moves to non-popup widget or focusable popup, the window
8146 // needs to update its nonclient area.
8147 if (!activeWindow
|| !activeWindow
->IsPopup()) {
8148 sSendingNCACTIVATE
= true;
8149 ::SendMessageW(aWnd
, WM_NCACTIVATE
, false, 0);
8150 sSendingNCACTIVATE
= false;
8152 sPendingNCACTIVATE
= false;
8154 // If focus moves from/to popup, we don't need to rollup the popup
8155 // because such case is caused by strange mouse drivers. And in
8156 // such case, we should consume the message here since we need to
8157 // hide this odd focus move from our content. (If we didn't consume
8158 // the message here, ProcessMessage() will notify widget listener of
8159 // inactivation and that causes unnecessary reflow for supporting
8160 // -moz-window-inactive pseudo class.
8162 if (activeWindow
->IsPopup()) {
8165 nsWindow
* deactiveWindow
= WinUtils::GetNSWindowPtr(aWnd
);
8166 if (deactiveWindow
&& deactiveWindow
->IsPopup()) {
8170 } else if (LOWORD(aWParam
) == WA_CLICKACTIVE
) {
8171 // If the WM_ACTIVATE message is caused by a click in a popup,
8172 // we should not rollup any popups.
8173 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
8174 if ((window
&& window
->IsPopup()) ||
8175 !GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8181 case MOZ_WM_REACTIVATE
:
8182 // The previous active window should take back focus.
8183 if (::IsWindow(reinterpret_cast<HWND
>(aLParam
))) {
8184 // FYI: Even without this API call, you see expected result (e.g., the
8185 // owner window of the popup keeps active without flickering
8186 // the non-client area). And also this causes initializing
8187 // TSF and it causes using CPU time a lot. However, even if we
8188 // consume WM_ACTIVE messages, native focus change has already
8189 // been occurred. I.e., a popup window is active now. Therefore,
8190 // you'll see some odd behavior if we don't reactivate the owner
8191 // window here. For example, if you do:
8192 // 1. Turn wheel on a bookmark panel.
8193 // 2. Turn wheel on another window.
8194 // then, you'll see that the another window becomes active but the
8195 // owner window of the bookmark panel looks still active and the
8196 // bookmark panel keeps open. The reason is that the first wheel
8197 // operation gives focus to the bookmark panel. Therefore, when
8198 // the next operation gives focus to the another window, previous
8199 // focus window is the bookmark panel (i.e., a popup window).
8200 // So, in this case, our hack around here prevents to inactivate
8201 // the owner window and roll up the bookmark panel.
8202 ::SetForegroundWindow(reinterpret_cast<HWND
>(aLParam
));
8207 if (!aWParam
&& !sSendingNCACTIVATE
&&
8208 NeedsToHandleNCActivateDelayed(aWnd
)) {
8209 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
8210 // nonclient area state change.
8211 ::DefWindowProcW(aWnd
, aMessage
, TRUE
, aLParam
);
8212 // Accept the deactivating because it's necessary to receive following
8215 sPendingNCACTIVATE
= true;
8220 case WM_MOUSEACTIVATE
:
8221 if (!EventIsInsideWindow(popupWindow
) &&
8222 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
8223 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
8224 // of TweakUI is enabled. Then, check if the popup should be rolled up
8225 // with rollup listener. If not, just consume the message.
8226 if (HIWORD(aLParam
) == WM_MOUSEMOVE
&&
8227 !rollupListener
->ShouldRollupOnMouseActivate()) {
8230 // Otherwise, it should be handled by wndproc.
8234 // Prevent the click inside the popup from causing a change in window
8235 // activation. Since the popup is shown non-activated, we need to eat any
8236 // requests to activate the window while it is displayed. Windows will
8237 // automatically activate the popup on the mousedown otherwise.
8241 // If the window is being minimized, close popups.
8242 if (aLParam
== SW_PARENTCLOSING
) {
8248 // If focus moves to other window created in different process/thread,
8249 // e.g., a plugin window, popups should be rolled up.
8250 if (IsDifferentThreadWindow(reinterpret_cast<HWND
>(aWParam
))) {
8263 // Only need to deal with the last rollup for left mouse down events.
8264 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
8266 if (nativeMessage
== WM_TOUCH
|| nativeMessage
== WM_LBUTTONDOWN
||
8267 nativeMessage
== WM_POINTERDOWN
) {
8268 LayoutDeviceIntPoint pos
;
8269 if (nativeMessage
== WM_TOUCH
) {
8270 pos
.x
= touchPoint
->x
;
8271 pos
.y
= touchPoint
->y
;
8274 pt
.x
= GET_X_LPARAM(aLParam
);
8275 pt
.y
= GET_Y_LPARAM(aLParam
);
8276 // POINTERDOWN is already in screen coords.
8277 if (nativeMessage
== WM_LBUTTONDOWN
) {
8278 ::ClientToScreen(aWnd
, &pt
);
8280 pos
= LayoutDeviceIntPoint(pt
.x
, pt
.y
);
8283 nsIContent
* lastRollup
;
8284 consumeRollupEvent
=
8285 rollupListener
->Rollup(popupsToRollup
, true, &pos
, &lastRollup
);
8286 nsAutoRollup::SetLastRollup(lastRollup
);
8288 consumeRollupEvent
=
8289 rollupListener
->Rollup(popupsToRollup
, true, nullptr, nullptr);
8292 // Tell hook to stop processing messages
8293 sProcessHook
= false;
8295 sRollupMsgWnd
= nullptr;
8297 // If we are NOT supposed to be consuming events, let it go through
8298 if (consumeRollupEvent
&& nativeMessage
!= WM_RBUTTONDOWN
) {
8299 *aResult
= MA_ACTIVATE
;
8306 /**************************************************************
8307 **************************************************************
8309 ** BLOCK: Misc. utility methods and functions.
8313 **************************************************************
8314 **************************************************************/
8316 // Note that the result of GetTopLevelWindow method can be different from the
8317 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8318 // window. Because our top level window may be contained in another window
8319 // which is not managed by us.
8320 nsWindow
* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup
) {
8321 nsWindow
* curWindow
= this;
8324 if (aStopOnDialogOrPopup
) {
8325 switch (curWindow
->mWindowType
) {
8326 case eWindowType_dialog
:
8327 case eWindowType_popup
:
8334 // Retrieve the top level parent or owner window
8335 nsWindow
* parentWindow
= curWindow
->GetParentWindow(true);
8337 if (!parentWindow
) return curWindow
;
8339 curWindow
= parentWindow
;
8343 // Set a flag if hwnd is a (non-popup) visible window from this process,
8344 // and bail out of the enumeration. Otherwise leave the flag unmodified
8345 // and continue the enumeration.
8346 // lParam must be a bool* pointing at the flag to be set.
8347 static BOOL CALLBACK
EnumVisibleWindowsProc(HWND hwnd
, LPARAM lParam
) {
8349 ::GetWindowThreadProcessId(hwnd
, &pid
);
8350 if (pid
== ::GetCurrentProcessId() && ::IsWindowVisible(hwnd
)) {
8351 // Don't count popups as visible windows, since they don't take focus,
8352 // in case we only have a popup visible (see bug 1554490 where the gfx
8353 // test window is an offscreen popup).
8354 nsWindow
* window
= WinUtils::GetNSWindowPtr(hwnd
);
8355 if (!window
|| !window
->IsPopup()) {
8356 bool* windowsVisible
= reinterpret_cast<bool*>(lParam
);
8357 *windowsVisible
= true;
8364 // Determine if it would be ok to activate a window, taking focus.
8365 // We want to avoid stealing focus from another app (bug 225305).
8366 bool nsWindow::CanTakeFocus() {
8367 HWND fgWnd
= ::GetForegroundWindow();
8369 // There is no foreground window, so don't worry about stealing focus.
8372 // We can take focus if the current foreground window is already from
8375 ::GetWindowThreadProcessId(fgWnd
, &pid
);
8376 if (pid
== ::GetCurrentProcessId()) {
8380 bool windowsVisible
= false;
8381 ::EnumWindows(EnumVisibleWindowsProc
,
8382 reinterpret_cast<LPARAM
>(&windowsVisible
));
8384 if (!windowsVisible
) {
8385 // We're probably creating our first visible window, allow that to
8392 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
8393 static const wchar_t* sMainWindowClass
= nullptr;
8394 if (!sMainWindowClass
) {
8395 nsAutoString className
;
8396 Preferences::GetString("ui.window_class_override", className
);
8397 if (!className
.IsEmpty()) {
8398 sMainWindowClass
= wcsdup(className
.get());
8400 sMainWindowClass
= kClassNameGeneral
;
8403 return sMainWindowClass
;
8406 LPARAM
nsWindow::lParamToScreen(LPARAM lParam
) {
8408 pt
.x
= GET_X_LPARAM(lParam
);
8409 pt
.y
= GET_Y_LPARAM(lParam
);
8410 ::ClientToScreen(mWnd
, &pt
);
8411 return MAKELPARAM(pt
.x
, pt
.y
);
8414 LPARAM
nsWindow::lParamToClient(LPARAM lParam
) {
8416 pt
.x
= GET_X_LPARAM(lParam
);
8417 pt
.y
= GET_Y_LPARAM(lParam
);
8418 ::ScreenToClient(mWnd
, &pt
);
8419 return MAKELPARAM(pt
.x
, pt
.y
);
8422 WPARAM
nsWindow::wParamFromGlobalMouseState() {
8425 if (!!::GetKeyState(VK_CONTROL
)) {
8426 result
|= MK_CONTROL
;
8429 if (!!::GetKeyState(VK_SHIFT
)) {
8433 if (!!::GetKeyState(VK_LBUTTON
)) {
8434 result
|= MK_LBUTTON
;
8437 if (!!::GetKeyState(VK_MBUTTON
)) {
8438 result
|= MK_MBUTTON
;
8441 if (!!::GetKeyState(VK_RBUTTON
)) {
8442 result
|= MK_RBUTTON
;
8445 if (!!::GetKeyState(VK_XBUTTON1
)) {
8446 result
|= MK_XBUTTON1
;
8449 if (!!::GetKeyState(VK_XBUTTON2
)) {
8450 result
|= MK_XBUTTON2
;
8456 void nsWindow::PickerOpen() { mPickerDisplayCount
++; }
8458 void nsWindow::PickerClosed() {
8459 NS_ASSERTION(mPickerDisplayCount
> 0, "mPickerDisplayCount out of sync!");
8460 if (!mPickerDisplayCount
) return;
8461 mPickerDisplayCount
--;
8462 if (!mPickerDisplayCount
&& mDestroyCalled
) {
8467 bool nsWindow::WidgetTypeSupportsAcceleration() {
8468 // We don't currently support using an accelerated layer manager with
8469 // transparent windows so don't even try. I'm also not sure if we even
8470 // want to support this case. See bug 593471.
8472 // Windows' support for transparent accelerated surfaces isn't great.
8473 // Some possible approaches:
8474 // - Readback the data and update it using
8475 // UpdateLayeredWindow/UpdateLayeredWindowIndirect
8476 // This is what WPF does. See
8477 // CD3DDeviceLevel1::PresentWithGDI/CD3DSwapChainWithSwDC in WpfGfx. The
8478 // rationale for not using IDirect3DSurface9::GetDC is explained here:
8479 // https://web.archive.org/web/20160521191104/https://blogs.msdn.microsoft.com/dwayneneed/2008/09/08/transparent-windows-in-wpf/
8480 // - Use D3D11_RESOURCE_MISC_GDI_COMPATIBLE, IDXGISurface1::GetDC(),
8481 // and UpdateLayeredWindowIndirect.
8482 // This is suggested here:
8483 // https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/december/windows-with-c-layered-windows-with-direct2d
8484 // but might have the same problem that IDirect3DSurface9::GetDC has.
8485 // - Creating the window with the WS_EX_NOREDIRECTIONBITMAP flag and use
8486 // DirectComposition.
8487 // Not supported on Win7.
8488 // - Using DwmExtendFrameIntoClientArea with negative margins and something
8489 // to turn off the glass effect.
8490 // This doesn't work when the DWM is not running (Win7)
8492 // Also see bug 1150376, D3D11 composition can cause issues on some devices
8493 // on Windows 7 where presentation fails randomly for windows with drop
8495 return mTransparencyMode
!= eTransparencyTransparent
&&
8496 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
8499 bool nsWindow::DispatchTouchEventFromWMPointer(
8500 UINT msg
, LPARAM aLParam
, const WinPointerInfo
& aPointerInfo
,
8501 mozilla::MouseButton aButton
) {
8502 MultiTouchInput::MultiTouchType touchType
;
8504 case WM_POINTERDOWN
:
8505 touchType
= MultiTouchInput::MULTITOUCH_START
;
8507 case WM_POINTERUPDATE
:
8508 if (aPointerInfo
.mPressure
== 0) {
8509 return false; // hover
8511 touchType
= MultiTouchInput::MULTITOUCH_MOVE
;
8514 touchType
= MultiTouchInput::MULTITOUCH_END
;
8520 nsPointWin touchPoint
;
8521 touchPoint
.x
= GET_X_LPARAM(aLParam
);
8522 touchPoint
.y
= GET_Y_LPARAM(aLParam
);
8523 touchPoint
.ScreenToClient(mWnd
);
8525 SingleTouchData
touchData(static_cast<int32_t>(aPointerInfo
.pointerId
),
8526 ScreenIntPoint::FromUnknownPoint(touchPoint
),
8527 ScreenSize(1, 1), // pixel size radius for pen
8528 0.0f
, // no radius rotation
8529 aPointerInfo
.mPressure
);
8530 touchData
.mTiltX
= aPointerInfo
.tiltX
;
8531 touchData
.mTiltY
= aPointerInfo
.tiltY
;
8532 touchData
.mTwist
= aPointerInfo
.twist
;
8534 MultiTouchInput touchInput
;
8535 touchInput
.mType
= touchType
;
8536 touchInput
.mTime
= ::GetMessageTime();
8537 touchInput
.mTimeStamp
=
8538 GetMessageTimeStamp(static_cast<long>(touchInput
.mTime
));
8539 touchInput
.mTouches
.AppendElement(touchData
);
8540 touchInput
.mButton
= aButton
;
8541 touchInput
.mButtons
= aPointerInfo
.mButtons
;
8543 // POINTER_INFO.dwKeyStates can't be used as it only supports Shift and Ctrl
8544 ModifierKeyState modifierKeyState
;
8545 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
8547 DispatchTouchInput(touchInput
, MouseEvent_Binding::MOZ_SOURCE_PEN
);
8551 static MouseButton
PenFlagsToMouseButton(PEN_FLAGS aPenFlags
) {
8552 // Theoretically flags can be set together but they do not
8553 if (aPenFlags
& PEN_FLAG_BARREL
) {
8554 return MouseButton::eSecondary
;
8556 if (aPenFlags
& PEN_FLAG_ERASER
) {
8557 return MouseButton::eEraser
;
8559 return MouseButton::ePrimary
;
8562 bool nsWindow::OnPointerEvents(UINT msg
, WPARAM aWParam
, LPARAM aLParam
) {
8564 // APZ is not available on context menu. Follow the behavior of touch input
8565 // which fallbacks to WM_LBUTTON* and WM_GESTURE, to keep consistency.
8568 if (!mPointerEvents
.ShouldHandleWinPointerMessages(msg
, aWParam
)) {
8571 if (!mPointerEvents
.ShouldFirePointerEventByWinPointerMessages()) {
8572 // We have to handle WM_POINTER* to fetch and cache pen related information
8573 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
8574 // handler. This is because Windows doesn't support ::DoDragDrop in the
8575 // touch or pen message handlers.
8576 mPointerEvents
.ConvertAndCachePointerInfo(msg
, aWParam
);
8577 // Don't consume the Windows WM_POINTER* messages
8581 uint32_t pointerId
= mPointerEvents
.GetPointerId(aWParam
);
8582 POINTER_PEN_INFO penInfo
{};
8583 if (!mPointerEvents
.GetPointerPenInfo(pointerId
, &penInfo
)) {
8587 // When dispatching mouse events with pen, there may be some
8588 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
8589 // small movements. Those events will reset sLastMousePoint and reset
8590 // sLastClickCount. To prevent that, we keep the last pen down position
8591 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
8592 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
8593 // eMouseMove for WM_POINTERUPDATE.
8594 static POINT sLastPointerDownPoint
= {0};
8596 // We don't support chorded buttons for pen. Keep the button at
8598 static mozilla::MouseButton sLastPenDownButton
= MouseButton::ePrimary
;
8599 static bool sPointerDown
= false;
8601 EventMessage message
;
8602 mozilla::MouseButton button
= MouseButton::ePrimary
;
8604 case WM_POINTERDOWN
: {
8605 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8606 GET_Y_LPARAM(aLParam
));
8607 sLastPointerDownPoint
.x
= eventPoint
.x
;
8608 sLastPointerDownPoint
.y
= eventPoint
.y
;
8609 message
= eMouseDown
;
8610 button
= PenFlagsToMouseButton(penInfo
.penFlags
);
8611 sLastPenDownButton
= button
;
8612 sPointerDown
= true;
8616 MOZ_ASSERT(sPointerDown
, "receive WM_POINTERUP w/o WM_POINTERDOWN");
8617 button
= sPointerDown
? sLastPenDownButton
: MouseButton::ePrimary
;
8618 sPointerDown
= false;
8620 case WM_POINTERUPDATE
:
8621 message
= eMouseMove
;
8623 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8624 GET_Y_LPARAM(aLParam
));
8625 int32_t movementX
= sLastPointerDownPoint
.x
> eventPoint
.x
8626 ? sLastPointerDownPoint
.x
- eventPoint
.x
8627 : eventPoint
.x
- sLastPointerDownPoint
.x
;
8628 int32_t movementY
= sLastPointerDownPoint
.y
> eventPoint
.y
8629 ? sLastPointerDownPoint
.y
- eventPoint
.y
8630 : eventPoint
.y
- sLastPointerDownPoint
.y
;
8631 bool insideMovementThreshold
=
8632 movementX
< (int32_t)::GetSystemMetrics(SM_CXDRAG
) &&
8633 movementY
< (int32_t)::GetSystemMetrics(SM_CYDRAG
);
8635 if (insideMovementThreshold
) {
8636 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
8637 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8640 button
= sLastPenDownButton
;
8643 case WM_POINTERLEAVE
:
8644 message
= eMouseExitFromWidget
;
8650 // Windows defines the pen pressure is normalized to a range between 0 and
8651 // 1024. Convert it to float.
8652 float pressure
= penInfo
.pressure
? (float)penInfo
.pressure
/ 1024 : 0;
8653 int16_t buttons
= sPointerDown
8654 ? nsContentUtils::GetButtonsFlagForButton(button
)
8655 : MouseButtonsFlag::eNoButtons
;
8656 WinPointerInfo
pointerInfo(pointerId
, penInfo
.tiltX
, penInfo
.tiltY
, pressure
,
8658 pointerInfo
.twist
= penInfo
.rotation
;
8660 // Fire touch events but not when the barrel button is pressed.
8661 if (button
!= MouseButton::eSecondary
&&
8662 StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
8663 DispatchTouchEventFromWMPointer(msg
, aLParam
, pointerInfo
, button
)) {
8667 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8669 LPARAM newLParam
= lParamToClient(aLParam
);
8670 DispatchMouseEvent(message
, aWParam
, newLParam
, false, button
,
8671 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8673 if (button
== MouseButton::eSecondary
&& message
== eMouseUp
) {
8674 // Fire eContextMenu manually since consuming WM_POINTER* blocks
8676 DispatchMouseEvent(eContextMenu
, aWParam
, newLParam
, false, button
,
8677 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8679 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8684 void nsWindow::GetCompositorWidgetInitData(
8685 mozilla::widget::CompositorWidgetInitData
* aInitData
) {
8686 *aInitData
= WinCompositorWidgetInitData(
8687 reinterpret_cast<uintptr_t>(mWnd
),
8688 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
8689 mTransparencyMode
, mFrameState
->GetSizeMode());
8692 bool nsWindow::SynchronouslyRepaintOnResize() {
8693 return !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
8696 void nsWindow::MaybeDispatchInitialFocusEvent() {
8697 if (mIsShowingPreXULSkeletonUI
&& ::GetActiveWindow() == mWnd
) {
8698 DispatchFocusToTopLevelWindow(true);
8702 already_AddRefed
<nsIWidget
> nsIWidget::CreateTopLevelWindow() {
8703 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
8704 return window
.forget();
8707 already_AddRefed
<nsIWidget
> nsIWidget::CreateChildWindow() {
8708 nsCOMPtr
<nsIWidget
> window
= new nsWindow(true);
8709 return window
.forget();
8713 bool nsWindow::InitTouchInjection() {
8714 if (!sTouchInjectInitialized
) {
8715 // Initialize touch injection on the first call
8716 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8721 InitializeTouchInjectionPtr func
=
8722 (InitializeTouchInjectionPtr
)GetProcAddress(hMod
,
8723 "InitializeTouchInjection");
8725 WinUtils::Log("InitializeTouchInjection not available.");
8729 if (!func(TOUCH_INJECT_MAX_POINTS
, TOUCH_FEEDBACK_DEFAULT
)) {
8730 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d",
8735 sInjectTouchFuncPtr
=
8736 (InjectTouchInputPtr
)GetProcAddress(hMod
, "InjectTouchInput");
8737 if (!sInjectTouchFuncPtr
) {
8738 WinUtils::Log("InjectTouchInput not available.");
8741 sTouchInjectInitialized
= true;
8746 bool nsWindow::InjectTouchPoint(uint32_t aId
, LayoutDeviceIntPoint
& aPoint
,
8747 POINTER_FLAGS aFlags
, uint32_t aPressure
,
8748 uint32_t aOrientation
) {
8749 if (aId
> TOUCH_INJECT_MAX_POINTS
) {
8750 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
8754 POINTER_TOUCH_INFO info
{};
8756 info
.touchFlags
= TOUCH_FLAG_NONE
;
8758 TOUCH_MASK_CONTACTAREA
| TOUCH_MASK_ORIENTATION
| TOUCH_MASK_PRESSURE
;
8759 info
.pressure
= aPressure
;
8760 info
.orientation
= aOrientation
;
8762 info
.pointerInfo
.pointerFlags
= aFlags
;
8763 info
.pointerInfo
.pointerType
= PT_TOUCH
;
8764 info
.pointerInfo
.pointerId
= aId
;
8765 info
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8766 info
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8768 info
.rcContact
.top
= info
.pointerInfo
.ptPixelLocation
.y
- 2;
8769 info
.rcContact
.bottom
= info
.pointerInfo
.ptPixelLocation
.y
+ 2;
8770 info
.rcContact
.left
= info
.pointerInfo
.ptPixelLocation
.x
- 2;
8771 info
.rcContact
.right
= info
.pointerInfo
.ptPixelLocation
.x
+ 2;
8773 for (int i
= 0; i
< 3; i
++) {
8774 if (sInjectTouchFuncPtr(1, &info
)) {
8777 DWORD error
= GetLastError();
8778 if (error
== ERROR_NOT_READY
&& i
< 2) {
8779 // We sent it too quickly after the previous injection (see bug 1535140
8780 // comment 10). On the first loop iteration we just yield (via Sleep(0))
8781 // and try again. If it happens again on the second loop iteration we
8782 // explicitly Sleep(1) and try again. If that doesn't work either we just
8787 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", error
);
8793 void nsWindow::ChangedDPI() {
8794 if (mWidgetListener
) {
8795 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
8796 presShell
->BackingScaleFactorChanged();
8801 static Result
<POINTER_FLAGS
, nsresult
> PointerStateToFlag(
8802 nsWindow::TouchPointerState aPointerState
, bool isUpdate
) {
8803 bool hover
= aPointerState
& nsWindow::TOUCH_HOVER
;
8804 bool contact
= aPointerState
& nsWindow::TOUCH_CONTACT
;
8805 bool remove
= aPointerState
& nsWindow::TOUCH_REMOVE
;
8806 bool cancel
= aPointerState
& nsWindow::TOUCH_CANCEL
;
8808 POINTER_FLAGS flags
;
8810 // We know about this pointer, send an update
8811 flags
= POINTER_FLAG_UPDATE
;
8813 flags
|= POINTER_FLAG_INRANGE
;
8814 } else if (contact
) {
8815 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_INRANGE
;
8816 } else if (remove
) {
8817 flags
= POINTER_FLAG_UP
;
8821 flags
|= POINTER_FLAG_CANCELED
;
8824 // Missing init state, error out
8825 if (remove
|| cancel
) {
8826 return Err(NS_ERROR_INVALID_ARG
);
8829 // Create a new pointer
8830 flags
= POINTER_FLAG_INRANGE
;
8832 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_DOWN
;
8838 nsresult
nsWindow::SynthesizeNativeTouchPoint(
8839 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8840 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
8841 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
8842 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
8844 if (StaticPrefs::apz_test_fails_with_native_injection() ||
8845 !InitTouchInjection()) {
8846 // If we don't have touch injection from the OS, or if we are running a test
8847 // that cannot properly inject events to satisfy the OS requirements (see
8848 // bug 1313170) we can just fake it and synthesize the events from here.
8849 MOZ_ASSERT(NS_IsMainThread());
8850 if (aPointerState
== TOUCH_HOVER
) {
8851 return NS_ERROR_UNEXPECTED
;
8854 if (!mSynthesizedTouchInput
) {
8855 mSynthesizedTouchInput
= MakeUnique
<MultiTouchInput
>();
8858 WidgetEventTime time
= CurrentMessageWidgetEventTime();
8859 LayoutDeviceIntPoint pointInWindow
= aPoint
- WidgetToScreenOffset();
8860 MultiTouchInput inputToDispatch
= UpdateSynthesizedTouchState(
8861 mSynthesizedTouchInput
.get(), time
.mTime
, time
.mTimeStamp
, aPointerId
,
8862 aPointerState
, pointInWindow
, aPointerPressure
, aPointerOrientation
);
8863 DispatchTouchInput(inputToDispatch
);
8867 // win api expects a value from 0 to 1024. aPointerPressure is a value
8869 uint32_t pressure
= (uint32_t)ceil(aPointerPressure
* 1024);
8871 // If we already know about this pointer id get it's record
8872 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8873 POINTER_FLAGS flags
;
8874 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8875 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8876 if (result
.isOk()) {
8877 flags
= result
.unwrap();
8879 return result
.unwrapErr();
8883 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8884 PointerInfo::PointerType::TOUCH
));
8886 if (entry
.Data()->mType
!= PointerInfo::PointerType::TOUCH
) {
8887 return NS_ERROR_UNEXPECTED
;
8889 if (aPointerState
& TOUCH_REMOVE
) {
8890 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8891 // so shouldn't leak.
8896 return !InjectTouchPoint(aPointerId
, aPoint
, flags
, pressure
,
8897 aPointerOrientation
)
8898 ? NS_ERROR_UNEXPECTED
8903 nsresult
nsWindow::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
8904 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
8905 if (!sTouchInjectInitialized
) {
8909 // cancel all input points
8910 for (auto iter
= mActivePointers
.Iter(); !iter
.Done(); iter
.Next()) {
8911 auto* info
= iter
.UserData();
8912 if (info
->mType
!= PointerInfo::PointerType::TOUCH
) {
8915 InjectTouchPoint(info
->mPointerId
, info
->mPosition
, POINTER_FLAG_CANCELED
);
8919 nsBaseWidget::ClearNativeTouchSequence(nullptr);
8924 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8925 static CreateSyntheticPointerDevicePtr CreateSyntheticPointerDevice
;
8926 static DestroySyntheticPointerDevicePtr DestroySyntheticPointerDevice
;
8927 static InjectSyntheticPointerInputPtr InjectSyntheticPointerInput
;
8929 static HSYNTHETICPOINTERDEVICE sSyntheticPenDevice
;
8931 static bool InitPenInjection() {
8932 if (sSyntheticPenDevice
) {
8935 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8936 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8940 CreateSyntheticPointerDevice
=
8941 (CreateSyntheticPointerDevicePtr
)GetProcAddress(
8942 hMod
, "CreateSyntheticPointerDevice");
8943 if (!CreateSyntheticPointerDevice
) {
8944 WinUtils::Log("CreateSyntheticPointerDevice not available.");
8947 DestroySyntheticPointerDevice
=
8948 (DestroySyntheticPointerDevicePtr
)GetProcAddress(
8949 hMod
, "DestroySyntheticPointerDevice");
8950 if (!DestroySyntheticPointerDevice
) {
8951 WinUtils::Log("DestroySyntheticPointerDevice not available.");
8954 InjectSyntheticPointerInput
= (InjectSyntheticPointerInputPtr
)GetProcAddress(
8955 hMod
, "InjectSyntheticPointerInput");
8956 if (!InjectSyntheticPointerInput
) {
8957 WinUtils::Log("InjectSyntheticPointerInput not available.");
8961 sSyntheticPenDevice
=
8962 CreateSyntheticPointerDevice(PT_PEN
, 1, POINTER_FEEDBACK_DEFAULT
);
8963 return !!sSyntheticPenDevice
;
8966 nsresult
nsWindow::SynthesizeNativePenInput(
8967 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8968 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
8969 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
8970 AutoObserverNotifier
notifier(aObserver
, "peninput");
8971 if (!InitPenInjection()) {
8972 return NS_ERROR_UNEXPECTED
;
8975 // win api expects a value from 0 to 1024. aPointerPressure is a value
8977 uint32_t pressure
= (uint32_t)ceil(aPressure
* 1024);
8979 // If we already know about this pointer id get it's record
8980 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8981 POINTER_FLAGS flags
;
8982 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8983 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8984 if (result
.isOk()) {
8985 flags
= result
.unwrap();
8987 return result
.unwrapErr();
8991 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8992 PointerInfo::PointerType::PEN
));
8994 if (entry
.Data()->mType
!= PointerInfo::PointerType::PEN
) {
8995 return NS_ERROR_UNEXPECTED
;
8997 if (aPointerState
& TOUCH_REMOVE
) {
8998 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8999 // so shouldn't leak.
9004 POINTER_TYPE_INFO info
{};
9007 info
.penInfo
.pointerInfo
.pointerType
= PT_PEN
;
9008 info
.penInfo
.pointerInfo
.pointerFlags
= flags
;
9009 info
.penInfo
.pointerInfo
.pointerId
= aPointerId
;
9010 info
.penInfo
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
9011 info
.penInfo
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
9013 info
.penInfo
.penFlags
= PEN_FLAG_NONE
;
9014 // PEN_FLAG_ERASER is not supported this way, unfortunately.
9016 info
.penInfo
.penFlags
|= PEN_FLAG_BARREL
;
9018 info
.penInfo
.penMask
= PEN_MASK_PRESSURE
| PEN_MASK_ROTATION
|
9019 PEN_MASK_TILT_X
| PEN_MASK_TILT_Y
;
9020 info
.penInfo
.pressure
= pressure
;
9021 info
.penInfo
.rotation
= aRotation
;
9022 info
.penInfo
.tiltX
= aTiltX
;
9023 info
.penInfo
.tiltY
= aTiltY
;
9025 return InjectSyntheticPointerInput(sSyntheticPenDevice
, &info
, 1)
9027 : NS_ERROR_UNEXPECTED
;
9031 bool nsWindow::HandleAppCommandMsg(const MSG
& aAppCommandMsg
,
9032 LRESULT
* aRetValue
) {
9033 ModifierKeyState modKeyState
;
9034 NativeKey
nativeKey(this, aAppCommandMsg
, modKeyState
);
9035 bool consumed
= nativeKey
.HandleAppCommandMessage();
9036 *aRetValue
= consumed
? 1 : 0;
9040 static nsSizeMode
GetSizeModeForWindowFrame(HWND aWnd
, bool aFullscreenMode
) {
9042 pl
.length
= sizeof(pl
);
9043 ::GetWindowPlacement(aWnd
, &pl
);
9045 if (pl
.showCmd
== SW_SHOWMINIMIZED
) {
9046 return nsSizeMode_Minimized
;
9047 } else if (aFullscreenMode
) {
9048 return nsSizeMode_Fullscreen
;
9049 } else if (pl
.showCmd
== SW_SHOWMAXIMIZED
) {
9050 return nsSizeMode_Maximized
;
9052 return nsSizeMode_Normal
;
9056 static void ShowWindowWithMode(HWND aWnd
, nsSizeMode aMode
) {
9057 // This will likely cause a callback to
9058 // nsWindow::FrameState::{OnFrameChanging() and OnFrameChanged()}
9060 case nsSizeMode_Fullscreen
:
9061 ::ShowWindow(aWnd
, SW_SHOW
);
9064 case nsSizeMode_Maximized
:
9065 ::ShowWindow(aWnd
, SW_MAXIMIZE
);
9068 case nsSizeMode_Minimized
:
9069 ::ShowWindow(aWnd
, SW_MINIMIZE
);
9073 // Don't call ::ShowWindow if we're trying to "restore" a window that is
9074 // already in a normal state. Prevents a bug where snapping to one side
9075 // of the screen and then minimizing would cause Windows to forget our
9076 // window's correct restored position/size.
9077 if (GetCurrentShowCmd(aWnd
) != SW_SHOWNORMAL
) {
9078 ::ShowWindow(aWnd
, SW_RESTORE
);
9083 nsWindow::FrameState::FrameState(nsWindow
* aWindow
) : mWindow(aWindow
) {}
9085 nsSizeMode
nsWindow::FrameState::GetSizeMode() const { return mSizeMode
; }
9087 void nsWindow::FrameState::CheckInvariant() const {
9088 MOZ_ASSERT(mSizeMode
>= 0 && mSizeMode
< nsSizeMode_Invalid
);
9089 MOZ_ASSERT(mLastSizeMode
>= 0 && mLastSizeMode
< nsSizeMode_Invalid
);
9090 MOZ_ASSERT(mOldSizeMode
>= 0 && mOldSizeMode
< nsSizeMode_Invalid
);
9091 MOZ_ASSERT(mWindow
);
9093 // We should never observe fullscreen sizemode unless fullscreen is enabled
9094 MOZ_ASSERT_IF(mSizeMode
== nsSizeMode_Fullscreen
, mFullscreenMode
);
9095 MOZ_ASSERT_IF(!mFullscreenMode
, mSizeMode
!= nsSizeMode_Fullscreen
);
9097 // Something went wrong if we somehow saved fullscreen mode when we are
9098 // changing into fullscreen mode
9099 MOZ_ASSERT(mOldSizeMode
!= nsSizeMode_Fullscreen
);
9102 void nsWindow::FrameState::ConsumePreXULSkeletonState(bool aWasMaximized
) {
9103 mSizeMode
= aWasMaximized
? nsSizeMode_Maximized
: nsSizeMode_Normal
;
9106 void nsWindow::FrameState::EnsureSizeMode(nsSizeMode aMode
) {
9107 if (mSizeMode
== aMode
) {
9111 if (aMode
== nsSizeMode_Fullscreen
) {
9112 EnsureFullscreenMode(true);
9113 } else if ((mSizeMode
== nsSizeMode_Fullscreen
) &&
9114 (aMode
== nsSizeMode_Normal
)) {
9115 // If we are in fullscreen mode, minimize should work like normal and
9116 // return us to fullscreen mode when unminimized. Maximize isn't really
9117 // available and won't do anything. "Restore" should do the same thing as
9118 // requesting to end fullscreen.
9119 EnsureFullscreenMode(false);
9121 SetSizeModeInternal(aMode
);
9125 void nsWindow::FrameState::EnsureFullscreenMode(bool aFullScreen
) {
9126 if (mFullscreenMode
== aFullScreen
) {
9130 mWindow
->OnFullscreenWillChange(aFullScreen
);
9132 mFullscreenMode
= aFullScreen
;
9134 mOldSizeMode
= mSizeMode
;
9135 SetSizeModeInternal(nsSizeMode_Fullscreen
);
9137 SetSizeModeInternal(mOldSizeMode
);
9140 mWindow
->OnFullscreenChanged(aFullScreen
);
9143 void nsWindow::FrameState::OnFrameChanging() {
9144 if (mSizeMode
== nsSizeMode_Fullscreen
) {
9149 pl
.length
= sizeof(pl
);
9150 ::GetWindowPlacement(mWindow
->mWnd
, &pl
);
9152 const nsSizeMode newSizeMode
=
9153 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
9155 mWindow
->OnSizeModeChange(newSizeMode
);
9157 mWindow
->UpdateNonClientMargins(newSizeMode
, false);
9160 void nsWindow::FrameState::OnFrameChanged() {
9161 if (mSizeMode
== nsSizeMode_Fullscreen
) {
9165 const nsSizeMode previousSizeMode
= mSizeMode
;
9167 // Windows has just changed the size mode of this window. The call to
9168 // SizeModeChanged will trigger a call into SetSizeMode where we will
9169 // set the min/max window state again or for nsSizeMode_Normal, call
9170 // SetWindow with a parameter of SW_RESTORE. There's no need however as
9171 // this window's mode has already changed. Updating SizeMode here
9172 // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
9173 // to window docking. (bug 489258)
9174 mSizeMode
= GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
9176 MaybeLogSizeMode(mSizeMode
);
9178 if (mSizeMode
!= previousSizeMode
) {
9179 mWindow
->OnSizeModeChange(mSizeMode
);
9182 // If window was restored, window activation was bypassed during the
9183 // SetSizeMode call originating from OnWindowPosChanging to avoid saving
9184 // pre-restore attributes. Force activation now to get correct attributes.
9185 if (mLastSizeMode
!= mSizeMode
) {
9186 if (mSizeMode
== nsSizeMode_Normal
) {
9187 mWindow
->DispatchFocusToTopLevelWindow(true);
9189 mLastSizeMode
= mSizeMode
;
9193 void nsWindow::FrameState::SetSizeModeInternal(nsSizeMode aMode
) {
9194 if (mSizeMode
== aMode
) {
9198 mLastSizeMode
= mSizeMode
;
9201 if (mWindow
->mIsVisible
) {
9202 ShowWindowWithMode(mWindow
->mWnd
, aMode
);
9205 // we activate here to ensure that the right child window is focused
9206 if (mWindow
->mIsVisible
&&
9207 (aMode
== nsSizeMode_Maximized
|| aMode
== nsSizeMode_Fullscreen
)) {
9208 mWindow
->DispatchFocusToTopLevelWindow(true);