1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * nsWindow - Native window management and event handling.
10 * nsWindow is organized into a set of major blocks and
11 * block subsections. The layout is as follows:
16 * nsIWidget methods and utilities
17 * nsSwitchToUIThread impl.
18 * nsSwitchToUIThread methods and utilities
20 * Event initialization
25 * OnEvent event handlers
26 * IME management and accessibility
32 * Search for "BLOCK:" to find major blocks.
33 * Search for "SECTION:" to find specific sections.
35 * Blocks should be split out into separate files if they
36 * become unmanageable.
38 * Notable related sources:
40 * nsWindowDefs.h - Definitions, macros, structs, enums
42 * nsWindowDbg.h/.cpp - Debug related code and directives.
43 * nsWindowGfx.h/.cpp - Graphics and painting.
47 /**************************************************************
48 **************************************************************
54 **************************************************************
55 **************************************************************/
57 #include "gfx2DGlue.h"
59 #include "gfxPlatform.h"
61 #include "mozilla/AppShutdown.h"
62 #include "mozilla/AutoRestore.h"
63 #include "mozilla/Likely.h"
64 #include "mozilla/PreXULSkeletonUI.h"
65 #include "mozilla/Logging.h"
66 #include "mozilla/MathAlgorithms.h"
67 #include "mozilla/MiscEvents.h"
68 #include "mozilla/MouseEvents.h"
69 #include "mozilla/PresShell.h"
70 #include "mozilla/ScopeExit.h"
71 #include "mozilla/StaticPrefs_browser.h"
72 #include "mozilla/SwipeTracker.h"
73 #include "mozilla/TouchEvents.h"
74 #include "mozilla/TimeStamp.h"
76 #include "mozilla/ipc/MessageChannel.h"
80 #include "mozilla/widget/WinMessages.h"
81 #include "nsLookAndFeel.h"
83 #include "nsWindowTaskbarConcealer.h"
84 #include "nsAppRunner.h"
95 #include <propvarutil.h>
98 #include "mozilla/Logging.h"
102 #include "mozilla/WidgetTraceEvent.h"
103 #include "nsContentUtils.h"
104 #include "nsISupportsPrimitives.h"
105 #include "nsITheme.h"
106 #include "nsIObserverService.h"
107 #include "nsIScreenManager.h"
108 #include "imgIContainer.h"
110 #include "nsIRollupListener.h"
111 #include "nsIClipboard.h"
112 #include "WinMouseScrollHandler.h"
113 #include "nsFontMetrics.h"
114 #include "nsIFontEnumerator.h"
117 #include "nsThreadUtils.h"
118 #include "nsNativeCharsetUtils.h"
119 #include "nsGkAtoms.h"
121 #include "nsAppDirectoryServiceDefs.h"
122 #include "nsWidgetsCID.h"
123 #include "nsTHashtable.h"
124 #include "nsHashKeys.h"
125 #include "nsString.h"
126 #include "mozilla/Components.h"
127 #include "nsNativeThemeWin.h"
128 #include "nsXULPopupManager.h"
129 #include "nsWindowsDllInterceptor.h"
130 #include "nsLayoutUtils.h"
132 #include "nsWindowGfx.h"
133 #include "gfxWindowsPlatform.h"
134 #include "gfxDWriteFonts.h"
135 #include "nsPrintfCString.h"
136 #include "mozilla/Preferences.h"
137 #include "SystemTimeConverter.h"
138 #include "WinTaskbar.h"
139 #include "WidgetUtils.h"
140 #include "WinWindowOcclusionTracker.h"
141 #include "nsIWidgetListener.h"
142 #include "mozilla/dom/Document.h"
143 #include "mozilla/dom/MouseEventBinding.h"
144 #include "mozilla/dom/Touch.h"
145 #include "mozilla/gfx/2D.h"
146 #include "mozilla/gfx/GPUProcessManager.h"
147 #include "mozilla/intl/LocaleService.h"
148 #include "mozilla/layers/WebRenderLayerManager.h"
149 #include "mozilla/WindowsVersion.h"
150 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
151 #include "mozilla/TextEventDispatcherListener.h"
152 #include "mozilla/widget/nsAutoRollup.h"
153 #include "mozilla/widget/PlatformWidgetTypes.h"
154 #include "mozilla/widget/Screen.h"
155 #include "nsStyleConsts.h"
156 #include "nsBidiKeyboard.h"
157 #include "nsStyleConsts.h"
158 #include "gfxConfig.h"
159 #include "InProcessWinCompositorWidget.h"
160 #include "InputDeviceUtils.h"
161 #include "ScreenHelperWin.h"
162 #include "mozilla/StaticPrefs_apz.h"
163 #include "mozilla/StaticPrefs_dom.h"
164 #include "mozilla/StaticPrefs_gfx.h"
165 #include "mozilla/StaticPrefs_layout.h"
166 #include "mozilla/StaticPrefs_ui.h"
167 #include "mozilla/StaticPrefs_widget.h"
168 #include "nsNativeAppSupportWin.h"
169 #include "mozilla/browser/NimbusFeatures.h"
171 #include "nsIGfxInfo.h"
172 #include "nsUXThemeConstants.h"
173 #include "KeyboardLayout.h"
174 #include "nsNativeDragTarget.h"
175 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
177 #include <richedit.h>
181 # include "mozilla/a11y/Logging.h"
183 # include "mozilla/a11y/Compatibility.h"
185 # include <winuser.h>
186 # include "nsAccessibilityService.h"
187 # include "mozilla/a11y/DocAccessible.h"
188 # include "mozilla/a11y/LazyInstantiator.h"
189 # include "mozilla/a11y/Platform.h"
190 # if !defined(WINABLEAPI)
191 # include <winable.h>
192 # endif // !defined(WINABLEAPI)
195 #include "WindowsUIUtils.h"
197 #include "nsWindowDefs.h"
199 #include "nsCrashOnException.h"
201 #include "nsIContent.h"
203 #include "mozilla/BackgroundHangMonitor.h"
204 #include "WinIMEHandler.h"
210 // ERROR from wingdi.h (below) gets undefined by some code.
212 // #define RGN_ERROR ERROR
215 #if !defined(SM_CONVERTIBLESLATEMODE)
216 # define SM_CONVERTIBLESLATEMODE 0x2003
219 #include "mozilla/gfx/DeviceManagerDx.h"
220 #include "mozilla/layers/APZInputBridge.h"
221 #include "mozilla/layers/InputAPZContext.h"
222 #include "mozilla/layers/KnowsCompositor.h"
223 #include "InputData.h"
225 #include "mozilla/TaskController.h"
226 #include "mozilla/Telemetry.h"
227 #include "mozilla/webrender/WebRenderAPI.h"
228 #include "mozilla/layers/IAPZCTreeManager.h"
230 #include "DirectManipulationOwner.h"
232 using namespace mozilla
;
233 using namespace mozilla::dom
;
234 using namespace mozilla::gfx
;
235 using namespace mozilla::layers
;
236 using namespace mozilla::widget
;
237 using namespace mozilla::plugins
;
239 /**************************************************************
240 **************************************************************
244 ** nsWindow Class static initializations and global variables.
246 **************************************************************
247 **************************************************************/
249 /**************************************************************
251 * SECTION: nsWindow statics
253 **************************************************************/
254 static const wchar_t kUser32LibName
[] = L
"user32.dll";
256 uint32_t nsWindow::sInstanceCount
= 0;
257 bool nsWindow::sIsOleInitialized
= false;
258 nsIWidget::Cursor
nsWindow::sCurrentCursor
= {};
259 nsWindow
* nsWindow::sCurrentWindow
= nullptr;
260 bool nsWindow::sJustGotDeactivate
= false;
261 bool nsWindow::sJustGotActivate
= false;
262 bool nsWindow::sIsInMouseCapture
= false;
264 // Urgent-message reentrancy depth for the static `WindowProc` callback.
266 // Three unfortunate facts collide:
268 // 𝛼) Some messages must be processed promptly. If not, Windows will leave the
269 // receiving window in an intermediate, and potentially unusable, state until
270 // the WindowProc invocation that is handling it returns.
272 // 𝛽) Some messages have indefinitely long processing time. These are mostly
273 // messages which may cause us to enter a nested modal loop (via
274 // `SpinEventLoopUntil` or similar).
276 // 𝛾) Sometimes, messages skip the queue entirely. Our `WindowProc` may be
277 // reentrantly reinvoked from the kernel while we're blocking _on_ the
278 // kernel, even briefly, during processing of other messages. (Relevant
279 // search term: `KeUserModeCallback`.)
281 // The nightmare scenario, then, is that during processing of an 𝛼-message, we
282 // briefly become blocked (e.g., by calling `::SendMessageW()`), and the kernel
283 // takes that opportunity to use 𝛾 to hand us a 𝛽-message. (Concretely, see
286 // There is little we can do to prevent the first half of this scenario. 𝛼) and
287 // 𝛾) are effectively immutable facts of Windows, and we sometimes legitimately
288 // need to make blocking calls to process 𝛼-messages. (We may not even be aware
289 // that we're making such calls, if they're undocumented implementation details
292 // In an ideal world, WindowProc would always return promptly (or at least in
293 // bounded time), and 𝛽-messages would not _per se_ exist; long-running modal
294 // states would instead be implemented in async fashion. In practice, that's far
295 // easier said than done -- replacing existing uses of `SpinEventLoopUntil` _et
296 // al._ with asynchronous mechanisms is a collection of mostly-unrelated cross-
297 // cutting architectural tasks, each of potentially unbounded scope. For now,
298 // and for the foreseeable future, we're stuck with them.
300 // We therefore simply punt. More specifically: if a known 𝛽-message jumps the
301 // queue to come in while we're in the middle of processing a known 𝛼-message,
303 // * properly queue the message for processing later;
304 // * respond to the 𝛽-message as though we actually had processed it; and
305 // * just hope that it can wait until we get around to it.
307 // The word "known" requires a bit of justification. There is no canonical set
308 // of 𝛼-messages, nor is the set of 𝛽-messages fixed (or even demarcable). We
309 // can't safely assume that all messages are 𝛼-messages, as that could cause
310 // 𝛽-messages to be arbitrarily and surprisingly delayed whenever any nested
311 // event loop is active. We also can't assume all messages are 𝛽-messages,
312 // since one 𝛼-message jumping the queue while processing another 𝛼-message is
313 // part of normal and required operation for windowed Windows applications.
315 // So we simply add messages to those sets as we identify them. (Or, preferably,
316 // rework the 𝛽-message's handling to make it no longer 𝛽. But see above.)
320 // The actual value of `sDepth` is the number of active invocations of
321 // `WindowProc` that are processing known 𝛼-messages.
322 size_t nsWindow::WndProcUrgentInvocation::sDepth
= 0;
324 // Hook Data Members for Dropdowns. sProcessHook Tells the
325 // hook methods whether they should be processing the hook
327 HHOOK
nsWindow::sMsgFilterHook
= nullptr;
328 HHOOK
nsWindow::sCallProcHook
= nullptr;
329 HHOOK
nsWindow::sCallMouseHook
= nullptr;
330 bool nsWindow::sProcessHook
= false;
331 UINT
nsWindow::sRollupMsgId
= 0;
332 HWND
nsWindow::sRollupMsgWnd
= nullptr;
333 UINT
nsWindow::sHookTimerId
= 0;
335 // Used to prevent dispatching mouse events that do not originate from user
337 POINT
nsWindow::sLastMouseMovePoint
= {0};
339 bool nsWindow::sIsRestoringSession
= false;
341 bool nsWindow::sTouchInjectInitialized
= false;
342 InjectTouchInputPtr
nsWindow::sInjectTouchFuncPtr
;
344 static SystemTimeConverter
<DWORD
>& TimeConverter() {
345 static SystemTimeConverter
<DWORD
> timeConverterSingleton
;
346 return timeConverterSingleton
;
349 // Global event hook for window cloaking. Never deregistered.
350 // - `Nothing` if not yet set.
351 // - `Some(nullptr)` if no attempt should be made to set it.
352 static mozilla::Maybe
<HWINEVENTHOOK
> sWinCloakEventHook
= Nothing();
353 static mozilla::LazyLogModule
sCloakingLog("DWMCloaking");
357 class CurrentWindowsTimeGetter
{
359 explicit CurrentWindowsTimeGetter(HWND aWnd
) : mWnd(aWnd
) {}
361 DWORD
GetCurrentTime() const { return ::GetTickCount(); }
363 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp
& aNow
) {
364 DWORD currentTime
= GetCurrentTime();
365 if (sBackwardsSkewStamp
&& currentTime
== sLastPostTime
) {
366 // There's already one inflight with this timestamp. Don't
370 sBackwardsSkewStamp
= Some(aNow
);
371 sLastPostTime
= currentTime
;
372 static_assert(sizeof(WPARAM
) >= sizeof(DWORD
),
373 "Can't fit a DWORD in a WPARAM");
374 ::PostMessage(mWnd
, MOZ_WM_SKEWFIX
, sLastPostTime
, 0);
377 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime
,
378 TimeStamp
* aOutSkewStamp
) {
379 if (aPostTime
!= sLastPostTime
) {
380 // The SKEWFIX message is stale; we've sent a new one since then.
384 MOZ_ASSERT(sBackwardsSkewStamp
);
385 *aOutSkewStamp
= sBackwardsSkewStamp
.value();
386 sBackwardsSkewStamp
= Nothing();
391 static Maybe
<TimeStamp
> sBackwardsSkewStamp
;
392 static DWORD sLastPostTime
;
396 Maybe
<TimeStamp
> CurrentWindowsTimeGetter::sBackwardsSkewStamp
;
397 DWORD
CurrentWindowsTimeGetter::sLastPostTime
= 0;
399 } // namespace mozilla
401 /**************************************************************
403 * SECTION: globals variables
405 **************************************************************/
407 static const char* sScreenManagerContractID
=
408 "@mozilla.org/gfx/screenmanager;1";
410 extern mozilla::LazyLogModule gWindowsLog
;
412 static NS_DEFINE_CID(kCClipboardCID
, NS_CLIPBOARD_CID
);
414 // General purpose user32.dll hook object
415 static WindowsDllInterceptor sUser32Intercept
;
417 // When the client area is extended out into the default window frame area,
418 // this is the minimum amount of space along the edge of resizable windows
419 // we will always display a resize cursor in, regardless of the underlying
421 static const int32_t kResizableBorderMinSize
= 3;
423 // Getting this object from the window server can be expensive. Keep it
424 // around, also get it off the main thread. (See bug 1640852)
425 StaticRefPtr
<IVirtualDesktopManager
> gVirtualDesktopManager
;
426 static bool gInitializedVirtualDesktopManager
= false;
428 // We should never really try to accelerate windows bigger than this. In some
429 // cases this might lead to no D3D9 acceleration where we could have had it
430 // but D3D9 does not reliably report when it supports bigger windows. 8192
431 // is as safe as we can get, we know at least D3D10 hardware always supports
432 // this, other hardware we expect to report correctly in D3D9.
433 #define MAX_ACCELERATED_DIMENSION 8192
435 // On window open (as well as after), Windows has an unfortunate habit of
436 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
437 // to DOM target conversions for these, we cache responses for a given
438 // coordinate this many milliseconds:
439 #define HITTEST_CACHE_LIFETIME_MS 50
441 #if defined(ACCESSIBILITY)
446 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
447 * injecting tiptsf.dll. The touchscreen process then posts registered messages
448 * to our main thread. The tiptsf hook picks up those registered messages and
449 * uses them as commands, some of which call into UIA, which then calls into
450 * MSAA, which then sends WM_GETOBJECT to us.
452 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
453 * hook. Since thread-local hooks are called ahead of global hooks, we will
454 * see these registered messages before tiptsf does. At this point we can then
455 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
456 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
457 * flag by calling TIPMessageHandler::IsA11yBlocked().
459 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
460 * function that also calls into UIA.
462 class TIPMessageHandler
{
464 ~TIPMessageHandler() {
466 ::UnhookWindowsHookEx(mHook
);
470 static void Initialize() {
475 sInstance
= new TIPMessageHandler();
476 ClearOnShutdown(&sInstance
);
479 static bool IsA11yBlocked() {
484 return sInstance
->mA11yBlockCount
> 0;
488 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
489 MOZ_ASSERT(NS_IsMainThread());
491 // Registered messages used by tiptsf
492 mMessages
[0] = ::RegisterWindowMessage(L
"ImmersiveFocusNotification");
493 mMessages
[1] = ::RegisterWindowMessage(L
"TipCloseMenus");
494 mMessages
[2] = ::RegisterWindowMessage(L
"TabletInputPanelOpening");
495 mMessages
[3] = ::RegisterWindowMessage(L
"IHM Pen or Touch Event noticed");
496 mMessages
[4] = ::RegisterWindowMessage(L
"ProgrammabilityCaretVisibility");
497 mMessages
[5] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPHidden");
498 mMessages
[6] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPInfo");
500 mHook
= ::SetWindowsHookEx(WH_GETMESSAGE
, &TIPHook
, nullptr,
501 ::GetCurrentThreadId());
504 if (!sSendMessageTimeoutWStub
) {
505 sUser32Intercept
.Init("user32.dll");
506 DebugOnly
<bool> hooked
= sSendMessageTimeoutWStub
.Set(
507 sUser32Intercept
, "SendMessageTimeoutW", &SendMessageTimeoutWHook
);
512 class MOZ_RAII A11yInstantiationBlocker
{
514 A11yInstantiationBlocker() {
515 if (!TIPMessageHandler::sInstance
) {
518 ++TIPMessageHandler::sInstance
->mA11yBlockCount
;
521 ~A11yInstantiationBlocker() {
522 if (!TIPMessageHandler::sInstance
) {
525 MOZ_ASSERT(TIPMessageHandler::sInstance
->mA11yBlockCount
> 0);
526 --TIPMessageHandler::sInstance
->mA11yBlockCount
;
530 friend class A11yInstantiationBlocker
;
532 static LRESULT CALLBACK
TIPHook(int aCode
, WPARAM aWParam
, LPARAM aLParam
) {
533 if (aCode
< 0 || !sInstance
) {
534 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
537 MSG
* msg
= reinterpret_cast<MSG
*>(aLParam
);
538 UINT
& msgCode
= msg
->message
;
540 for (uint32_t i
= 0; i
< ArrayLength(sInstance
->mMessages
); ++i
) {
541 if (msgCode
== sInstance
->mMessages
[i
]) {
542 A11yInstantiationBlocker block
;
543 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
547 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
550 static LRESULT WINAPI
SendMessageTimeoutWHook(HWND aHwnd
, UINT aMsgCode
,
551 WPARAM aWParam
, LPARAM aLParam
,
552 UINT aFlags
, UINT aTimeout
,
553 PDWORD_PTR aMsgResult
) {
554 // We don't want to handle this unless the message is a WM_GETOBJECT that we
555 // want to block, and the aHwnd is a nsWindow that belongs to the current
556 // (i.e., main) thread.
557 if (!aMsgResult
|| aMsgCode
!= WM_GETOBJECT
||
558 static_cast<LONG
>(aLParam
) != OBJID_CLIENT
|| !::NS_IsMainThread() ||
559 !WinUtils::GetNSWindowPtr(aHwnd
) || !IsA11yBlocked()) {
560 return sSendMessageTimeoutWStub(aHwnd
, aMsgCode
, aWParam
, aLParam
, aFlags
,
561 aTimeout
, aMsgResult
);
564 // In this case we want to fake the result that would happen if we had
565 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
566 // off to DefWindowProc to accomplish this.
567 *aMsgResult
= static_cast<DWORD_PTR
>(
568 ::DefWindowProcW(aHwnd
, aMsgCode
, aWParam
, aLParam
));
570 return static_cast<LRESULT
>(TRUE
);
573 static WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
574 sSendMessageTimeoutWStub
;
575 static StaticAutoPtr
<TIPMessageHandler
> sInstance
;
579 uint32_t mA11yBlockCount
;
582 WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
583 TIPMessageHandler::sSendMessageTimeoutWStub
;
584 StaticAutoPtr
<TIPMessageHandler
> TIPMessageHandler::sInstance
;
586 } // namespace mozilla
588 #endif // defined(ACCESSIBILITY)
592 // This task will get the VirtualDesktopManager from the generic thread pool
593 // since doing this on the main thread on startup causes performance issues.
597 // This should be fine and should not require any locking, as when the main
598 // thread will access it, if it races with this function it will either find
599 // it to be null or to have a valid value.
600 class InitializeVirtualDesktopManagerTask
: public Task
{
602 InitializeVirtualDesktopManagerTask()
603 : Task(Kind::OffMainThreadOnly
, kDefaultPriorityValue
) {}
605 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
606 bool GetName(nsACString
& aName
) override
{
607 aName
.AssignLiteral("InitializeVirtualDesktopManagerTask");
612 virtual TaskResult
Run() override
{
613 RefPtr
<IVirtualDesktopManager
> desktopManager
;
614 HRESULT hr
= ::CoCreateInstance(
615 CLSID_VirtualDesktopManager
, NULL
, CLSCTX_INPROC_SERVER
,
616 __uuidof(IVirtualDesktopManager
), getter_AddRefs(desktopManager
));
618 return TaskResult::Complete
;
621 gVirtualDesktopManager
= desktopManager
;
622 return TaskResult::Complete
;
626 // Ground-truth query: does Windows claim the window is cloaked right now?
627 static bool IsCloaked(HWND hwnd
) {
629 HRESULT hr
= ::DwmGetWindowAttribute(hwnd
, DWMWA_CLOAKED
, &cloakedState
,
630 sizeof(cloakedState
));
633 MOZ_LOG(sCloakingLog
, LogLevel::Warning
,
634 ("failed (%08lX) to query cloaking state for HWND %p", hr
, hwnd
));
638 return cloakedState
!= 0;
641 } // namespace mozilla
643 /**************************************************************
644 **************************************************************
646 ** BLOCK: nsIWidget impl.
648 ** nsIWidget interface implementation, broken down into
651 **************************************************************
652 **************************************************************/
654 /**************************************************************
656 * SECTION: nsWindow construction and destruction
658 **************************************************************/
660 nsWindow::nsWindow(bool aIsChildWindow
)
661 : nsBaseWidget(BorderStyle::Default
),
662 mBrush(::CreateSolidBrush(NSRGB_2_COLOREF(::GetSysColor(COLOR_BTNFACE
)))),
663 mFrameState(std::in_place
, this),
664 mIsChildWindow(aIsChildWindow
),
665 mLastPaintEndTime(TimeStamp::Now()),
666 mCachedHitTestTime(TimeStamp::Now()),
667 mSizeConstraintsScale(GetDefaultScale().scale
),
668 mDesktopId("DesktopIdMutex") {
669 MOZ_ASSERT(mWindowType
== WindowType::Child
);
671 if (!gInitializedVirtualDesktopManager
) {
672 TaskController::Get()->AddTask(
673 MakeAndAddRef
<InitializeVirtualDesktopManagerTask
>());
674 gInitializedVirtualDesktopManager
= true;
677 // Global initialization
678 if (!sInstanceCount
) {
679 // Global app registration id for Win7 and up. See
680 // WinTaskbar.cpp for details.
681 // MSIX packages explicitly do not support setting the appid from within
682 // the app, as it is set in the package manifest instead.
683 if (!WinUtils::HasPackageIdentity()) {
684 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
686 if (!StaticPrefs::ui_key_layout_load_when_first_needed()) {
687 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
689 #if defined(ACCESSIBILITY)
690 mozilla::TIPMessageHandler::Initialize();
691 #endif // defined(ACCESSIBILITY)
692 if (SUCCEEDED(::OleInitialize(nullptr))) {
693 sIsOleInitialized
= true;
695 NS_ASSERTION(sIsOleInitialized
, "***** OLE is not initialized!\n");
696 MouseScrollHandler::Initialize();
698 nsUXThemeData::UpdateNativeThemeInfo();
699 RedirectedKeyDownMessageManager::Forget();
705 nsWindow::~nsWindow() {
708 // If the widget was released without calling Destroy() then the native window
709 // still exists, and we need to destroy it. Destroy() will early-return if it
710 // was already called. In any case it is important to call it before
711 // destroying mPresentLock (cf. 1156182).
714 // Free app icon resources. This must happen after `OnDestroy` (see bug
716 if (mIconSmall
) ::DestroyIcon(mIconSmall
);
718 if (mIconBig
) ::DestroyIcon(mIconBig
);
723 if (sInstanceCount
== 0) {
724 IMEHandler::Terminate();
726 if (sIsOleInitialized
) {
727 ::OleFlushClipboard();
729 sIsOleInitialized
= false;
733 NS_IF_RELEASE(mNativeDragTarget
);
736 /**************************************************************
738 * SECTION: nsIWidget::Create, nsIWidget::Destroy
740 * Creating and destroying windows for this widget.
742 **************************************************************/
744 // Allow Derived classes to modify the height that is passed
745 // when the window is created or resized.
746 int32_t nsWindow::GetHeight(int32_t aProposedHeight
) { return aProposedHeight
; }
748 void nsWindow::SendAnAPZEvent(InputData
& aEvent
) {
749 LRESULT popupHandlingResult
;
750 if (DealWithPopups(mWnd
, MOZ_WM_DMANIP
, 0, 0, &popupHandlingResult
)) {
751 // We need to consume the event after using it to roll up the popup(s).
755 if (mSwipeTracker
&& aEvent
.mInputType
== PANGESTURE_INPUT
) {
756 // Give the swipe tracker a first pass at the event. If a new pan gesture
757 // has been started since the beginning of the swipe, the swipe tracker
758 // will know to ignore the event.
759 nsEventStatus status
=
760 mSwipeTracker
->ProcessEvent(aEvent
.AsPanGestureInput());
761 if (status
== nsEventStatus_eConsumeNoDefault
) {
766 APZEventResult result
;
768 result
= mAPZC
->InputBridge()->ReceiveInputEvent(aEvent
);
770 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
774 MOZ_ASSERT(aEvent
.mInputType
== PANGESTURE_INPUT
||
775 aEvent
.mInputType
== PINCHGESTURE_INPUT
);
777 if (aEvent
.mInputType
== PANGESTURE_INPUT
) {
778 PanGestureInput
& panInput
= aEvent
.AsPanGestureInput();
779 WidgetWheelEvent event
= panInput
.ToWidgetEvent(this);
781 if (MayStartSwipeForNonAPZ(panInput
)) {
785 event
= MayStartSwipeForAPZ(panInput
, result
);
788 ProcessUntransformedAPZEvent(&event
, result
);
793 PinchGestureInput
& pinchInput
= aEvent
.AsPinchGestureInput();
794 WidgetWheelEvent event
= pinchInput
.ToWidgetEvent(this);
795 ProcessUntransformedAPZEvent(&event
, result
);
798 void nsWindow::RecreateDirectManipulationIfNeeded() {
799 DestroyDirectManipulation();
801 if (mWindowType
!= WindowType::TopLevel
&& mWindowType
!= WindowType::Popup
) {
805 if (!(StaticPrefs::apz_allow_zooming() ||
806 StaticPrefs::apz_windows_use_direct_manipulation()) ||
807 StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
811 mDmOwner
= MakeUnique
<DirectManipulationOwner
>(this);
813 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
814 GetHeight(mBounds
.Height()));
815 mDmOwner
->Init(bounds
);
818 void nsWindow::ResizeDirectManipulationViewport() {
820 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
821 GetHeight(mBounds
.Height()));
822 mDmOwner
->ResizeViewport(bounds
);
826 void nsWindow::DestroyDirectManipulation() {
833 // Create the proper widget
834 nsresult
nsWindow::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
835 const LayoutDeviceIntRect
& aRect
,
836 widget::InitData
* aInitData
) {
837 // Historical note: there was once some belief and/or intent that nsWindows
838 // could be created on arbitrary threads, and this may still be reflected in
840 MOZ_ASSERT(NS_IsMainThread());
842 widget::InitData defaultInitData
;
843 if (!aInitData
) aInitData
= &defaultInitData
;
845 nsIWidget
* baseParent
=
846 aInitData
->mWindowType
== WindowType::Dialog
||
847 aInitData
->mWindowType
== WindowType::TopLevel
||
848 aInitData
->mWindowType
== WindowType::Invisible
852 mIsTopWidgetWindow
= (nullptr == baseParent
);
855 // Ensure that the toolkit is created.
856 nsToolkit::GetToolkit();
858 BaseCreate(baseParent
, aInitData
);
861 if (aParent
) { // has a nsIWidget parent
862 parent
= aParent
? (HWND
)aParent
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
864 } else { // has a nsNative parent
865 parent
= (HWND
)aNativeParent
;
867 aNativeParent
? WinUtils::GetNSWindowPtr((HWND
)aNativeParent
) : nullptr;
870 mIsRTL
= aInitData
->mRTL
;
871 mOpeningAnimationSuppressed
= aInitData
->mIsAnimationSuppressed
;
872 mAlwaysOnTop
= aInitData
->mAlwaysOnTop
;
873 mIsAlert
= aInitData
->mIsAlert
;
874 mResizable
= aInitData
->mResizable
;
876 DWORD style
= WindowStyle();
877 DWORD extendedStyle
= WindowExStyle();
879 if (mWindowType
== WindowType::Popup
) {
883 } else if (mWindowType
== WindowType::Invisible
) {
884 // Make sure CreateWindowEx succeeds at creating a toplevel window
885 style
&= ~0x40000000; // WS_CHILDWINDOW
887 // See if the caller wants to explictly set clip children and clip siblings
888 if (aInitData
->mClipChildren
) {
889 style
|= WS_CLIPCHILDREN
;
891 style
&= ~WS_CLIPCHILDREN
;
893 if (aInitData
->mClipSiblings
) {
894 style
|= WS_CLIPSIBLINGS
;
898 const wchar_t* className
= ChooseWindowClass(mWindowType
);
900 // Take specific actions when creating the first top-level window
901 static bool sFirstTopLevelWindowCreated
= false;
902 if (aInitData
->mWindowType
== WindowType::TopLevel
&& !aParent
&&
903 !sFirstTopLevelWindowCreated
) {
904 sFirstTopLevelWindowCreated
= true;
905 mWnd
= ConsumePreXULSkeletonUIHandle();
906 auto skeletonUIError
= GetPreXULSkeletonUIErrorReason();
907 if (skeletonUIError
) {
908 nsAutoString
errorString(
909 GetPreXULSkeletonUIErrorString(skeletonUIError
.value()));
910 Telemetry::ScalarSet(
911 Telemetry::ScalarID::STARTUP_SKELETON_UI_DISABLED_REASON
,
915 MOZ_ASSERT(style
== kPreXULSkeletonUIWindowStyle
,
916 "The skeleton UI window style should match the expected "
917 "style for the first window created");
918 MOZ_ASSERT(extendedStyle
== kPreXULSkeletonUIWindowStyleEx
,
919 "The skeleton UI window extended style should match the "
920 "expected extended style for the first window created");
922 ::GetWindowThreadProcessId(mWnd
, nullptr) == ::GetCurrentThreadId(),
923 "The skeleton UI window should be created on the same thread as "
925 mIsShowingPreXULSkeletonUI
= true;
927 // If we successfully consumed the pre-XUL skeleton UI, just update
928 // our internal state to match what is currently being displayed.
930 mIsCloaked
= mozilla::IsCloaked(mWnd
);
931 mFrameState
->ConsumePreXULSkeletonState(WasPreXULSkeletonUIMaximized());
933 // These match the margins set in browser-tabsintitlebar.js with
934 // default prefs on Windows. Bug 1673092 tracks lining this up with
935 // that more correctly instead of hard-coding it.
936 SetNonClientMargins(LayoutDeviceIntMargin(0, 2, 2, 2));
938 // Reset the WNDPROC for this window and its whole class, as we had
939 // to use our own WNDPROC when creating the the skeleton UI window.
940 ::SetWindowLongPtrW(mWnd
, GWLP_WNDPROC
,
941 reinterpret_cast<LONG_PTR
>(
942 WinUtils::NonClientDpiScalingDefWindowProcW
));
943 ::SetClassLongPtrW(mWnd
, GCLP_WNDPROC
,
944 reinterpret_cast<LONG_PTR
>(
945 WinUtils::NonClientDpiScalingDefWindowProcW
));
951 ::CreateWindowExW(extendedStyle
, className
, L
"", style
, aRect
.X(),
952 aRect
.Y(), aRect
.Width(), GetHeight(aRect
.Height()),
953 parent
, nullptr, nsToolkit::mDllInstance
, nullptr);
957 NS_WARNING("nsWindow CreateWindowEx failed.");
958 return NS_ERROR_FAILURE
;
961 if (!sWinCloakEventHook
) {
962 MOZ_LOG(sCloakingLog
, LogLevel::Info
, ("Registering cloaking event hook"));
964 // C++03 lambda approximation until P2173R1 is available (-std=c++2b)
965 struct StdcallLambda
{
966 static void CALLBACK
OnCloakUncloakHook(HWINEVENTHOOK hWinEventHook
,
967 DWORD event
, HWND hwnd
,
968 LONG idObject
, LONG idChild
,
970 DWORD dwmsEventTime
) {
971 const bool isCloaked
= event
== EVENT_OBJECT_CLOAKED
? true : false;
972 nsWindow::OnCloakEvent(hwnd
, isCloaked
);
976 const HWINEVENTHOOK hook
= ::SetWinEventHook(
977 EVENT_OBJECT_CLOAKED
, EVENT_OBJECT_UNCLOAKED
, HMODULE(nullptr),
978 &StdcallLambda::OnCloakUncloakHook
, ::GetCurrentProcessId(),
979 ::GetCurrentThreadId(), WINEVENT_OUTOFCONTEXT
);
980 sWinCloakEventHook
= Some(hook
);
983 const DWORD err
= ::GetLastError();
984 MOZ_LOG(sCloakingLog
, LogLevel::Error
,
985 ("Failed to register cloaking event hook! GLE = %lu (0x%lX)", err
,
990 if (aInitData
->mIsPrivate
) {
991 if (NimbusFeatures::GetBool("majorRelease2022"_ns
,
992 "feltPrivacyWindowSeparation"_ns
, true) &&
993 // Although permanent Private Browsing mode is indeed Private Browsing,
994 // we choose to make it look like regular Firefox in terms of the icon
995 // it uses (which also means we shouldn't use the Private Browsing
997 !StaticPrefs::browser_privatebrowsing_autostart()) {
998 RefPtr
<IPropertyStore
> pPropStore
;
999 if (!FAILED(SHGetPropertyStoreForWindow(mWnd
, IID_IPropertyStore
,
1000 getter_AddRefs(pPropStore
)))) {
1003 // make sure we're using the private browsing AUMID so that taskbar
1004 // grouping works properly
1005 Unused
<< NS_WARN_IF(
1006 !mozilla::widget::WinTaskbar::GenerateAppUserModelID(aumid
, true));
1007 if (!FAILED(InitPropVariantFromString(aumid
.get(), &pv
))) {
1008 if (!FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
))) {
1009 pPropStore
->Commit();
1012 PropVariantClear(&pv
);
1015 HICON icon
= ::LoadIconW(::GetModuleHandleW(nullptr),
1016 MAKEINTRESOURCEW(IDI_PBMODE
));
1022 mDeviceNotifyHandle
= InputDeviceUtils::RegisterNotification(mWnd
);
1024 // If mDefaultScale is set before mWnd has been set, it will have the scale of
1025 // the primary monitor, rather than the monitor that the window is actually
1026 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
1027 // which resets mDefaultScale, but for popup windows we don't reset
1028 // mDefaultScale on that message. In order to ensure that popup windows
1029 // spawned on a non-primary monitor end up with the correct scale, we reset
1030 // mDefaultScale here so that it gets recomputed using the correct monitor now
1031 // that we have a mWnd.
1032 mDefaultScale
= -1.0;
1035 DWORD dwAttribute
= TRUE
;
1036 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1037 sizeof dwAttribute
);
1040 UpdateDarkModeToolbar();
1042 if (mOpeningAnimationSuppressed
) {
1043 SuppressAnimation(true);
1047 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1048 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1051 if (mWindowType
!= WindowType::Invisible
&&
1052 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
1053 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
1055 // We create two zero-sized windows as descendants of the top-level window,
1058 // Top-level window (MozillaWindowClass)
1059 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
1060 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
1062 // We need to have the middle window, otherwise the Trackpoint driver
1063 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
1064 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
1065 // window hierarchy until they are handled by nsWindow::WindowProc.
1066 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
1067 // but these do not propagate automatically, so we have the window
1068 // procedure pretend that they were dispatched to the top-level window
1071 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
1072 // is given below so that it catches the Trackpoint driver's heuristics.
1073 HWND scrollContainerWnd
= ::CreateWindowW(
1074 className
, L
"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD
| WS_VISIBLE
, 0,
1075 0, 0, 0, mWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1076 HWND scrollableWnd
= ::CreateWindowW(
1077 className
, L
"FAKETRACKPOINTSCROLLABLE",
1078 WS_CHILD
| WS_VISIBLE
| WS_VSCROLL
| WS_TABSTOP
| 0x30, 0, 0, 0, 0,
1079 scrollContainerWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1081 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
1082 // WindowProcInternal can distinguish it from the top-level window
1084 ::SetWindowLongPtrW(scrollableWnd
, GWLP_ID
, eFakeTrackPointScrollableID
);
1086 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
1087 // old window procedure in its "user data".
1088 WNDPROC oldWndProc
= (WNDPROC
)::SetWindowLongPtrW(
1089 scrollableWnd
, GWLP_WNDPROC
, (LONG_PTR
)nsWindow::WindowProc
);
1090 ::SetWindowLongPtrW(scrollableWnd
, GWLP_USERDATA
, (LONG_PTR
)oldWndProc
);
1093 // We will start receiving native events after associating with our native
1094 // window. We will also become the output of WinUtils::GetNSWindowPtr for that
1096 if (!AssociateWithNativeWindow()) {
1097 return NS_ERROR_FAILURE
;
1100 // Starting with Windows XP, a process always runs within a terminal services
1101 // session. In order to play nicely with RDP, fast user switching, and the
1102 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
1103 // our HWND in order to receive this message.
1104 DebugOnly
<BOOL
> wtsRegistered
=
1105 ::WTSRegisterSessionNotification(mWnd
, NOTIFY_FOR_THIS_SESSION
);
1106 NS_ASSERTION(wtsRegistered
, "WTSRegisterSessionNotification failed!\n");
1108 mDefaultIMC
.Init(this);
1109 IMEHandler::InitInputContext(this, mInputContext
);
1111 static bool a11yPrimed
= false;
1112 if (!a11yPrimed
&& mWindowType
== WindowType::TopLevel
) {
1114 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
1115 ::PostMessage(mWnd
, MOZ_WM_STARTA11Y
, 0, 0);
1119 RecreateDirectManipulationIfNeeded();
1124 void nsWindow::LocalesChanged() {
1125 bool isRTL
= intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1126 if (mIsRTL
!= isRTL
) {
1127 DWORD dwAttribute
= isRTL
;
1128 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1129 sizeof dwAttribute
);
1134 // Close this nsWindow
1135 void nsWindow::Destroy() {
1136 // WM_DESTROY has already fired, avoid calling it twice
1137 if (mOnDestroyCalled
) return;
1139 // Don't destroy windows that have file pickers open, we'll tear these down
1140 // later once the picker is closed.
1141 mDestroyCalled
= true;
1142 if (mPickerDisplayCount
) return;
1144 // During the destruction of all of our children, make sure we don't get
1146 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1148 DestroyDirectManipulation();
1151 * On windows the LayerManagerOGL destructor wants the widget to be around for
1152 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
1154 DestroyLayerManager();
1156 InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle
);
1157 mDeviceNotifyHandle
= nullptr;
1159 // The DestroyWindow function destroys the specified window. The function
1160 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
1161 // and remove the keyboard focus from it. The function also destroys the
1162 // window's menu, flushes the thread message queue, destroys timers, removes
1163 // clipboard ownership, and breaks the clipboard viewer chain (if the window
1164 // is at the top of the viewer chain).
1166 // If the specified window is a parent or owner window, DestroyWindow
1167 // automatically destroys the associated child or owned windows when it
1168 // destroys the parent or owner window. The function first destroys child or
1169 // owned windows, and then it destroys the parent or owner window.
1170 VERIFY(::DestroyWindow(mWnd
));
1172 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
1173 // OnDestroy() didn't get called, call it now.
1174 if (false == mOnDestroyCalled
) {
1175 MSGResult msgResult
;
1176 mWindowHook
.Notify(mWnd
, WM_DESTROY
, 0, 0, msgResult
);
1181 /**************************************************************
1183 * SECTION: Window class utilities
1185 * Utilities for calculating the proper window class name for
1188 **************************************************************/
1191 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName
,
1192 UINT aExtraStyle
, LPWSTR aIconID
) {
1194 if (::GetClassInfoW(nsToolkit::mDllInstance
, aClassName
, &wc
)) {
1195 // already registered
1199 wc
.style
= CS_DBLCLKS
| aExtraStyle
;
1200 wc
.lpfnWndProc
= WinUtils::NonClientDpiScalingDefWindowProcW
;
1203 wc
.hInstance
= nsToolkit::mDllInstance
;
1205 aIconID
? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID
) : nullptr;
1206 wc
.hCursor
= nullptr;
1207 wc
.hbrBackground
= nullptr;
1208 wc
.lpszMenuName
= nullptr;
1209 wc
.lpszClassName
= aClassName
;
1211 if (!::RegisterClassW(&wc
)) {
1212 // For older versions of Win32 (i.e., not XP), the registration may
1213 // fail with aExtraStyle, so we have to re-register without it.
1214 wc
.style
= CS_DBLCLKS
;
1215 ::RegisterClassW(&wc
);
1220 static LPWSTR
const gStockApplicationIcon
= MAKEINTRESOURCEW(32512);
1223 const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType
) {
1224 const wchar_t* className
= [aWindowType
] {
1225 switch (aWindowType
) {
1226 case WindowType::Invisible
:
1227 return kClassNameHidden
;
1228 case WindowType::Dialog
:
1229 return kClassNameDialog
;
1230 case WindowType::Popup
:
1231 return kClassNameDropShadow
;
1233 return GetMainWindowClass();
1236 return RegisterWindowClass(className
, 0, gStockApplicationIcon
);
1239 /**************************************************************
1241 * SECTION: Window styles utilities
1243 * Return the proper windows styles and extended styles.
1245 **************************************************************/
1247 // Return nsWindow styles
1248 DWORD
nsWindow::WindowStyle() {
1251 switch (mWindowType
) {
1252 case WindowType::Child
:
1253 style
= WS_OVERLAPPED
;
1256 case WindowType::Dialog
:
1257 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
| DS_3DLOOK
|
1258 DS_MODALFRAME
| WS_CLIPCHILDREN
;
1259 if (mBorderStyle
!= BorderStyle::Default
)
1260 style
|= WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
;
1263 case WindowType::Popup
:
1264 style
= WS_POPUP
| WS_OVERLAPPED
;
1268 NS_ERROR("unknown border style");
1271 case WindowType::TopLevel
:
1272 case WindowType::Invisible
:
1273 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
|
1274 WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_CLIPCHILDREN
;
1278 if (mBorderStyle
!= BorderStyle::Default
&&
1279 mBorderStyle
!= BorderStyle::All
) {
1280 if (mBorderStyle
== BorderStyle::None
||
1281 !(mBorderStyle
& BorderStyle::Border
))
1282 style
&= ~WS_BORDER
;
1284 if (mBorderStyle
== BorderStyle::None
||
1285 !(mBorderStyle
& BorderStyle::Title
)) {
1286 style
&= ~WS_DLGFRAME
;
1289 if (mBorderStyle
== BorderStyle::None
||
1290 !(mBorderStyle
& BorderStyle::Close
))
1292 // XXX The close box can only be removed by changing the window class,
1293 // as far as I know --- roc+moz@cs.cmu.edu
1295 if (mBorderStyle
== BorderStyle::None
||
1296 !(mBorderStyle
& (BorderStyle::Menu
| BorderStyle::Close
)))
1297 style
&= ~WS_SYSMENU
;
1298 // Looks like getting rid of the system menu also does away with the
1299 // close box. So, we only get rid of the system menu if you want neither it
1300 // nor the close box. How does the Windows "Dialog" window class get just
1301 // closebox and no sysmenu? Who knows.
1303 if (mBorderStyle
== BorderStyle::None
||
1304 !(mBorderStyle
& BorderStyle::ResizeH
))
1305 style
&= ~WS_THICKFRAME
;
1307 if (mBorderStyle
== BorderStyle::None
||
1308 !(mBorderStyle
& BorderStyle::Minimize
))
1309 style
&= ~WS_MINIMIZEBOX
;
1311 if (mBorderStyle
== BorderStyle::None
||
1312 !(mBorderStyle
& BorderStyle::Maximize
))
1313 style
&= ~WS_MAXIMIZEBOX
;
1316 if (mIsChildWindow
) {
1317 style
|= WS_CLIPCHILDREN
;
1318 if (!(style
& WS_POPUP
)) {
1319 style
|= WS_CHILD
; // WS_POPUP and WS_CHILD are mutually exclusive.
1323 VERIFY_WINDOW_STYLE(style
);
1327 // Return nsWindow extended styles
1328 DWORD
nsWindow::WindowExStyle() {
1329 switch (mWindowType
) {
1330 case WindowType::Child
:
1332 case WindowType::Popup
: {
1333 DWORD extendedStyle
= WS_EX_TOOLWINDOW
;
1334 if (mPopupLevel
== PopupLevel::Top
) {
1335 extendedStyle
|= WS_EX_TOPMOST
;
1337 return extendedStyle
;
1339 case WindowType::Dialog
:
1340 case WindowType::TopLevel
:
1341 case WindowType::Invisible
:
1345 MOZ_ASSERT(mWindowType
== WindowType::Dialog
,
1346 "Expect alert windows to have type=dialog");
1347 return WS_EX_TOOLWINDOW
| WS_EX_TOPMOST
;
1349 return WS_EX_WINDOWEDGE
;
1352 /**************************************************************
1354 * SECTION: Native window association utilities
1356 * Used in Create and Destroy. A nsWindow can associate with its
1357 * underlying native window mWnd. Once a native window is
1358 * associated with a nsWindow, its native events will be handled
1359 * by the static member function nsWindow::WindowProc. Moreover,
1360 * the association will be registered in the WinUtils association
1361 * list, that is, calling WinUtils::GetNSWindowPtr on the native
1362 * window will return the associated nsWindow. This is used in
1363 * nsWindow::WindowProc to correctly dispatch native events to
1364 * the handler methods defined in nsWindow, even though it is a
1365 * static member function.
1367 * After dissociation, the native events of the native window will
1368 * no longer be handled by nsWindow::WindowProc, and will thus not
1369 * be dispatched to the nsWindow native event handler methods.
1370 * Moreover, the association will no longer be registered in the
1371 * WinUtils association list, so calling WinUtils::GetNSWindowPtr
1372 * on the native window will return nullptr.
1374 **************************************************************/
1376 bool nsWindow::AssociateWithNativeWindow() {
1377 if (!mWnd
|| !IsWindow(mWnd
)) {
1378 NS_ERROR("Invalid window handle");
1382 // Connect the this pointer to the native window handle.
1383 // This should be done before SetWindowLongPtrW, because nsWindow::WindowProc
1384 // uses WinUtils::GetNSWindowPtr internally.
1385 WinUtils::SetNSWindowPtr(mWnd
, this);
1387 ::SetLastError(ERROR_SUCCESS
);
1388 const auto prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1389 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(nsWindow::WindowProc
)));
1390 if (!prevWndProc
&& GetLastError() != ERROR_SUCCESS
) {
1391 NS_ERROR("Failure in SetWindowLongPtrW");
1392 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1396 mPrevWndProc
.emplace(prevWndProc
);
1400 void nsWindow::DissociateFromNativeWindow() {
1401 if (!mWnd
|| !IsWindow(mWnd
) || mPrevWndProc
.isNothing()) {
1405 DebugOnly
<WNDPROC
> wndProcBeforeDissociate
=
1406 reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1407 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(*mPrevWndProc
)));
1408 NS_ASSERTION(wndProcBeforeDissociate
== nsWindow::WindowProc
,
1409 "Unstacked an unexpected native window procedure");
1411 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1412 mPrevWndProc
.reset();
1415 /**************************************************************
1417 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1419 * Set or clear the parent widgets using window properties, and
1420 * handles calculating native parent handles.
1422 **************************************************************/
1424 // Get and set parent widgets
1425 void nsWindow::SetParent(nsIWidget
* aNewParent
) {
1426 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1427 nsIWidget
* parent
= GetParent();
1429 parent
->RemoveChild(this);
1432 mParent
= aNewParent
;
1435 ReparentNativeWidget(aNewParent
);
1436 aNewParent
->AddChild(this);
1440 // If we have no parent, SetParent should return the desktop.
1441 VERIFY(::SetParent(mWnd
, nullptr));
1442 RecreateDirectManipulationIfNeeded();
1446 void nsWindow::ReparentNativeWidget(nsIWidget
* aNewParent
) {
1447 MOZ_ASSERT(aNewParent
, "null widget");
1449 mParent
= aNewParent
;
1450 if (mWindowType
== WindowType::Popup
) {
1453 HWND newParent
= (HWND
)aNewParent
->GetNativeData(NS_NATIVE_WINDOW
);
1454 NS_ASSERTION(newParent
, "Parent widget has a null native window handle");
1455 if (newParent
&& mWnd
) {
1456 ::SetParent(mWnd
, newParent
);
1457 RecreateDirectManipulationIfNeeded();
1461 nsIWidget
* nsWindow::GetParent(void) {
1462 if (mIsTopWidgetWindow
) {
1465 if (mInDtor
|| mOnDestroyCalled
) {
1471 static int32_t RoundDown(double aDouble
) {
1472 return aDouble
> 0 ? static_cast<int32_t>(floor(aDouble
))
1473 : static_cast<int32_t>(ceil(aDouble
));
1476 float nsWindow::GetDPI() {
1478 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1480 screen
->GetDpi(&dpi
);
1485 double nsWindow::GetDefaultScaleInternal() {
1486 if (mDefaultScale
<= 0.0) {
1487 mDefaultScale
= WinUtils::LogToPhysFactor(mWnd
);
1489 return mDefaultScale
;
1492 int32_t nsWindow::LogToPhys(double aValue
) {
1493 return WinUtils::LogToPhys(
1494 ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTOPRIMARY
), aValue
);
1497 nsWindow
* nsWindow::GetParentWindow(bool aIncludeOwner
) {
1498 return static_cast<nsWindow
*>(GetParentWindowBase(aIncludeOwner
));
1501 nsWindow
* nsWindow::GetParentWindowBase(bool aIncludeOwner
) {
1502 if (mIsTopWidgetWindow
) {
1503 // Must use a flag instead of mWindowType to tell if the window is the
1504 // owned by the topmost widget, because a child window can be embedded
1505 // inside a HWND which is not associated with a nsIWidget.
1509 // If this widget has already been destroyed, pretend we have no parent.
1510 // This corresponds to code in Destroy which removes the destroyed
1511 // widget from its parent's child list.
1512 if (mInDtor
|| mOnDestroyCalled
) return nullptr;
1514 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1515 // root owner. aIncludeOwner set to false implies the search will stop at the
1516 // true parent (default).
1517 nsWindow
* widget
= nullptr;
1519 HWND parent
= nullptr;
1521 parent
= ::GetParent(mWnd
);
1523 parent
= ::GetAncestor(mWnd
, GA_PARENT
);
1526 widget
= WinUtils::GetNSWindowPtr(parent
);
1528 // If the widget is in the process of being destroyed then
1530 if (widget
->mInDtor
) {
1540 /**************************************************************
1542 * SECTION: nsIWidget::Show
1544 * Hide or show this component.
1546 **************************************************************/
1548 void nsWindow::Show(bool bState
) {
1549 if (bState
&& mIsShowingPreXULSkeletonUI
) {
1550 // The first time we decide to actually show the window is when we decide
1551 // that we've taken over the window from the skeleton UI, and we should
1552 // no longer treat resizes / moves specially.
1553 mIsShowingPreXULSkeletonUI
= false;
1554 #if defined(ACCESSIBILITY)
1555 // If our HWND has focus and the a11y engine hasn't started yet, fire a
1556 // focus win event. Windows already did this when the skeleton UI appeared,
1557 // but a11y wouldn't have been able to start at that point even if a client
1558 // responded. Firing this now gives clients the chance to respond with
1559 // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
1560 // this if the a11y engine has already started because it has probably
1561 // already fired focus on a descendant.
1562 if (::GetFocus() == mWnd
&& !GetAccService()) {
1563 ::NotifyWinEvent(EVENT_OBJECT_FOCUS
, mWnd
, OBJID_CLIENT
, CHILDID_SELF
);
1565 #endif // defined(ACCESSIBILITY)
1568 if (mWindowType
== WindowType::Popup
) {
1569 MOZ_ASSERT(ChooseWindowClass(mWindowType
) == kClassNameDropShadow
);
1570 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1571 // some popup menus to become invisible.
1572 LONG_PTR exStyle
= ::GetWindowLongPtrW(mWnd
, GWL_EXSTYLE
);
1573 if (exStyle
& WS_EX_LAYERED
) {
1574 ::SetWindowLongPtrW(mWnd
, GWL_EXSTYLE
, exStyle
& ~WS_EX_COMPOSITED
);
1578 bool syncInvalidate
= false;
1580 bool wasVisible
= mIsVisible
;
1581 // Set the status now so that anyone asking during ShowWindow or
1582 // SetWindowPos would get the correct answer.
1583 mIsVisible
= bState
;
1585 // We may have cached an out of date visible state. This can happen
1586 // when session restore sets the full screen mode.
1588 mOldStyle
|= WS_VISIBLE
;
1590 mOldStyle
&= ~WS_VISIBLE
;
1594 if (!wasVisible
&& mWindowType
== WindowType::TopLevel
) {
1595 // speed up the initial paint after show for
1596 // top level windows:
1597 syncInvalidate
= true;
1599 // Set the cursor before showing the window to avoid the default wait
1601 SetCursor(Cursor
{eCursor_standard
});
1603 switch (mFrameState
->GetSizeMode()) {
1604 case nsSizeMode_Fullscreen
:
1605 ::ShowWindow(mWnd
, SW_SHOW
);
1607 case nsSizeMode_Maximized
:
1608 ::ShowWindow(mWnd
, SW_SHOWMAXIMIZED
);
1610 case nsSizeMode_Minimized
:
1611 ::ShowWindow(mWnd
, SW_SHOWMINIMIZED
);
1614 if (CanTakeFocus() && !mAlwaysOnTop
) {
1615 ::ShowWindow(mWnd
, SW_SHOWNORMAL
);
1617 ::ShowWindow(mWnd
, SW_SHOWNOACTIVATE
);
1618 // Don't flicker the window if we're restoring session
1619 if (!sIsRestoringSession
) {
1620 Unused
<< GetAttention(2);
1626 DWORD flags
= SWP_NOSIZE
| SWP_NOMOVE
| SWP_SHOWWINDOW
;
1628 flags
|= SWP_NOZORDER
;
1630 if (mAlwaysOnTop
|| mIsAlert
) {
1631 flags
|= SWP_NOACTIVATE
;
1634 if (mWindowType
== WindowType::Popup
) {
1635 // ensure popups are the topmost of the TOPMOST
1636 // layer. Remember not to set the SWP_NOZORDER
1637 // flag as that might allow the taskbar to overlap
1639 flags
|= SWP_NOACTIVATE
;
1640 HWND owner
= ::GetWindow(mWnd
, GW_OWNER
);
1642 // PopupLevel::Top popups should be above all else. All other
1643 // types should be placed in front of their owner, without
1644 // changing the owner's z-level relative to other windows.
1645 if (mPopupLevel
!= PopupLevel::Top
) {
1646 ::SetWindowPos(mWnd
, owner
, 0, 0, 0, 0, flags
);
1647 ::SetWindowPos(owner
, mWnd
, 0, 0, 0, 0,
1648 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1650 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1653 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0, flags
);
1656 if (mWindowType
== WindowType::Dialog
&& !CanTakeFocus())
1657 flags
|= SWP_NOACTIVATE
;
1659 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1663 // Clear contents to avoid ghosting of old content if we display
1664 // this window again.
1665 if (wasVisible
&& mTransparencyMode
== TransparencyMode::Transparent
) {
1666 if (mCompositorWidgetDelegate
) {
1667 mCompositorWidgetDelegate
->ClearTransparentWindow();
1670 if (mWindowType
!= WindowType::Dialog
) {
1671 ::ShowWindow(mWnd
, SW_HIDE
);
1673 ::SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
1674 SWP_HIDEWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOZORDER
|
1680 if (!wasVisible
&& bState
) {
1682 if (syncInvalidate
&& !mInDtor
&& !mOnDestroyCalled
) {
1683 ::UpdateWindow(mWnd
);
1687 if (mOpeningAnimationSuppressed
) {
1688 SuppressAnimation(false);
1692 /**************************************************************
1694 * SECTION: nsIWidget::IsVisible
1696 * Returns the visibility state.
1698 **************************************************************/
1700 // Return true if the component is visible, false otherwise.
1702 // This does not take cloaking into account.
1703 bool nsWindow::IsVisible() const { return mIsVisible
; }
1705 /**************************************************************
1707 * SECTION: Touch and APZ-related functions
1709 **************************************************************/
1711 void nsWindow::RegisterTouchWindow() {
1712 mTouchWindow
= true;
1713 ::RegisterTouchWindow(mWnd
, TWF_WANTPALM
);
1714 ::EnumChildWindows(mWnd
, nsWindow::RegisterTouchForDescendants
, 0);
1717 BOOL CALLBACK
nsWindow::RegisterTouchForDescendants(HWND aWnd
, LPARAM aMsg
) {
1718 nsWindow
* win
= WinUtils::GetNSWindowPtr(aWnd
);
1720 ::RegisterTouchWindow(aWnd
, TWF_WANTPALM
);
1725 void nsWindow::LockAspectRatio(bool aShouldLock
) {
1727 mAspectRatio
= (float)mBounds
.Width() / (float)mBounds
.Height();
1733 /**************************************************************
1735 * SECTION: nsIWidget::SetInputRegion
1737 * Sets whether the window should ignore mouse events.
1739 **************************************************************/
1740 void nsWindow::SetInputRegion(const InputRegion
& aInputRegion
) {
1741 mInputRegion
= aInputRegion
;
1744 /**************************************************************
1746 * SECTION: nsIWidget::Move, nsIWidget::Resize, nsIWidget::Size
1748 * Repositioning and sizing a window.
1750 **************************************************************/
1752 void nsWindow::SetSizeConstraints(const SizeConstraints
& aConstraints
) {
1753 SizeConstraints c
= aConstraints
;
1755 if (mWindowType
!= WindowType::Popup
&& mResizable
) {
1757 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK
)), c
.mMinSize
.width
);
1759 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK
)), c
.mMinSize
.height
);
1762 if (mMaxTextureSize
> 0) {
1763 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1764 // a window grow bigger as we won't be able to draw content there in
1766 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
1767 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
1770 mSizeConstraintsScale
= GetDefaultScale().scale
;
1772 nsBaseWidget::SetSizeConstraints(c
);
1775 const SizeConstraints
nsWindow::GetSizeConstraints() {
1776 double scale
= GetDefaultScale().scale
;
1777 if (mSizeConstraintsScale
== scale
|| mSizeConstraintsScale
== 0.0) {
1778 return mSizeConstraints
;
1780 scale
/= mSizeConstraintsScale
;
1781 SizeConstraints c
= mSizeConstraints
;
1782 if (c
.mMinSize
.width
!= NS_MAXSIZE
) {
1783 c
.mMinSize
.width
= NSToIntRound(c
.mMinSize
.width
* scale
);
1785 if (c
.mMinSize
.height
!= NS_MAXSIZE
) {
1786 c
.mMinSize
.height
= NSToIntRound(c
.mMinSize
.height
* scale
);
1788 if (c
.mMaxSize
.width
!= NS_MAXSIZE
) {
1789 c
.mMaxSize
.width
= NSToIntRound(c
.mMaxSize
.width
* scale
);
1791 if (c
.mMaxSize
.height
!= NS_MAXSIZE
) {
1792 c
.mMaxSize
.height
= NSToIntRound(c
.mMaxSize
.height
* scale
);
1797 // Move this component
1798 void nsWindow::Move(double aX
, double aY
) {
1799 if (mWindowType
== WindowType::TopLevel
||
1800 mWindowType
== WindowType::Dialog
) {
1801 SetSizeMode(nsSizeMode_Normal
);
1804 // for top-level windows only, convert coordinates from desktop pixels
1805 // (the "parent" coordinate space) to the window's device pixel space
1807 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1808 int32_t x
= NSToIntRound(aX
* scale
);
1809 int32_t y
= NSToIntRound(aY
* scale
);
1811 // Check to see if window needs to be moved first
1812 // to avoid a costly call to SetWindowPos. This check
1813 // can not be moved to the calling code in nsView, because
1814 // some platforms do not position child windows correctly
1816 // Only perform this check for non-popup windows, since the positioning can
1817 // in fact change even when the x/y do not. We always need to perform the
1818 // check. See bug #97805 for details.
1819 if (mWindowType
!= WindowType::Popup
&& mBounds
.IsEqualXY(x
, y
)) {
1820 // Nothing to do, since it is already positioned correctly.
1824 mBounds
.MoveTo(x
, y
);
1828 // complain if a window is moved offscreen (legal, but potentially
1830 if (mIsTopWidgetWindow
) { // only a problem for top-level windows
1831 // Make sure this window is actually on the screen before we move it
1832 // XXX: Needs multiple monitor support
1833 HDC dc
= ::GetDC(mWnd
);
1835 if (::GetDeviceCaps(dc
, TECHNOLOGY
) == DT_RASDISPLAY
) {
1837 ::SystemParametersInfo(SPI_GETWORKAREA
, 0, &workArea
, 0);
1838 // no annoying assertions. just mention the issue.
1839 if (x
< 0 || x
>= workArea
.right
|| y
< 0 || y
>= workArea
.bottom
) {
1840 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
1841 ("window moved to offscreen position\n"));
1844 ::ReleaseDC(mWnd
, dc
);
1849 // Normally, when the skeleton UI is disabled, we resize+move the window
1850 // before showing it in order to ensure that it restores to the correct
1851 // position when the user un-maximizes it. However, when we are using the
1852 // skeleton UI, this results in the skeleton UI window being moved around
1853 // undesirably before being locked back into the maximized position. To
1854 // avoid this, we simply set the placement to restore to via
1855 // SetWindowPlacement. It's a little bit more of a dance, though, since we
1856 // need to convert the workspace coords that SetWindowPlacement uses to the
1857 // screen space coordinates we normally use with SetWindowPos.
1858 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
1859 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
1860 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
1862 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
1863 if (NS_WARN_IF(!monitor
)) {
1866 MONITORINFO mi
= {sizeof(MONITORINFO
)};
1867 VERIFY(::GetMonitorInfo(monitor
, &mi
));
1870 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
1872 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
1873 pl
.rcNormalPosition
.left
+= deltaX
;
1874 pl
.rcNormalPosition
.right
+= deltaX
;
1875 pl
.rcNormalPosition
.top
+= deltaY
;
1876 pl
.rcNormalPosition
.bottom
+= deltaY
;
1877 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
1879 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOSIZE
;
1880 double oldScale
= mDefaultScale
;
1881 mResizeState
= IN_SIZEMOVE
;
1882 VERIFY(::SetWindowPos(mWnd
, nullptr, x
, y
, 0, 0, flags
));
1883 mResizeState
= NOT_RESIZING
;
1884 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
1889 ResizeDirectManipulationViewport();
1893 // Resize this component
1894 void nsWindow::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
1895 // for top-level windows only, convert coordinates from desktop pixels
1896 // (the "parent" coordinate space) to the window's device pixel space
1898 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1899 int32_t width
= NSToIntRound(aWidth
* scale
);
1900 int32_t height
= NSToIntRound(aHeight
* scale
);
1902 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
1903 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
1904 if (width
< 0 || height
< 0) {
1905 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << width
<< ", "
1906 << height
<< ") repaint: " << aRepaint
;
1909 ConstrainSize(&width
, &height
);
1911 // Avoid unnecessary resizing calls
1912 if (mBounds
.IsEqualSize(width
, height
)) {
1919 // Set cached value for lightweight and printing
1920 bool wasLocking
= mAspectRatio
!= 0.0;
1921 mBounds
.SizeTo(width
, height
);
1923 LockAspectRatio(true); // This causes us to refresh the mAspectRatio value
1927 // Refer to the comment above a similar check in nsWindow::Move
1928 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
1929 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
1930 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
1931 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
1932 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
1933 mResizeState
= RESIZING
;
1934 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
1935 mResizeState
= NOT_RESIZING
;
1937 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
;
1940 flags
|= SWP_NOREDRAW
;
1943 double oldScale
= mDefaultScale
;
1944 mResizeState
= RESIZING
;
1946 ::SetWindowPos(mWnd
, nullptr, 0, 0, width
, GetHeight(height
), flags
));
1948 mResizeState
= NOT_RESIZING
;
1949 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
1954 ResizeDirectManipulationViewport();
1957 if (aRepaint
) Invalidate();
1960 // Resize this component
1961 void nsWindow::Resize(double aX
, double aY
, double aWidth
, double aHeight
,
1963 // for top-level windows only, convert coordinates from desktop pixels
1964 // (the "parent" coordinate space) to the window's device pixel space
1966 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1967 int32_t x
= NSToIntRound(aX
* scale
);
1968 int32_t y
= NSToIntRound(aY
* scale
);
1969 int32_t width
= NSToIntRound(aWidth
* scale
);
1970 int32_t height
= NSToIntRound(aHeight
* scale
);
1972 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
1973 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
1974 if (width
< 0 || height
< 0) {
1975 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << x
<< " ," << y
1976 << ", " << width
<< ", " << height
1977 << ") repaint: " << aRepaint
;
1980 ConstrainSize(&width
, &height
);
1982 // Avoid unnecessary resizing calls
1983 if (mBounds
.IsEqualRect(x
, y
, width
, height
)) {
1990 // Set cached value for lightweight and printing
1991 mBounds
.SetRect(x
, y
, width
, height
);
1994 // Refer to the comment above a similar check in nsWindow::Move
1995 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
1996 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
1997 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
1999 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2000 if (NS_WARN_IF(!monitor
)) {
2003 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2004 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2007 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2009 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2010 pl
.rcNormalPosition
.left
+= deltaX
;
2011 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2012 pl
.rcNormalPosition
.top
+= deltaY
;
2013 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2014 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2016 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
;
2018 flags
|= SWP_NOREDRAW
;
2021 double oldScale
= mDefaultScale
;
2022 mResizeState
= RESIZING
;
2024 ::SetWindowPos(mWnd
, nullptr, x
, y
, width
, GetHeight(height
), flags
));
2025 mResizeState
= NOT_RESIZING
;
2026 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2030 if (mTransitionWnd
) {
2031 // If we have a fullscreen transition window, we need to make
2032 // it topmost again, otherwise the taskbar may be raised by
2033 // the system unexpectedly when we leave fullscreen state.
2034 ::SetWindowPos(mTransitionWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
2035 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
2039 ResizeDirectManipulationViewport();
2042 if (aRepaint
) Invalidate();
2045 mozilla::Maybe
<bool> nsWindow::IsResizingNativeWidget() {
2046 if (mResizeState
== RESIZING
) {
2052 /**************************************************************
2054 * SECTION: Window Z-order and state.
2056 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
2057 * nsIWidget::ConstrainPosition
2059 * Z-order, positioning, restore, minimize, and maximize.
2061 **************************************************************/
2063 // Position the window behind the given window
2064 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement
,
2065 nsIWidget
* aWidget
, bool aActivate
) {
2066 HWND behind
= HWND_TOP
;
2067 if (aPlacement
== eZPlacementBottom
)
2068 behind
= HWND_BOTTOM
;
2069 else if (aPlacement
== eZPlacementBelow
&& aWidget
)
2070 behind
= (HWND
)aWidget
->GetNativeData(NS_NATIVE_WINDOW
);
2071 UINT flags
= SWP_NOMOVE
| SWP_NOREPOSITION
| SWP_NOSIZE
;
2072 if (!aActivate
) flags
|= SWP_NOACTIVATE
;
2074 if (!CanTakeFocus() && behind
== HWND_TOP
) {
2075 // Can't place the window to top so place it behind the foreground window
2076 // (as long as it is not topmost)
2077 HWND wndAfter
= ::GetForegroundWindow();
2079 behind
= HWND_BOTTOM
;
2080 else if (!(GetWindowLongPtrW(wndAfter
, GWL_EXSTYLE
) & WS_EX_TOPMOST
))
2082 flags
|= SWP_NOACTIVATE
;
2085 ::SetWindowPos(mWnd
, behind
, 0, 0, 0, 0, flags
);
2088 static UINT
GetCurrentShowCmd(HWND aWnd
) {
2090 pl
.length
= sizeof(pl
);
2091 ::GetWindowPlacement(aWnd
, &pl
);
2095 // Maximize, minimize or restore the window.
2096 void nsWindow::SetSizeMode(nsSizeMode aMode
) {
2097 // If we are still displaying a maximized pre-XUL skeleton UI, ignore the
2098 // noise of sizemode changes. Once we have "shown" the window for the first
2099 // time (called nsWindow::Show(true), even though the window is already
2100 // technically displayed), we will again accept sizemode changes.
2101 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2105 mFrameState
->EnsureSizeMode(aMode
);
2108 nsSizeMode
nsWindow::SizeMode() { return mFrameState
->GetSizeMode(); }
2110 void DoGetWorkspaceID(HWND aWnd
, nsAString
* aWorkspaceID
) {
2111 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2112 if (!desktopManager
|| !aWnd
) {
2117 HRESULT hr
= desktopManager
->GetWindowDesktopId(aWnd
, &desktop
);
2122 RPC_WSTR workspaceIDStr
= nullptr;
2123 if (UuidToStringW(&desktop
, &workspaceIDStr
) == RPC_S_OK
) {
2124 aWorkspaceID
->Assign((wchar_t*)workspaceIDStr
);
2125 RpcStringFreeW(&workspaceIDStr
);
2129 void nsWindow::GetWorkspaceID(nsAString
& workspaceID
) {
2130 // If we have a value cached, use that, but also make sure it is
2131 // scheduled to be updated. If we don't yet have a value, get
2132 // one synchronously.
2133 auto desktop
= mDesktopId
.Lock();
2134 if (desktop
->mID
.IsEmpty()) {
2135 DoGetWorkspaceID(mWnd
, &desktop
->mID
);
2136 desktop
->mUpdateIsQueued
= false;
2138 AsyncUpdateWorkspaceID(*desktop
);
2141 workspaceID
= desktop
->mID
;
2144 void nsWindow::AsyncUpdateWorkspaceID(Desktop
& aDesktop
) {
2145 struct UpdateWorkspaceIdTask
: public Task
{
2146 explicit UpdateWorkspaceIdTask(nsWindow
* aSelf
)
2147 : Task(Kind::OffMainThreadOnly
, EventQueuePriority::Normal
),
2150 TaskResult
Run() override
{
2151 auto desktop
= mSelf
->mDesktopId
.Lock();
2152 if (desktop
->mUpdateIsQueued
) {
2153 DoGetWorkspaceID(mSelf
->mWnd
, &desktop
->mID
);
2154 desktop
->mUpdateIsQueued
= false;
2156 return TaskResult::Complete
;
2159 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
2160 bool GetName(nsACString
& aName
) override
{
2161 aName
.AssignLiteral("UpdateWorkspaceIdTask");
2166 RefPtr
<nsWindow
> mSelf
;
2169 if (aDesktop
.mUpdateIsQueued
) {
2173 aDesktop
.mUpdateIsQueued
= true;
2174 TaskController::Get()->AddTask(MakeAndAddRef
<UpdateWorkspaceIdTask
>(this));
2177 void nsWindow::MoveToWorkspace(const nsAString
& workspaceID
) {
2178 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2179 if (!desktopManager
) {
2184 const nsString flat
= PromiseFlatString(workspaceID
);
2185 RPC_WSTR workspaceIDStr
= reinterpret_cast<RPC_WSTR
>((wchar_t*)flat
.get());
2186 if (UuidFromStringW(workspaceIDStr
, &desktop
) == RPC_S_OK
) {
2187 if (SUCCEEDED(desktopManager
->MoveWindowToDesktop(mWnd
, desktop
))) {
2188 auto desktop
= mDesktopId
.Lock();
2189 desktop
->mID
= workspaceID
;
2194 void nsWindow::SuppressAnimation(bool aSuppress
) {
2195 DWORD dwAttribute
= aSuppress
? TRUE
: FALSE
;
2196 DwmSetWindowAttribute(mWnd
, DWMWA_TRANSITIONS_FORCEDISABLED
, &dwAttribute
,
2197 sizeof dwAttribute
);
2200 // Constrain a potential move to fit onscreen
2201 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2202 // except when using per-monitor DPI, in which case it's device pixels.
2203 void nsWindow::ConstrainPosition(DesktopIntPoint
& aPoint
) {
2204 if (!mIsTopWidgetWindow
) // only a problem for top-level windows
2207 double dpiScale
= GetDesktopToDeviceScale().scale
;
2209 // We need to use the window size in the kind of pixels used for window-
2210 // manipulation APIs.
2212 std::max
<int32_t>(NSToIntRound(mBounds
.Width() / dpiScale
), 1);
2214 std::max
<int32_t>(NSToIntRound(mBounds
.Height() / dpiScale
), 1);
2216 /* get our playing field. use the current screen, or failing that
2217 for any reason, use device caps for the default screen. */
2220 nsCOMPtr
<nsIScreenManager
> screenmgr
=
2221 do_GetService(sScreenManagerContractID
);
2225 nsCOMPtr
<nsIScreen
> screen
;
2226 int32_t left
, top
, width
, height
;
2228 screenmgr
->ScreenForRect(aPoint
.x
, aPoint
.y
, logWidth
, logHeight
,
2229 getter_AddRefs(screen
));
2230 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
2231 // For normalized windows, use the desktop work area.
2232 nsresult rv
= screen
->GetAvailRectDisplayPix(&left
, &top
, &width
, &height
);
2233 if (NS_FAILED(rv
)) {
2237 // For full screen windows, use the desktop.
2238 nsresult rv
= screen
->GetRectDisplayPix(&left
, &top
, &width
, &height
);
2239 if (NS_FAILED(rv
)) {
2243 screenRect
.left
= left
;
2244 screenRect
.right
= left
+ width
;
2245 screenRect
.top
= top
;
2246 screenRect
.bottom
= top
+ height
;
2248 if (aPoint
.x
< screenRect
.left
)
2249 aPoint
.x
= screenRect
.left
;
2250 else if (aPoint
.x
>= screenRect
.right
- logWidth
)
2251 aPoint
.x
= screenRect
.right
- logWidth
;
2253 if (aPoint
.y
< screenRect
.top
)
2254 aPoint
.y
= screenRect
.top
;
2255 else if (aPoint
.y
>= screenRect
.bottom
- logHeight
)
2256 aPoint
.y
= screenRect
.bottom
- logHeight
;
2259 /**************************************************************
2261 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2263 * Enabling and disabling the widget.
2265 **************************************************************/
2267 // Enable/disable this component
2268 void nsWindow::Enable(bool bState
) {
2270 ::EnableWindow(mWnd
, bState
);
2274 // Return the current enable state
2275 bool nsWindow::IsEnabled() const {
2276 return !mWnd
|| (::IsWindowEnabled(mWnd
) &&
2277 ::IsWindowEnabled(::GetAncestor(mWnd
, GA_ROOT
)));
2280 /**************************************************************
2282 * SECTION: nsIWidget::SetFocus
2284 * Give the focus to this widget.
2286 **************************************************************/
2288 void nsWindow::SetFocus(Raise aRaise
, mozilla::dom::CallerType aCallerType
) {
2290 #ifdef WINSTATE_DEBUG_OUTPUT
2291 if (mWnd
== WinUtils::GetTopLevelHWND(mWnd
)) {
2292 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2293 ("*** SetFocus: [ top] raise=%d\n", aRaise
== Raise::Yes
));
2295 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2296 ("*** SetFocus: [child] raise=%d\n", aRaise
== Raise::Yes
));
2299 // Uniconify, if necessary
2300 HWND toplevelWnd
= WinUtils::GetTopLevelHWND(mWnd
);
2301 if (aRaise
== Raise::Yes
&& ::IsIconic(toplevelWnd
)) {
2302 ::ShowWindow(toplevelWnd
, SW_RESTORE
);
2308 /**************************************************************
2312 * GetBounds, GetClientBounds, GetScreenBounds,
2313 * GetRestoredBounds, GetClientOffset, SetNonClientMargins
2315 * Bound calculations.
2317 **************************************************************/
2319 // Return the window's full dimensions in screen coordinates.
2320 // If the window has a parent, converts the origin to an offset
2321 // of the parent's screen origin.
2322 LayoutDeviceIntRect
nsWindow::GetBounds() {
2328 VERIFY(::GetWindowRect(mWnd
, &r
));
2330 LayoutDeviceIntRect rect
;
2333 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2335 // popup window bounds' are in screen coordinates, not relative to parent
2337 if (mWindowType
== WindowType::Popup
) {
2338 rect
.MoveTo(r
.left
, r
.top
);
2342 // chrome on parent:
2343 // ___ 5,5 (chrome start)
2344 // | ____ 10,10 (client start)
2345 // | | ____ 20,20 (child start)
2347 // 20,20 - 5,5 = 15,15 (??)
2348 // minus GetClientOffset:
2349 // 15,15 - 5,5 = 10,10
2351 // no chrome on parent:
2352 // ______ 10,10 (win start)
2353 // | ____ 20,20 (child start)
2355 // 20,20 - 10,10 = 10,10
2357 // walking the chain:
2358 // ___ 5,5 (chrome start)
2359 // | ___ 10,10 (client start)
2360 // | | ___ 20,20 (child start)
2361 // | | | __ 30,30 (child start)
2363 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2364 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2365 // minus GetClientOffset:
2366 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2368 // convert coordinates if parent exists
2369 HWND parent
= ::GetParent(mWnd
);
2372 VERIFY(::GetWindowRect(parent
, &pr
));
2375 // adjust for chrome
2376 nsWindow
* pWidget
= static_cast<nsWindow
*>(GetParent());
2377 if (pWidget
&& pWidget
->IsTopLevelWidget()) {
2378 LayoutDeviceIntPoint clientOffset
= pWidget
->GetClientOffset();
2379 r
.left
-= clientOffset
.x
;
2380 r
.top
-= clientOffset
.y
;
2383 rect
.MoveTo(r
.left
, r
.top
);
2384 if (mCompositorSession
&&
2385 !wr::WindowSizeSanityCheck(rect
.width
, rect
.height
)) {
2386 gfxCriticalNoteOnce
<< "Invalid size" << rect
<< " size mode "
2387 << mFrameState
->GetSizeMode();
2393 // Get this component dimension
2394 LayoutDeviceIntRect
nsWindow::GetClientBounds() {
2396 return LayoutDeviceIntRect(0, 0, 0, 0);
2400 if (!::GetClientRect(mWnd
, &r
)) {
2401 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
2402 gfxCriticalNoteOnce
<< "GetClientRect failed " << ::GetLastError();
2406 LayoutDeviceIntRect bounds
= GetBounds();
2407 LayoutDeviceIntRect rect
;
2408 rect
.MoveTo(bounds
.TopLeft() + GetClientOffset());
2409 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2413 // Like GetBounds, but don't offset by the parent
2414 LayoutDeviceIntRect
nsWindow::GetScreenBounds() {
2420 VERIFY(::GetWindowRect(mWnd
, &r
));
2422 return LayoutDeviceIntRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2425 nsresult
nsWindow::GetRestoredBounds(LayoutDeviceIntRect
& aRect
) {
2426 if (SizeMode() == nsSizeMode_Normal
) {
2427 aRect
= GetScreenBounds();
2431 return NS_ERROR_FAILURE
;
2434 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2435 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2436 const RECT
& r
= pl
.rcNormalPosition
;
2438 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2440 return NS_ERROR_FAILURE
;
2442 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2443 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2445 aRect
.SetRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2446 aRect
.MoveBy(mi
.rcWork
.left
- mi
.rcMonitor
.left
,
2447 mi
.rcWork
.top
- mi
.rcMonitor
.top
);
2451 // Return the x,y offset of the client area from the origin of the window. If
2452 // the window is borderless returns (0,0).
2453 LayoutDeviceIntPoint
nsWindow::GetClientOffset() {
2455 return LayoutDeviceIntPoint(0, 0);
2459 GetWindowRect(mWnd
, &r1
);
2460 LayoutDeviceIntPoint pt
= WidgetToScreenOffset();
2461 return LayoutDeviceIntPoint(pt
.x
- LayoutDeviceIntCoord(r1
.left
),
2462 pt
.y
- LayoutDeviceIntCoord(r1
.top
));
2465 void nsWindow::ResetLayout() {
2466 // This will trigger a frame changed event, triggering
2467 // nc calc size and a sizemode gecko event.
2468 SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
2469 SWP_FRAMECHANGED
| SWP_NOACTIVATE
| SWP_NOMOVE
|
2470 SWP_NOOWNERZORDER
| SWP_NOSIZE
| SWP_NOZORDER
);
2472 // If hidden, just send the frame changed event for now.
2477 // Send a gecko size event to trigger reflow.
2478 RECT clientRc
= {0};
2479 GetClientRect(mWnd
, &clientRc
);
2480 OnResize(WinUtils::ToIntRect(clientRc
).Size());
2482 // Invalidate and update
2486 // Internally track the caption status via a window property. Required
2487 // due to our internal handling of WM_NCACTIVATE when custom client
2489 static const wchar_t kManageWindowInfoProperty
[] = L
"ManageWindowInfoProperty";
2490 typedef BOOL(WINAPI
* GetWindowInfoPtr
)(HWND hwnd
, PWINDOWINFO pwi
);
2491 static WindowsDllInterceptor::FuncHookType
<GetWindowInfoPtr
>
2492 sGetWindowInfoPtrStub
;
2494 BOOL WINAPI
GetWindowInfoHook(HWND hWnd
, PWINDOWINFO pwi
) {
2495 if (!sGetWindowInfoPtrStub
) {
2496 NS_ASSERTION(FALSE
, "Something is horribly wrong in GetWindowInfoHook!");
2500 reinterpret_cast<LONG_PTR
>(GetPropW(hWnd
, kManageWindowInfoProperty
));
2501 // No property set, return the default data.
2502 if (!windowStatus
) return sGetWindowInfoPtrStub(hWnd
, pwi
);
2503 // Call GetWindowInfo and update dwWindowStatus with our
2504 // internally tracked value.
2505 BOOL result
= sGetWindowInfoPtrStub(hWnd
, pwi
);
2507 pwi
->dwWindowStatus
= (windowStatus
== 1 ? 0 : WS_ACTIVECAPTION
);
2511 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption
) {
2514 sUser32Intercept
.Init("user32.dll");
2515 sGetWindowInfoPtrStub
.Set(sUser32Intercept
, "GetWindowInfo",
2516 &GetWindowInfoHook
);
2517 if (!sGetWindowInfoPtrStub
) {
2521 // Update our internally tracked caption status
2522 SetPropW(mWnd
, kManageWindowInfoProperty
,
2523 reinterpret_cast<HANDLE
>(static_cast<INT_PTR
>(aActiveCaption
) + 1));
2526 #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
2527 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
2529 void nsWindow::UpdateDarkModeToolbar() {
2530 PreferenceSheet::EnsureInitialized();
2531 BOOL dark
= PreferenceSheet::ColorSchemeForChrome() == ColorScheme::Dark
;
2532 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
, &dark
,
2534 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE
, &dark
,
2538 LayoutDeviceIntMargin
nsWindow::NormalWindowNonClientOffset() const {
2539 LayoutDeviceIntMargin nonClientOffset
;
2541 // We're dealing with a "normal" window (not maximized, minimized, or
2542 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2545 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2546 // frame intact. Setting it to a value greater than 0 reduces the frame
2547 // size by that amount.
2549 if (mNonClientMargins
.top
> 0) {
2550 nonClientOffset
.top
= std::min(mCaptionHeight
, mNonClientMargins
.top
);
2551 } else if (mNonClientMargins
.top
== 0) {
2552 nonClientOffset
.top
= mCaptionHeight
;
2554 nonClientOffset
.top
= 0;
2557 if (mNonClientMargins
.bottom
> 0) {
2558 nonClientOffset
.bottom
=
2559 std::min(mVertResizeMargin
, mNonClientMargins
.bottom
);
2560 } else if (mNonClientMargins
.bottom
== 0) {
2561 nonClientOffset
.bottom
= mVertResizeMargin
;
2563 nonClientOffset
.bottom
= 0;
2566 if (mNonClientMargins
.left
> 0) {
2567 nonClientOffset
.left
= std::min(mHorResizeMargin
, mNonClientMargins
.left
);
2568 } else if (mNonClientMargins
.left
== 0) {
2569 nonClientOffset
.left
= mHorResizeMargin
;
2571 nonClientOffset
.left
= 0;
2574 if (mNonClientMargins
.right
> 0) {
2575 nonClientOffset
.right
= std::min(mHorResizeMargin
, mNonClientMargins
.right
);
2576 } else if (mNonClientMargins
.right
== 0) {
2577 nonClientOffset
.right
= mHorResizeMargin
;
2579 nonClientOffset
.right
= 0;
2581 return nonClientOffset
;
2585 * Called when the window layout changes: full screen mode transitions,
2586 * theme changes, and composition changes. Calculates the new non-client
2587 * margins and fires off a frame changed event, which triggers an nc calc
2588 * size windows event, kicking the changes in.
2590 * The offsets calculated here are based on the value of `mNonClientMargins`
2591 * which is specified in the "chromemargins" attribute of the window. For
2592 * each margin, the value specified has the following meaning:
2593 * -1 - leave the default frame in place
2594 * 0 - remove the frame
2595 * >0 - frame size equals min(0, (default frame size - margin value))
2597 * This function calculates and populates `mNonClientOffset`.
2598 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2599 * as (default frame size - offset). For example, if the left frame should
2600 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2603 * For maximized, fullscreen, and minimized windows, the values stored in
2604 * `mNonClientMargins` are ignored, and special processing takes place.
2606 * For non-glass windows, we only allow frames to be their default size
2607 * or removed entirely.
2609 bool nsWindow::UpdateNonClientMargins(bool aReflowWindow
) {
2610 if (!mCustomNonClient
) {
2614 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
2617 bool(mBorderStyle
& (BorderStyle::All
| BorderStyle::Title
|
2618 BorderStyle::Menu
| BorderStyle::Default
));
2620 float dpi
= GetDPI();
2622 // mCaptionHeight is the default size of the NC area at
2623 // the top of the window. If the window has a caption,
2624 // the size is calculated as the sum of:
2625 // SM_CYFRAME - The thickness of the sizing border
2626 // around a resizable window
2627 // SM_CXPADDEDBORDER - The amount of border padding
2628 // for captioned windows
2629 // SM_CYCAPTION - The height of the caption area
2631 // If the window does not have a caption, mCaptionHeight will be equal to
2632 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2634 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2635 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CYCAPTION
, dpi
) +
2636 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2638 if (!mUseResizeMarginOverrides
) {
2639 // mHorResizeMargin is the size of the default NC areas on the
2640 // left and right sides of our window. It is calculated as
2642 // SM_CXFRAME - The thickness of the sizing border
2643 // SM_CXPADDEDBORDER - The amount of border padding
2644 // for captioned windows
2646 // If the window does not have a caption, mHorResizeMargin will be equal to
2647 // `WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)`
2649 WinUtils::GetSystemMetricsForDpi(SM_CXFRAME
, dpi
) +
2650 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2653 // mVertResizeMargin is the size of the default NC area at the
2654 // bottom of the window. It is calculated as the sum of:
2655 // SM_CYFRAME - The thickness of the sizing border
2656 // SM_CXPADDEDBORDER - The amount of border padding
2657 // for captioned windows.
2659 // If the window does not have a caption, mVertResizeMargin will be equal to
2660 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2662 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2663 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2667 if (sizeMode
== nsSizeMode_Minimized
) {
2668 // Use default frame size for minimized windows
2669 mNonClientOffset
.top
= 0;
2670 mNonClientOffset
.left
= 0;
2671 mNonClientOffset
.right
= 0;
2672 mNonClientOffset
.bottom
= 0;
2673 } else if (sizeMode
== nsSizeMode_Fullscreen
) {
2674 // Remove the default frame from the top of our fullscreen window. This
2675 // makes the whole caption part of our client area, allowing us to draw
2676 // in the whole caption area. Additionally remove the default frame from
2677 // the left, right, and bottom.
2678 mNonClientOffset
.top
= mCaptionHeight
;
2679 mNonClientOffset
.bottom
= mVertResizeMargin
;
2680 mNonClientOffset
.left
= mHorResizeMargin
;
2681 mNonClientOffset
.right
= mHorResizeMargin
;
2682 } else if (sizeMode
== nsSizeMode_Maximized
) {
2683 // We make the entire frame part of the client area. We leave the default
2684 // frame sizes for left, right and bottom since Windows will automagically
2685 // position the edges "offscreen" for maximized windows.
2686 int verticalResize
=
2687 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2688 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2691 mNonClientOffset
.top
= mCaptionHeight
- verticalResize
;
2692 mNonClientOffset
.bottom
= 0;
2693 mNonClientOffset
.left
= 0;
2694 mNonClientOffset
.right
= 0;
2696 mozilla::Maybe
<UINT
> maybeEdge
= GetHiddenTaskbarEdge();
2698 auto edge
= maybeEdge
.value();
2699 if (ABE_LEFT
== edge
) {
2700 mNonClientOffset
.left
-= kHiddenTaskbarSize
;
2701 } else if (ABE_RIGHT
== edge
) {
2702 mNonClientOffset
.right
-= kHiddenTaskbarSize
;
2703 } else if (ABE_BOTTOM
== edge
|| ABE_TOP
== edge
) {
2704 mNonClientOffset
.bottom
-= kHiddenTaskbarSize
;
2707 // When we are drawing the non-client region, we need
2708 // to clear the portion of the NC region that is exposed by the
2709 // hidden taskbar. As above, we clear the bottom of the NC region
2710 // when the taskbar is at the top of the screen.
2711 UINT clearEdge
= (edge
== ABE_TOP
) ? ABE_BOTTOM
: edge
;
2712 mClearNCEdge
= Some(clearEdge
);
2715 mNonClientOffset
= NormalWindowNonClientOffset();
2718 if (aReflowWindow
) {
2719 // Force a reflow of content based on the new client
2727 nsresult
nsWindow::SetNonClientMargins(const LayoutDeviceIntMargin
& margins
) {
2728 if (!mIsTopWidgetWindow
|| mBorderStyle
== BorderStyle::None
)
2729 return NS_ERROR_INVALID_ARG
;
2732 mFutureMarginsOnceChromeShows
= margins
;
2733 mFutureMarginsToUse
= true;
2736 mFutureMarginsToUse
= false;
2738 // Request for a reset
2739 if (margins
.top
== -1 && margins
.left
== -1 && margins
.right
== -1 &&
2740 margins
.bottom
== -1) {
2741 mCustomNonClient
= false;
2742 mNonClientMargins
= margins
;
2743 // Force a reflow of content based on the new client
2748 reinterpret_cast<LONG_PTR
>(GetPropW(mWnd
, kManageWindowInfoProperty
));
2750 ::SendMessageW(mWnd
, WM_NCACTIVATE
, 1 != windowStatus
, 0);
2756 if (margins
.top
< -1 || margins
.bottom
< -1 || margins
.left
< -1 ||
2758 return NS_ERROR_INVALID_ARG
;
2760 mNonClientMargins
= margins
;
2761 mCustomNonClient
= true;
2762 if (!UpdateNonClientMargins()) {
2763 NS_WARNING("UpdateNonClientMargins failed!");
2770 void nsWindow::SetResizeMargin(mozilla::LayoutDeviceIntCoord aResizeMargin
) {
2771 mUseResizeMarginOverrides
= true;
2772 mHorResizeMargin
= aResizeMargin
;
2773 mVertResizeMargin
= aResizeMargin
;
2774 UpdateNonClientMargins();
2777 void nsWindow::InvalidateNonClientRegion() {
2778 // +-+-----------------------+-+
2779 // | | app non-client chrome | |
2780 // | +-----------------------+ |
2781 // | | app client chrome | | }
2782 // | +-----------------------+ | }
2783 // | | app content | | } area we don't want to invalidate
2784 // | +-----------------------+ | }
2785 // | | app client chrome | | }
2786 // | +-----------------------+ |
2787 // +---------------------------+ <
2788 // ^ ^ windows non-client chrome
2789 // client area = app *
2791 GetWindowRect(mWnd
, &rect
);
2792 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2793 HRGN winRgn
= CreateRectRgnIndirect(&rect
);
2795 // Subtract app client chrome and app content leaving
2796 // windows non-client chrome and app non-client chrome
2798 GetWindowRect(mWnd
, &rect
);
2799 rect
.top
+= mCaptionHeight
;
2800 rect
.right
-= mHorResizeMargin
;
2801 rect
.bottom
-= mVertResizeMargin
;
2802 rect
.left
+= mHorResizeMargin
;
2803 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2804 HRGN clientRgn
= CreateRectRgnIndirect(&rect
);
2805 CombineRgn(winRgn
, winRgn
, clientRgn
, RGN_DIFF
);
2806 DeleteObject(clientRgn
);
2808 // triggers ncpaint and paint events for the two areas
2809 RedrawWindow(mWnd
, nullptr, winRgn
, RDW_FRAME
| RDW_INVALIDATE
);
2810 DeleteObject(winRgn
);
2813 /**************************************************************
2815 * SECTION: nsIWidget::SetBackgroundColor
2817 * Sets the window background paint color.
2819 **************************************************************/
2821 void nsWindow::SetBackgroundColor(const nscolor
& aColor
) {
2822 if (mBrush
) ::DeleteObject(mBrush
);
2824 mBrush
= ::CreateSolidBrush(NSRGB_2_COLOREF(aColor
));
2825 if (mWnd
!= nullptr) {
2826 ::SetClassLongPtrW(mWnd
, GCLP_HBRBACKGROUND
, (LONG_PTR
)mBrush
);
2830 /**************************************************************
2832 * SECTION: nsIWidget::SetCursor
2834 * SetCursor and related utilities for manging cursor state.
2836 **************************************************************/
2838 // Set this component cursor
2839 static HCURSOR
CursorFor(nsCursor aCursor
) {
2841 case eCursor_select
:
2842 return ::LoadCursor(nullptr, IDC_IBEAM
);
2844 return ::LoadCursor(nullptr, IDC_WAIT
);
2845 case eCursor_hyperlink
:
2846 return ::LoadCursor(nullptr, IDC_HAND
);
2847 case eCursor_standard
:
2848 case eCursor_context_menu
: // XXX See bug 258960.
2849 return ::LoadCursor(nullptr, IDC_ARROW
);
2851 case eCursor_n_resize
:
2852 case eCursor_s_resize
:
2853 return ::LoadCursor(nullptr, IDC_SIZENS
);
2855 case eCursor_w_resize
:
2856 case eCursor_e_resize
:
2857 return ::LoadCursor(nullptr, IDC_SIZEWE
);
2859 case eCursor_nw_resize
:
2860 case eCursor_se_resize
:
2861 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
2863 case eCursor_ne_resize
:
2864 case eCursor_sw_resize
:
2865 return ::LoadCursor(nullptr, IDC_SIZENESW
);
2867 case eCursor_crosshair
:
2868 return ::LoadCursor(nullptr, IDC_CROSS
);
2871 return ::LoadCursor(nullptr, IDC_SIZEALL
);
2874 return ::LoadCursor(nullptr, IDC_HELP
);
2876 case eCursor_copy
: // CSS3
2877 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_COPY
));
2880 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ALIAS
));
2883 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_CELL
));
2885 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_GRAB
));
2887 case eCursor_grabbing
:
2888 return ::LoadCursor(nsToolkit::mDllInstance
,
2889 MAKEINTRESOURCE(IDC_GRABBING
));
2891 case eCursor_spinning
:
2892 return ::LoadCursor(nullptr, IDC_APPSTARTING
);
2894 case eCursor_zoom_in
:
2895 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ZOOMIN
));
2897 case eCursor_zoom_out
:
2898 return ::LoadCursor(nsToolkit::mDllInstance
,
2899 MAKEINTRESOURCE(IDC_ZOOMOUT
));
2901 case eCursor_not_allowed
:
2902 case eCursor_no_drop
:
2903 return ::LoadCursor(nullptr, IDC_NO
);
2905 case eCursor_col_resize
:
2906 return ::LoadCursor(nsToolkit::mDllInstance
,
2907 MAKEINTRESOURCE(IDC_COLRESIZE
));
2909 case eCursor_row_resize
:
2910 return ::LoadCursor(nsToolkit::mDllInstance
,
2911 MAKEINTRESOURCE(IDC_ROWRESIZE
));
2913 case eCursor_vertical_text
:
2914 return ::LoadCursor(nsToolkit::mDllInstance
,
2915 MAKEINTRESOURCE(IDC_VERTICALTEXT
));
2917 case eCursor_all_scroll
:
2918 // XXX not 100% appropriate perhaps
2919 return ::LoadCursor(nullptr, IDC_SIZEALL
);
2921 case eCursor_nesw_resize
:
2922 return ::LoadCursor(nullptr, IDC_SIZENESW
);
2924 case eCursor_nwse_resize
:
2925 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
2927 case eCursor_ns_resize
:
2928 return ::LoadCursor(nullptr, IDC_SIZENS
);
2930 case eCursor_ew_resize
:
2931 return ::LoadCursor(nullptr, IDC_SIZEWE
);
2934 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_NONE
));
2937 NS_ERROR("Invalid cursor type");
2942 static HCURSOR
CursorForImage(const nsIWidget::Cursor
& aCursor
,
2943 CSSToLayoutDeviceScale aScale
) {
2944 if (!aCursor
.IsCustom()) {
2948 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
2950 // Reject cursors greater than 128 pixels in either direction, to prevent
2952 // XXX ideally we should rescale. Also, we could modify the API to
2953 // allow trusted content to set larger cursors.
2954 if (size
.width
> 128 || size
.height
> 128) {
2958 LayoutDeviceIntSize layoutSize
=
2959 RoundedToInt(CSSIntSize(size
.width
, size
.height
) * aScale
);
2960 LayoutDeviceIntPoint hotspot
=
2961 RoundedToInt(CSSIntPoint(aCursor
.mHotspotX
, aCursor
.mHotspotY
) * aScale
);
2963 nsresult rv
= nsWindowGfx::CreateIcon(aCursor
.mContainer
, true, hotspot
,
2964 layoutSize
, &cursor
);
2965 if (NS_FAILED(rv
)) {
2972 void nsWindow::SetCursor(const Cursor
& aCursor
) {
2973 static HCURSOR sCurrentHCursor
= nullptr;
2974 static bool sCurrentHCursorIsCustom
= false;
2978 if (sCurrentCursor
== aCursor
&& sCurrentHCursor
&& !mUpdateCursor
) {
2979 // Cursors in windows are global, so even if our mUpdateCursor flag is
2980 // false we always need to make sure the Windows cursor is up-to-date,
2981 // since stuff like native drag and drop / resizers code can mutate it
2982 // outside of this method.
2983 ::SetCursor(sCurrentHCursor
);
2987 mUpdateCursor
= false;
2989 if (sCurrentHCursorIsCustom
) {
2990 ::DestroyIcon(sCurrentHCursor
);
2992 sCurrentHCursor
= nullptr;
2993 sCurrentHCursorIsCustom
= false;
2994 sCurrentCursor
= aCursor
;
2996 HCURSOR cursor
= nullptr;
2997 if (mCustomCursorAllowed
) {
2998 cursor
= CursorForImage(aCursor
, GetDefaultScale());
3000 bool custom
= false;
3004 cursor
= CursorFor(aCursor
.mDefaultCursor
);
3011 sCurrentHCursor
= cursor
;
3012 sCurrentHCursorIsCustom
= custom
;
3013 ::SetCursor(cursor
);
3016 /**************************************************************
3018 * SECTION: nsIWidget::Get/SetTransparencyMode
3020 * Manage the transparency mode of the window containing this
3021 * widget. Only works for popup and dialog windows when the
3022 * Desktop Window Manager compositor is not enabled.
3024 **************************************************************/
3026 TransparencyMode
nsWindow::GetTransparencyMode() {
3027 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
3030 void nsWindow::SetTransparencyMode(TransparencyMode aMode
) {
3031 nsWindow
* window
= GetTopLevelWindow(true);
3034 if (!window
|| window
->DestroyCalled()) {
3038 window
->SetWindowTranslucencyInner(aMode
);
3041 /**************************************************************
3043 * SECTION: nsIWidget::UpdateWindowDraggingRegion
3045 * For setting the draggable titlebar region from CSS
3046 * with -moz-window-dragging: drag.
3048 **************************************************************/
3050 void nsWindow::UpdateWindowDraggingRegion(
3051 const LayoutDeviceIntRegion
& aRegion
) {
3052 mDraggableRegion
= aRegion
;
3055 /**************************************************************
3057 * SECTION: nsIWidget::HideWindowChrome
3059 * Show or hide window chrome.
3061 **************************************************************/
3063 void nsWindow::HideWindowChrome(bool aShouldHide
) {
3064 HWND hwnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
3065 if (!WinUtils::GetNSWindowPtr(hwnd
)) {
3066 NS_WARNING("Trying to hide window decorations in an embedded context");
3070 if (mHideChrome
== aShouldHide
) return;
3072 DWORD_PTR style
, exStyle
;
3073 mHideChrome
= aShouldHide
;
3075 DWORD_PTR tempStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3076 DWORD_PTR tempExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3078 style
= tempStyle
& ~(WS_CAPTION
| WS_THICKFRAME
);
3079 exStyle
= tempExStyle
& ~(WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
|
3080 WS_EX_CLIENTEDGE
| WS_EX_STATICEDGE
);
3082 mOldStyle
= tempStyle
;
3083 mOldExStyle
= tempExStyle
;
3085 if (!mOldStyle
|| !mOldExStyle
) {
3086 mOldStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3087 mOldExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3091 exStyle
= mOldExStyle
;
3092 if (mFutureMarginsToUse
) {
3093 SetNonClientMargins(mFutureMarginsOnceChromeShows
);
3097 VERIFY_WINDOW_STYLE(style
);
3098 ::SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
3099 ::SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exStyle
);
3102 /**************************************************************
3104 * SECTION: nsWindow::Invalidate
3106 * Invalidate an area of the client for painting.
3108 **************************************************************/
3110 // Invalidate this component visible area
3111 void nsWindow::Invalidate(bool aEraseBackground
, bool aUpdateNCArea
,
3112 bool aIncludeChildren
) {
3117 #ifdef WIDGET_DEBUG_OUTPUT
3118 debug_DumpInvalidate(stdout
, this, nullptr, "noname", (int32_t)mWnd
);
3119 #endif // WIDGET_DEBUG_OUTPUT
3121 DWORD flags
= RDW_INVALIDATE
;
3122 if (aEraseBackground
) {
3125 if (aUpdateNCArea
) {
3128 if (aIncludeChildren
) {
3129 flags
|= RDW_ALLCHILDREN
;
3132 VERIFY(::RedrawWindow(mWnd
, nullptr, nullptr, flags
));
3135 // Invalidate this component visible area
3136 void nsWindow::Invalidate(const LayoutDeviceIntRect
& aRect
) {
3138 #ifdef WIDGET_DEBUG_OUTPUT
3139 debug_DumpInvalidate(stdout
, this, &aRect
, "noname", (int32_t)mWnd
);
3140 #endif // WIDGET_DEBUG_OUTPUT
3144 rect
.left
= aRect
.X();
3145 rect
.top
= aRect
.Y();
3146 rect
.right
= aRect
.XMost();
3147 rect
.bottom
= aRect
.YMost();
3149 VERIFY(::InvalidateRect(mWnd
, &rect
, FALSE
));
3153 static LRESULT CALLBACK
FullscreenTransitionWindowProc(HWND hWnd
, UINT uMsg
,
3157 case WM_FULLSCREEN_TRANSITION_BEFORE
:
3158 case WM_FULLSCREEN_TRANSITION_AFTER
: {
3159 DWORD duration
= (DWORD
)lParam
;
3160 DWORD flags
= AW_BLEND
;
3161 if (uMsg
== WM_FULLSCREEN_TRANSITION_AFTER
) {
3164 ::AnimateWindow(hWnd
, duration
, flags
);
3165 // The message sender should have added ref for us.
3166 NS_DispatchToMainThread(
3167 already_AddRefed
<nsIRunnable
>((nsIRunnable
*)wParam
));
3171 ::PostQuitMessage(0);
3174 return ::DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3179 struct FullscreenTransitionInitData
{
3180 LayoutDeviceIntRect mBounds
;
3185 FullscreenTransitionInitData()
3186 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3188 ~FullscreenTransitionInitData() {
3190 ::CloseHandle(mSemaphore
);
3193 ::CloseHandle(mThread
);
3198 static DWORD WINAPI
FullscreenTransitionThreadProc(LPVOID lpParam
) {
3199 // Initialize window class
3200 static bool sInitialized
= false;
3201 if (!sInitialized
) {
3203 wc
.lpfnWndProc
= ::FullscreenTransitionWindowProc
;
3204 wc
.hInstance
= nsToolkit::mDllInstance
;
3205 wc
.hbrBackground
= ::CreateSolidBrush(RGB(0, 0, 0));
3206 wc
.lpszClassName
= kClassNameTransition
;
3207 ::RegisterClassW(&wc
);
3208 sInitialized
= true;
3211 auto data
= static_cast<FullscreenTransitionInitData
*>(lpParam
);
3212 HWND wnd
= ::CreateWindowW(kClassNameTransition
, L
"", 0, 0, 0, 0, 0, nullptr,
3213 nullptr, nsToolkit::mDllInstance
, nullptr);
3215 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3219 // Since AnimateWindow blocks the thread of the transition window,
3220 // we need to hide the cursor for that window, otherwise the system
3221 // would show the busy pointer to the user.
3222 ::ShowCursor(false);
3223 ::SetWindowLongW(wnd
, GWL_STYLE
, 0);
3226 WS_EX_LAYERED
| WS_EX_TRANSPARENT
| WS_EX_TOOLWINDOW
| WS_EX_NOACTIVATE
);
3227 ::SetWindowPos(wnd
, HWND_TOPMOST
, data
->mBounds
.X(), data
->mBounds
.Y(),
3228 data
->mBounds
.Width(), data
->mBounds
.Height(), 0);
3230 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3231 // The initialization data may no longer be valid
3232 // after we release the semaphore.
3236 while (::GetMessageW(&msg
, nullptr, 0, 0)) {
3237 ::TranslateMessage(&msg
);
3238 ::DispatchMessage(&msg
);
3241 ::DestroyWindow(wnd
);
3245 class FullscreenTransitionData final
: public nsISupports
{
3249 explicit FullscreenTransitionData(HWND aWnd
) : mWnd(aWnd
) {
3250 MOZ_ASSERT(NS_IsMainThread(),
3251 "FullscreenTransitionData "
3252 "should be constructed in the main thread");
3258 ~FullscreenTransitionData() {
3259 MOZ_ASSERT(NS_IsMainThread(),
3260 "FullscreenTransitionData "
3261 "should be deconstructed in the main thread");
3262 ::PostMessageW(mWnd
, WM_DESTROY
, 0, 0);
3266 NS_IMPL_ISUPPORTS0(FullscreenTransitionData
)
3269 bool nsWindow::PrepareForFullscreenTransition(nsISupports
** aData
) {
3270 FullscreenTransitionInitData initData
;
3271 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
3272 const DesktopIntRect rect
= screen
->GetRectDisplayPix();
3273 MOZ_ASSERT(BoundsUseDesktopPixels(),
3274 "Should only be called on top-level window");
3276 LayoutDeviceIntRect::Round(rect
* GetDesktopToDeviceScale());
3278 // Create a semaphore for synchronizing the window handle which will
3279 // be created by the transition thread and used by the main thread for
3280 // posting the transition messages.
3281 initData
.mSemaphore
= ::CreateSemaphore(nullptr, 0, 1, nullptr);
3282 if (initData
.mSemaphore
) {
3283 initData
.mThread
= ::CreateThread(
3284 nullptr, 0, FullscreenTransitionThreadProc
, &initData
, 0, nullptr);
3285 if (initData
.mThread
) {
3286 ::WaitForSingleObject(initData
.mSemaphore
, INFINITE
);
3289 if (!initData
.mWnd
) {
3293 mTransitionWnd
= initData
.mWnd
;
3295 auto data
= new FullscreenTransitionData(initData
.mWnd
);
3302 void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage
,
3305 nsIRunnable
* aCallback
) {
3306 auto data
= static_cast<FullscreenTransitionData
*>(aData
);
3307 nsCOMPtr
<nsIRunnable
> callback
= aCallback
;
3308 UINT msg
= aStage
== eBeforeFullscreenToggle
? WM_FULLSCREEN_TRANSITION_BEFORE
3309 : WM_FULLSCREEN_TRANSITION_AFTER
;
3310 WPARAM wparam
= (WPARAM
)callback
.forget().take();
3311 ::PostMessage(data
->mWnd
, msg
, wparam
, (LPARAM
)aDuration
);
3315 void nsWindow::CleanupFullscreenTransition() {
3316 MOZ_ASSERT(NS_IsMainThread(),
3317 "CleanupFullscreenTransition "
3318 "should only run on the main thread");
3320 mTransitionWnd
= nullptr;
3323 void nsWindow::TryDwmResizeHack() {
3324 // The "DWM resize hack", aka the "fullscreen resize hack", is a workaround
3325 // for DWM's occasional and not-entirely-predictable failure to update its
3326 // internal state when the client area of a window changes without changing
3327 // the window size. The effect of this is that DWM will clip the content of
3328 // the window to its former client area.
3330 // It is not known under what circumstances the bug will trigger. Windows 11
3331 // is known to be required, but many Windows 11 machines do not exhibit the
3332 // issue. Even machines that _do_ exhibit it will sometimes not do so when
3333 // apparently-irrelevant changes are made to the configuration. (See bug
3336 // The bug is triggered by Firefox when a maximized window (which has window
3337 // decorations) becomes fullscreen (which doesn't). To work around this, if we
3338 // think it may occur, we "flicker-resize" the relevant window -- that is, we
3339 // reduce its height by 1px, then restore it. This causes DWM to acquire the
3340 // new client-area metrics.
3342 // Note that, in particular, this bug will not occur when using a separate
3343 // compositor window, as our compositor windows never have any nonclient area.
3345 // This is admittedly a sledgehammer where a screwdriver should suffice.
3347 // ---------------------------------------------------------------------------
3349 // Regardless of preferences or heuristics, only apply the hack if this is the
3350 // first time we've entered fullscreen across the entire Firefox session.
3351 // (Subsequent transitions to fullscreen, even with different windows, don't
3352 // appear to induce the bug.)
3354 // (main thread only; `atomic` not needed)
3355 static bool sIsFirstFullscreenEntry
= true;
3356 bool isFirstFullscreenEntry
= sIsFirstFullscreenEntry
;
3357 sIsFirstFullscreenEntry
= false;
3358 if (MOZ_LIKELY(!isFirstFullscreenEntry
)) {
3361 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3362 ("%s: first fullscreen entry", __PRETTY_FUNCTION__
));
3365 // Check whether to try to apply the DWM resize hack, based on the override
3366 // pref and/or some internal heuristics.
3368 const auto hackApplicationHeuristics
= [&]() -> bool {
3369 // The bug has only been seen under Windows 11. (At time of writing, this
3370 // is the latest version of Windows.)
3371 if (!IsWin11OrLater()) {
3375 KnowsCompositor
const* const kc
= mWindowRenderer
->AsKnowsCompositor();
3376 // This should never happen...
3378 // ... so if it does, we are in uncharted territory: don't apply the hack.
3383 // The bug doesn't occur when we're using a separate compositor window
3384 // (since the compositor window always comprises exactly its client area,
3385 // with no non-client border).
3386 if (kc
->GetUseCompositorWnd()) {
3390 // Otherwise, apply the hack.
3394 // Figure out whether or not we should perform the hack, and -- arguably
3395 // more importantly -- log that decision.
3396 bool const shouldApplyHack
= [&]() {
3397 enum Reason
: bool { Pref
, Heuristics
};
3398 auto const msg
= [&](bool decision
, Reason reason
) -> bool {
3399 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3400 ("%s %s per %s", decision
? "applying" : "skipping",
3401 "DWM resize hack", reason
== Pref
? "pref" : "heuristics"));
3404 switch (StaticPrefs::widget_windows_apply_dwm_resize_hack()) {
3406 return msg(false, Pref
);
3408 return msg(true, Pref
);
3409 default: // treat all other values as `auto`
3410 return msg(hackApplicationHeuristics(), Heuristics
);
3414 if (!shouldApplyHack
) {
3419 // The DWM bug is believed to involve a race condition: some users have
3420 // reported that setting a custom theme or adding unused command-line
3421 // parameters sometimes causes the bug to vanish.
3423 // Out of an abundance of caution, we therefore apply the hack in a later
3424 // event, rather than inline.
3425 NS_DispatchToMainThread(NS_NewRunnableFunction(
3426 "nsWindow::TryFullscreenResizeHack", [self
= RefPtr(this)]() {
3427 HWND
const hwnd
= self
->GetWindowHandle();
3429 if (self
->mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
3430 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Info
,
3431 ("DWM resize hack: window no longer fullscreen; aborting"));
3436 if (!::GetWindowRect(hwnd
, &origRect
)) {
3437 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Error
,
3438 ("DWM resize hack: could not get window size?!"));
3441 LONG
const x
= origRect
.left
;
3442 LONG
const y
= origRect
.top
;
3443 LONG
const width
= origRect
.right
- origRect
.left
;
3444 LONG
const height
= origRect
.bottom
- origRect
.top
;
3446 MOZ_DIAGNOSTIC_ASSERT(!self
->mIsPerformingDwmFlushHack
);
3448 MakeScopeExit([&, oldVal
= self
->mIsPerformingDwmFlushHack
]() {
3449 self
->mIsPerformingDwmFlushHack
= oldVal
;
3451 self
->mIsPerformingDwmFlushHack
= true;
3453 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3454 ("beginning DWM resize hack for HWND %08" PRIXPTR
,
3456 ::MoveWindow(hwnd
, x
, y
, width
, height
- 1, FALSE
);
3457 ::MoveWindow(hwnd
, x
, y
, width
, height
, TRUE
);
3458 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3459 ("concluded DWM resize hack for HWND %08" PRIXPTR
,
3464 void nsWindow::OnFullscreenChanged(nsSizeMode aOldSizeMode
, bool aFullScreen
) {
3465 MOZ_ASSERT((aOldSizeMode
!= nsSizeMode_Fullscreen
) == aFullScreen
);
3467 // HACK: Potentially flicker-resize the window, to force DWM to get the right
3468 // client-area information.
3473 // Hide chrome and reposition window. Note this will also cache dimensions for
3474 // restoration, so it should only be called once per fullscreen request.
3476 // Don't do this when minimized, since our bounds make no sense then, nor when
3477 // coming back from that state.
3478 const bool toOrFromMinimized
=
3479 mFrameState
->GetSizeMode() == nsSizeMode_Minimized
||
3480 aOldSizeMode
== nsSizeMode_Minimized
;
3481 if (!toOrFromMinimized
) {
3482 InfallibleMakeFullScreen(aFullScreen
);
3485 // Possibly notify the taskbar that we have changed our fullscreen mode.
3486 TaskbarConcealer::OnFullscreenChanged(this, aFullScreen
);
3489 nsresult
nsWindow::MakeFullScreen(bool aFullScreen
) {
3490 mFrameState
->EnsureFullscreenMode(aFullScreen
);
3494 /**************************************************************
3496 * SECTION: Native data storage
3498 * nsIWidget::GetNativeData
3499 * nsIWidget::FreeNativeData
3501 * Set or clear native data based on a constant.
3503 **************************************************************/
3505 // Return some native data according to aDataType
3506 void* nsWindow::GetNativeData(uint32_t aDataType
) {
3507 switch (aDataType
) {
3508 case NS_NATIVE_WIDGET
:
3509 case NS_NATIVE_WINDOW
:
3510 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID
:
3512 case NS_NATIVE_GRAPHIC
:
3513 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3515 case NS_RAW_NATIVE_IME_CONTEXT
: {
3516 void* pseudoIMEContext
= GetPseudoIMEContext();
3517 if (pseudoIMEContext
) {
3518 return pseudoIMEContext
;
3522 case NS_NATIVE_TSF_THREAD_MGR
:
3523 case NS_NATIVE_TSF_CATEGORY_MGR
:
3524 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR
:
3525 return IMEHandler::GetNativeData(this, aDataType
);
3534 // Free some native data according to aDataType
3535 void nsWindow::FreeNativeData(void* data
, uint32_t aDataType
) {
3536 switch (aDataType
) {
3537 case NS_NATIVE_GRAPHIC
:
3538 case NS_NATIVE_WIDGET
:
3539 case NS_NATIVE_WINDOW
:
3546 /**************************************************************
3548 * SECTION: nsIWidget::SetTitle
3550 * Set the main windows title text.
3552 **************************************************************/
3554 nsresult
nsWindow::SetTitle(const nsAString
& aTitle
) {
3555 const nsString
& strTitle
= PromiseFlatString(aTitle
);
3556 AutoRestore
<bool> sendingText(mSendingSetText
);
3557 mSendingSetText
= true;
3558 ::SendMessageW(mWnd
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)(LPCWSTR
)strTitle
.get());
3562 /**************************************************************
3564 * SECTION: nsIWidget::SetIcon
3566 * Set the main windows icon.
3568 **************************************************************/
3570 void nsWindow::SetBigIcon(HICON aIcon
) {
3572 (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)aIcon
);
3574 ::DestroyIcon(icon
);
3580 void nsWindow::SetSmallIcon(HICON aIcon
) {
3581 HICON icon
= (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_SMALL
,
3584 ::DestroyIcon(icon
);
3590 void nsWindow::SetIcon(const nsAString
& aIconSpec
) {
3591 // Assume the given string is a local identifier for an icon file.
3593 nsCOMPtr
<nsIFile
> iconFile
;
3594 ResolveIconName(aIconSpec
, u
".ico"_ns
, getter_AddRefs(iconFile
));
3595 if (!iconFile
) return;
3597 nsAutoString iconPath
;
3598 iconFile
->GetPath(iconPath
);
3600 // XXX this should use MZLU (see bug 239279)
3605 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3606 ::GetSystemMetrics(SM_CXICON
),
3607 ::GetSystemMetrics(SM_CYICON
), LR_LOADFROMFILE
);
3609 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3610 ::GetSystemMetrics(SM_CXSMICON
),
3611 ::GetSystemMetrics(SM_CYSMICON
), LR_LOADFROMFILE
);
3614 SetBigIcon(bigIcon
);
3616 #ifdef DEBUG_SetIcon
3618 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3619 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3620 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3625 SetSmallIcon(smallIcon
);
3627 #ifdef DEBUG_SetIcon
3629 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3630 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3631 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3637 void nsWindow::SetBigIconNoData() {
3639 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3640 SetBigIcon(bigIcon
);
3643 void nsWindow::SetSmallIconNoData() {
3645 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3646 SetSmallIcon(smallIcon
);
3649 /**************************************************************
3651 * SECTION: nsIWidget::WidgetToScreenOffset
3653 * Return this widget's origin in screen coordinates.
3655 **************************************************************/
3657 LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset() {
3661 ::ClientToScreen(mWnd
, &point
);
3662 return LayoutDeviceIntPoint(point
.x
, point
.y
);
3665 LayoutDeviceIntMargin
nsWindow::ClientToWindowMargin() {
3666 if (mWindowType
== WindowType::Popup
) {
3670 if (mCustomNonClient
) {
3671 return NonClientSizeMargin(NormalWindowNonClientOffset());
3674 // Just use a dummy 200x200 at (200, 200) client rect as the rect.
3676 clientRect
.left
= 200;
3677 clientRect
.top
= 200;
3678 clientRect
.right
= 400;
3679 clientRect
.bottom
= 400;
3681 auto ToRect
= [](const RECT
& aRect
) -> LayoutDeviceIntRect
{
3682 return {aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
3683 aRect
.bottom
- aRect
.top
};
3686 RECT windowRect
= clientRect
;
3687 ::AdjustWindowRectEx(&windowRect
, WindowStyle(), false, WindowExStyle());
3689 return ToRect(windowRect
) - ToRect(clientRect
);
3692 /**************************************************************
3694 * SECTION: nsIWidget::EnableDragDrop
3696 * Enables/Disables drag and drop of files on this widget.
3698 **************************************************************/
3700 void nsWindow::EnableDragDrop(bool aEnable
) {
3702 // Return early if the window already closed
3707 if (!mNativeDragTarget
) {
3708 mNativeDragTarget
= new nsNativeDragTarget(this);
3709 mNativeDragTarget
->AddRef();
3710 ::RegisterDragDrop(mWnd
, (LPDROPTARGET
)mNativeDragTarget
);
3713 if (mWnd
&& mNativeDragTarget
) {
3714 ::RevokeDragDrop(mWnd
);
3715 mNativeDragTarget
->DragCancel();
3716 NS_RELEASE(mNativeDragTarget
);
3721 /**************************************************************
3723 * SECTION: nsIWidget::CaptureMouse
3725 * Enables/Disables system mouse capture.
3727 **************************************************************/
3729 void nsWindow::CaptureMouse(bool aCapture
) {
3730 TRACKMOUSEEVENT mTrack
;
3731 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3732 mTrack
.dwHoverTime
= 0;
3733 mTrack
.hwndTrack
= mWnd
;
3735 mTrack
.dwFlags
= TME_CANCEL
| TME_LEAVE
;
3738 mTrack
.dwFlags
= TME_LEAVE
;
3741 sIsInMouseCapture
= aCapture
;
3742 TrackMouseEvent(&mTrack
);
3745 /**************************************************************
3747 * SECTION: nsIWidget::CaptureRollupEvents
3749 * Dealing with event rollup on destroy for popups. Enables &
3750 * Disables system capture of any and all events that would
3751 * cause a dropdown to be rolled up.
3753 **************************************************************/
3755 void nsWindow::CaptureRollupEvents(bool aDoCapture
) {
3757 if (!sMsgFilterHook
&& !sCallProcHook
&& !sCallMouseHook
) {
3758 RegisterSpecialDropdownHooks();
3760 sProcessHook
= true;
3762 sProcessHook
= false;
3763 UnregisterSpecialDropdownHooks();
3767 /**************************************************************
3769 * SECTION: nsIWidget::GetAttention
3771 * Bring this window to the user's attention.
3773 **************************************************************/
3775 // Draw user's attention to this window until it comes to foreground.
3776 nsresult
nsWindow::GetAttention(int32_t aCycleCount
) {
3778 if (!mWnd
) return NS_ERROR_NOT_INITIALIZED
;
3780 HWND flashWnd
= WinUtils::GetTopLevelHWND(mWnd
, false, false);
3781 HWND fgWnd
= ::GetForegroundWindow();
3782 // Don't flash if the flash count is 0 or if the foreground window is our
3783 // window handle or that of our owned-most window.
3784 if (aCycleCount
== 0 || flashWnd
== fgWnd
||
3785 flashWnd
== WinUtils::GetTopLevelHWND(fgWnd
, false, false)) {
3789 DWORD defaultCycleCount
= 0;
3790 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT
, 0, &defaultCycleCount
, 0);
3792 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_ALL
,
3793 aCycleCount
> 0 ? aCycleCount
: defaultCycleCount
, 0};
3794 ::FlashWindowEx(&flashInfo
);
3799 void nsWindow::StopFlashing() {
3800 HWND flashWnd
= mWnd
;
3801 while (HWND ownerWnd
= ::GetWindow(flashWnd
, GW_OWNER
)) {
3802 flashWnd
= ownerWnd
;
3805 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_STOP
, 0, 0};
3806 ::FlashWindowEx(&flashInfo
);
3809 /**************************************************************
3811 * SECTION: nsIWidget::HasPendingInputEvent
3813 * Ask whether there user input events pending. All input events are
3814 * included, including those not targeted at this nsIwidget instance.
3816 **************************************************************/
3818 bool nsWindow::HasPendingInputEvent() {
3819 // If there is pending input or the user is currently
3820 // moving the window then return true.
3821 // Note: When the user is moving the window WIN32 spins
3822 // a separate event loop and input events are not
3823 // reported to the application.
3824 if (HIWORD(GetQueueStatus(QS_INPUT
))) return true;
3825 GUITHREADINFO guiInfo
;
3826 guiInfo
.cbSize
= sizeof(GUITHREADINFO
);
3827 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo
)) return false;
3828 return GUI_INMOVESIZE
== (guiInfo
.flags
& GUI_INMOVESIZE
);
3831 /**************************************************************
3833 * SECTION: nsIWidget::GetWindowRenderer
3835 * Get the window renderer associated with this widget.
3837 **************************************************************/
3839 WindowRenderer
* nsWindow::GetWindowRenderer() {
3840 if (mWindowRenderer
) {
3841 return mWindowRenderer
;
3844 if (!mLocalesChangedObserver
) {
3845 mLocalesChangedObserver
= new LocalesChangedObserver(this);
3849 if (!mWindowRenderer
&& ShouldUseOffMainThreadCompositing()) {
3850 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
3854 if (!mWindowRenderer
) {
3855 MOZ_ASSERT(!mCompositorSession
&& !mCompositorBridgeChild
);
3856 MOZ_ASSERT(!mCompositorWidgetDelegate
);
3858 // Ensure we have a widget proxy even if we're not using the compositor,
3859 // since all our transparent window handling lives there.
3860 WinCompositorWidgetInitData
initData(
3861 reinterpret_cast<uintptr_t>(mWnd
),
3862 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
3863 mTransparencyMode
, mFrameState
->GetSizeMode());
3864 // If we're not using the compositor, the options don't actually matter.
3865 CompositorOptions
options(false, false);
3866 mBasicLayersSurface
=
3867 new InProcessWinCompositorWidget(initData
, options
, this);
3868 mCompositorWidgetDelegate
= mBasicLayersSurface
;
3869 mWindowRenderer
= CreateFallbackRenderer();
3872 NS_ASSERTION(mWindowRenderer
, "Couldn't provide a valid window renderer.");
3874 if (mWindowRenderer
) {
3875 // Update the size constraints now that the layer manager has been
3877 KnowsCompositor
* knowsCompositor
= mWindowRenderer
->AsKnowsCompositor();
3878 if (knowsCompositor
) {
3879 SizeConstraints c
= mSizeConstraints
;
3880 mMaxTextureSize
= knowsCompositor
->GetMaxTextureSize();
3881 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
3882 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
3883 nsBaseWidget::SetSizeConstraints(c
);
3887 return mWindowRenderer
;
3890 /**************************************************************
3892 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
3894 * Called to connect the nsWindow to the delegate providing
3895 * platform compositing API access.
3897 **************************************************************/
3899 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate
* delegate
) {
3901 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
3902 MOZ_ASSERT(mCompositorWidgetDelegate
,
3903 "nsWindow::SetCompositorWidgetDelegate called with a "
3904 "non-PlatformCompositorWidgetDelegate");
3906 mCompositorWidgetDelegate
= nullptr;
3910 /**************************************************************
3912 * SECTION: nsIWidget::OnDefaultButtonLoaded
3914 * Called after the dialog is loaded and it has a default button.
3916 **************************************************************/
3918 nsresult
nsWindow::OnDefaultButtonLoaded(
3919 const LayoutDeviceIntRect
& aButtonRect
) {
3920 if (aButtonRect
.IsEmpty()) return NS_OK
;
3922 // Don't snap when we are not active.
3923 HWND activeWnd
= ::GetActiveWindow();
3924 if (activeWnd
!= ::GetForegroundWindow() ||
3925 WinUtils::GetTopLevelHWND(mWnd
, true) !=
3926 WinUtils::GetTopLevelHWND(activeWnd
, true)) {
3930 bool isAlwaysSnapCursor
=
3931 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
3933 if (!isAlwaysSnapCursor
) {
3934 BOOL snapDefaultButton
;
3935 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON
, 0, &snapDefaultButton
,
3941 LayoutDeviceIntRect widgetRect
= GetScreenBounds();
3942 LayoutDeviceIntRect
buttonRect(aButtonRect
+ widgetRect
.TopLeft());
3944 LayoutDeviceIntPoint
centerOfButton(buttonRect
.X() + buttonRect
.Width() / 2,
3945 buttonRect
.Y() + buttonRect
.Height() / 2);
3946 // The center of the button can be outside of the widget.
3947 // E.g., it could be hidden by scrolling.
3948 if (!widgetRect
.Contains(centerOfButton
)) {
3952 if (!::SetCursorPos(centerOfButton
.x
, centerOfButton
.y
)) {
3953 NS_ERROR("SetCursorPos failed");
3954 return NS_ERROR_FAILURE
;
3959 uint32_t nsWindow::GetMaxTouchPoints() const {
3960 return WinUtils::GetMaxTouchPoints();
3963 void nsWindow::SetWindowClass(const nsAString
& xulWinType
,
3964 const nsAString
& xulWinClass
,
3965 const nsAString
& xulWinName
) {
3966 mIsEarlyBlankWindow
= xulWinType
.EqualsLiteral("navigator:blank");
3969 /**************************************************************
3970 **************************************************************
3972 ** BLOCK: Moz Events
3974 ** Moz GUI event management.
3976 **************************************************************
3977 **************************************************************/
3979 /**************************************************************
3981 * SECTION: Mozilla event initialization
3983 * Helpers for initializing moz events.
3985 **************************************************************/
3987 // Event initialization
3988 void nsWindow::InitEvent(WidgetGUIEvent
& event
, LayoutDeviceIntPoint
* aPoint
) {
3989 if (nullptr == aPoint
) { // use the point from the event
3990 // get the message position in client coordinates
3991 if (mWnd
!= nullptr) {
3992 DWORD pos
= ::GetMessagePos();
3995 cpos
.x
= GET_X_LPARAM(pos
);
3996 cpos
.y
= GET_Y_LPARAM(pos
);
3998 ::ScreenToClient(mWnd
, &cpos
);
3999 event
.mRefPoint
= LayoutDeviceIntPoint(cpos
.x
, cpos
.y
);
4001 event
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
4004 // use the point override if provided
4005 event
.mRefPoint
= *aPoint
;
4008 event
.AssignEventTime(CurrentMessageWidgetEventTime());
4011 WidgetEventTime
nsWindow::CurrentMessageWidgetEventTime() const {
4012 LONG messageTime
= ::GetMessageTime();
4013 return WidgetEventTime(GetMessageTimeStamp(messageTime
));
4016 /**************************************************************
4018 * SECTION: Moz event dispatch helpers
4020 * Helpers for dispatching different types of moz events.
4022 **************************************************************/
4024 // Main event dispatch. Invokes callback and ProcessEvent method on
4025 // Event Listener object. Part of nsIWidget.
4026 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* event
,
4027 nsEventStatus
& aStatus
) {
4028 #ifdef WIDGET_DEBUG_OUTPUT
4029 debug_DumpEvent(stdout
, event
->mWidget
, event
, "something", (int32_t)mWnd
);
4030 #endif // WIDGET_DEBUG_OUTPUT
4032 aStatus
= nsEventStatus_eIgnore
;
4034 // Top level windows can have a view attached which requires events be sent
4035 // to the underlying base window and the view. Added when we combined the
4036 // base chrome window with the main content child for nc client area (title
4038 if (mAttachedWidgetListener
) {
4039 aStatus
= mAttachedWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4040 } else if (mWidgetListener
) {
4041 aStatus
= mWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4044 // the window can be destroyed during processing of seemingly innocuous events
4045 // like, say, mousedowns due to the magic of scripting. mousedowns will return
4046 // nsEventStatus_eIgnore, which causes problems with the deleted window.
4048 if (mOnDestroyCalled
) aStatus
= nsEventStatus_eConsumeNoDefault
;
4052 bool nsWindow::DispatchStandardEvent(EventMessage aMsg
) {
4053 WidgetGUIEvent
event(true, aMsg
, this);
4056 bool result
= DispatchWindowEvent(event
);
4060 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent
* event
) {
4061 nsEventStatus status
= DispatchInputEvent(event
).mContentStatus
;
4062 return ConvertStatus(status
);
4065 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent
* aEvent
) {
4066 nsEventStatus status
;
4067 DispatchEvent(aEvent
, status
);
4068 return ConvertStatus(status
);
4071 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent
* aEvent
) {
4072 nsEventStatus status
=
4073 DispatchInputEvent(aEvent
->AsInputEvent()).mContentStatus
;
4074 return ConvertStatus(status
);
4077 // Recursively dispatch synchronous paints for nsIWidget
4078 // descendants with invalidated rectangles.
4079 BOOL CALLBACK
nsWindow::DispatchStarvedPaints(HWND aWnd
, LPARAM aMsg
) {
4080 LONG_PTR proc
= ::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
4081 if (proc
== (LONG_PTR
)&nsWindow::WindowProc
) {
4082 // its one of our windows so check to see if it has a
4083 // invalidated rect. If it does. Dispatch a synchronous
4085 if (GetUpdateRect(aWnd
, nullptr, FALSE
)) VERIFY(::UpdateWindow(aWnd
));
4090 // Check for pending paints and dispatch any pending paint
4091 // messages for any nsIWidget which is a descendant of the
4092 // top-level window that *this* window is embedded within.
4094 // Note: We do not dispatch pending paint messages for non
4095 // nsIWidget managed windows.
4096 void nsWindow::DispatchPendingEvents() {
4097 // We need to ensure that reflow events do not get starved.
4098 // At the same time, we don't want to recurse through here
4099 // as that would prevent us from dispatching starved paints.
4100 static int recursionBlocker
= 0;
4101 if (recursionBlocker
++ == 0) {
4102 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4106 // Quickly check to see if there are any paint events pending,
4107 // but only dispatch them if it has been long enough since the
4108 // last paint completed.
4109 if (::GetQueueStatus(QS_PAINT
) &&
4110 ((TimeStamp::Now() - mLastPaintEndTime
).ToMilliseconds() >= 50)) {
4111 // Find the top level window.
4112 HWND topWnd
= WinUtils::GetTopLevelHWND(mWnd
);
4114 // Dispatch pending paints for topWnd and all its descendant windows.
4115 // Note: EnumChildWindows enumerates all descendant windows not just
4116 // the children (but not the window itself).
4117 nsWindow::DispatchStarvedPaints(topWnd
, 0);
4118 ::EnumChildWindows(topWnd
, nsWindow::DispatchStarvedPaints
, 0);
4122 void nsWindow::DispatchCustomEvent(const nsString
& eventName
) {
4123 if (Document
* doc
= GetDocument()) {
4124 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
4125 win
->DispatchCustomEvent(eventName
, ChromeOnlyDispatch::eYes
);
4130 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage
,
4131 LayoutDeviceIntPoint aEventPoint
) {
4132 // Allow users to start dragging by double-tapping.
4133 if (aEventMessage
== eMouseDoubleClick
) {
4137 // In chrome UI, allow touchdownstartsdrag attributes
4138 // to cause any touchdown event to trigger a drag.
4139 if (aEventMessage
== eMouseDown
) {
4140 WidgetMouseEvent
hittest(true, eMouseHitTest
, this,
4141 WidgetMouseEvent::eReal
);
4142 hittest
.mRefPoint
= aEventPoint
;
4143 hittest
.mIgnoreRootScrollFrame
= true;
4144 hittest
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
4145 DispatchInputEvent(&hittest
);
4147 if (EventTarget
* target
= hittest
.GetDOMEventTarget()) {
4148 if (nsIContent
* content
= nsIContent::FromEventTarget(target
)) {
4149 // Check if the element or any parent element has the
4150 // attribute we're looking for.
4151 for (Element
* element
= content
->GetAsElementOrParentElement(); element
;
4152 element
= element
->GetParentElement()) {
4153 nsAutoString startDrag
;
4154 element
->GetAttribute(u
"touchdownstartsdrag"_ns
, startDrag
);
4155 if (!startDrag
.IsEmpty()) {
4166 // Deal with all sort of mouse event
4167 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage
, WPARAM wParam
,
4168 LPARAM lParam
, bool aIsContextMenuKey
,
4169 int16_t aButton
, uint16_t aInputSource
,
4170 WinPointerInfo
* aPointerInfo
,
4172 ContextMenuPreventer
contextMenuPreventer(this);
4173 bool result
= false;
4177 if (!mWidgetListener
) {
4181 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
4182 LayoutDeviceIntPoint mpScreen
= eventPoint
+ WidgetToScreenOffset();
4184 // Suppress mouse moves caused by widget creation. Make sure to do this early
4185 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4187 if (aEventMessage
== eMouseMove
) {
4188 if ((sLastMouseMovePoint
.x
== mpScreen
.x
.value
) &&
4189 (sLastMouseMovePoint
.y
== mpScreen
.y
.value
)) {
4192 sLastMouseMovePoint
.x
= mpScreen
.x
;
4193 sLastMouseMovePoint
.y
= mpScreen
.y
;
4196 if (!aIgnoreAPZ
&& WinUtils::GetIsMouseFromTouch(aEventMessage
)) {
4198 // If mTouchWindow is true, then we must have APZ enabled and be
4199 // feeding it raw touch events. In that case we only want to
4200 // send touch-generated mouse events to content if they should
4201 // start a touch-based drag-and-drop gesture, such as on
4202 // double-tapping or when tapping elements marked with the
4203 // touchdownstartsdrag attribute in chrome UI.
4205 if (TouchEventShouldStartDrag(aEventMessage
, eventPoint
)) {
4206 aEventMessage
= eMouseTouchDrag
;
4213 uint32_t pointerId
=
4214 aPointerInfo
? aPointerInfo
->pointerId
: MOUSE_POINTERID();
4216 switch (aEventMessage
) {
4221 // eMouseMove and eMouseExitFromWidget are here because we need to make
4222 // sure capture flag isn't left on after a drag where we wouldn't see a
4223 // button up message (see bug 324131).
4226 case eMouseExitFromWidget
:
4227 if (!(wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) &&
4229 CaptureMouse(false);
4237 WidgetMouseEvent
event(true, aEventMessage
, this, WidgetMouseEvent::eReal
,
4238 aIsContextMenuKey
? WidgetMouseEvent::eContextMenuKey
4239 : WidgetMouseEvent::eNormal
);
4240 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
) {
4241 LayoutDeviceIntPoint
zero(0, 0);
4242 InitEvent(event
, &zero
);
4244 InitEvent(event
, &eventPoint
);
4247 ModifierKeyState modifierKeyState
;
4248 modifierKeyState
.InitInputEvent(event
);
4250 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4251 // event in the web content for blocking web content to prevent its default.
4252 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4253 // this should not block web page to prevent its default. I.e., it should
4254 // behave same as ContextMenu key without Shift key.
4255 // XXX Should we allow to block web page to prevent its default with
4256 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4257 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
&& event
.IsShift() &&
4258 NativeKey::LastKeyOrCharMSG().message
== WM_SYSKEYDOWN
&&
4259 NativeKey::LastKeyOrCharMSG().wParam
== VK_F10
) {
4260 event
.mModifiers
&= ~MODIFIER_SHIFT
;
4263 event
.mButton
= aButton
;
4264 event
.mInputSource
= aInputSource
;
4266 // Mouse events from Windows WM_POINTER*. Fill more information in
4267 // WidgetMouseEvent.
4268 event
.AssignPointerHelperData(*aPointerInfo
);
4269 event
.mPressure
= aPointerInfo
->mPressure
;
4270 event
.mButtons
= aPointerInfo
->mButtons
;
4272 // If we get here the mouse events must be from non-touch sources, so
4273 // convert it to pointer events as well
4274 event
.convertToPointer
= true;
4275 event
.pointerId
= pointerId
;
4278 // Static variables used to distinguish simple-, double- and triple-clicks.
4279 static POINT sLastMousePoint
= {0};
4280 static LONG sLastMouseDownTime
= 0L;
4281 static LONG sLastClickCount
= 0L;
4282 static BYTE sLastMouseButton
= 0;
4284 bool insideMovementThreshold
=
4285 (DeprecatedAbs(sLastMousePoint
.x
- eventPoint
.x
.value
) <
4286 (short)::GetSystemMetrics(SM_CXDOUBLECLK
)) &&
4287 (DeprecatedAbs(sLastMousePoint
.y
- eventPoint
.y
.value
) <
4288 (short)::GetSystemMetrics(SM_CYDOUBLECLK
));
4292 case MouseButton::ePrimary
:
4293 eventButton
= VK_LBUTTON
;
4295 case MouseButton::eMiddle
:
4296 eventButton
= VK_MBUTTON
;
4298 case MouseButton::eSecondary
:
4299 eventButton
= VK_RBUTTON
;
4306 // Doubleclicks are used to set the click count, then changed to mousedowns
4307 // We're going to time double-clicks from mouse *up* to next mouse *down*
4308 LONG curMsgTime
= ::GetMessageTime();
4310 switch (aEventMessage
) {
4311 case eMouseDoubleClick
:
4312 event
.mMessage
= eMouseDown
;
4313 event
.mButton
= aButton
;
4314 sLastClickCount
= 2;
4315 sLastMouseDownTime
= curMsgTime
;
4318 // remember when this happened for the next mouse down
4319 sLastMousePoint
.x
= eventPoint
.x
;
4320 sLastMousePoint
.y
= eventPoint
.y
;
4321 sLastMouseButton
= eventButton
;
4324 // now look to see if we want to convert this to a double- or triple-click
4325 if (((curMsgTime
- sLastMouseDownTime
) < (LONG
)::GetDoubleClickTime()) &&
4326 insideMovementThreshold
&& eventButton
== sLastMouseButton
) {
4329 // reset the click count, to count *this* click
4330 sLastClickCount
= 1;
4332 // Set last Click time on MouseDown only
4333 sLastMouseDownTime
= curMsgTime
;
4336 if (!insideMovementThreshold
) {
4337 sLastClickCount
= 0;
4340 case eMouseExitFromWidget
:
4342 Some(IsTopLevelMouseExit(mWnd
) ? WidgetMouseEvent::ePlatformTopLevel
4343 : WidgetMouseEvent::ePlatformChild
);
4348 event
.mClickCount
= sLastClickCount
;
4351 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
4352 ("Msg Time: %d Click Count: %d\n", curMsgTime
, event
.mClickCount
));
4355 // call the event callback
4356 if (mWidgetListener
) {
4357 if (aEventMessage
== eMouseMove
) {
4358 LayoutDeviceIntRect rect
= GetBounds();
4361 if (rect
.Contains(event
.mRefPoint
)) {
4362 if (sCurrentWindow
== nullptr || sCurrentWindow
!= this) {
4363 if ((nullptr != sCurrentWindow
) && (!sCurrentWindow
->mInDtor
)) {
4364 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4365 sCurrentWindow
->DispatchMouseEvent(
4366 eMouseExitFromWidget
, wParam
, pos
, false, MouseButton::ePrimary
,
4367 aInputSource
, aPointerInfo
);
4369 sCurrentWindow
= this;
4371 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4372 sCurrentWindow
->DispatchMouseEvent(
4373 eMouseEnterIntoWidget
, wParam
, pos
, false,
4374 MouseButton::ePrimary
, aInputSource
, aPointerInfo
);
4378 } else if (aEventMessage
== eMouseExitFromWidget
) {
4379 if (sCurrentWindow
== this) {
4380 sCurrentWindow
= nullptr;
4384 nsIWidget::ContentAndAPZEventStatus eventStatus
=
4385 DispatchInputEvent(&event
);
4386 contextMenuPreventer
.Update(event
, eventStatus
);
4387 return ConvertStatus(eventStatus
.mContentStatus
);
4393 HWND
nsWindow::GetTopLevelForFocus(HWND aCurWnd
) {
4394 // retrieve the toplevel window or dialogue
4395 HWND toplevelWnd
= nullptr;
4397 toplevelWnd
= aCurWnd
;
4398 nsWindow
* win
= WinUtils::GetNSWindowPtr(aCurWnd
);
4400 if (win
->mWindowType
== WindowType::TopLevel
||
4401 win
->mWindowType
== WindowType::Dialog
) {
4406 aCurWnd
= ::GetParent(aCurWnd
); // Parent or owner (if has no parent)
4411 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate
) {
4412 if (aIsActivate
&& mPickerDisplayCount
) {
4413 // We disable the root window when a picker opens. See PickerOpen. When the
4414 // picker closes (but before PickerClosed is called), our window will get
4415 // focus, but it will still be disabled. This confuses the focus system.
4416 // Therefore, we ignore this focus and explicitly call this function once
4417 // we re-enable the window. Rarely, the picker seems to re-enable our root
4418 // window before we do, but for simplicity, we always ignore focus before
4419 // the final call to PickerClosed. See bug 1883568 for further details.
4424 sJustGotActivate
= false;
4426 sJustGotDeactivate
= false;
4427 mLastKillFocusWindow
= nullptr;
4429 HWND toplevelWnd
= GetTopLevelForFocus(mWnd
);
4432 nsWindow
* win
= WinUtils::GetNSWindowPtr(toplevelWnd
);
4433 if (win
&& win
->mWidgetListener
) {
4435 win
->mWidgetListener
->WindowActivated();
4437 win
->mWidgetListener
->WindowDeactivated();
4443 HWND
nsWindow::WindowAtMouse() {
4444 DWORD pos
= ::GetMessagePos();
4446 mp
.x
= GET_X_LPARAM(pos
);
4447 mp
.y
= GET_Y_LPARAM(pos
);
4448 return ::WindowFromPoint(mp
);
4451 bool nsWindow::IsTopLevelMouseExit(HWND aWnd
) {
4452 HWND mouseWnd
= WindowAtMouse();
4454 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4455 // (which includes the non-client area). If the mouse has moved into
4456 // the non-client area, we should treat it as a top-level exit.
4457 HWND mouseTopLevel
= WinUtils::GetTopLevelHWND(mouseWnd
);
4458 if (mouseWnd
== mouseTopLevel
) return true;
4460 return WinUtils::GetTopLevelHWND(aWnd
) != mouseTopLevel
;
4463 /**************************************************************
4467 * IPC related helpers.
4469 **************************************************************/
4472 bool nsWindow::IsAsyncResponseEvent(UINT aMsg
, LRESULT
& aResult
) {
4477 case WM_WINDOWPOSCHANGING
:
4478 case WM_WINDOWPOSCHANGED
:
4479 case WM_PARENTNOTIFY
:
4480 case WM_ACTIVATEAPP
:
4483 case WM_CHILDACTIVATE
:
4484 case WM_IME_SETCONTEXT
:
4488 case WM_MOUSEACTIVATE
:
4489 case WM_CONTEXTMENU
:
4493 case WM_SETTINGCHANGE
:
4501 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4509 void nsWindow::IPCWindowProcHandler(UINT
& msg
, WPARAM
& wParam
, LPARAM
& lParam
) {
4511 msg
!= WM_GETOBJECT
,
4512 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4513 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4515 // Modal UI being displayed in windowless plugins.
4516 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4517 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4519 if (IsAsyncResponseEvent(msg
, res
)) {
4525 // Handle certain sync plugin events sent to the parent which
4526 // trigger ipc calls that result in deadlocks.
4529 bool handled
= false;
4532 // Windowless flash sending WM_ACTIVATE events to the main window
4533 // via calls to ShowWindow.
4535 if (lParam
!= 0 && LOWORD(wParam
) == WA_ACTIVE
&&
4536 IsWindow((HWND
)lParam
)) {
4537 // Check for Adobe Reader X sync activate message from their
4538 // helper window and ignore. Fixes an annoying focus problem.
4539 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) ==
4541 wchar_t szClass
[10];
4542 HWND focusWnd
= (HWND
)lParam
;
4543 if (IsWindowVisible(focusWnd
) &&
4544 GetClassNameW(focusWnd
, szClass
,
4545 sizeof(szClass
) / sizeof(char16_t
)) &&
4546 !wcscmp(szClass
, L
"Edit") &&
4547 !WinUtils::IsOurProcessWindow(focusWnd
)) {
4554 // Plugins taking or losing focus triggering focus app messages.
4557 // Windowed plugins that pass sys key events to defwndproc generate
4558 // WM_SYSCOMMAND events to the main window.
4560 // Windowed plugins that fire context menu selection events to parent
4562 case WM_CONTEXTMENU
:
4563 // IME events fired as a result of synchronous focus changes
4564 case WM_IME_SETCONTEXT
:
4570 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4571 ReplyMessage(dwResult
);
4575 /**************************************************************
4576 **************************************************************
4578 ** BLOCK: Native events
4580 ** Main Windows message handlers and OnXXX handlers for
4581 ** Windows event handling.
4583 **************************************************************
4584 **************************************************************/
4586 /**************************************************************
4588 * SECTION: Wind proc.
4590 * The main Windows event procedures and associated
4591 * message processing methods.
4593 **************************************************************/
4595 static bool DisplaySystemMenu(HWND hWnd
, nsSizeMode sizeMode
, bool isRtl
,
4596 int32_t x
, int32_t y
) {
4597 HMENU hMenu
= GetSystemMenu(hWnd
, FALSE
);
4600 mii
.cbSize
= sizeof(MENUITEMINFO
);
4601 mii
.fMask
= MIIM_STATE
;
4604 // update the options
4605 mii
.fState
= MF_ENABLED
;
4606 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4607 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4608 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4609 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4610 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4612 mii
.fState
= MF_GRAYED
;
4614 case nsSizeMode_Fullscreen
:
4615 // intentional fall through
4616 case nsSizeMode_Maximized
:
4617 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4618 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4619 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4621 case nsSizeMode_Minimized
:
4622 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4624 case nsSizeMode_Normal
:
4625 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4627 case nsSizeMode_Invalid
:
4628 NS_ASSERTION(false, "Did the argument come from invalid IPC?");
4631 MOZ_ASSERT_UNREACHABLE("Unhnalded nsSizeMode value detected");
4634 LPARAM cmd
= TrackPopupMenu(
4636 (TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
| TPM_TOPALIGN
|
4637 (isRtl
? TPM_RIGHTALIGN
: TPM_LEFTALIGN
)),
4638 x
, y
, 0, hWnd
, nullptr);
4640 PostMessage(hWnd
, WM_SYSCOMMAND
, cmd
, 0);
4647 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4648 // SEH exceptions and passes the real work to WindowProcInternal. See bug 587406
4649 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
4650 LRESULT CALLBACK
nsWindow::WindowProc(HWND hWnd
, UINT msg
, WPARAM wParam
,
4652 mozilla::ipc::CancelCPOWs();
4654 BackgroundHangMonitor().NotifyActivity();
4656 return mozilla::CallWindowProcCrashProtected(WindowProcInternal
, hWnd
, msg
,
4660 LRESULT CALLBACK
nsWindow::WindowProcInternal(HWND hWnd
, UINT msg
,
4661 WPARAM wParam
, LPARAM lParam
) {
4662 if (::GetWindowLongPtrW(hWnd
, GWLP_ID
) == eFakeTrackPointScrollableID
) {
4663 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4664 if (msg
== WM_HSCROLL
) {
4665 // Route WM_HSCROLL messages to the main window.
4666 hWnd
= ::GetParent(::GetParent(hWnd
));
4668 // Handle all other messages with its original window procedure.
4669 WNDPROC prevWindowProc
= (WNDPROC
)::GetWindowLongPtr(hWnd
, GWLP_USERDATA
);
4670 return ::CallWindowProcW(prevWindowProc
, hWnd
, msg
, wParam
, lParam
);
4674 if (msg
== MOZ_WM_TRACE
) {
4675 // This is a tracer event for measuring event loop latency.
4676 // See WidgetTraceEvent.cpp for more details.
4677 mozilla::SignalTracerThread();
4681 // Get the window which caused the event and ask it to process the message
4682 nsWindow
* targetWindow
= WinUtils::GetNSWindowPtr(hWnd
);
4683 NS_ASSERTION(targetWindow
, "nsWindow* is null!");
4684 if (!targetWindow
) return ::DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4686 // Hold the window for the life of this method, in case it gets
4687 // destroyed during processing, unless we're in the dtor already.
4688 nsCOMPtr
<nsIWidget
> kungFuDeathGrip
;
4689 if (!targetWindow
->mInDtor
) kungFuDeathGrip
= targetWindow
;
4691 targetWindow
->IPCWindowProcHandler(msg
, wParam
, lParam
);
4693 // Create this here so that we store the last rolled up popup until after
4694 // the event has been processed.
4695 nsAutoRollup autoRollup
;
4697 LRESULT popupHandlingResult
;
4698 if (DealWithPopups(hWnd
, msg
, wParam
, lParam
, &popupHandlingResult
))
4699 return popupHandlingResult
;
4701 // Call ProcessMessage
4703 if (targetWindow
->ProcessMessage(msg
, wParam
, lParam
, &retValue
)) {
4707 LRESULT res
= ::CallWindowProcW(targetWindow
->GetPrevWindowProc(), hWnd
, msg
,
4713 const char16_t
* GetQuitType() {
4714 if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART
, false)) {
4715 DWORD cchCmdLine
= 0;
4716 HRESULT rc
= ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
4717 &cchCmdLine
, nullptr);
4719 return u
"os-restart";
4725 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage
, WPARAM
& aWParam
,
4727 MSGResult
& aResult
) {
4728 if (mWindowHook
.Notify(mWnd
, aMessage
, aWParam
, aLParam
, aResult
)) {
4732 if (IMEHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
, aResult
)) {
4736 if (MouseScrollHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
,
4744 // The main windows message processing method. Wraps ProcessMessageInternal so
4745 // we can log aRetValue.
4746 bool nsWindow::ProcessMessage(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4747 LRESULT
* aRetValue
) {
4748 // For some events we might change the parameter values, so log
4749 // before and after we process them.
4750 NativeEventLogger
eventLogger("nsWindow", mWnd
, msg
, wParam
, lParam
);
4751 bool result
= ProcessMessageInternal(msg
, wParam
, lParam
, aRetValue
);
4752 eventLogger
.SetResult(*aRetValue
, result
);
4757 // The main windows message processing method. Called by ProcessMessage.
4758 bool nsWindow::ProcessMessageInternal(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4759 LRESULT
* aRetValue
) {
4760 MSGResult
msgResult(aRetValue
);
4761 if (ExternalHandlerProcessMessage(msg
, wParam
, lParam
, msgResult
)) {
4762 return (msgResult
.mConsumed
|| !mWnd
);
4765 bool result
= false; // call the default nsWindow proc
4768 // The DWM resize hack (see bug 1763981) causes us to process a number of
4769 // messages, notably including some WM_WINDOWPOSCHANG{ING,ED} messages which
4770 // would ordinarily result in a whole lot of internal state being updated.
4772 // Since we're supposed to end in the same state we started in (and since the
4773 // content shouldn't know about any of this nonsense), just discard any
4774 // messages synchronously dispatched from within the hack.
4775 if (MOZ_UNLIKELY(mIsPerformingDwmFlushHack
)) {
4779 // Glass hit testing w/custom transparent margins.
4781 // FIXME(emilio): is this needed? We deal with titlebar buttons non-natively
4783 LRESULT dwmHitResult
;
4784 if (mCustomNonClient
&&
4785 DwmDefWindowProc(mWnd
, msg
, wParam
, lParam
, &dwmHitResult
)) {
4786 *aRetValue
= dwmHitResult
;
4790 // The preference whether to use a different keyboard layout for each
4791 // window is cached, and updating it will not take effect until the
4792 // next restart. We read the preference here and not upon WM_ACTIVATE to make
4793 // sure that this behavior is consistent. Otherwise, if the user changed the
4794 // preference before having ever lowered the window, the preference would take
4795 // effect immediately.
4796 static const bool sSwitchKeyboardLayout
=
4797 Preferences::GetBool("intl.keyboard.per_window_layout", false);
4798 AppShutdownReason shutdownReason
= AppShutdownReason::Unknown
;
4800 // (Large blocks of code should be broken out into OnEvent handlers.)
4802 // WM_QUERYENDSESSION must be handled by all windows.
4803 // Otherwise Windows thinks the window can just be killed at will.
4804 case WM_QUERYENDSESSION
: {
4805 // Ask around if it's ok to quit.
4806 nsCOMPtr
<nsIObserverService
> obsServ
=
4807 mozilla::services::GetObserverService();
4808 nsCOMPtr
<nsISupportsPRBool
> cancelQuitWrapper
=
4809 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
4810 cancelQuitWrapper
->SetData(false);
4812 const char16_t
* quitType
= GetQuitType();
4813 obsServ
->NotifyObservers(cancelQuitWrapper
, "quit-application-requested",
4816 bool shouldCancelQuit
;
4817 cancelQuitWrapper
->GetData(&shouldCancelQuit
);
4818 *aRetValue
= !shouldCancelQuit
;
4822 case MOZ_WM_STARTA11Y
:
4823 #if defined(ACCESSIBILITY)
4824 Unused
<< GetAccessible();
4831 case WM_ENDSESSION
: {
4832 // For WM_ENDSESSION, wParam indicates whether we need to shutdown
4833 // (TRUE) or not (FALSE).
4838 // According to WM_ENDSESSION lParam documentation:
4839 // 0 -> OS shutdown or restart (no way to distinguish)
4840 // ENDSESSION_LOGOFF -> User is logging off
4841 // ENDSESSION_CLOSEAPP -> Application must shutdown
4842 // ENDSESSION_CRITICAL -> Application is forced to shutdown
4843 // The difference of the last two is not very clear.
4845 shutdownReason
= AppShutdownReason::OSShutdown
;
4846 } else if (lParam
& ENDSESSION_LOGOFF
) {
4847 shutdownReason
= AppShutdownReason::OSSessionEnd
;
4848 } else if (lParam
& (ENDSESSION_CLOSEAPP
| ENDSESSION_CRITICAL
)) {
4849 shutdownReason
= AppShutdownReason::OSForceClose
;
4851 MOZ_DIAGNOSTIC_ASSERT(false,
4852 "Received WM_ENDSESSION with unknown flags.");
4853 shutdownReason
= AppShutdownReason::OSForceClose
;
4857 case MOZ_WM_APP_QUIT
: {
4858 if (shutdownReason
== AppShutdownReason::Unknown
) {
4859 // TODO: We do not expect that these days anybody sends us
4860 // MOZ_WM_APP_QUIT, see bug 1827807.
4861 shutdownReason
= AppShutdownReason::WinUnexpectedMozQuit
;
4863 // Let's fake a shutdown sequence without actually closing windows etc.
4864 // to avoid Windows killing us in the middle. A proper shutdown would
4865 // require having a chance to pump some messages. Unfortunately
4866 // Windows won't let us do that. Bug 212316.
4867 nsCOMPtr
<nsIObserverService
> obsServ
=
4868 mozilla::services::GetObserverService();
4869 const char16_t
* syncShutdown
= u
"syncShutdown";
4870 const char16_t
* quitType
= GetQuitType();
4872 AppShutdown::Init(AppShutdownMode::Normal
, 0, shutdownReason
);
4874 obsServ
->NotifyObservers(nullptr, "quit-application-granted",
4876 obsServ
->NotifyObservers(nullptr, "quit-application-forced", nullptr);
4878 AppShutdown::OnShutdownConfirmed();
4880 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed
,
4882 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown
,
4884 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown
,
4886 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown
, nullptr);
4887 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM
, nullptr);
4888 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry
,
4891 AppShutdown::DoImmediateExit();
4892 MOZ_ASSERT_UNREACHABLE("Our process was supposed to exit.");
4895 case WM_SYSCOLORCHANGE
:
4896 // No need to invalidate layout for system color changes, but we need to
4897 // invalidate style.
4898 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
4901 case WM_THEMECHANGED
: {
4902 // Update non-client margin offsets
4903 UpdateNonClientMargins();
4904 nsUXThemeData::UpdateNativeThemeInfo();
4906 // We assume pretty much everything could've changed here.
4907 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
4909 UpdateDarkModeToolbar();
4911 // Invalidate the window so that the repaint will
4912 // pick up the new theme.
4913 Invalidate(true, true, true);
4916 case WM_WTSSESSION_CHANGE
: {
4918 case WTS_CONSOLE_CONNECT
:
4919 case WTS_REMOTE_CONNECT
:
4920 case WTS_SESSION_UNLOCK
:
4921 // When a session becomes visible, we should invalidate.
4922 Invalidate(true, true, true);
4929 case WM_FONTCHANGE
: {
4930 // We only handle this message for the hidden window,
4931 // as we only need to update the (global) font list once
4932 // for any given change, not once per window!
4933 if (mWindowType
!= WindowType::Invisible
) {
4937 // update the global font list
4938 gfxPlatform::GetPlatform()->UpdateFontList();
4941 case WM_SETTINGCHANGE
: {
4942 if (wParam
== SPI_SETCLIENTAREAANIMATION
||
4943 wParam
== SPI_SETKEYBOARDDELAY
|| wParam
== SPI_SETMOUSEVANISH
||
4944 wParam
== MOZ_SPI_SETCURSORSIZE
) {
4945 // These need to update LookAndFeel cached values.
4946 // They affect reduced motion settings / caret blink count / show
4947 // pointer while typing / tooltip offset, so no need to invalidate style
4949 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
4952 if (wParam
== SPI_SETFONTSMOOTHING
||
4953 wParam
== SPI_SETFONTSMOOTHINGTYPE
) {
4954 gfxDWriteFont::UpdateSystemTextVars();
4957 if (wParam
== SPI_SETWORKAREA
) {
4958 // NB: We also refresh screens on WM_DISPLAYCHANGE but the rcWork
4959 // values are sometimes wrong at that point. This message then
4960 // arrives soon afterward, when we can get the right rcWork values.
4961 ScreenHelperWin::RefreshScreens();
4964 if (auto lParamString
= reinterpret_cast<const wchar_t*>(lParam
)) {
4965 if (!wcscmp(lParamString
, L
"ImmersiveColorSet")) {
4966 // This affects system colors (-moz-win-accentcolor), so gotta pass
4968 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
4972 // UserInteractionMode, ConvertibleSlateMode, SystemDockMode may cause
4973 // @media(pointer) queries to change, which layout needs to know about
4975 // (WM_SETTINGCHANGE will be sent to all top-level windows, so we
4976 // only respond to the hidden top-level window to avoid hammering
4977 // layout with a bunch of NotifyThemeChanged() calls)
4979 if (mWindowType
== WindowType::Invisible
) {
4980 if (!wcscmp(lParamString
, L
"UserInteractionMode") ||
4981 !wcscmp(lParamString
, L
"ConvertibleSlateMode") ||
4982 !wcscmp(lParamString
, L
"SystemDockMode")) {
4983 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
4984 WindowsUIUtils::UpdateInTabletMode();
4990 case WM_DEVICECHANGE
: {
4991 if (wParam
== DBT_DEVICEARRIVAL
|| wParam
== DBT_DEVICEREMOVECOMPLETE
) {
4992 DEV_BROADCAST_HDR
* hdr
= reinterpret_cast<DEV_BROADCAST_HDR
*>(lParam
);
4993 // Check dbch_devicetype explicitly since we will get other device types
4994 // (e.g. DBT_DEVTYP_VOLUME) for some reasons even if we specify
4995 // DBT_DEVTYP_DEVICEINTERFACE in the filter for
4996 // RegisterDeviceNotification.
4997 if (hdr
->dbch_devicetype
== DBT_DEVTYP_DEVICEINTERFACE
) {
4998 // This can only change media queries (any-hover/any-pointer).
4999 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5004 case WM_NCCALCSIZE
: {
5005 // NOTE: the following block is mirrored in PreXULSkeletonUI.cpp, and
5006 // will need to be kept in sync.
5007 if (mCustomNonClient
) {
5008 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5009 // the proposed window rectangle for our window. During our
5010 // processing of the `WM_NCCALCSIZE` message, we are expected to
5011 // modify the `RECT` that `lParam` points to, so that its value upon
5012 // our return is the new client area. We must return 0 if `wParam`
5015 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5016 // struct. This struct contains an array of 3 `RECT`s, the first of
5017 // which has the exact same meaning as the `RECT` that is pointed to
5018 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5019 // conjunction with our return value, can
5020 // be used to specify portions of the source and destination window
5021 // rectangles that are valid and should be preserved. We opt not to
5022 // implement an elaborate client-area preservation technique, and
5023 // simply return 0, which means "preserve the entire old client area
5024 // and align it with the upper-left corner of our new client area".
5026 wParam
? &(reinterpret_cast<NCCALCSIZE_PARAMS
*>(lParam
))->rgrc
[0]
5027 : (reinterpret_cast<RECT
*>(lParam
));
5028 auto margin
= NonClientSizeMargin();
5029 clientRect
->top
+= margin
.top
;
5030 clientRect
->left
+= margin
.left
;
5031 clientRect
->right
-= margin
.right
;
5032 clientRect
->bottom
-= margin
.bottom
;
5033 // Make client rect's width and height more than 0 to
5034 // avoid problems of webrender and angle.
5035 clientRect
->right
= std::max(clientRect
->right
, clientRect
->left
+ 1);
5036 clientRect
->bottom
= std::max(clientRect
->bottom
, clientRect
->top
+ 1);
5044 case WM_GETTITLEBARINFOEX
: {
5045 if (!mCustomNonClient
) {
5048 auto* info
= reinterpret_cast<TITLEBARINFOEX
*>(lParam
);
5049 const LayoutDeviceIntPoint origin
= WidgetToScreenOffset();
5050 auto GeckoClientToWinScreenRect
=
5051 [&origin
](LayoutDeviceIntRect aRect
) -> RECT
{
5052 aRect
.MoveBy(origin
);
5056 .right
= aRect
.XMost(),
5057 .bottom
= aRect
.YMost(),
5060 auto SetButton
= [&](size_t aIndex
, WindowButtonType aType
) {
5061 info
->rgrect
[aIndex
] =
5062 GeckoClientToWinScreenRect(mWindowBtnRect
[aType
]);
5063 DWORD
& state
= info
->rgstate
[aIndex
];
5064 if (mWindowBtnRect
[aType
].IsEmpty()) {
5065 state
= STATE_SYSTEM_INVISIBLE
;
5067 state
= STATE_SYSTEM_FOCUSABLE
;
5070 info
->rgrect
[0] = info
->rcTitleBar
=
5071 GeckoClientToWinScreenRect(mDraggableRegion
.GetBounds());
5072 info
->rgstate
[0] = 0;
5073 SetButton(2, WindowButtonType::Minimize
);
5074 SetButton(3, WindowButtonType::Maximize
);
5075 SetButton(5, WindowButtonType::Close
);
5076 // We don't have a help button.
5077 info
->rgstate
[4] = STATE_SYSTEM_INVISIBLE
;
5078 info
->rgrect
[4] = {0, 0, 0, 0};
5082 case WM_NCHITTEST
: {
5083 if (mInputRegion
.mFullyTransparent
) {
5084 // Treat this window as transparent.
5085 *aRetValue
= HTTRANSPARENT
;
5090 if (mInputRegion
.mMargin
) {
5091 const LayoutDeviceIntPoint
screenPoint(GET_X_LPARAM(lParam
),
5092 GET_Y_LPARAM(lParam
));
5093 LayoutDeviceIntRect screenRect
= GetScreenBounds();
5094 screenRect
.Deflate(mInputRegion
.mMargin
);
5095 if (!screenRect
.Contains(screenPoint
)) {
5096 *aRetValue
= HTTRANSPARENT
;
5103 * If an nc client area margin has been moved, we are responsible
5104 * for calculating where the resize margins are and returning the
5105 * appropriate set of hit test constants. DwmDefWindowProc (above)
5106 * will handle hit testing on it's command buttons if we are on a
5107 * composited desktop.
5110 if (!mCustomNonClient
) {
5115 ClientMarginHitTestPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5122 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5123 * custom titlebar we paint ourselves, or if we're the ones
5124 * sending the message with an updated title
5127 if (mSendingSetText
|| !mCustomNonClient
|| mNonClientMargins
.top
== -1)
5131 // From msdn, the way around this is to disable the visible state
5132 // temporarily. We need the text to be set but we don't want the
5133 // redraw to occur. However, we need to make sure that we don't
5134 // do this at the same time that a Present is happening.
5136 // To do this we take mPresentLock in nsWindow::PreRender and
5137 // if that lock is taken we wait before doing WM_SETTEXT
5138 if (mCompositorWidgetDelegate
) {
5139 mCompositorWidgetDelegate
->EnterPresentLock();
5141 DWORD style
= GetWindowLong(mWnd
, GWL_STYLE
);
5142 SetWindowLong(mWnd
, GWL_STYLE
, style
& ~WS_VISIBLE
);
5144 CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
, wParam
, lParam
);
5145 SetWindowLong(mWnd
, GWL_STYLE
, style
);
5146 if (mCompositorWidgetDelegate
) {
5147 mCompositorWidgetDelegate
->LeavePresentLock();
5153 case WM_NCACTIVATE
: {
5155 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5156 * through WM_NCPAINT via InvalidateNonClientRegion.
5158 UpdateGetWindowInfoCaptionStatus(FALSE
!= wParam
);
5160 if (!mCustomNonClient
) {
5164 // There is a case that rendered result is not kept. Bug 1237617
5165 if (wParam
== TRUE
&& !gfxEnv::MOZ_DISABLE_FORCE_PRESENT()) {
5166 NS_DispatchToMainThread(NewRunnableMethod(
5167 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent
));
5170 // let the dwm handle nc painting on glass
5171 // Never allow native painting if we are on fullscreen
5172 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) break;
5174 if (wParam
== TRUE
) {
5176 *aRetValue
= FALSE
; // ignored
5178 // invalidate to trigger a paint
5179 InvalidateNonClientRegion();
5183 *aRetValue
= TRUE
; // go ahead and deactive
5185 // invalidate to trigger a paint
5186 InvalidateNonClientRegion();
5193 * ClearType changes often don't send a WM_SETTINGCHANGE message. But they
5194 * do seem to always send a WM_NCPAINT message, so let's update on that.
5196 gfxDWriteFont::UpdateSystemTextVars();
5199 case WM_POWERBROADCAST
:
5201 case PBT_APMSUSPEND
:
5202 PostSleepWakeNotification(true);
5204 case PBT_APMRESUMEAUTOMATIC
:
5205 case PBT_APMRESUMECRITICAL
:
5206 case PBT_APMRESUMESUSPEND
:
5207 PostSleepWakeNotification(false);
5212 case WM_CLOSE
: // close request
5213 if (mWidgetListener
) mWidgetListener
->RequestWindowClose(this);
5214 result
= true; // abort window closure
5219 DestroyLayerManager();
5225 *aRetValue
= (int)OnPaint(0);
5230 result
= OnHotKey(wParam
, lParam
);
5235 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5236 result
= ProcessCharMessage(nativeMsg
, nullptr);
5237 DispatchPendingEvents();
5242 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5243 nativeMsg
.time
= ::GetMessageTime();
5244 result
= ProcessKeyUpMessage(nativeMsg
, nullptr);
5245 DispatchPendingEvents();
5250 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5251 result
= ProcessKeyDownMessage(nativeMsg
, nullptr);
5252 DispatchPendingEvents();
5255 // Say we've dealt with erasing the background. (This is actually handled in
5256 // WM_PAINT, where necessary.)
5257 case WM_ERASEBKGND
: {
5262 case WM_MOUSEMOVE
: {
5263 LPARAM lParamScreen
= lParamToScreen(lParam
);
5264 mSimulatedClientArea
= IsSimulatedClientArea(GET_X_LPARAM(lParamScreen
),
5265 GET_Y_LPARAM(lParamScreen
));
5267 if (!mMousePresent
&& !sIsInMouseCapture
) {
5268 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5269 TRACKMOUSEEVENT mTrack
;
5270 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5271 mTrack
.dwFlags
= TME_LEAVE
;
5272 mTrack
.dwHoverTime
= 0;
5273 mTrack
.hwndTrack
= mWnd
;
5274 TrackMouseEvent(&mTrack
);
5276 mMousePresent
= true;
5278 // Suppress dispatch of pending events
5279 // when mouse moves are generated by widget
5280 // creation instead of user input.
5282 mp
.x
= GET_X_LPARAM(lParamScreen
);
5283 mp
.y
= GET_Y_LPARAM(lParamScreen
);
5284 bool userMovedMouse
= false;
5285 if ((sLastMouseMovePoint
.x
!= mp
.x
) || (sLastMouseMovePoint
.y
!= mp
.y
)) {
5286 userMovedMouse
= true;
5289 if (userMovedMouse
) {
5290 result
= DispatchMouseEvent(
5291 eMouseMove
, wParam
, lParam
, false, MouseButton::ePrimary
,
5292 MOUSE_INPUT_SOURCE(),
5293 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5294 DispatchPendingEvents();
5298 case WM_NCMOUSEMOVE
: {
5299 LPARAM lParamClient
= lParamToClient(lParam
);
5300 if (IsSimulatedClientArea(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
))) {
5301 if (!sIsInMouseCapture
) {
5302 TRACKMOUSEEVENT mTrack
;
5303 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5304 mTrack
.dwFlags
= TME_LEAVE
| TME_NONCLIENT
;
5305 mTrack
.dwHoverTime
= 0;
5306 mTrack
.hwndTrack
= mWnd
;
5307 TrackMouseEvent(&mTrack
);
5309 // If we noticed the mouse moving in our draggable region, forward the
5310 // message as a normal WM_MOUSEMOVE.
5311 SendMessage(mWnd
, WM_MOUSEMOVE
, 0, lParamClient
);
5313 // We've transitioned from a draggable area to somewhere else within
5314 // the non-client area - perhaps one of the edges of the window for
5316 mSimulatedClientArea
= false;
5319 if (mMousePresent
&& !sIsInMouseCapture
&& !mSimulatedClientArea
) {
5320 SendMessage(mWnd
, WM_MOUSELEAVE
, 0, 0);
5324 case WM_LBUTTONDOWN
: {
5326 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5327 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5328 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5329 DispatchPendingEvents();
5332 case WM_LBUTTONUP
: {
5334 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5335 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5336 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5337 DispatchPendingEvents();
5340 case WM_NCMOUSELEAVE
: {
5341 mSimulatedClientArea
= false;
5343 if (EventIsInsideWindow(this)) {
5344 // If we're handling WM_NCMOUSELEAVE and the mouse is still over the
5345 // window, then by process of elimination, the mouse has moved from the
5346 // non-client to client area, so no need to fall-through to the
5347 // WM_MOUSELEAVE handler. We also need to re-register for the
5348 // WM_MOUSELEAVE message, since according to the documentation at [1],
5349 // all tracking requested via TrackMouseEvent is cleared once
5350 // WM_NCMOUSELEAVE or WM_MOUSELEAVE fires.
5352 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-trackmouseevent
5353 TRACKMOUSEEVENT mTrack
;
5354 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5355 mTrack
.dwFlags
= TME_LEAVE
;
5356 mTrack
.dwHoverTime
= 0;
5357 mTrack
.hwndTrack
= mWnd
;
5358 TrackMouseEvent(&mTrack
);
5361 // We've transitioned from non-client to outside of the window, so
5362 // fall-through to the WM_MOUSELEAVE handler.
5365 case WM_MOUSELEAVE
: {
5366 if (!mMousePresent
) break;
5367 if (mSimulatedClientArea
) break;
5368 mMousePresent
= false;
5370 // Check if the mouse is over the fullscreen transition window, if so
5371 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5372 // transition window disappears will not be ignored, even if the mouse
5374 if (mTransitionWnd
&& WindowAtMouse() == mTransitionWnd
) {
5375 sLastMouseMovePoint
= {0};
5378 // We need to check mouse button states and put them in for
5380 WPARAM mouseState
= (GetKeyState(VK_LBUTTON
) ? MK_LBUTTON
: 0) |
5381 (GetKeyState(VK_MBUTTON
) ? MK_MBUTTON
: 0) |
5382 (GetKeyState(VK_RBUTTON
) ? MK_RBUTTON
: 0);
5383 // Synthesize an event position because we don't get one from
5385 LPARAM pos
= lParamToClient(::GetMessagePos());
5386 DispatchMouseEvent(eMouseExitFromWidget
, mouseState
, pos
, false,
5387 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5390 case WM_CONTEXTMENU
: {
5391 // If the context menu is brought up by a touch long-press, then
5392 // the APZ code is responsible for dealing with this, so we don't
5393 // need to do anything.
5395 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
5396 MOZ_ASSERT(mAPZC
); // since mTouchWindow is true, APZ must be enabled
5401 // If this WM_CONTEXTMENU is triggered by a mouse's secondary button up
5402 // event in overscroll gutter, we shouldn't open context menu.
5403 if (MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
5404 mNeedsToPreventContextMenu
) {
5409 // if the context menu is brought up from the keyboard, |lParam|
5412 bool contextMenukey
= false;
5414 contextMenukey
= true;
5415 pos
= lParamToClient(GetMessagePos());
5417 pos
= lParamToClient(lParam
);
5420 result
= DispatchMouseEvent(
5421 eContextMenu
, wParam
, pos
, contextMenukey
,
5422 contextMenukey
? MouseButton::ePrimary
: MouseButton::eSecondary
,
5423 MOUSE_INPUT_SOURCE());
5424 if (lParam
!= -1 && !result
&& mCustomNonClient
&&
5425 mDraggableRegion
.Contains(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
))) {
5426 // Blank area hit, throw up the system menu.
5427 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5428 GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5433 case WM_POINTERLEAVE
:
5434 case WM_POINTERDOWN
:
5436 case WM_POINTERUPDATE
:
5437 result
= OnPointerEvents(msg
, wParam
, lParam
);
5439 DispatchPendingEvents();
5443 case DM_POINTERHITTEST
:
5445 UINT contactId
= GET_POINTERID_WPARAM(wParam
);
5446 POINTER_INPUT_TYPE pointerType
;
5447 if (mPointerEvents
.GetPointerType(contactId
, &pointerType
) &&
5448 pointerType
== PT_TOUCHPAD
) {
5449 mDmOwner
->SetContact(contactId
);
5454 case WM_LBUTTONDBLCLK
:
5455 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5456 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5457 DispatchPendingEvents();
5460 case WM_MBUTTONDOWN
:
5461 result
= DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5462 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5463 DispatchPendingEvents();
5467 result
= DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5468 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5469 DispatchPendingEvents();
5472 case WM_MBUTTONDBLCLK
:
5473 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5474 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5475 DispatchPendingEvents();
5478 case WM_NCMBUTTONDOWN
:
5479 result
= DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5480 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5481 DispatchPendingEvents();
5484 case WM_NCMBUTTONUP
:
5485 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5486 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5487 DispatchPendingEvents();
5490 case WM_NCMBUTTONDBLCLK
:
5492 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5493 false, MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5494 DispatchPendingEvents();
5497 case WM_RBUTTONDOWN
:
5499 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5500 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5501 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5502 DispatchPendingEvents();
5507 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5508 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5509 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5510 DispatchPendingEvents();
5513 case WM_RBUTTONDBLCLK
:
5515 DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5516 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5517 DispatchPendingEvents();
5520 case WM_NCRBUTTONDOWN
:
5522 DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5523 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5524 DispatchPendingEvents();
5527 case WM_NCRBUTTONUP
:
5529 DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5530 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5531 DispatchPendingEvents();
5534 case WM_NCRBUTTONDBLCLK
:
5535 result
= DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5536 false, MouseButton::eSecondary
,
5537 MOUSE_INPUT_SOURCE());
5538 DispatchPendingEvents();
5541 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5542 // of mouse. If 5-button mouse works with standard mouse deriver of
5543 // Windows, users cannot disable 4th button (browser back) nor 5th button
5544 // (browser forward). We should allow to do it with our prefs since we can
5545 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5546 // messages are not sent to DefWindowProc.
5547 case WM_XBUTTONDOWN
:
5549 case WM_NCXBUTTONDOWN
:
5550 case WM_NCXBUTTONUP
:
5552 switch (GET_XBUTTON_WPARAM(wParam
)) {
5554 result
= !Preferences::GetBool("mousebutton.4th.enabled", true);
5557 result
= !Preferences::GetBool("mousebutton.5th.enabled", true);
5565 if (mAspectRatio
> 0) {
5566 LPRECT rect
= (LPRECT
)lParam
;
5567 int32_t newWidth
, newHeight
;
5569 // The following conditions and switch statement borrow heavily from the
5570 // Chromium source code from
5571 // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
5572 if (wParam
== WMSZ_LEFT
|| wParam
== WMSZ_RIGHT
||
5573 wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_BOTTOMLEFT
) {
5574 newWidth
= rect
->right
- rect
->left
;
5575 newHeight
= newWidth
/ mAspectRatio
;
5576 if (newHeight
< mSizeConstraints
.mMinSize
.height
) {
5577 newHeight
= mSizeConstraints
.mMinSize
.height
;
5578 newWidth
= newHeight
* mAspectRatio
;
5579 } else if (newHeight
> mSizeConstraints
.mMaxSize
.height
) {
5580 newHeight
= mSizeConstraints
.mMaxSize
.height
;
5581 newWidth
= newHeight
* mAspectRatio
;
5584 newHeight
= rect
->bottom
- rect
->top
;
5585 newWidth
= newHeight
* mAspectRatio
;
5586 if (newWidth
< mSizeConstraints
.mMinSize
.width
) {
5587 newWidth
= mSizeConstraints
.mMinSize
.width
;
5588 newHeight
= newWidth
/ mAspectRatio
;
5589 } else if (newWidth
> mSizeConstraints
.mMaxSize
.width
) {
5590 newWidth
= mSizeConstraints
.mMaxSize
.width
;
5591 newHeight
= newWidth
/ mAspectRatio
;
5598 rect
->right
= newWidth
+ rect
->left
;
5599 rect
->bottom
= rect
->top
+ newHeight
;
5602 rect
->right
= newWidth
+ rect
->left
;
5603 rect
->top
= rect
->bottom
- newHeight
;
5607 rect
->left
= rect
->right
- newWidth
;
5608 rect
->top
= rect
->bottom
- newHeight
;
5611 rect
->right
= rect
->left
+ newWidth
;
5612 rect
->top
= rect
->bottom
- newHeight
;
5614 case WMSZ_BOTTOMLEFT
:
5615 rect
->left
= rect
->right
- newWidth
;
5616 rect
->bottom
= rect
->top
+ newHeight
;
5618 case WMSZ_BOTTOMRIGHT
:
5619 rect
->right
= rect
->left
+ newWidth
;
5620 rect
->bottom
= rect
->top
+ newHeight
;
5625 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5626 // resize or move event. Instead we wait for first VM_SIZING message
5627 // within a ENTERSIZEMOVE to consider this a live resize event.
5628 if (mResizeState
== IN_SIZEMOVE
) {
5629 mResizeState
= RESIZING
;
5630 NotifyLiveResizeStarted();
5636 FinishLiveResizing(MOVING
);
5637 if (WinUtils::IsPerMonitorDPIAware()) {
5638 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5639 // a window around. Therefore, call ChangedDPI and ResetLayout here
5640 // if it appears that the window's scaling is not what we expect.
5641 // This causes the prescontext and appshell window management code to
5642 // check the appUnitsPerDevPixel value and current widget size, and
5643 // refresh them if necessary. If nothing has changed, these calls will
5644 // return without actually triggering any extra reflow or painting.
5645 if (WinUtils::LogToPhysFactor(mWnd
) != mDefaultScale
) {
5648 if (mWidgetListener
) {
5649 mWidgetListener
->UIResolutionChanged();
5655 case WM_ENTERSIZEMOVE
: {
5656 if (mResizeState
== NOT_RESIZING
) {
5657 mResizeState
= IN_SIZEMOVE
;
5662 case WM_EXITSIZEMOVE
: {
5663 FinishLiveResizing(NOT_RESIZING
);
5665 if (!sIsInMouseCapture
) {
5666 NotifySizeMoveDone();
5669 // Windows spins a separate hidden event loop when moving a window so we
5670 // don't hear mouse events during this time and WM_EXITSIZEMOVE is fired
5671 // when the hidden event loop exits. We set mDraggingWindowWithMouse to
5672 // true in WM_NCLBUTTONDOWN when we started moving the window with the
5673 // mouse so we know that if mDraggingWindowWithMouse is true, we can send
5674 // a mouse up event.
5675 if (mDraggingWindowWithMouse
) {
5676 mDraggingWindowWithMouse
= false;
5677 result
= DispatchMouseEvent(
5678 eMouseUp
, wParam
, lParam
, false, MouseButton::ePrimary
,
5679 MOUSE_INPUT_SOURCE(),
5680 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5686 case WM_DISPLAYCHANGE
: {
5687 ScreenHelperWin::RefreshScreens();
5688 if (mWidgetListener
) {
5689 mWidgetListener
->UIResolutionChanged();
5694 case WM_NCLBUTTONDBLCLK
:
5695 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
), false,
5696 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5697 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5698 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5699 DispatchPendingEvents();
5702 case WM_NCLBUTTONDOWN
: {
5703 // Dispatch a custom event when this happens in the draggable region, so
5704 // that non-popup-based panels can react to it. This doesn't send an
5705 // actual mousedown event because that would break dragging or interfere
5706 // with other mousedown handling in the caption area.
5707 if (ClientMarginHitTestPoint(GET_X_LPARAM(lParam
),
5708 GET_Y_LPARAM(lParam
)) == HTCAPTION
) {
5709 DispatchCustomEvent(u
"draggableregionleftmousedown"_ns
);
5710 mDraggingWindowWithMouse
= true;
5713 if (IsWindowButton(wParam
) && mCustomNonClient
) {
5714 DispatchMouseEvent(eMouseDown
, wParamFromGlobalMouseState(),
5715 lParamToClient(lParam
), false, MouseButton::ePrimary
,
5716 MOUSE_INPUT_SOURCE(), nullptr, true);
5717 DispatchPendingEvents();
5723 case WM_APPCOMMAND
: {
5724 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5725 result
= HandleAppCommandMsg(nativeMsg
, aRetValue
);
5729 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5730 // and the loword of wParam specifies which. But we don't want to tell
5731 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5732 // events are fired. Instead, set either the sJustGotActivate or
5733 // gJustGotDeactivate flags and activate/deactivate once the focus
5736 int32_t fActive
= LOWORD(wParam
);
5737 if (mWidgetListener
) {
5738 if (WA_INACTIVE
== fActive
) {
5739 // when minimizing a window, the deactivation and focus events will
5740 // be fired in the reverse order. Instead, just deactivate right away.
5741 // This can also happen when a modal system dialog is opened, so check
5742 // if the last window to receive the WM_KILLFOCUS message was this one
5743 // or a child of this one.
5744 if (HIWORD(wParam
) ||
5745 (mLastKillFocusWindow
&&
5746 (GetTopLevelForFocus(mLastKillFocusWindow
) == mWnd
))) {
5747 DispatchFocusToTopLevelWindow(false);
5749 sJustGotDeactivate
= true;
5751 if (mIsTopWidgetWindow
) {
5752 mLastKeyboardLayout
= KeyboardLayout::GetLayout();
5757 sJustGotActivate
= true;
5758 WidgetMouseEvent
event(true, eMouseActivate
, this,
5759 WidgetMouseEvent::eReal
);
5761 ModifierKeyState modifierKeyState
;
5762 modifierKeyState
.InitInputEvent(event
);
5763 DispatchInputEvent(&event
);
5764 if (sSwitchKeyboardLayout
&& mLastKeyboardLayout
)
5765 ActivateKeyboardLayout(mLastKeyboardLayout
, 0);
5767 #ifdef ACCESSIBILITY
5768 a11y::LazyInstantiator::ResetUiaDetectionCache();
5774 case WM_ACTIVATEAPP
: {
5775 // Bug 1851991: Sometimes this can be called before gfxPlatform::Init
5776 // when a window is created very early. In that case we just forego
5777 // setting this and accept the GPU process might briefly run at a lower
5779 if (GPUProcessManager::Get()) {
5780 GPUProcessManager::Get()->SetAppInForeground(wParam
);
5784 case WM_MOUSEACTIVATE
:
5785 // A popup with a parent owner should not be activated when clicked but
5786 // should still allow the mouse event to be fired, so the return value
5787 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
5788 // just use default processing so that the window is activated.
5789 if (IsPopup() && IsOwnerForegroundWindow()) {
5790 *aRetValue
= MA_NOACTIVATE
;
5795 case WM_WINDOWPOSCHANGING
: {
5796 LPWINDOWPOS info
= (LPWINDOWPOS
)lParam
;
5797 OnWindowPosChanging(info
);
5801 // Workaround for race condition in explorer.exe.
5802 case MOZ_WM_FULLSCREEN_STATE_UPDATE
: {
5803 TaskbarConcealer::OnAsyncStateUpdateRequest(mWnd
);
5807 case WM_GETMINMAXINFO
: {
5808 MINMAXINFO
* mmi
= (MINMAXINFO
*)lParam
;
5809 // Set the constraints. The minimum size should also be constrained to the
5810 // default window maximum size so that it fits on screen.
5811 mmi
->ptMinTrackSize
.x
=
5812 std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
5813 std::max((int32_t)mmi
->ptMinTrackSize
.x
,
5814 mSizeConstraints
.mMinSize
.width
));
5815 mmi
->ptMinTrackSize
.y
=
5816 std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
5817 std::max((int32_t)mmi
->ptMinTrackSize
.y
,
5818 mSizeConstraints
.mMinSize
.height
));
5819 mmi
->ptMaxTrackSize
.x
= std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
5820 mSizeConstraints
.mMaxSize
.width
);
5821 mmi
->ptMaxTrackSize
.y
= std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
5822 mSizeConstraints
.mMaxSize
.height
);
5826 WndProcUrgentInvocation::Marker _marker
;
5828 // If previous focused window isn't ours, it must have received the
5829 // redirected message. So, we should forget it.
5830 if (!WinUtils::IsOurProcessWindow(HWND(wParam
))) {
5831 RedirectedKeyDownMessageManager::Forget();
5833 if (sJustGotActivate
) {
5834 DispatchFocusToTopLevelWindow(true);
5836 TaskbarConcealer::OnFocusAcquired(this);
5840 if (sJustGotDeactivate
) {
5841 DispatchFocusToTopLevelWindow(false);
5843 mLastKillFocusWindow
= mWnd
;
5847 case WM_WINDOWPOSCHANGED
: {
5848 WINDOWPOS
* wp
= (LPWINDOWPOS
)lParam
;
5849 OnWindowPosChanged(wp
);
5850 TaskbarConcealer::OnWindowPosChanged(this);
5854 case WM_INPUTLANGCHANGEREQUEST
:
5859 case WM_INPUTLANGCHANGE
:
5860 KeyboardLayout::GetInstance()->OnLayoutChange(
5861 reinterpret_cast<HKL
>(lParam
));
5862 nsBidiKeyboard::OnLayoutChange();
5863 result
= false; // always pass to child window
5866 case WM_DESTROYCLIPBOARD
: {
5867 nsIClipboard
* clipboard
;
5868 nsresult rv
= CallGetService(kCClipboardCID
, &clipboard
);
5869 if (NS_SUCCEEDED(rv
)) {
5870 clipboard
->EmptyClipboard(nsIClipboard::kGlobalClipboard
);
5871 NS_RELEASE(clipboard
);
5875 #ifdef ACCESSIBILITY
5876 case WM_GETOBJECT
: {
5878 // Do explicit casting to make it working on 64bit systems (see bug 649236
5880 int32_t objId
= static_cast<DWORD
>(lParam
);
5881 if (objId
== OBJID_CLIENT
) { // oleacc.dll will be loaded dynamically
5882 RefPtr
<IAccessible
> root(
5883 a11y::LazyInstantiator::GetRootAccessible(mWnd
));
5885 *aRetValue
= LresultFromObject(IID_IAccessible
, wParam
, root
);
5886 a11y::LazyInstantiator::EnableBlindAggregation(mWnd
);
5893 case WM_SYSCOMMAND
: {
5894 WPARAM
const filteredWParam
= (wParam
& 0xFFF0);
5896 // SC_CLOSE may trigger a synchronous confirmation prompt. If we're in the
5897 // middle of something important, put off responding to it.
5898 if (filteredWParam
== SC_CLOSE
&& WndProcUrgentInvocation::IsActive()) {
5899 ::PostMessageW(mWnd
, msg
, wParam
, lParam
);
5904 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
5905 filteredWParam
== SC_RESTORE
&&
5906 GetCurrentShowCmd(mWnd
) != SW_SHOWMINIMIZED
) {
5907 mFrameState
->EnsureFullscreenMode(false);
5911 // Handle the system menu manually when we're in full screen mode
5912 // so we can set the appropriate options.
5913 if (filteredWParam
== SC_KEYMENU
&& lParam
== VK_SPACE
&&
5914 mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
) {
5915 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5916 MOZ_SYSCONTEXT_X_POS
, MOZ_SYSCONTEXT_Y_POS
);
5921 case WM_DPICHANGED
: {
5922 LPRECT rect
= (LPRECT
)lParam
;
5923 OnDPIChanged(rect
->left
, rect
->top
, rect
->right
- rect
->left
,
5924 rect
->bottom
- rect
->top
);
5928 /* Gesture support events */
5929 case WM_TABLET_QUERYSYSTEMGESTURESTATUS
:
5930 // According to MS samples, this must be handled to enable
5931 // rotational support in multi-touch drivers.
5933 *aRetValue
= TABLET_ROTATE_GESTURE_ENABLE
;
5937 result
= OnTouch(wParam
, lParam
);
5944 result
= OnGesture(wParam
, lParam
);
5947 case WM_GESTURENOTIFY
: {
5948 if (mWindowType
!= WindowType::Invisible
) {
5949 // A GestureNotify event is dispatched to decide which single-finger
5950 // panning direction should be active (including none) and if pan
5951 // feedback should be displayed. Java and plugin windows can make their
5954 GESTURENOTIFYSTRUCT
* gestureinfo
= (GESTURENOTIFYSTRUCT
*)lParam
;
5955 nsPointWin touchPoint
;
5956 touchPoint
= gestureinfo
->ptsLocation
;
5957 touchPoint
.ScreenToClient(mWnd
);
5958 WidgetGestureNotifyEvent
gestureNotifyEvent(true, eGestureNotify
, this);
5959 gestureNotifyEvent
.mRefPoint
=
5960 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint
);
5961 nsEventStatus status
;
5962 DispatchEvent(&gestureNotifyEvent
, status
);
5963 mDisplayPanFeedback
= gestureNotifyEvent
.mDisplayPanFeedback
;
5965 mGesture
.SetWinGestureSupport(mWnd
, gestureNotifyEvent
.mPanDirection
);
5967 result
= false; // should always bubble to DefWindowProc
5971 WidgetContentCommandEvent
command(true, eContentCommandDelete
, this);
5972 DispatchWindowEvent(command
);
5977 WidgetContentCommandEvent
command(true, eContentCommandCut
, this);
5978 DispatchWindowEvent(command
);
5983 WidgetContentCommandEvent
command(true, eContentCommandCopy
, this);
5984 DispatchWindowEvent(command
);
5989 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this);
5990 DispatchWindowEvent(command
);
5995 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this);
5996 DispatchWindowEvent(command
);
5997 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6002 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this);
6003 DispatchWindowEvent(command
);
6004 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6009 // Support EM_CANPASTE message only when wParam isn't specified or
6010 // is plain text format.
6011 if (wParam
== 0 || wParam
== CF_TEXT
|| wParam
== CF_UNICODETEXT
) {
6012 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this,
6014 DispatchWindowEvent(command
);
6015 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6021 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this, true);
6022 DispatchWindowEvent(command
);
6023 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6028 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this, true);
6029 DispatchWindowEvent(command
);
6030 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6034 case MOZ_WM_SKEWFIX
: {
6035 TimeStamp skewStamp
;
6036 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam
,
6038 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
6044 if (msg
== nsAppShell::GetTaskbarButtonCreatedMessage()) {
6045 SetHasTaskbarIconBeenCreated();
6050 //*aRetValue = result;
6054 // Events which caused mWnd destruction and aren't consumed
6055 // will crash during the Windows default processing.
6060 void nsWindow::FinishLiveResizing(ResizeState aNewState
) {
6061 if (mResizeState
== RESIZING
) {
6062 NotifyLiveResizeStopped();
6064 mResizeState
= aNewState
;
6068 /**************************************************************
6070 * SECTION: Event processing helpers
6072 * Special processing for certain event types and
6073 * synthesized events.
6075 **************************************************************/
6077 LayoutDeviceIntMargin
nsWindow::NonClientSizeMargin(
6078 const LayoutDeviceIntMargin
& aNonClientOffset
) const {
6079 return LayoutDeviceIntMargin(mCaptionHeight
- aNonClientOffset
.top
,
6080 mHorResizeMargin
- aNonClientOffset
.right
,
6081 mVertResizeMargin
- aNonClientOffset
.bottom
,
6082 mHorResizeMargin
- aNonClientOffset
.left
);
6085 int32_t nsWindow::ClientMarginHitTestPoint(int32_t aX
, int32_t aY
) {
6086 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
6087 if (sizeMode
== nsSizeMode_Minimized
|| sizeMode
== nsSizeMode_Fullscreen
) {
6091 // Calculations are done in screen coords
6092 const LayoutDeviceIntRect winRect
= GetScreenBounds();
6093 const LayoutDeviceIntPoint
point(aX
, aY
);
6095 // hit return constants:
6096 // HTBORDER - non-resizable border
6097 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
6098 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
6099 // HTTOPLEFT, HTTOPRIGHT - resizable corner
6100 // HTCAPTION - general title bar area
6101 // HTCLIENT - area considered the client
6102 // HTCLOSE - hovering over the close button
6103 // HTMAXBUTTON - maximize button
6104 // HTMINBUTTON - minimize button
6106 int32_t testResult
= HTCLIENT
;
6107 const bool isResizable
=
6108 sizeMode
!= nsSizeMode_Maximized
&&
6110 (BorderStyle::All
| BorderStyle::ResizeH
| BorderStyle::Default
));
6112 LayoutDeviceIntMargin nonClientSizeMargin
= NonClientSizeMargin();
6114 // Ensure being accessible to borders of window. Even if contents are in
6115 // this area, the area must behave as border.
6116 nonClientSizeMargin
.EnsureAtLeast(
6117 LayoutDeviceIntMargin(kResizableBorderMinSize
, kResizableBorderMinSize
,
6118 kResizableBorderMinSize
, kResizableBorderMinSize
));
6120 LayoutDeviceIntRect clientRect
= winRect
;
6121 clientRect
.Deflate(nonClientSizeMargin
);
6123 const bool allowContentOverride
=
6124 sizeMode
== nsSizeMode_Maximized
|| clientRect
.Contains(point
);
6126 // The border size. If there is no content under mouse cursor, the border
6127 // size should be larger than the values in system settings. Otherwise,
6128 // contents under the mouse cursor should be able to override the behavior.
6129 // E.g., user must expect that Firefox button always opens the popup menu
6130 // even when the user clicks on the above edge of it.
6131 LayoutDeviceIntMargin borderSize
= nonClientSizeMargin
;
6132 borderSize
.EnsureAtLeast(
6133 LayoutDeviceIntMargin(mVertResizeMargin
, mHorResizeMargin
,
6134 mVertResizeMargin
, mHorResizeMargin
));
6137 bool bottom
= false;
6141 if (point
.y
>= winRect
.y
&& point
.y
< winRect
.y
+ borderSize
.top
) {
6143 } else if (point
.y
<= winRect
.YMost() &&
6144 point
.y
> winRect
.YMost() - borderSize
.bottom
) {
6148 // (the 2x case here doubles the resize area for corners)
6149 int multiplier
= (top
|| bottom
) ? 2 : 1;
6150 if (point
.x
>= winRect
.x
&&
6151 point
.x
< winRect
.x
+ (multiplier
* borderSize
.left
)) {
6153 } else if (point
.x
<= winRect
.XMost() &&
6154 point
.x
> winRect
.XMost() - (multiplier
* borderSize
.right
)) {
6158 bool inResizeRegion
= false;
6163 testResult
= HTTOPLEFT
;
6165 testResult
= HTTOPRIGHT
;
6167 } else if (bottom
) {
6168 testResult
= HTBOTTOM
;
6170 testResult
= HTBOTTOMLEFT
;
6172 testResult
= HTBOTTOMRIGHT
;
6176 testResult
= HTLEFT
;
6179 testResult
= HTRIGHT
;
6182 inResizeRegion
= (testResult
!= HTCLIENT
);
6185 testResult
= HTCAPTION
;
6186 } else if (bottom
|| left
|| right
) {
6187 testResult
= HTBORDER
;
6191 if (!sIsInMouseCapture
&& allowContentOverride
) {
6193 POINT pt
= {aX
, aY
};
6194 ::ScreenToClient(mWnd
, &pt
);
6196 if (pt
.x
== mCachedHitTestPoint
.x
.value
&&
6197 pt
.y
== mCachedHitTestPoint
.y
.value
&&
6198 TimeStamp::Now() - mCachedHitTestTime
<
6199 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS
)) {
6200 return mCachedHitTestResult
;
6203 mCachedHitTestPoint
= {pt
.x
, pt
.y
};
6204 mCachedHitTestTime
= TimeStamp::Now();
6207 auto pt
= mCachedHitTestPoint
;
6209 if (mWindowBtnRect
[WindowButtonType::Minimize
].Contains(pt
)) {
6210 testResult
= HTMINBUTTON
;
6211 } else if (mWindowBtnRect
[WindowButtonType::Maximize
].Contains(pt
)) {
6212 #ifdef ACCESSIBILITY
6213 a11y::Compatibility::SuppressA11yForSnapLayouts();
6215 testResult
= HTMAXBUTTON
;
6216 } else if (mWindowBtnRect
[WindowButtonType::Close
].Contains(pt
)) {
6217 testResult
= HTCLOSE
;
6218 } else if (!inResizeRegion
) {
6219 // If we're in the resize region, avoid overriding that with either a
6220 // drag or a client result; resize takes priority over either (but not
6221 // over the window controls, which is why we check this after those).
6222 if (mDraggableRegion
.Contains(pt
)) {
6223 testResult
= HTCAPTION
;
6225 testResult
= HTCLIENT
;
6229 mCachedHitTestResult
= testResult
;
6235 bool nsWindow::IsSimulatedClientArea(int32_t screenX
, int32_t screenY
) {
6236 int32_t testResult
= ClientMarginHitTestPoint(screenX
, screenY
);
6237 return testResult
== HTCAPTION
|| IsWindowButton(testResult
);
6240 bool nsWindow::IsWindowButton(int32_t hitTestResult
) {
6241 return hitTestResult
== HTMINBUTTON
|| hitTestResult
== HTMAXBUTTON
||
6242 hitTestResult
== HTCLOSE
;
6245 TimeStamp
nsWindow::GetMessageTimeStamp(LONG aEventTime
) const {
6246 CurrentWindowsTimeGetter
getCurrentTime(mWnd
);
6247 return TimeConverter().GetTimeStampFromSystemTime(aEventTime
, getCurrentTime
);
6250 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode
) {
6251 // Retain the previous mode that was notified to observers
6252 static bool sWasSleepMode
= false;
6254 // Only notify observers if mode changed
6255 if (aIsSleepMode
== sWasSleepMode
) return;
6257 sWasSleepMode
= aIsSleepMode
;
6259 nsCOMPtr
<nsIObserverService
> observerService
=
6260 mozilla::services::GetObserverService();
6261 if (observerService
)
6262 observerService
->NotifyObservers(nullptr,
6264 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6265 : NS_WIDGET_WAKE_OBSERVER_TOPIC
,
6269 LRESULT
nsWindow::ProcessCharMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6270 if (IMEHandler::IsComposingOn(this)) {
6271 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION
);
6273 // These must be checked here too as a lone WM_CHAR could be received
6274 // if a child window didn't handle it (for example Alt+Space in a content
6276 ModifierKeyState modKeyState
;
6277 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6278 return static_cast<LRESULT
>(nativeKey
.HandleCharMessage(aEventDispatched
));
6281 LRESULT
nsWindow::ProcessKeyUpMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6282 ModifierKeyState modKeyState
;
6283 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6284 bool result
= nativeKey
.HandleKeyUpMessage(aEventDispatched
);
6285 if (aMsg
.wParam
== VK_F10
) {
6286 // Bug 1382199: Windows default behavior will trigger the System menu bar
6287 // when F10 is released. Among other things, this causes the System menu bar
6288 // to appear when a web page overrides the contextmenu event. We *never*
6289 // want this default behavior, so eat this key (never pass it to Windows).
6295 LRESULT
nsWindow::ProcessKeyDownMessage(const MSG
& aMsg
,
6296 bool* aEventDispatched
) {
6297 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6298 // must clean up the redirected message information itself. For more
6299 // information, see above comment of
6300 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6301 // KeyboardLayout.h.
6302 RedirectedKeyDownMessageManager::AutoFlusher
redirectedMsgFlusher(this, aMsg
);
6304 ModifierKeyState modKeyState
;
6306 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6308 static_cast<LRESULT
>(nativeKey
.HandleKeyDownMessage(aEventDispatched
));
6309 // HandleKeyDownMessage cleaned up the redirected message information
6310 // itself, so, we should do nothing.
6311 redirectedMsgFlusher
.Cancel();
6313 if (aMsg
.wParam
== VK_MENU
||
6314 (aMsg
.wParam
== VK_F10
&& !modKeyState
.IsShift())) {
6315 // We need to let Windows handle this keypress,
6316 // by returning false, if there's a native menu
6317 // bar somewhere in our containing window hierarchy.
6318 // Otherwise we handle the keypress and don't pass
6319 // it on to Windows, by returning true.
6320 bool hasNativeMenu
= false;
6323 if (::GetMenu(hWnd
)) {
6324 hasNativeMenu
= true;
6327 hWnd
= ::GetParent(hWnd
);
6329 result
= !hasNativeMenu
;
6335 nsresult
nsWindow::SynthesizeNativeKeyEvent(
6336 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
6337 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
6338 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
6339 AutoObserverNotifier
notifier(aObserver
, "keyevent");
6341 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
6342 return keyboardLayout
->SynthesizeNativeKeyEvent(
6343 this, aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
, aCharacters
,
6344 aUnmodifiedCharacters
);
6347 nsresult
nsWindow::SynthesizeNativeMouseEvent(
6348 LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
6349 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
6350 nsIObserver
* aObserver
) {
6351 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
6354 memset(&input
, 0, sizeof(input
));
6356 // TODO (bug 1693240):
6357 // Now, we synthesize native mouse events asynchronously since we want to
6358 // synthesize the event on the front window at the point. However, Windows
6359 // does not provide a way to set modifier only while a mouse message is
6360 // being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
6361 // need a trick for handling it.
6363 switch (aNativeMessage
) {
6364 case NativeMouseMessage::Move
:
6365 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
;
6366 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6367 // to the position it's already at, we still dispatch a mousemove
6368 // event, because the callers of this function expect that.
6369 sLastMouseMovePoint
= {0};
6371 case NativeMouseMessage::ButtonDown
:
6372 case NativeMouseMessage::ButtonUp
: {
6373 const bool isDown
= aNativeMessage
== NativeMouseMessage::ButtonDown
;
6375 case MouseButton::ePrimary
:
6376 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
6378 case MouseButton::eMiddle
:
6380 isDown
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
6382 case MouseButton::eSecondary
:
6384 isDown
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
6386 case MouseButton::eX1
:
6387 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6388 input
.mi
.mouseData
= XBUTTON1
;
6390 case MouseButton::eX2
:
6391 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6392 input
.mi
.mouseData
= XBUTTON2
;
6395 return NS_ERROR_INVALID_ARG
;
6399 case NativeMouseMessage::EnterWindow
:
6400 case NativeMouseMessage::LeaveWindow
:
6401 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Windows");
6402 return NS_ERROR_INVALID_ARG
;
6405 input
.type
= INPUT_MOUSE
;
6406 ::SetCursorPos(aPoint
.x
, aPoint
.y
);
6407 ::SendInput(1, &input
, sizeof(INPUT
));
6412 nsresult
nsWindow::SynthesizeNativeMouseScrollEvent(
6413 LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
, double aDeltaX
,
6414 double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
6415 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
6416 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
6417 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6418 this, aPoint
, aNativeMessage
,
6419 (aNativeMessage
== WM_MOUSEWHEEL
|| aNativeMessage
== WM_VSCROLL
)
6420 ? static_cast<int32_t>(aDeltaY
)
6421 : static_cast<int32_t>(aDeltaX
),
6422 aModifierFlags
, aAdditionalFlags
);
6425 nsresult
nsWindow::SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase
,
6426 LayoutDeviceIntPoint aPoint
,
6427 double aDeltaX
, double aDeltaY
,
6428 int32_t aModifierFlags
,
6429 nsIObserver
* aObserver
) {
6430 AutoObserverNotifier
notifier(aObserver
, "touchpadpanevent");
6431 DirectManipulationOwner::SynthesizeNativeTouchpadPan(
6432 this, aEventPhase
, aPoint
, aDeltaX
, aDeltaY
, aModifierFlags
);
6436 static void MaybeLogPosChanged(HWND aWnd
, WINDOWPOS
* wp
) {
6437 #ifdef WINSTATE_DEBUG_OUTPUT
6438 if (aWnd
== WinUtils::GetTopLevelHWND(aWnd
)) {
6439 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [ top] "));
6441 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [child] "));
6443 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("WINDOWPOS flags:"));
6444 if (wp
->flags
& SWP_FRAMECHANGED
) {
6445 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_FRAMECHANGED "));
6447 if (wp
->flags
& SWP_SHOWWINDOW
) {
6448 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_SHOWWINDOW "));
6450 if (wp
->flags
& SWP_NOSIZE
) {
6451 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOSIZE "));
6453 if (wp
->flags
& SWP_HIDEWINDOW
) {
6454 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_HIDEWINDOW "));
6456 if (wp
->flags
& SWP_NOZORDER
) {
6457 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOZORDER "));
6459 if (wp
->flags
& SWP_NOACTIVATE
) {
6460 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOACTIVATE "));
6462 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("\n"));
6466 /**************************************************************
6468 * SECTION: OnXXX message handlers
6470 * For message handlers that need to be broken out or
6471 * implemented in specific platform code.
6473 **************************************************************/
6475 void nsWindow::OnWindowPosChanged(WINDOWPOS
* wp
) {
6480 MaybeLogPosChanged(mWnd
, wp
);
6482 // Handle window size mode changes
6483 if (wp
->flags
& SWP_FRAMECHANGED
) {
6484 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6485 // windows when fullscreen games disable desktop composition. If we're
6486 // minimized and not being activated, ignore the event and let windows
6488 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
&&
6489 (wp
->flags
& SWP_NOACTIVATE
)) {
6493 mFrameState
->OnFrameChanged();
6495 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
6496 // Skip window size change events below on minimization.
6501 // Notify visibility change when window is activated.
6502 if (!(wp
->flags
& SWP_NOACTIVATE
) && NeedsToTrackWindowOcclusionState()) {
6503 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
6504 this, mFrameState
->GetSizeMode() != nsSizeMode_Minimized
);
6507 // Handle window position changes
6508 if (!(wp
->flags
& SWP_NOMOVE
)) {
6509 mBounds
.MoveTo(wp
->x
, wp
->y
);
6510 NotifyWindowMoved(wp
->x
, wp
->y
);
6513 // Handle window size changes
6514 if (!(wp
->flags
& SWP_NOSIZE
)) {
6516 int32_t newWidth
, newHeight
;
6518 ::GetWindowRect(mWnd
, &r
);
6520 newWidth
= r
.right
- r
.left
;
6521 newHeight
= r
.bottom
- r
.top
;
6523 if (newWidth
> mLastSize
.width
) {
6527 drect
.left
= wp
->x
+ mLastSize
.width
;
6529 drect
.right
= drect
.left
+ (newWidth
- mLastSize
.width
);
6530 drect
.bottom
= drect
.top
+ newHeight
;
6532 ::RedrawWindow(mWnd
, &drect
, nullptr,
6533 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6534 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6536 if (newHeight
> mLastSize
.height
) {
6541 drect
.top
= wp
->y
+ mLastSize
.height
;
6542 drect
.right
= drect
.left
+ newWidth
;
6543 drect
.bottom
= drect
.top
+ (newHeight
- mLastSize
.height
);
6545 ::RedrawWindow(mWnd
, &drect
, nullptr,
6546 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6547 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6550 mBounds
.SizeTo(newWidth
, newHeight
);
6551 mLastSize
.width
= newWidth
;
6552 mLastSize
.height
= newHeight
;
6554 #ifdef WINSTATE_DEBUG_OUTPUT
6555 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6556 ("*** Resize window: %d x %d x %d x %d\n", wp
->x
, wp
->y
, newWidth
,
6560 if (mAspectRatio
> 0) {
6561 // It's possible (via Windows Aero Snap) that the size of the window
6562 // has changed such that it violates the aspect ratio constraint. If so,
6563 // queue up an event to enforce the aspect ratio constraint and repaint.
6564 // When resized with Windows Aero Snap, we are in the NOT_RESIZING state.
6565 float newAspectRatio
= (float)newWidth
/ newHeight
;
6566 if (mResizeState
== NOT_RESIZING
&& mAspectRatio
!= newAspectRatio
) {
6567 // Hold a reference to self alive and pass it into the lambda to make
6568 // sure this nsIWidget stays alive long enough to run this function.
6569 nsCOMPtr
<nsIWidget
> self(this);
6570 NS_DispatchToMainThread(NS_NewRunnableFunction(
6571 "EnforceAspectRatio", [self
, this, newWidth
]() -> void {
6573 Resize(newWidth
, newWidth
/ mAspectRatio
, true);
6579 // If a maximized window is resized, recalculate the non-client margins.
6580 if (mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
6581 if (UpdateNonClientMargins(true)) {
6582 // gecko resize event already sent by UpdateNonClientMargins.
6588 // Notify the widget listener for size change of client area for gecko
6589 // events. This needs to be done when either window size is changed,
6590 // or window frame is changed. They may not happen together.
6591 // However, we don't invoke that for popup when window frame changes,
6592 // because popups may trigger frame change before size change via
6593 // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
6594 // code below call OnResize with a wrong client size first, which can
6595 // lead to flickerling for some popups.
6596 if (!(wp
->flags
& SWP_NOSIZE
) ||
6597 ((wp
->flags
& SWP_FRAMECHANGED
) && !IsPopup())) {
6599 LayoutDeviceIntSize clientSize
;
6600 if (::GetClientRect(mWnd
, &r
)) {
6601 clientSize
= WinUtils::ToIntRect(r
).Size();
6603 clientSize
= mBounds
.Size();
6605 // Send a gecko resize event
6606 OnResize(clientSize
);
6610 void nsWindow::OnWindowPosChanging(WINDOWPOS
* info
) {
6611 // Update non-client margins if the frame size is changing, and let the
6612 // browser know we are changing size modes, so alternative css can kick in.
6613 // If we're going into fullscreen mode, ignore this, since it'll reset
6614 // margins to normal mode.
6615 if (info
->flags
& SWP_FRAMECHANGED
&& !(info
->flags
& SWP_NOSIZE
)) {
6616 mFrameState
->OnFrameChanging();
6619 // Force fullscreen. This works around a bug in Windows 10 1809 where
6620 // using fullscreen when a window is "snapped" causes a spurious resize
6621 // smaller than the full screen, see bug 1482920.
6622 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6623 !(info
->flags
& SWP_NOMOVE
) && !(info
->flags
& SWP_NOSIZE
)) {
6624 nsCOMPtr
<nsIScreenManager
> screenmgr
=
6625 do_GetService(sScreenManagerContractID
);
6627 LayoutDeviceIntRect
bounds(info
->x
, info
->y
, info
->cx
, info
->cy
);
6628 DesktopIntRect deskBounds
=
6629 RoundedToInt(bounds
/ GetDesktopToDeviceScale());
6630 nsCOMPtr
<nsIScreen
> screen
;
6631 screenmgr
->ScreenForRect(deskBounds
.X(), deskBounds
.Y(),
6632 deskBounds
.Width(), deskBounds
.Height(),
6633 getter_AddRefs(screen
));
6636 auto rect
= screen
->GetRect();
6639 info
->cx
= rect
.width
;
6640 info
->cy
= rect
.height
;
6645 // enforce local z-order rules
6646 if (!(info
->flags
& SWP_NOZORDER
)) {
6647 HWND hwndAfter
= info
->hwndInsertAfter
;
6649 nsWindow
* aboveWindow
= 0;
6650 nsWindowZ placement
;
6652 if (hwndAfter
== HWND_BOTTOM
)
6653 placement
= nsWindowZBottom
;
6654 else if (hwndAfter
== HWND_TOP
|| hwndAfter
== HWND_TOPMOST
||
6655 hwndAfter
== HWND_NOTOPMOST
)
6656 placement
= nsWindowZTop
;
6658 placement
= nsWindowZRelative
;
6659 aboveWindow
= WinUtils::GetNSWindowPtr(hwndAfter
);
6662 if (mWidgetListener
) {
6663 nsCOMPtr
<nsIWidget
> actualBelow
= nullptr;
6664 if (mWidgetListener
->ZLevelChanged(false, &placement
, aboveWindow
,
6665 getter_AddRefs(actualBelow
))) {
6666 if (placement
== nsWindowZBottom
)
6667 info
->hwndInsertAfter
= HWND_BOTTOM
;
6668 else if (placement
== nsWindowZTop
)
6669 info
->hwndInsertAfter
= HWND_TOP
;
6671 info
->hwndInsertAfter
=
6672 (HWND
)actualBelow
->GetNativeData(NS_NATIVE_WINDOW
);
6677 // prevent rude external programs from making hidden window visible
6678 if (mWindowType
== WindowType::Invisible
) info
->flags
&= ~SWP_SHOWWINDOW
;
6680 // When waking from sleep or switching out of tablet mode, Windows 10
6681 // Version 1809 will reopen popup windows that should be hidden. Detect
6682 // this case and refuse to show the window.
6683 static bool sDWMUnhidesPopups
= IsWin10Sep2018UpdateOrLater();
6684 if (sDWMUnhidesPopups
&& (info
->flags
& SWP_SHOWWINDOW
) &&
6685 mWindowType
== WindowType::Popup
&& mWidgetListener
&&
6686 mWidgetListener
->ShouldNotBeVisible()) {
6687 info
->flags
&= ~SWP_SHOWWINDOW
;
6691 void nsWindow::UserActivity() {
6692 // Check if we have the idle service, if not we try to get it.
6693 if (!mIdleService
) {
6694 mIdleService
= do_GetService("@mozilla.org/widget/useridleservice;1");
6697 // Check that we now have the idle service.
6699 mIdleService
->ResetIdleTimeOut(0);
6703 // Helper function for TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT,
6705 static bool TouchDeviceNeedsPanGestureConversion(HANDLE aSource
) {
6706 std::string deviceName
;
6708 // The first call just queries how long the name string will be.
6709 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, nullptr, &dataSize
);
6710 if (!dataSize
|| dataSize
> 0x10000) {
6713 deviceName
.resize(dataSize
);
6714 // The second call actually populates the string.
6715 UINT result
= GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, &deviceName
[0],
6717 if (result
== UINT_MAX
) {
6720 // The affected device name is "\\?\VIRTUAL_DIGITIZER", but each backslash
6721 // needs to be escaped with another one.
6722 std::string expectedDeviceName
= "\\\\?\\VIRTUAL_DIGITIZER";
6723 // For some reason, the dataSize returned by the first call is double the
6724 // actual length of the device name (as if it were returning the size of a
6725 // wide-character string in bytes) even though we are using the narrow
6726 // version of the API. For the comparison against the expected device name
6727 // to pass, we truncate the buffer to be no longer tha the expected device
6729 if (deviceName
.substr(0, expectedDeviceName
.length()) != expectedDeviceName
) {
6733 RID_DEVICE_INFO deviceInfo
;
6734 deviceInfo
.cbSize
= sizeof(deviceInfo
);
6735 dataSize
= sizeof(deviceInfo
);
6737 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICEINFO
, &deviceInfo
, &dataSize
);
6738 if (result
== UINT_MAX
) {
6741 // The device identifiers that we check for here come from bug 1355162
6742 // comment 1 (see also bug 1511901 comment 35).
6743 return deviceInfo
.dwType
== RIM_TYPEHID
&& deviceInfo
.hid
.dwVendorId
== 0 &&
6744 deviceInfo
.hid
.dwProductId
== 0 &&
6745 deviceInfo
.hid
.dwVersionNumber
== 1 &&
6746 deviceInfo
.hid
.usUsagePage
== 13 && deviceInfo
.hid
.usUsage
== 4;
6749 // Determine if the touch device that originated |aOSEvent| needs to have
6750 // touch events representing a two-finger gesture converted to pan
6752 // We only do this for touch devices with a specific name and identifiers.
6753 static bool TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent
,
6754 uint32_t aTouchCount
) {
6755 if (!StaticPrefs::apz_windows_check_for_pan_gesture_conversion()) {
6758 if (aTouchCount
== 0) {
6761 HANDLE source
= aOSEvent
[0].hSource
;
6763 // Cache the result of this computation for each touch device.
6764 // Touch devices are identified by the HANDLE stored in the hSource
6765 // field of TOUCHINPUT.
6766 static std::map
<HANDLE
, bool> sResultCache
;
6767 auto [iter
, inserted
] = sResultCache
.emplace(source
, false);
6769 iter
->second
= TouchDeviceNeedsPanGestureConversion(source
);
6771 return iter
->second
;
6774 Maybe
<PanGestureInput
> nsWindow::ConvertTouchToPanGesture(
6775 const MultiTouchInput
& aTouchInput
, PTOUCHINPUT aOSEvent
) {
6776 // Checks if the touch device that originated the touch event is one
6777 // for which we want to convert the touch events to pang gesture events.
6778 bool shouldConvert
= TouchDeviceNeedsPanGestureConversion(
6779 aOSEvent
, aTouchInput
.mTouches
.Length());
6780 if (!shouldConvert
) {
6784 // Only two-finger gestures need conversion.
6785 if (aTouchInput
.mTouches
.Length() != 2) {
6789 PanGestureInput::PanGestureType eventType
= PanGestureInput::PANGESTURE_PAN
;
6790 if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_START
) {
6791 eventType
= PanGestureInput::PANGESTURE_START
;
6792 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_END
) {
6793 eventType
= PanGestureInput::PANGESTURE_END
;
6794 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_CANCEL
) {
6795 eventType
= PanGestureInput::PANGESTURE_CANCELLED
;
6798 // Use the midpoint of the two touches as the start point of the pan gesture.
6799 ScreenPoint focusPoint
= (aTouchInput
.mTouches
[0].mScreenPoint
+
6800 aTouchInput
.mTouches
[1].mScreenPoint
) /
6802 // To compute the displacement of the pan gesture, we keep track of the
6803 // location of the previous event.
6804 ScreenPoint displacement
= (eventType
== PanGestureInput::PANGESTURE_START
)
6806 : (focusPoint
- mLastPanGestureFocus
);
6807 mLastPanGestureFocus
= focusPoint
;
6809 // We need to negate the displacement because for a touch event, moving the
6810 // fingers down results in scrolling up, but for a touchpad gesture, we want
6811 // moving the fingers down to result in scrolling down.
6812 PanGestureInput
result(eventType
, aTouchInput
.mTimeStamp
, focusPoint
,
6813 -displacement
, aTouchInput
.modifiers
);
6814 result
.mSimulateMomentum
= true;
6816 return Some(result
);
6819 // Dispatch an event that originated as an OS touch event.
6820 // Usually, we want to dispatch it as a touch event, but some touchpads
6821 // produce touch events for two-finger scrolling, which need to be converted
6822 // to pan gesture events for correct behaviour.
6823 void nsWindow::DispatchTouchOrPanGestureInput(MultiTouchInput
& aTouchInput
,
6824 PTOUCHINPUT aOSEvent
) {
6825 if (Maybe
<PanGestureInput
> panInput
=
6826 ConvertTouchToPanGesture(aTouchInput
, aOSEvent
)) {
6827 DispatchPanGestureInput(*panInput
);
6831 DispatchTouchInput(aTouchInput
);
6834 bool nsWindow::OnTouch(WPARAM wParam
, LPARAM lParam
) {
6835 uint32_t cInputs
= LOWORD(wParam
);
6836 PTOUCHINPUT pInputs
= new TOUCHINPUT
[cInputs
];
6838 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, pInputs
,
6839 sizeof(TOUCHINPUT
))) {
6840 MultiTouchInput touchInput
, touchEndInput
;
6842 // Walk across the touch point array processing each contact point.
6843 for (uint32_t i
= 0; i
< cInputs
; i
++) {
6844 bool addToEvent
= false, addToEndEvent
= false;
6846 // N.B.: According with MS documentation
6847 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
6848 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
6849 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
6850 // TOUCHEVENTF_UP can be combined together.
6852 if (pInputs
[i
].dwFlags
& (TOUCHEVENTF_DOWN
| TOUCHEVENTF_MOVE
)) {
6853 if (touchInput
.mTimeStamp
.IsNull()) {
6854 // Initialize a touch event to send.
6855 touchInput
.mType
= MultiTouchInput::MULTITOUCH_MOVE
;
6856 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6857 ModifierKeyState modifierKeyState
;
6858 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
6860 // Pres shell expects this event to be a eTouchStart
6861 // if any new contact points have been added since the last event sent.
6862 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_DOWN
) {
6863 touchInput
.mType
= MultiTouchInput::MULTITOUCH_START
;
6867 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_UP
) {
6868 // Pres shell expects removed contacts points to be delivered in a
6869 // separate eTouchEnd event containing only the contact points that were
6871 if (touchEndInput
.mTimeStamp
.IsNull()) {
6872 // Initialize a touch event to send.
6873 touchEndInput
.mType
= MultiTouchInput::MULTITOUCH_END
;
6874 touchEndInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6875 ModifierKeyState modifierKeyState
;
6876 touchEndInput
.modifiers
= modifierKeyState
.GetModifiers();
6878 addToEndEvent
= true;
6880 if (!addToEvent
&& !addToEndEvent
) {
6881 // Filter out spurious Windows events we don't understand, like palm
6886 // Setup the touch point we'll append to the touch event array.
6887 nsPointWin touchPoint
;
6888 touchPoint
.x
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].x
);
6889 touchPoint
.y
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].y
);
6890 touchPoint
.ScreenToClient(mWnd
);
6892 // Initialize the touch data.
6893 SingleTouchData
touchData(
6894 pInputs
[i
].dwID
, // aIdentifier
6895 ScreenIntPoint::FromUnknownPoint(touchPoint
), // aScreenPoint
6896 // The contact area info cannot be trusted even when
6897 // TOUCHINPUTMASKF_CONTACTAREA is set when the input source is pen,
6898 // which somehow violates the API docs. (bug 1710509) Ultimately the
6899 // dwFlags check will become redundant since we want to migrate to
6900 // WM_POINTER for pens. (bug 1707075)
6901 (pInputs
[i
].dwMask
& TOUCHINPUTMASKF_CONTACTAREA
) &&
6902 !(pInputs
[i
].dwFlags
& TOUCHEVENTF_PEN
)
6903 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs
[i
].cxContact
) / 2,
6904 TOUCH_COORD_TO_PIXEL(pInputs
[i
].cyContact
) / 2)
6905 : ScreenSize(1, 1), // aRadius
6906 0.0f
, // aRotationAngle
6909 // Append touch data to the appropriate event.
6911 touchInput
.mTouches
.AppendElement(touchData
);
6913 if (addToEndEvent
) {
6914 touchEndInput
.mTouches
.AppendElement(touchData
);
6918 // Dispatch touch start and touch move event if we have one.
6919 if (!touchInput
.mTimeStamp
.IsNull()) {
6920 DispatchTouchOrPanGestureInput(touchInput
, pInputs
);
6922 // Dispatch touch end event if we have one.
6923 if (!touchEndInput
.mTimeStamp
.IsNull()) {
6924 DispatchTouchOrPanGestureInput(touchEndInput
, pInputs
);
6929 CloseTouchInputHandle((HTOUCHINPUT
)lParam
);
6933 // Gesture event processing. Handles WM_GESTURE events.
6934 bool nsWindow::OnGesture(WPARAM wParam
, LPARAM lParam
) {
6935 // Treatment for pan events which translate into scroll events:
6936 if (mGesture
.IsPanEvent(lParam
)) {
6937 if (!mGesture
.ProcessPanMessage(mWnd
, wParam
, lParam
))
6938 return false; // ignore
6940 nsEventStatus status
;
6942 WidgetWheelEvent
wheelEvent(true, eWheel
, this);
6944 ModifierKeyState modifierKeyState
;
6945 modifierKeyState
.InitInputEvent(wheelEvent
);
6947 wheelEvent
.mButton
= 0;
6948 wheelEvent
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6949 wheelEvent
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
6951 bool endFeedback
= true;
6953 if (mGesture
.PanDeltaToPixelScroll(wheelEvent
)) {
6954 DispatchEvent(&wheelEvent
, status
);
6957 if (mDisplayPanFeedback
) {
6958 mGesture
.UpdatePanFeedbackX(
6959 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaX
)),
6961 mGesture
.UpdatePanFeedbackY(
6962 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaY
)),
6964 mGesture
.PanFeedbackFinalize(mWnd
, endFeedback
);
6967 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
6972 // Other gestures translate into simple gesture events:
6973 WidgetSimpleGestureEvent
event(true, eVoidEvent
, this);
6974 if (!mGesture
.ProcessGestureMessage(mWnd
, wParam
, lParam
, event
)) {
6975 return false; // fall through to DefWndProc
6978 // Polish up and send off the new event
6979 ModifierKeyState modifierKeyState
;
6980 modifierKeyState
.InitInputEvent(event
);
6982 event
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6983 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
6985 nsEventStatus status
;
6986 DispatchEvent(&event
, status
);
6987 if (status
== nsEventStatus_eIgnore
) {
6988 return false; // Ignored, fall through
6991 // Only close this if we process and return true.
6992 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
6994 return true; // Handled
6997 // WM_DESTROY event handler
6998 void nsWindow::OnDestroy() {
6999 mOnDestroyCalled
= true;
7001 // If this is a toplevel window, notify the taskbar concealer to clean up any
7004 TaskbarConcealer::OnWindowDestroyed(mWnd
);
7007 // Make sure we don't get destroyed in the process of tearing down.
7008 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
7010 // Dispatch the destroy notification.
7011 if (!mInDtor
) NotifyWindowDestroyed();
7013 // Prevent the widget from sending additional events.
7014 mWidgetListener
= nullptr;
7015 mAttachedWidgetListener
= nullptr;
7017 DestroyDirectManipulation();
7019 if (mWnd
== mLastKillFocusWindow
) {
7020 mLastKillFocusWindow
= nullptr;
7022 // Unregister notifications from terminal services
7023 ::WTSUnRegisterSessionNotification(mWnd
);
7025 // We will stop receiving native events after dissociating from our native
7026 // window. We will also disappear from the output of WinUtils::GetNSWindowPtr
7028 DissociateFromNativeWindow();
7030 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
7031 // can be cleared. (It's used in tracking windows for mouse events.)
7032 if (sCurrentWindow
== this) sCurrentWindow
= nullptr;
7034 // Disconnects us from our parent, will call our GetParent().
7035 nsBaseWidget::Destroy();
7037 // Release references to children, device context, toolkit, and app shell.
7038 nsBaseWidget::OnDestroy();
7040 // Clear our native parent handle.
7041 // XXX Windows will take care of this in the proper order, and
7042 // SetParent(nullptr)'s remove child on the parent already took place in
7043 // nsBaseWidget's Destroy call above.
7044 // SetParent(nullptr);
7047 // We have to destroy the native drag target before we null out our window
7049 EnableDragDrop(false);
7051 // If we're going away and for some reason we're still the rollup widget,
7052 // rollup and turn off capture.
7053 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7054 nsCOMPtr
<nsIWidget
> rollupWidget
;
7055 if (rollupListener
) {
7056 rollupWidget
= rollupListener
->GetRollupWidget();
7058 if (this == rollupWidget
) {
7059 rollupListener
->Rollup({});
7060 CaptureRollupEvents(false);
7063 IMEHandler::OnDestroyWindow(this);
7065 // Free GDI window class objects
7067 VERIFY(::DeleteObject(mBrush
));
7071 // Destroy any custom cursor resources.
7072 if (mCursor
.IsCustom()) {
7073 SetCursor(Cursor
{eCursor_standard
});
7076 if (mCompositorWidgetDelegate
) {
7077 mCompositorWidgetDelegate
->OnDestroyWindow();
7079 mBasicLayersSurface
= nullptr;
7081 // Finalize panning feedback to possibly restore window displacement
7082 mGesture
.PanFeedbackFinalize(mWnd
, true);
7084 // Clear the main HWND.
7088 // Send a resize message to the listener
7089 bool nsWindow::OnResize(const LayoutDeviceIntSize
& aSize
) {
7090 if (mCompositorWidgetDelegate
&&
7091 !mCompositorWidgetDelegate
->OnWindowResize(aSize
)) {
7095 bool result
= false;
7096 if (mWidgetListener
) {
7097 result
= mWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
7100 // If there is an attached view, inform it as well as the normal widget
7102 if (mAttachedWidgetListener
) {
7103 return mAttachedWidgetListener
->WindowResized(this, aSize
.width
,
7110 void nsWindow::OnSizeModeChange() {
7111 const nsSizeMode mode
= mFrameState
->GetSizeMode();
7113 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7114 ("nsWindow::OnSizeModeChange() sizeMode %d", mode
));
7116 if (NeedsToTrackWindowOcclusionState()) {
7117 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
7118 this, mode
!= nsSizeMode_Minimized
);
7120 wr::DebugFlags flags
{0};
7121 flags
._0
= gfx::gfxVars::WebRenderDebugFlags();
7122 bool debugEnabled
= bool(flags
& wr::DebugFlags::WINDOW_VISIBILITY_DBG
);
7123 if (debugEnabled
&& mCompositorWidgetDelegate
) {
7124 mCompositorWidgetDelegate
->NotifyVisibilityUpdated(mode
,
7129 if (mCompositorWidgetDelegate
) {
7130 mCompositorWidgetDelegate
->OnWindowModeChange(mode
);
7133 if (mWidgetListener
) {
7134 mWidgetListener
->SizeModeChanged(mode
);
7138 bool nsWindow::OnHotKey(WPARAM wParam
, LPARAM lParam
) { return true; }
7140 bool nsWindow::IsPopup() { return mWindowType
== WindowType::Popup
; }
7142 bool nsWindow::ShouldUseOffMainThreadCompositing() {
7143 if (mWindowType
== WindowType::Popup
&& mPopupType
== PopupType::Tooltip
) {
7147 // Content rendering of popup is always done by child window.
7148 // See nsDocumentViewer::ShouldAttachToTopLevel().
7149 if (mWindowType
== WindowType::Popup
&& !mIsChildWindow
) {
7150 MOZ_ASSERT(!mParent
);
7154 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
7157 void nsWindow::WindowUsesOMTC() {
7158 ULONG_PTR style
= ::GetClassLongPtr(mWnd
, GCL_STYLE
);
7160 NS_WARNING("Could not get window class style");
7163 style
|= CS_HREDRAW
| CS_VREDRAW
;
7164 DebugOnly
<ULONG_PTR
> result
= ::SetClassLongPtr(mWnd
, GCL_STYLE
, style
);
7165 NS_WARNING_ASSERTION(result
, "Could not reset window class style");
7168 void nsWindow::OnDPIChanged(int32_t x
, int32_t y
, int32_t width
,
7170 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
7171 // they remain tied to their original parent's resolution.
7172 if (mWindowType
== WindowType::Popup
) {
7175 if (StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
7178 mDefaultScale
= -1.0; // force recomputation of scale factor
7180 if (mResizeState
!= RESIZING
&&
7181 mFrameState
->GetSizeMode() == nsSizeMode_Normal
) {
7182 // Limit the position (if not in the middle of a drag-move) & size,
7183 // if it would overflow the destination screen
7184 nsCOMPtr
<nsIScreenManager
> sm
= do_GetService(sScreenManagerContractID
);
7186 nsCOMPtr
<nsIScreen
> screen
;
7187 sm
->ScreenForRect(x
, y
, width
, height
, getter_AddRefs(screen
));
7189 int32_t availLeft
, availTop
, availWidth
, availHeight
;
7190 screen
->GetAvailRect(&availLeft
, &availTop
, &availWidth
, &availHeight
);
7191 if (mResizeState
!= MOVING
) {
7192 x
= std::max(x
, availLeft
);
7193 y
= std::max(y
, availTop
);
7195 width
= std::min(width
, availWidth
);
7196 height
= std::min(height
, availHeight
);
7200 Resize(x
, y
, width
, height
, true);
7202 UpdateNonClientMargins();
7207 // Callback to generate OnCloakChanged pseudo-events.
7209 void nsWindow::OnCloakEvent(HWND aWnd
, bool aCloaked
) {
7210 MOZ_ASSERT(NS_IsMainThread());
7212 const char* const kEventName
= aCloaked
? "CLOAKED" : "UNCLOAKED";
7213 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(aWnd
);
7216 sCloakingLog
, LogLevel::Debug
,
7217 ("Received %s event for HWND %p (not an nsWindow)", kEventName
, aWnd
));
7221 const char* const kWasCloakedStr
= pWin
->mIsCloaked
? "cloaked" : "uncloaked";
7222 if (mozilla::IsCloaked(aWnd
) == pWin
->mIsCloaked
) {
7223 MOZ_LOG(sCloakingLog
, LogLevel::Debug
,
7224 ("Received redundant %s event for %s HWND %p; discarding",
7225 kEventName
, kWasCloakedStr
, aWnd
));
7230 sCloakingLog
, LogLevel::Info
,
7231 ("Received %s event for %s HWND %p", kEventName
, kWasCloakedStr
, aWnd
));
7233 // Cloaking events like the one we've just received are sent asynchronously.
7234 // Rather than process them one-by-one, we jump the gun a bit and perform
7235 // updates on all newly cloaked/uncloaked nsWindows at once. This also lets us
7236 // batch operations that consider more than one window's state.
7241 nsTArray
<Item
> changedWindows
;
7243 mozilla::EnumerateThreadWindows([&](HWND hwnd
) {
7244 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(hwnd
);
7249 const bool isCloaked
= mozilla::IsCloaked(hwnd
);
7250 if (isCloaked
!= pWin
->mIsCloaked
) {
7251 changedWindows
.AppendElement(Item
{pWin
, isCloaked
});
7255 if (changedWindows
.IsEmpty()) {
7259 for (const Item
& item
: changedWindows
) {
7260 item
.win
->OnCloakChanged(item
.nowCloaked
);
7263 nsWindow::TaskbarConcealer::OnCloakChanged();
7266 void nsWindow::OnCloakChanged(bool aCloaked
) {
7267 MOZ_LOG(sCloakingLog
, LogLevel::Info
,
7268 ("Calling OnCloakChanged(): HWND %p, aCloaked %s", mWnd
,
7269 aCloaked
? "true" : "false"));
7270 mIsCloaked
= aCloaked
;
7273 /**************************************************************
7274 **************************************************************
7276 ** BLOCK: IME management and accessibility
7278 ** Handles managing IME input and accessibility.
7280 **************************************************************
7281 **************************************************************/
7283 void nsWindow::SetInputContext(const InputContext
& aContext
,
7284 const InputContextAction
& aAction
) {
7285 InputContext newInputContext
= aContext
;
7286 IMEHandler::SetInputContext(this, newInputContext
, aAction
);
7287 mInputContext
= newInputContext
;
7290 InputContext
nsWindow::GetInputContext() {
7291 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7292 if (WinUtils::IsIMEEnabled(mInputContext
) && IMEHandler::GetOpenState(this)) {
7293 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN
;
7295 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7297 return mInputContext
;
7300 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
7301 return IMEHandler::GetNativeTextEventDispatcherListener();
7304 #ifdef ACCESSIBILITY
7306 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7307 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7309 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: " \
7311 aHwnd, ::GetParent(aHwnd), aWnd); \
7312 printf(" acc: %p", aAcc); \
7314 nsAutoString name; \
7316 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7322 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7325 a11y::LocalAccessible
* nsWindow::GetAccessible() {
7326 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7327 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled
)
7330 if (mInDtor
|| mOnDestroyCalled
|| mWindowType
== WindowType::Invisible
) {
7334 // In case of popup window return a popup accessible.
7335 nsView
* view
= nsView::GetViewFor(this);
7337 nsIFrame
* frame
= view
->GetFrame();
7338 if (frame
&& nsLayoutUtils::IsPopup(frame
)) {
7339 nsAccessibilityService
* accService
= GetOrCreateAccService();
7341 a11y::DocAccessible
* docAcc
=
7342 GetAccService()->GetDocAccessible(frame
->PresShell());
7346 docAcc
->GetAccessibleOrDescendant(frame
->GetContent()));
7347 return docAcc
->GetAccessibleOrDescendant(frame
->GetContent());
7353 // otherwise root document accessible.
7354 NS_LOG_WMGETOBJECT(this, mWnd
, GetRootAccessible());
7355 return GetRootAccessible();
7359 /**************************************************************
7360 **************************************************************
7362 ** BLOCK: Transparency
7364 ** Window transparency helpers.
7366 **************************************************************
7367 **************************************************************/
7369 void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode
) {
7370 if (aMode
== mTransparencyMode
) return;
7372 // stop on dialogs and popups!
7373 HWND hWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
7374 nsWindow
* parent
= WinUtils::GetNSWindowPtr(hWnd
);
7377 NS_WARNING("Trying to use transparent chrome in an embedded context");
7381 if (parent
!= this) {
7383 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7386 if (aMode
== TransparencyMode::Transparent
) {
7387 // If we're switching to the use of a transparent window, hide the chrome
7389 HideWindowChrome(true);
7390 } else if (mHideChrome
&&
7391 mTransparencyMode
== TransparencyMode::Transparent
) {
7392 // if we're switching out of transparent, re-enable our parent's chrome.
7393 HideWindowChrome(false);
7396 LONG_PTR style
= ::GetWindowLongPtrW(hWnd
, GWL_STYLE
),
7397 exStyle
= ::GetWindowLongPtr(hWnd
, GWL_EXSTYLE
);
7399 if (parent
->mIsVisible
) {
7400 style
|= WS_VISIBLE
;
7401 if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
7402 style
|= WS_MAXIMIZE
;
7403 } else if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
7404 style
|= WS_MINIMIZE
;
7408 if (aMode
== TransparencyMode::Transparent
)
7409 exStyle
|= WS_EX_LAYERED
;
7411 exStyle
&= ~WS_EX_LAYERED
;
7413 VERIFY_WINDOW_STYLE(style
);
7414 ::SetWindowLongPtrW(hWnd
, GWL_STYLE
, style
);
7415 ::SetWindowLongPtrW(hWnd
, GWL_EXSTYLE
, exStyle
);
7417 mTransparencyMode
= aMode
;
7419 if (mCompositorWidgetDelegate
) {
7420 mCompositorWidgetDelegate
->UpdateTransparency(aMode
);
7424 /**************************************************************
7425 **************************************************************
7427 ** BLOCK: Popup rollup hooks
7429 ** Deals with CaptureRollup on popup windows.
7431 **************************************************************
7432 **************************************************************/
7434 // Schedules a timer for a window, so we can rollup after processing the hook
7436 void nsWindow::ScheduleHookTimer(HWND aWnd
, UINT aMsgId
) {
7437 // In some cases multiple hooks may be scheduled
7438 // so ignore any other requests once one timer is scheduled
7439 if (sHookTimerId
== 0) {
7440 // Remember the window handle and the message ID to be used later
7441 sRollupMsgId
= aMsgId
;
7442 sRollupMsgWnd
= aWnd
;
7443 // Schedule native timer for doing the rollup after
7444 // this event is done being processed
7445 sHookTimerId
= ::SetTimer(nullptr, 0, 0, (TIMERPROC
)HookTimerForPopups
);
7446 NS_ASSERTION(sHookTimerId
, "Timer couldn't be created.");
7450 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7451 int gLastMsgCode
= 0;
7452 extern MSGFEventMsgInfo gMSGFEvents
[];
7455 // Process Menu messages, rollup when popup is clicked.
7456 LRESULT CALLBACK
nsWindow::MozSpecialMsgFilter(int code
, WPARAM wParam
,
7458 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7460 MSG
* pMsg
= (MSG
*)lParam
;
7463 while (gMSGFEvents
[inx
].mId
!= code
&& gMSGFEvents
[inx
].mStr
!= nullptr) {
7466 if (code
!= gLastMsgCode
) {
7467 if (gMSGFEvents
[inx
].mId
== code
) {
7469 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7470 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code
,
7471 gMSGFEvents
[inx
].mStr
, pMsg
->hwnd
));
7475 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7476 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code
,
7477 gMSGFEvents
[inx
].mId
, pMsg
->hwnd
));
7480 gLastMsgCode
= code
;
7482 PrintEvent(pMsg
->message
, FALSE
, FALSE
);
7484 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7486 if (sProcessHook
&& code
== MSGF_MENU
) {
7487 MSG
* pMsg
= (MSG
*)lParam
;
7488 ScheduleHookTimer(pMsg
->hwnd
, pMsg
->message
);
7491 return ::CallNextHookEx(sMsgFilterHook
, code
, wParam
, lParam
);
7494 // Process all mouse messages. Roll up when a click is in a native window
7495 // that doesn't have an nsIWidget.
7496 LRESULT CALLBACK
nsWindow::MozSpecialMouseProc(int code
, WPARAM wParam
,
7499 switch (WinUtils::GetNativeMessage(wParam
)) {
7500 case WM_LBUTTONDOWN
:
7501 case WM_RBUTTONDOWN
:
7502 case WM_MBUTTONDOWN
:
7504 case WM_MOUSEHWHEEL
: {
7505 MOUSEHOOKSTRUCT
* ms
= (MOUSEHOOKSTRUCT
*)lParam
;
7506 nsIWidget
* mozWin
= WinUtils::GetNSWindowPtr(ms
->hwnd
);
7508 ScheduleHookTimer(ms
->hwnd
, (UINT
)wParam
);
7514 return ::CallNextHookEx(sCallMouseHook
, code
, wParam
, lParam
);
7517 // Process all messages. Roll up when the window is moving, or
7518 // is resizing or when maximized or mininized.
7519 LRESULT CALLBACK
nsWindow::MozSpecialWndProc(int code
, WPARAM wParam
,
7521 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7523 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7524 PrintEvent(cwpt
->message
, FALSE
, FALSE
);
7529 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7530 if (cwpt
->message
== WM_MOVING
|| cwpt
->message
== WM_SIZING
||
7531 cwpt
->message
== WM_GETMINMAXINFO
) {
7532 ScheduleHookTimer(cwpt
->hwnd
, (UINT
)cwpt
->message
);
7536 return ::CallNextHookEx(sCallProcHook
, code
, wParam
, lParam
);
7539 // Register the special "hooks" for dropdown processing.
7540 void nsWindow::RegisterSpecialDropdownHooks() {
7541 NS_ASSERTION(!sMsgFilterHook
, "sMsgFilterHook must be NULL!");
7542 NS_ASSERTION(!sCallProcHook
, "sCallProcHook must be NULL!");
7544 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7546 // Install msg hook for moving the window and resizing
7547 if (!sMsgFilterHook
) {
7548 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7549 sMsgFilterHook
= SetWindowsHookEx(WH_MSGFILTER
, MozSpecialMsgFilter
,
7550 nullptr, GetCurrentThreadId());
7551 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7552 if (!sMsgFilterHook
) {
7553 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7554 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7559 // Install msg hook for menus
7560 if (!sCallProcHook
) {
7561 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7562 sCallProcHook
= SetWindowsHookEx(WH_CALLWNDPROC
, MozSpecialWndProc
, nullptr,
7563 GetCurrentThreadId());
7564 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7565 if (!sCallProcHook
) {
7567 gWindowsLog
, LogLevel::Info
,
7568 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7573 // Install msg hook for the mouse
7574 if (!sCallMouseHook
) {
7575 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7576 sCallMouseHook
= SetWindowsHookEx(WH_MOUSE
, MozSpecialMouseProc
, nullptr,
7577 GetCurrentThreadId());
7578 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7579 if (!sCallMouseHook
) {
7580 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7581 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7587 // Unhook special message hooks for dropdowns.
7588 void nsWindow::UnregisterSpecialDropdownHooks() {
7590 "***************** De-installing Msg Hooks ***************\n");
7592 if (sCallProcHook
) {
7593 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7594 if (!::UnhookWindowsHookEx(sCallProcHook
)) {
7595 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7597 sCallProcHook
= nullptr;
7600 if (sMsgFilterHook
) {
7601 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7602 if (!::UnhookWindowsHookEx(sMsgFilterHook
)) {
7603 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7605 sMsgFilterHook
= nullptr;
7608 if (sCallMouseHook
) {
7609 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7610 if (!::UnhookWindowsHookEx(sCallMouseHook
)) {
7611 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7613 sCallMouseHook
= nullptr;
7617 // This timer is designed to only fire one time at most each time a "hook"
7618 // function is used to rollup the dropdown. In some cases, the timer may be
7619 // scheduled from the hook, but that hook event or a subsequent event may roll
7620 // up the dropdown before this timer function is executed.
7622 // For example, if an MFC control takes focus, the combobox will lose focus and
7623 // rollup before this function fires.
7624 VOID CALLBACK
nsWindow::HookTimerForPopups(HWND hwnd
, UINT uMsg
, UINT idEvent
,
7626 if (sHookTimerId
!= 0) {
7627 // if the window is nullptr then we need to use the ID to kill the timer
7628 DebugOnly
<BOOL
> status
= ::KillTimer(nullptr, sHookTimerId
);
7629 NS_ASSERTION(status
, "Hook Timer was not killed.");
7633 if (sRollupMsgId
!= 0) {
7634 // Note: DealWithPopups does the check to make sure that the rollup widget
7636 LRESULT popupHandlingResult
;
7637 nsAutoRollup autoRollup
;
7638 DealWithPopups(sRollupMsgWnd
, sRollupMsgId
, 0, 0, &popupHandlingResult
);
7640 sRollupMsgWnd
= nullptr;
7644 static bool IsDifferentThreadWindow(HWND aWnd
) {
7645 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd
, nullptr);
7649 bool nsWindow::EventIsInsideWindow(nsWindow
* aWindow
,
7650 Maybe
<POINT
> aEventPoint
) {
7652 ::GetWindowRect(aWindow
->mWnd
, &r
);
7657 DWORD pos
= ::GetMessagePos();
7658 mp
.x
= GET_X_LPARAM(pos
);
7659 mp
.y
= GET_Y_LPARAM(pos
);
7662 auto margin
= aWindow
->mInputRegion
.mMargin
;
7670 // was the event inside this window?
7671 return static_cast<bool>(::PtInRect(&r
, mp
));
7675 bool nsWindow::GetPopupsToRollup(nsIRollupListener
* aRollupListener
,
7676 uint32_t* aPopupsToRollup
,
7677 Maybe
<POINT
> aEventPoint
) {
7678 // If we're dealing with menus, we probably have submenus and we don't want
7679 // to rollup some of them if the click is in a parent menu of the current
7681 *aPopupsToRollup
= UINT32_MAX
;
7682 AutoTArray
<nsIWidget
*, 5> widgetChain
;
7683 uint32_t sameTypeCount
= aRollupListener
->GetSubmenuWidgetChain(&widgetChain
);
7684 for (uint32_t i
= 0; i
< widgetChain
.Length(); ++i
) {
7685 nsIWidget
* widget
= widgetChain
[i
];
7686 if (EventIsInsideWindow(static_cast<nsWindow
*>(widget
), aEventPoint
)) {
7687 // Don't roll up if the mouse event occurred within a menu of the
7688 // same type. If the mouse event occurred in a menu higher than that,
7689 // roll up, but pass the number of popups to Rollup so that only those
7690 // of the same type close up.
7691 if (i
< sameTypeCount
) {
7695 *aPopupsToRollup
= sameTypeCount
;
7703 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd
) {
7704 // While popup is open, popup window might be activated by other application.
7705 // At this time, we need to take back focus to the previous window but it
7706 // causes flickering its nonclient area because WM_NCACTIVATE comes before
7707 // WM_ACTIVATE and we cannot know which window will take focus at receiving
7708 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
7710 // If non-popup window receives WM_NCACTIVATE at deactivating, default
7711 // wndproc shouldn't handle it as deactivating. Instead, at receiving
7712 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
7713 // This returns true if the window needs to handle WM_NCACTIVATE later.
7715 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7716 return window
&& !window
->IsPopup();
7719 static bool IsTouchSupportEnabled(HWND aWnd
) {
7720 nsWindow
* topWindow
=
7721 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd
, true));
7722 return topWindow
? topWindow
->IsTouchWindow() : false;
7725 static Maybe
<POINT
> GetSingleTouch(WPARAM wParam
, LPARAM lParam
) {
7727 uint32_t cInputs
= LOWORD(wParam
);
7732 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, &input
,
7733 sizeof(TOUCHINPUT
))) {
7735 ret
->x
= TOUCH_COORD_TO_PIXEL(input
.x
);
7736 ret
->y
= TOUCH_COORD_TO_PIXEL(input
.y
);
7738 // Note that we don't call CloseTouchInputHandle here because we need
7739 // to read the touch input info again in OnTouch later.
7744 bool nsWindow::DealWithPopups(HWND aWnd
, UINT aMessage
, WPARAM aWParam
,
7745 LPARAM aLParam
, LRESULT
* aResult
) {
7746 NS_ASSERTION(aResult
, "Bad outResult");
7748 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
7749 *aResult
= MA_NOACTIVATE
;
7751 if (!::IsWindowVisible(aWnd
)) {
7755 if (MOZ_UNLIKELY(aMessage
== WM_KILLFOCUS
)) {
7756 // NOTE: We deal with this here rather than on the switch below because we
7757 // want to do this even if there are no menus to rollup (tooltips don't set
7758 // the rollup listener etc).
7759 if (RefPtr pm
= nsXULPopupManager::GetInstance()) {
7760 pm
->RollupTooltips();
7764 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7765 NS_ENSURE_TRUE(rollupListener
, false);
7767 nsCOMPtr
<nsIWidget
> popup
= rollupListener
->GetRollupWidget();
7772 static bool sSendingNCACTIVATE
= false;
7773 static bool sPendingNCACTIVATE
= false;
7774 uint32_t popupsToRollup
= UINT32_MAX
;
7776 bool consumeRollupEvent
= false;
7777 Maybe
<POINT
> touchPoint
; // In screen coords.
7779 // If we rollup with animations but get occluded right away, we might not
7780 // advance the refresh driver enough for the animation to finish.
7781 auto allowAnimations
= nsIRollupListener::AllowAnimations::Yes
;
7782 nsWindow
* popupWindow
= static_cast<nsWindow
*>(popup
.get());
7783 UINT nativeMessage
= WinUtils::GetNativeMessage(aMessage
);
7784 switch (nativeMessage
) {
7786 if (!IsTouchSupportEnabled(aWnd
)) {
7787 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
7788 // compatibility mouse events will do it instead.
7791 touchPoint
= GetSingleTouch(aWParam
, aLParam
);
7796 case WM_LBUTTONDOWN
:
7797 case WM_RBUTTONDOWN
:
7798 case WM_MBUTTONDOWN
:
7799 case WM_NCLBUTTONDOWN
:
7800 case WM_NCRBUTTONDOWN
:
7801 case WM_NCMBUTTONDOWN
:
7802 if (nativeMessage
!= WM_TOUCH
&& IsTouchSupportEnabled(aWnd
) &&
7803 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
7804 // If any of these mouse events are really compatibility events that
7805 // Windows is sending for touch inputs, then don't allow them to dismiss
7806 // popups when APZ is enabled (instead we do the dismissing as part of
7807 // WM_TOUCH handling which is more correct).
7808 // If we don't do this, then when the user lifts their finger after a
7809 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
7810 // us will dismiss the contextmenu popup that we displayed as part of
7811 // handling the long-tap-up.
7814 if (!EventIsInsideWindow(popupWindow
, touchPoint
) &&
7815 GetPopupsToRollup(rollupListener
, &popupsToRollup
, touchPoint
)) {
7819 case WM_POINTERDOWN
: {
7820 WinPointerEvents pointerEvents
;
7821 if (!pointerEvents
.ShouldRollupOnPointerEvent(nativeMessage
, aWParam
)) {
7825 pt
.x
= GET_X_LPARAM(aLParam
);
7826 pt
.y
= GET_Y_LPARAM(aLParam
);
7827 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
7830 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
7831 // Don't roll up if the event is inside the popup window.
7835 case MOZ_WM_DMANIP
: {
7837 ::GetCursorPos(&pt
);
7838 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
7841 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
7842 // Don't roll up if the event is inside the popup window
7847 case WM_MOUSEHWHEEL
:
7848 // We need to check if the popup thinks that it should cause closing
7849 // itself when mouse wheel events are fired outside the rollup widget.
7850 if (!EventIsInsideWindow(popupWindow
)) {
7851 // Check if we should consume this event even if we don't roll-up:
7852 consumeRollupEvent
= rollupListener
->ShouldConsumeOnMouseWheelEvent();
7853 *aResult
= MA_ACTIVATE
;
7854 if (rollupListener
->ShouldRollupOnMouseWheelEvent() &&
7855 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7859 return consumeRollupEvent
;
7861 case WM_ACTIVATEAPP
:
7862 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7866 WndProcUrgentInvocation::Marker _marker
;
7868 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
7869 // because we cannot distinguish it's caused by mouse or not.
7870 if (LOWORD(aWParam
) == WA_ACTIVE
&& aLParam
) {
7871 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7872 if (window
&& (window
->IsPopup() || window
->mIsAlert
)) {
7873 // Cancel notifying widget listeners of deactivating the previous
7874 // active window (see WM_KILLFOCUS case in ProcessMessage()).
7875 sJustGotDeactivate
= false;
7876 // Reactivate the window later.
7877 ::PostMessageW(aWnd
, MOZ_WM_REACTIVATE
, aWParam
, aLParam
);
7880 // Don't rollup the popup when focus moves back to the parent window
7881 // from a popup because such case is caused by strange mouse drivers.
7882 nsWindow
* prevWindow
=
7883 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
7884 if (prevWindow
&& prevWindow
->IsPopup()) {
7885 // Consume this message here since previous window must not have
7886 // been inactivated since we've already stopped accepting the
7887 // inactivation below.
7890 } else if (LOWORD(aWParam
) == WA_INACTIVE
) {
7891 nsWindow
* activeWindow
=
7892 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
7893 if (sPendingNCACTIVATE
&& NeedsToHandleNCActivateDelayed(aWnd
)) {
7894 // If focus moves to non-popup widget or focusable popup, the window
7895 // needs to update its nonclient area.
7896 if (!activeWindow
|| !activeWindow
->IsPopup()) {
7897 sSendingNCACTIVATE
= true;
7898 ::SendMessageW(aWnd
, WM_NCACTIVATE
, false, 0);
7899 sSendingNCACTIVATE
= false;
7901 sPendingNCACTIVATE
= false;
7903 // If focus moves from/to popup, we don't need to rollup the popup
7904 // because such case is caused by strange mouse drivers. And in
7905 // such case, we should consume the message here since we need to
7906 // hide this odd focus move from our content. (If we didn't consume
7907 // the message here, ProcessMessage() will notify widget listener of
7908 // inactivation and that causes unnecessary reflow for supporting
7909 // -moz-window-inactive pseudo class.
7911 if (activeWindow
->IsPopup()) {
7914 nsWindow
* deactiveWindow
= WinUtils::GetNSWindowPtr(aWnd
);
7915 if (deactiveWindow
&& deactiveWindow
->IsPopup()) {
7919 } else if (LOWORD(aWParam
) == WA_CLICKACTIVE
) {
7920 // If the WM_ACTIVATE message is caused by a click in a popup,
7921 // we should not rollup any popups.
7922 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7923 if ((window
&& window
->IsPopup()) ||
7924 !GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7928 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7931 case MOZ_WM_REACTIVATE
:
7932 // The previous active window should take back focus.
7933 if (::IsWindow(reinterpret_cast<HWND
>(aLParam
))) {
7934 // FYI: Even without this API call, you see expected result (e.g., the
7935 // owner window of the popup keeps active without flickering
7936 // the non-client area). And also this causes initializing
7937 // TSF and it causes using CPU time a lot. However, even if we
7938 // consume WM_ACTIVE messages, native focus change has already
7939 // been occurred. I.e., a popup window is active now. Therefore,
7940 // you'll see some odd behavior if we don't reactivate the owner
7941 // window here. For example, if you do:
7942 // 1. Turn wheel on a bookmark panel.
7943 // 2. Turn wheel on another window.
7944 // then, you'll see that the another window becomes active but the
7945 // owner window of the bookmark panel looks still active and the
7946 // bookmark panel keeps open. The reason is that the first wheel
7947 // operation gives focus to the bookmark panel. Therefore, when
7948 // the next operation gives focus to the another window, previous
7949 // focus window is the bookmark panel (i.e., a popup window).
7950 // So, in this case, our hack around here prevents to inactivate
7951 // the owner window and roll up the bookmark panel.
7952 ::SetForegroundWindow(reinterpret_cast<HWND
>(aLParam
));
7957 if (!aWParam
&& !sSendingNCACTIVATE
&&
7958 NeedsToHandleNCActivateDelayed(aWnd
)) {
7959 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
7960 // nonclient area state change.
7961 ::DefWindowProcW(aWnd
, aMessage
, TRUE
, aLParam
);
7962 // Accept the deactivating because it's necessary to receive following
7965 sPendingNCACTIVATE
= true;
7970 case WM_MOUSEACTIVATE
:
7971 if (!EventIsInsideWindow(popupWindow
) &&
7972 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7973 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
7974 // of TweakUI is enabled. Then, check if the popup should be rolled up
7975 // with rollup listener. If not, just consume the message.
7976 if (HIWORD(aLParam
) == WM_MOUSEMOVE
&&
7977 !rollupListener
->ShouldRollupOnMouseActivate()) {
7980 // Otherwise, it should be handled by wndproc.
7984 // Prevent the click inside the popup from causing a change in window
7985 // activation. Since the popup is shown non-activated, we need to eat any
7986 // requests to activate the window while it is displayed. Windows will
7987 // automatically activate the popup on the mousedown otherwise.
7991 // If the window is being minimized, close popups.
7992 if (aLParam
== SW_PARENTCLOSING
) {
7993 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7999 // If focus moves to other window created in different process/thread,
8000 // e.g., a plugin window, popups should be rolled up.
8001 if (IsDifferentThreadWindow(reinterpret_cast<HWND
>(aWParam
))) {
8002 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
8015 // Only need to deal with the last rollup for left mouse down events.
8016 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
8018 nsIRollupListener::RollupOptions rollupOptions
{
8020 nsIRollupListener::FlushViews::Yes
,
8021 /* mPoint = */ nullptr,
8025 if (nativeMessage
== WM_TOUCH
|| nativeMessage
== WM_LBUTTONDOWN
||
8026 nativeMessage
== WM_POINTERDOWN
) {
8027 LayoutDeviceIntPoint pos
;
8028 if (nativeMessage
== WM_TOUCH
) {
8029 pos
.x
= touchPoint
->x
;
8030 pos
.y
= touchPoint
->y
;
8033 pt
.x
= GET_X_LPARAM(aLParam
);
8034 pt
.y
= GET_Y_LPARAM(aLParam
);
8035 // POINTERDOWN is already in screen coords.
8036 if (nativeMessage
== WM_LBUTTONDOWN
) {
8037 ::ClientToScreen(aWnd
, &pt
);
8039 pos
= LayoutDeviceIntPoint(pt
.x
, pt
.y
);
8042 rollupOptions
.mPoint
= &pos
;
8043 nsIContent
* lastRollup
= nullptr;
8044 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
, &lastRollup
);
8045 nsAutoRollup::SetLastRollup(lastRollup
);
8047 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
);
8050 // Tell hook to stop processing messages
8051 sProcessHook
= false;
8053 sRollupMsgWnd
= nullptr;
8055 // If we are NOT supposed to be consuming events, let it go through
8056 if (consumeRollupEvent
&& nativeMessage
!= WM_RBUTTONDOWN
) {
8057 *aResult
= MA_ACTIVATE
;
8064 /**************************************************************
8065 **************************************************************
8067 ** BLOCK: Misc. utility methods and functions.
8071 **************************************************************
8072 **************************************************************/
8074 // Note that the result of GetTopLevelWindow method can be different from the
8075 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8076 // window. Because our top level window may be contained in another window
8077 // which is not managed by us.
8078 nsWindow
* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup
) {
8079 nsWindow
* curWindow
= this;
8082 if (aStopOnDialogOrPopup
) {
8083 switch (curWindow
->mWindowType
) {
8084 case WindowType::Dialog
:
8085 case WindowType::Popup
:
8092 // Retrieve the top level parent or owner window
8093 nsWindow
* parentWindow
= curWindow
->GetParentWindow(true);
8095 if (!parentWindow
) return curWindow
;
8097 curWindow
= parentWindow
;
8101 // Set a flag if hwnd is a (non-popup) visible window from this process,
8102 // and bail out of the enumeration. Otherwise leave the flag unmodified
8103 // and continue the enumeration.
8104 // lParam must be a bool* pointing at the flag to be set.
8105 static BOOL CALLBACK
EnumVisibleWindowsProc(HWND hwnd
, LPARAM lParam
) {
8107 ::GetWindowThreadProcessId(hwnd
, &pid
);
8108 if (pid
== ::GetCurrentProcessId() && ::IsWindowVisible(hwnd
)) {
8109 // Don't count popups as visible windows, since they don't take focus,
8110 // in case we only have a popup visible (see bug 1554490 where the gfx
8111 // test window is an offscreen popup).
8112 nsWindow
* window
= WinUtils::GetNSWindowPtr(hwnd
);
8113 if (!window
|| !window
->IsPopup()) {
8114 bool* windowsVisible
= reinterpret_cast<bool*>(lParam
);
8115 *windowsVisible
= true;
8122 // Determine if it would be ok to activate a window, taking focus.
8123 // We want to avoid stealing focus from another app (bug 225305).
8124 bool nsWindow::CanTakeFocus() {
8125 HWND fgWnd
= ::GetForegroundWindow();
8127 // There is no foreground window, so don't worry about stealing focus.
8130 // We can take focus if the current foreground window is already from
8133 ::GetWindowThreadProcessId(fgWnd
, &pid
);
8134 if (pid
== ::GetCurrentProcessId()) {
8138 bool windowsVisible
= false;
8139 ::EnumWindows(EnumVisibleWindowsProc
,
8140 reinterpret_cast<LPARAM
>(&windowsVisible
));
8142 if (!windowsVisible
) {
8143 // We're probably creating our first visible window, allow that to
8150 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
8151 static const wchar_t* sMainWindowClass
= nullptr;
8152 if (!sMainWindowClass
) {
8153 nsAutoString className
;
8154 Preferences::GetString("ui.window_class_override", className
);
8155 if (!className
.IsEmpty()) {
8156 sMainWindowClass
= wcsdup(className
.get());
8158 sMainWindowClass
= kClassNameGeneral
;
8161 return sMainWindowClass
;
8164 LPARAM
nsWindow::lParamToScreen(LPARAM lParam
) {
8166 pt
.x
= GET_X_LPARAM(lParam
);
8167 pt
.y
= GET_Y_LPARAM(lParam
);
8168 ::ClientToScreen(mWnd
, &pt
);
8169 return MAKELPARAM(pt
.x
, pt
.y
);
8172 LPARAM
nsWindow::lParamToClient(LPARAM lParam
) {
8174 pt
.x
= GET_X_LPARAM(lParam
);
8175 pt
.y
= GET_Y_LPARAM(lParam
);
8176 ::ScreenToClient(mWnd
, &pt
);
8177 return MAKELPARAM(pt
.x
, pt
.y
);
8180 WPARAM
nsWindow::wParamFromGlobalMouseState() {
8183 if (!!::GetKeyState(VK_CONTROL
)) {
8184 result
|= MK_CONTROL
;
8187 if (!!::GetKeyState(VK_SHIFT
)) {
8191 if (!!::GetKeyState(VK_LBUTTON
)) {
8192 result
|= MK_LBUTTON
;
8195 if (!!::GetKeyState(VK_MBUTTON
)) {
8196 result
|= MK_MBUTTON
;
8199 if (!!::GetKeyState(VK_RBUTTON
)) {
8200 result
|= MK_RBUTTON
;
8203 if (!!::GetKeyState(VK_XBUTTON1
)) {
8204 result
|= MK_XBUTTON1
;
8207 if (!!::GetKeyState(VK_XBUTTON2
)) {
8208 result
|= MK_XBUTTON2
;
8214 // WORKAROUND FOR UNDOCUMENTED BEHAVIOR: `IFileDialog::Show` disables the
8215 // top-level ancestor of its provided owner-window. If the modal window's
8216 // container process crashes, it will never get a chance to undo that.
8218 // For simplicity's sake we simply unconditionally perform both the disabling
8219 // and reenabling here, synchronously, on the main thread, rather than leaving
8220 // it to happen in our asynchronously-operated IFileDialog.
8222 void nsWindow::PickerOpen() {
8223 AssertIsOnMainThread();
8225 // Disable the root-level window synchronously before any file-dialogs get a
8226 // chance to fight over doing it asynchronously.
8227 if (!mPickerDisplayCount
) {
8228 ::EnableWindow(::GetAncestor(GetWindowHandle(), GA_ROOT
), FALSE
);
8231 mPickerDisplayCount
++;
8234 void nsWindow::PickerClosed() {
8235 AssertIsOnMainThread();
8236 NS_ASSERTION(mPickerDisplayCount
> 0, "mPickerDisplayCount out of sync!");
8237 if (!mPickerDisplayCount
) return;
8238 mPickerDisplayCount
--;
8240 // Once all the file-dialogs are gone, reenable the root-level window.
8241 if (!mPickerDisplayCount
) {
8242 ::EnableWindow(::GetAncestor(GetWindowHandle(), GA_ROOT
), TRUE
);
8243 DispatchFocusToTopLevelWindow(true);
8246 if (!mPickerDisplayCount
&& mDestroyCalled
) {
8251 bool nsWindow::WidgetTypeSupportsAcceleration() {
8252 // We don't currently support using an accelerated layer manager with
8253 // transparent windows so don't even try. I'm also not sure if we even
8254 // want to support this case. See bug 593471.
8256 // Windows' support for transparent accelerated surfaces isn't great.
8257 // Some possible approaches:
8258 // - Readback the data and update it using
8259 // UpdateLayeredWindow/UpdateLayeredWindowIndirect
8260 // This is what WPF does. See
8261 // CD3DDeviceLevel1::PresentWithGDI/CD3DSwapChainWithSwDC in WpfGfx. The
8262 // rationale for not using IDirect3DSurface9::GetDC is explained here:
8263 // https://web.archive.org/web/20160521191104/https://blogs.msdn.microsoft.com/dwayneneed/2008/09/08/transparent-windows-in-wpf/
8264 // - Use D3D11_RESOURCE_MISC_GDI_COMPATIBLE, IDXGISurface1::GetDC(),
8265 // and UpdateLayeredWindowIndirect.
8266 // This is suggested here:
8267 // https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/december/windows-with-c-layered-windows-with-direct2d
8268 // but might have the same problem that IDirect3DSurface9::GetDC has.
8269 // - Creating the window with the WS_EX_NOREDIRECTIONBITMAP flag and use
8270 // DirectComposition.
8271 // Not supported on Win7.
8272 // - Using DwmExtendFrameIntoClientArea with negative margins and something
8273 // to turn off the glass effect.
8274 // This doesn't work when the DWM is not running (Win7)
8276 // Also see bug 1150376, D3D11 composition can cause issues on some devices
8277 // on Windows 7 where presentation fails randomly for windows with drop
8279 return mTransparencyMode
!= TransparencyMode::Transparent
&&
8280 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
8283 bool nsWindow::DispatchTouchEventFromWMPointer(
8284 UINT msg
, LPARAM aLParam
, const WinPointerInfo
& aPointerInfo
,
8285 mozilla::MouseButton aButton
) {
8286 MultiTouchInput::MultiTouchType touchType
;
8288 case WM_POINTERDOWN
:
8289 touchType
= MultiTouchInput::MULTITOUCH_START
;
8291 case WM_POINTERUPDATE
:
8292 if (aPointerInfo
.mPressure
== 0) {
8293 return false; // hover
8295 touchType
= MultiTouchInput::MULTITOUCH_MOVE
;
8298 touchType
= MultiTouchInput::MULTITOUCH_END
;
8304 nsPointWin touchPoint
;
8305 touchPoint
.x
= GET_X_LPARAM(aLParam
);
8306 touchPoint
.y
= GET_Y_LPARAM(aLParam
);
8307 touchPoint
.ScreenToClient(mWnd
);
8309 SingleTouchData
touchData(static_cast<int32_t>(aPointerInfo
.pointerId
),
8310 ScreenIntPoint::FromUnknownPoint(touchPoint
),
8311 ScreenSize(1, 1), // pixel size radius for pen
8312 0.0f
, // no radius rotation
8313 aPointerInfo
.mPressure
);
8314 touchData
.mTiltX
= aPointerInfo
.tiltX
;
8315 touchData
.mTiltY
= aPointerInfo
.tiltY
;
8316 touchData
.mTwist
= aPointerInfo
.twist
;
8318 MultiTouchInput touchInput
;
8319 touchInput
.mType
= touchType
;
8320 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
8321 touchInput
.mTouches
.AppendElement(touchData
);
8322 touchInput
.mButton
= aButton
;
8323 touchInput
.mButtons
= aPointerInfo
.mButtons
;
8325 // POINTER_INFO.dwKeyStates can't be used as it only supports Shift and Ctrl
8326 ModifierKeyState modifierKeyState
;
8327 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
8329 DispatchTouchInput(touchInput
, MouseEvent_Binding::MOZ_SOURCE_PEN
);
8333 static MouseButton
PenFlagsToMouseButton(PEN_FLAGS aPenFlags
) {
8334 // Theoretically flags can be set together but they do not
8335 if (aPenFlags
& PEN_FLAG_BARREL
) {
8336 return MouseButton::eSecondary
;
8338 if (aPenFlags
& PEN_FLAG_ERASER
) {
8339 return MouseButton::eEraser
;
8341 return MouseButton::ePrimary
;
8344 bool nsWindow::OnPointerEvents(UINT msg
, WPARAM aWParam
, LPARAM aLParam
) {
8346 // APZ is not available on context menu. Follow the behavior of touch input
8347 // which fallbacks to WM_LBUTTON* and WM_GESTURE, to keep consistency.
8350 if (!mPointerEvents
.ShouldHandleWinPointerMessages(msg
, aWParam
)) {
8353 if (!mPointerEvents
.ShouldFirePointerEventByWinPointerMessages()) {
8354 // We have to handle WM_POINTER* to fetch and cache pen related information
8355 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
8356 // handler. This is because Windows doesn't support ::DoDragDrop in the
8357 // touch or pen message handlers.
8358 mPointerEvents
.ConvertAndCachePointerInfo(msg
, aWParam
);
8359 // Don't consume the Windows WM_POINTER* messages
8363 uint32_t pointerId
= mPointerEvents
.GetPointerId(aWParam
);
8364 POINTER_PEN_INFO penInfo
{};
8365 if (!mPointerEvents
.GetPointerPenInfo(pointerId
, &penInfo
)) {
8369 // When dispatching mouse events with pen, there may be some
8370 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
8371 // small movements. Those events will reset sLastMousePoint and reset
8372 // sLastClickCount. To prevent that, we keep the last pen down position
8373 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
8374 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
8375 // eMouseMove for WM_POINTERUPDATE.
8376 static POINT sLastPointerDownPoint
= {0};
8378 // We don't support chorded buttons for pen. Keep the button at
8380 static mozilla::MouseButton sLastPenDownButton
= MouseButton::ePrimary
;
8381 static bool sPointerDown
= false;
8383 EventMessage message
;
8384 mozilla::MouseButton button
= MouseButton::ePrimary
;
8386 case WM_POINTERDOWN
: {
8387 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8388 GET_Y_LPARAM(aLParam
));
8389 sLastPointerDownPoint
.x
= eventPoint
.x
;
8390 sLastPointerDownPoint
.y
= eventPoint
.y
;
8391 message
= eMouseDown
;
8392 button
= PenFlagsToMouseButton(penInfo
.penFlags
);
8393 sLastPenDownButton
= button
;
8394 sPointerDown
= true;
8398 MOZ_ASSERT(sPointerDown
, "receive WM_POINTERUP w/o WM_POINTERDOWN");
8399 button
= sPointerDown
? sLastPenDownButton
: MouseButton::ePrimary
;
8400 sPointerDown
= false;
8402 case WM_POINTERUPDATE
:
8403 message
= eMouseMove
;
8405 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8406 GET_Y_LPARAM(aLParam
));
8407 int32_t movementX
= sLastPointerDownPoint
.x
> eventPoint
.x
8408 ? sLastPointerDownPoint
.x
- eventPoint
.x
.value
8409 : eventPoint
.x
.value
- sLastPointerDownPoint
.x
;
8410 int32_t movementY
= sLastPointerDownPoint
.y
> eventPoint
.y
8411 ? sLastPointerDownPoint
.y
- eventPoint
.y
.value
8412 : eventPoint
.y
.value
- sLastPointerDownPoint
.y
;
8413 bool insideMovementThreshold
=
8414 movementX
< (int32_t)::GetSystemMetrics(SM_CXDRAG
) &&
8415 movementY
< (int32_t)::GetSystemMetrics(SM_CYDRAG
);
8417 if (insideMovementThreshold
) {
8418 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
8419 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8422 button
= sLastPenDownButton
;
8425 case WM_POINTERLEAVE
:
8426 message
= eMouseExitFromWidget
;
8432 // Windows defines the pen pressure is normalized to a range between 0 and
8433 // 1024. Convert it to float.
8434 float pressure
= penInfo
.pressure
? (float)penInfo
.pressure
/ 1024 : 0;
8435 int16_t buttons
= sPointerDown
8436 ? nsContentUtils::GetButtonsFlagForButton(button
)
8437 : MouseButtonsFlag::eNoButtons
;
8438 WinPointerInfo
pointerInfo(pointerId
, penInfo
.tiltX
, penInfo
.tiltY
, pressure
,
8441 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_pen_info,
8442 // the rotation is normalized in a range of 0 to 359.
8443 MOZ_ASSERT(penInfo
.rotation
<= 359);
8444 pointerInfo
.twist
= (int32_t)penInfo
.rotation
;
8446 // Fire touch events but not when the barrel button is pressed.
8447 if (button
!= MouseButton::eSecondary
&&
8448 StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
8449 DispatchTouchEventFromWMPointer(msg
, aLParam
, pointerInfo
, button
)) {
8453 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8455 LPARAM newLParam
= lParamToClient(aLParam
);
8456 DispatchMouseEvent(message
, aWParam
, newLParam
, false, button
,
8457 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8459 if (button
== MouseButton::eSecondary
&& message
== eMouseUp
) {
8460 // Fire eContextMenu manually since consuming WM_POINTER* blocks
8462 DispatchMouseEvent(eContextMenu
, aWParam
, newLParam
, false, button
,
8463 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8465 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8470 void nsWindow::GetCompositorWidgetInitData(
8471 mozilla::widget::CompositorWidgetInitData
* aInitData
) {
8472 *aInitData
= WinCompositorWidgetInitData(
8473 reinterpret_cast<uintptr_t>(mWnd
),
8474 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
8475 mTransparencyMode
, mFrameState
->GetSizeMode());
8478 bool nsWindow::SynchronouslyRepaintOnResize() { return false; }
8480 void nsWindow::MaybeDispatchInitialFocusEvent() {
8481 if (mIsShowingPreXULSkeletonUI
&& ::GetActiveWindow() == mWnd
) {
8482 DispatchFocusToTopLevelWindow(true);
8486 already_AddRefed
<nsIWidget
> nsIWidget::CreateTopLevelWindow() {
8487 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
8488 return window
.forget();
8491 already_AddRefed
<nsIWidget
> nsIWidget::CreateChildWindow() {
8492 nsCOMPtr
<nsIWidget
> window
= new nsWindow(true);
8493 return window
.forget();
8497 bool nsWindow::InitTouchInjection() {
8498 if (!sTouchInjectInitialized
) {
8499 // Initialize touch injection on the first call
8500 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8505 InitializeTouchInjectionPtr func
=
8506 (InitializeTouchInjectionPtr
)GetProcAddress(hMod
,
8507 "InitializeTouchInjection");
8509 WinUtils::Log("InitializeTouchInjection not available.");
8513 if (!func(TOUCH_INJECT_MAX_POINTS
, TOUCH_FEEDBACK_DEFAULT
)) {
8514 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d",
8519 sInjectTouchFuncPtr
=
8520 (InjectTouchInputPtr
)GetProcAddress(hMod
, "InjectTouchInput");
8521 if (!sInjectTouchFuncPtr
) {
8522 WinUtils::Log("InjectTouchInput not available.");
8525 sTouchInjectInitialized
= true;
8530 bool nsWindow::InjectTouchPoint(uint32_t aId
, LayoutDeviceIntPoint
& aPoint
,
8531 POINTER_FLAGS aFlags
, uint32_t aPressure
,
8532 uint32_t aOrientation
) {
8533 if (aId
> TOUCH_INJECT_MAX_POINTS
) {
8534 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
8538 POINTER_TOUCH_INFO info
{};
8540 info
.touchFlags
= TOUCH_FLAG_NONE
;
8542 TOUCH_MASK_CONTACTAREA
| TOUCH_MASK_ORIENTATION
| TOUCH_MASK_PRESSURE
;
8543 info
.pressure
= aPressure
;
8544 info
.orientation
= aOrientation
;
8546 info
.pointerInfo
.pointerFlags
= aFlags
;
8547 info
.pointerInfo
.pointerType
= PT_TOUCH
;
8548 info
.pointerInfo
.pointerId
= aId
;
8549 info
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8550 info
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8552 info
.rcContact
.top
= info
.pointerInfo
.ptPixelLocation
.y
- 2;
8553 info
.rcContact
.bottom
= info
.pointerInfo
.ptPixelLocation
.y
+ 2;
8554 info
.rcContact
.left
= info
.pointerInfo
.ptPixelLocation
.x
- 2;
8555 info
.rcContact
.right
= info
.pointerInfo
.ptPixelLocation
.x
+ 2;
8557 for (int i
= 0; i
< 3; i
++) {
8558 if (sInjectTouchFuncPtr(1, &info
)) {
8561 DWORD error
= GetLastError();
8562 if (error
== ERROR_NOT_READY
&& i
< 2) {
8563 // We sent it too quickly after the previous injection (see bug 1535140
8564 // comment 10). On the first loop iteration we just yield (via Sleep(0))
8565 // and try again. If it happens again on the second loop iteration we
8566 // explicitly Sleep(1) and try again. If that doesn't work either we just
8571 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", error
);
8577 void nsWindow::ChangedDPI() {
8578 if (mWidgetListener
) {
8579 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
8580 presShell
->BackingScaleFactorChanged();
8583 NotifyAPZOfDPIChange();
8586 static Result
<POINTER_FLAGS
, nsresult
> PointerStateToFlag(
8587 nsWindow::TouchPointerState aPointerState
, bool isUpdate
) {
8588 bool hover
= aPointerState
& nsWindow::TOUCH_HOVER
;
8589 bool contact
= aPointerState
& nsWindow::TOUCH_CONTACT
;
8590 bool remove
= aPointerState
& nsWindow::TOUCH_REMOVE
;
8591 bool cancel
= aPointerState
& nsWindow::TOUCH_CANCEL
;
8593 POINTER_FLAGS flags
;
8595 // We know about this pointer, send an update
8596 flags
= POINTER_FLAG_UPDATE
;
8598 flags
|= POINTER_FLAG_INRANGE
;
8599 } else if (contact
) {
8600 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_INRANGE
;
8601 } else if (remove
) {
8602 flags
= POINTER_FLAG_UP
;
8606 flags
|= POINTER_FLAG_CANCELED
;
8609 // Missing init state, error out
8610 if (remove
|| cancel
) {
8611 return Err(NS_ERROR_INVALID_ARG
);
8614 // Create a new pointer
8615 flags
= POINTER_FLAG_INRANGE
;
8617 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_DOWN
;
8623 nsresult
nsWindow::SynthesizeNativeTouchPoint(
8624 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8625 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
8626 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
8627 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
8629 if (StaticPrefs::apz_test_fails_with_native_injection() ||
8630 !InitTouchInjection()) {
8631 // If we don't have touch injection from the OS, or if we are running a test
8632 // that cannot properly inject events to satisfy the OS requirements (see
8633 // bug 1313170) we can just fake it and synthesize the events from here.
8634 MOZ_ASSERT(NS_IsMainThread());
8635 if (aPointerState
== TOUCH_HOVER
) {
8636 return NS_ERROR_UNEXPECTED
;
8639 if (!mSynthesizedTouchInput
) {
8640 mSynthesizedTouchInput
= MakeUnique
<MultiTouchInput
>();
8643 WidgetEventTime time
= CurrentMessageWidgetEventTime();
8644 LayoutDeviceIntPoint pointInWindow
= aPoint
- WidgetToScreenOffset();
8645 MultiTouchInput inputToDispatch
= UpdateSynthesizedTouchState(
8646 mSynthesizedTouchInput
.get(), time
.mTimeStamp
, aPointerId
,
8647 aPointerState
, pointInWindow
, aPointerPressure
, aPointerOrientation
);
8648 DispatchTouchInput(inputToDispatch
);
8652 // win api expects a value from 0 to 1024. aPointerPressure is a value
8654 uint32_t pressure
= (uint32_t)ceil(aPointerPressure
* 1024);
8656 // If we already know about this pointer id get it's record
8657 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8658 POINTER_FLAGS flags
;
8659 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8660 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8661 if (result
.isOk()) {
8662 flags
= result
.unwrap();
8664 return result
.unwrapErr();
8668 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8669 PointerInfo::PointerType::TOUCH
));
8671 if (entry
.Data()->mType
!= PointerInfo::PointerType::TOUCH
) {
8672 return NS_ERROR_UNEXPECTED
;
8674 if (aPointerState
& TOUCH_REMOVE
) {
8675 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8676 // so shouldn't leak.
8681 return !InjectTouchPoint(aPointerId
, aPoint
, flags
, pressure
,
8682 aPointerOrientation
)
8683 ? NS_ERROR_UNEXPECTED
8688 nsresult
nsWindow::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
8689 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
8690 if (!sTouchInjectInitialized
) {
8694 // cancel all input points
8695 for (auto iter
= mActivePointers
.Iter(); !iter
.Done(); iter
.Next()) {
8696 auto* info
= iter
.UserData();
8697 if (info
->mType
!= PointerInfo::PointerType::TOUCH
) {
8700 InjectTouchPoint(info
->mPointerId
, info
->mPosition
, POINTER_FLAG_CANCELED
);
8704 nsBaseWidget::ClearNativeTouchSequence(nullptr);
8709 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8710 static CreateSyntheticPointerDevicePtr CreateSyntheticPointerDevice
;
8711 static DestroySyntheticPointerDevicePtr DestroySyntheticPointerDevice
;
8712 static InjectSyntheticPointerInputPtr InjectSyntheticPointerInput
;
8714 static HSYNTHETICPOINTERDEVICE sSyntheticPenDevice
;
8716 static bool InitPenInjection() {
8717 if (sSyntheticPenDevice
) {
8720 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8721 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8725 CreateSyntheticPointerDevice
=
8726 (CreateSyntheticPointerDevicePtr
)GetProcAddress(
8727 hMod
, "CreateSyntheticPointerDevice");
8728 if (!CreateSyntheticPointerDevice
) {
8729 WinUtils::Log("CreateSyntheticPointerDevice not available.");
8732 DestroySyntheticPointerDevice
=
8733 (DestroySyntheticPointerDevicePtr
)GetProcAddress(
8734 hMod
, "DestroySyntheticPointerDevice");
8735 if (!DestroySyntheticPointerDevice
) {
8736 WinUtils::Log("DestroySyntheticPointerDevice not available.");
8739 InjectSyntheticPointerInput
= (InjectSyntheticPointerInputPtr
)GetProcAddress(
8740 hMod
, "InjectSyntheticPointerInput");
8741 if (!InjectSyntheticPointerInput
) {
8742 WinUtils::Log("InjectSyntheticPointerInput not available.");
8746 sSyntheticPenDevice
=
8747 CreateSyntheticPointerDevice(PT_PEN
, 1, POINTER_FEEDBACK_DEFAULT
);
8748 return !!sSyntheticPenDevice
;
8751 nsresult
nsWindow::SynthesizeNativePenInput(
8752 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8753 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
8754 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
8755 AutoObserverNotifier
notifier(aObserver
, "peninput");
8756 if (!InitPenInjection()) {
8757 return NS_ERROR_UNEXPECTED
;
8760 // win api expects a value from 0 to 1024. aPointerPressure is a value
8762 uint32_t pressure
= (uint32_t)ceil(aPressure
* 1024);
8764 // If we already know about this pointer id get it's record
8765 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8766 POINTER_FLAGS flags
;
8767 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8768 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8769 if (result
.isOk()) {
8770 flags
= result
.unwrap();
8772 return result
.unwrapErr();
8776 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8777 PointerInfo::PointerType::PEN
));
8779 if (entry
.Data()->mType
!= PointerInfo::PointerType::PEN
) {
8780 return NS_ERROR_UNEXPECTED
;
8782 if (aPointerState
& TOUCH_REMOVE
) {
8783 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8784 // so shouldn't leak.
8789 POINTER_TYPE_INFO info
{};
8792 info
.penInfo
.pointerInfo
.pointerType
= PT_PEN
;
8793 info
.penInfo
.pointerInfo
.pointerFlags
= flags
;
8794 info
.penInfo
.pointerInfo
.pointerId
= aPointerId
;
8795 info
.penInfo
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8796 info
.penInfo
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8798 info
.penInfo
.penFlags
= PEN_FLAG_NONE
;
8799 // PEN_FLAG_ERASER is not supported this way, unfortunately.
8801 info
.penInfo
.penFlags
|= PEN_FLAG_BARREL
;
8803 info
.penInfo
.penMask
= PEN_MASK_PRESSURE
| PEN_MASK_ROTATION
|
8804 PEN_MASK_TILT_X
| PEN_MASK_TILT_Y
;
8805 info
.penInfo
.pressure
= pressure
;
8806 info
.penInfo
.rotation
= aRotation
;
8807 info
.penInfo
.tiltX
= aTiltX
;
8808 info
.penInfo
.tiltY
= aTiltY
;
8810 return InjectSyntheticPointerInput(sSyntheticPenDevice
, &info
, 1)
8812 : NS_ERROR_UNEXPECTED
;
8816 bool nsWindow::HandleAppCommandMsg(const MSG
& aAppCommandMsg
,
8817 LRESULT
* aRetValue
) {
8818 ModifierKeyState modKeyState
;
8819 NativeKey
nativeKey(this, aAppCommandMsg
, modKeyState
);
8820 bool consumed
= nativeKey
.HandleAppCommandMessage();
8821 *aRetValue
= consumed
? 1 : 0;
8826 nsresult
nsWindow::SetHiDPIMode(bool aHiDPI
) {
8827 return WinUtils::SetHiDPIMode(aHiDPI
);
8830 nsresult
nsWindow::RestoreHiDPIMode() { return WinUtils::RestoreHiDPIMode(); }
8833 mozilla::Maybe
<UINT
> nsWindow::GetHiddenTaskbarEdge() {
8834 HMONITOR windowMonitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONEAREST
);
8836 // Check all four sides of our monitor for an appbar. Skip any that aren't
8837 // the system taskbar.
8839 mi
.cbSize
= sizeof(MONITORINFO
);
8840 ::GetMonitorInfo(windowMonitor
, &mi
);
8842 APPBARDATA appBarData
;
8843 appBarData
.cbSize
= sizeof(appBarData
);
8844 appBarData
.rc
= mi
.rcMonitor
;
8845 const auto kEdges
= {ABE_BOTTOM
, ABE_TOP
, ABE_LEFT
, ABE_RIGHT
};
8846 for (auto edge
: kEdges
) {
8847 appBarData
.uEdge
= edge
;
8848 HWND appBarHwnd
= (HWND
)SHAppBarMessage(ABM_GETAUTOHIDEBAREX
, &appBarData
);
8850 nsAutoString className
;
8851 if (WinUtils::GetClassName(appBarHwnd
, className
)) {
8852 if (className
.Equals(L
"Shell_TrayWnd") ||
8853 className
.Equals(L
"Shell_SecondaryTrayWnd")) {
8863 static nsSizeMode
GetSizeModeForWindowFrame(HWND aWnd
, bool aFullscreenMode
) {
8865 pl
.length
= sizeof(pl
);
8866 ::GetWindowPlacement(aWnd
, &pl
);
8868 if (pl
.showCmd
== SW_SHOWMINIMIZED
) {
8869 return nsSizeMode_Minimized
;
8870 } else if (aFullscreenMode
) {
8871 return nsSizeMode_Fullscreen
;
8872 } else if (pl
.showCmd
== SW_SHOWMAXIMIZED
) {
8873 return nsSizeMode_Maximized
;
8875 return nsSizeMode_Normal
;
8879 static void ShowWindowWithMode(HWND aWnd
, nsSizeMode aMode
) {
8880 // This will likely cause a callback to
8881 // nsWindow::FrameState::{OnFrameChanging() and OnFrameChanged()}
8883 case nsSizeMode_Fullscreen
:
8884 ::ShowWindow(aWnd
, SW_SHOW
);
8887 case nsSizeMode_Maximized
:
8888 ::ShowWindow(aWnd
, SW_MAXIMIZE
);
8891 case nsSizeMode_Minimized
:
8892 ::ShowWindow(aWnd
, SW_MINIMIZE
);
8896 // Don't call ::ShowWindow if we're trying to "restore" a window that is
8897 // already in a normal state. Prevents a bug where snapping to one side
8898 // of the screen and then minimizing would cause Windows to forget our
8899 // window's correct restored position/size.
8900 if (GetCurrentShowCmd(aWnd
) != SW_SHOWNORMAL
) {
8901 ::ShowWindow(aWnd
, SW_RESTORE
);
8906 nsWindow::FrameState::FrameState(nsWindow
* aWindow
) : mWindow(aWindow
) {}
8908 nsSizeMode
nsWindow::FrameState::GetSizeMode() const { return mSizeMode
; }
8910 void nsWindow::FrameState::CheckInvariant() const {
8911 MOZ_ASSERT(mSizeMode
>= 0 && mSizeMode
< nsSizeMode_Invalid
);
8912 MOZ_ASSERT(mLastSizeMode
>= 0 && mLastSizeMode
< nsSizeMode_Invalid
);
8913 MOZ_ASSERT(mPreFullscreenSizeMode
>= 0 &&
8914 mPreFullscreenSizeMode
< nsSizeMode_Invalid
);
8915 MOZ_ASSERT(mWindow
);
8917 // We should never observe fullscreen sizemode unless fullscreen is enabled
8918 MOZ_ASSERT_IF(mSizeMode
== nsSizeMode_Fullscreen
, mFullscreenMode
);
8919 MOZ_ASSERT_IF(!mFullscreenMode
, mSizeMode
!= nsSizeMode_Fullscreen
);
8921 // Something went wrong if we somehow saved fullscreen mode when we are
8922 // changing into fullscreen mode
8923 MOZ_ASSERT(mPreFullscreenSizeMode
!= nsSizeMode_Fullscreen
);
8926 void nsWindow::FrameState::ConsumePreXULSkeletonState(bool aWasMaximized
) {
8927 mSizeMode
= aWasMaximized
? nsSizeMode_Maximized
: nsSizeMode_Normal
;
8930 void nsWindow::FrameState::EnsureSizeMode(nsSizeMode aMode
,
8931 DoShowWindow aDoShowWindow
) {
8932 if (mSizeMode
== aMode
) {
8936 if (StaticPrefs::widget_windows_fullscreen_remind_taskbar()) {
8937 // If we're unminimizing a window, asynchronously notify the taskbar after
8938 // the message has been processed. This redundant notification works around
8939 // a race condition in explorer.exe. (See bug 1835851, or comments in
8940 // TaskbarConcealer.)
8942 // Note that we notify regardless of `aMode`: unminimizing a non-fullscreen
8943 // window can also affect the correct taskbar state, yet fail to affect the
8944 // current taskbar state.
8945 if (mSizeMode
== nsSizeMode_Minimized
) {
8946 ::PostMessage(mWindow
->mWnd
, MOZ_WM_FULLSCREEN_STATE_UPDATE
, 0, 0);
8950 if (aMode
== nsSizeMode_Fullscreen
) {
8951 EnsureFullscreenMode(true, aDoShowWindow
);
8952 MOZ_ASSERT(mSizeMode
== nsSizeMode_Fullscreen
);
8953 } else if (mSizeMode
== nsSizeMode_Fullscreen
&& aMode
== nsSizeMode_Normal
) {
8954 // If we are in fullscreen mode, minimize should work like normal and
8955 // return us to fullscreen mode when unminimized. Maximize isn't really
8956 // available and won't do anything. "Restore" should do the same thing as
8957 // requesting to end fullscreen.
8958 EnsureFullscreenMode(false, aDoShowWindow
);
8960 SetSizeModeInternal(aMode
, aDoShowWindow
);
8964 void nsWindow::FrameState::EnsureFullscreenMode(bool aFullScreen
,
8965 DoShowWindow aDoShowWindow
) {
8966 const bool changed
= aFullScreen
!= mFullscreenMode
;
8967 if (changed
&& aFullScreen
) {
8968 // Save the size mode from before fullscreen.
8969 mPreFullscreenSizeMode
= mSizeMode
;
8971 mFullscreenMode
= aFullScreen
;
8972 if (changed
|| aFullScreen
) {
8973 // NOTE(emilio): When minimizing a fullscreen window we remain with
8974 // mFullscreenMode = true, but mSizeMode = nsSizeMode_Minimized. We need to
8975 // make sure to call SetSizeModeInternal even if mFullscreenMode didn't
8976 // change, to ensure we actually end up with a fullscreen sizemode when
8977 // restoring a window from that state.
8978 SetSizeModeInternal(
8979 aFullScreen
? nsSizeMode_Fullscreen
: mPreFullscreenSizeMode
,
8984 void nsWindow::FrameState::OnFrameChanging() {
8985 const nsSizeMode newSizeMode
=
8986 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
8987 EnsureSizeMode(newSizeMode
);
8988 mWindow
->UpdateNonClientMargins(false);
8991 void nsWindow::FrameState::OnFrameChanged() {
8992 // We don't want to perform the ShowWindow ourselves if we're on the frame
8993 // changed message. Windows has done the frame change for us, and we take care
8994 // of activating as needed. We also don't want to potentially trigger
8995 // more focus / restore. Among other things, this addresses a bug on Win7
8996 // related to window docking. (bug 489258)
8997 const auto newSizeMode
=
8998 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
8999 EnsureSizeMode(newSizeMode
, DoShowWindow::No
);
9001 // If window was restored, activate the window now to get correct attributes.
9002 if (mWindow
->mIsVisible
&& mWindow
->IsForegroundWindow() &&
9003 mLastSizeMode
== nsSizeMode_Minimized
&&
9004 mSizeMode
!= nsSizeMode_Minimized
) {
9005 mWindow
->DispatchFocusToTopLevelWindow(true);
9007 mLastSizeMode
= mSizeMode
;
9010 static void MaybeLogSizeMode(nsSizeMode aMode
) {
9011 #ifdef WINSTATE_DEBUG_OUTPUT
9012 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** SizeMode: %d\n", int(aMode
)));
9016 void nsWindow::FrameState::SetSizeModeInternal(nsSizeMode aMode
,
9017 DoShowWindow aDoShowWindow
) {
9018 if (mSizeMode
== aMode
) {
9022 const auto oldSizeMode
= mSizeMode
;
9023 const bool fullscreenChange
=
9024 mSizeMode
== nsSizeMode_Fullscreen
|| aMode
== nsSizeMode_Fullscreen
;
9025 const bool fullscreen
= aMode
== nsSizeMode_Fullscreen
;
9027 mLastSizeMode
= mSizeMode
;
9030 MaybeLogSizeMode(mSizeMode
);
9032 if (bool(aDoShowWindow
) && mWindow
->mIsVisible
) {
9033 ShowWindowWithMode(mWindow
->mWnd
, aMode
);
9036 mWindow
->UpdateNonClientMargins(false);
9038 if (fullscreenChange
) {
9039 mWindow
->OnFullscreenChanged(oldSizeMode
, fullscreen
);
9042 mWindow
->OnSizeModeChange();
9045 void nsWindow::ContextMenuPreventer::Update(
9046 const WidgetMouseEvent
& aEvent
,
9047 const nsIWidget::ContentAndAPZEventStatus
& aEventStatus
) {
9048 mNeedsToPreventContextMenu
=
9049 aEvent
.mMessage
== eMouseUp
&&
9050 aEvent
.mButton
== MouseButton::eSecondary
&&
9051 aEvent
.mInputSource
== MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
9052 aEventStatus
.mApzStatus
== nsEventStatus_eConsumeNoDefault
;