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"
82 #include "nsWindowTaskbarConcealer.h"
83 #include "nsAppRunner.h"
94 #include <propvarutil.h>
97 #include "mozilla/Logging.h"
101 #include "mozilla/WidgetTraceEvent.h"
102 #include "nsContentUtils.h"
103 #include "nsISupportsPrimitives.h"
104 #include "nsITheme.h"
105 #include "nsIObserverService.h"
106 #include "nsIScreenManager.h"
107 #include "imgIContainer.h"
109 #include "nsIRollupListener.h"
110 #include "nsIClipboard.h"
111 #include "WinMouseScrollHandler.h"
112 #include "nsFontMetrics.h"
113 #include "nsIFontEnumerator.h"
116 #include "nsThreadUtils.h"
117 #include "nsNativeCharsetUtils.h"
118 #include "nsGkAtoms.h"
120 #include "nsAppDirectoryServiceDefs.h"
121 #include "nsWidgetsCID.h"
122 #include "nsTHashtable.h"
123 #include "nsHashKeys.h"
124 #include "nsString.h"
125 #include "mozilla/Components.h"
126 #include "nsNativeThemeWin.h"
127 #include "nsXULPopupManager.h"
128 #include "nsWindowsDllInterceptor.h"
129 #include "nsLayoutUtils.h"
131 #include "nsWindowGfx.h"
132 #include "gfxWindowsPlatform.h"
133 #include "gfxDWriteFonts.h"
134 #include "nsPrintfCString.h"
135 #include "mozilla/Preferences.h"
136 #include "SystemTimeConverter.h"
137 #include "WinTaskbar.h"
138 #include "WidgetUtils.h"
139 #include "WinWindowOcclusionTracker.h"
140 #include "nsIWidgetListener.h"
141 #include "mozilla/dom/Document.h"
142 #include "mozilla/dom/MouseEventBinding.h"
143 #include "mozilla/dom/Touch.h"
144 #include "mozilla/gfx/2D.h"
145 #include "mozilla/gfx/GPUProcessManager.h"
146 #include "mozilla/intl/LocaleService.h"
147 #include "mozilla/layers/WebRenderLayerManager.h"
148 #include "mozilla/WindowsVersion.h"
149 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
150 #include "mozilla/TextEventDispatcherListener.h"
151 #include "mozilla/widget/nsAutoRollup.h"
152 #include "mozilla/widget/PlatformWidgetTypes.h"
153 #include "mozilla/widget/Screen.h"
154 #include "nsStyleConsts.h"
155 #include "nsBidiKeyboard.h"
156 #include "nsStyleConsts.h"
157 #include "gfxConfig.h"
158 #include "InProcessWinCompositorWidget.h"
159 #include "InputDeviceUtils.h"
160 #include "ScreenHelperWin.h"
161 #include "mozilla/StaticPrefs_apz.h"
162 #include "mozilla/StaticPrefs_dom.h"
163 #include "mozilla/StaticPrefs_gfx.h"
164 #include "mozilla/StaticPrefs_layout.h"
165 #include "mozilla/StaticPrefs_widget.h"
166 #include "nsNativeAppSupportWin.h"
167 #include "mozilla/browser/NimbusFeatures.h"
169 #include "nsIGfxInfo.h"
170 #include "nsUXThemeConstants.h"
171 #include "KeyboardLayout.h"
172 #include "nsNativeDragTarget.h"
173 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
175 #include <richedit.h>
177 #if defined(ACCESSIBILITY)
180 # include "mozilla/a11y/Logging.h"
184 # include <winuser.h>
185 # include "nsAccessibilityService.h"
186 # include "mozilla/a11y/DocAccessible.h"
187 # include "mozilla/a11y/LazyInstantiator.h"
188 # include "mozilla/a11y/Platform.h"
189 # if !defined(WINABLEAPI)
190 # include <winable.h>
191 # endif // !defined(WINABLEAPI)
192 #endif // defined(ACCESSIBILITY)
194 #include "WindowsUIUtils.h"
196 #include "nsWindowDefs.h"
198 #include "nsCrashOnException.h"
200 #include "nsIContent.h"
202 #include "mozilla/BackgroundHangMonitor.h"
203 #include "WinIMEHandler.h"
209 // ERROR from wingdi.h (below) gets undefined by some code.
211 // #define RGN_ERROR ERROR
214 #if !defined(SM_CONVERTIBLESLATEMODE)
215 # define SM_CONVERTIBLESLATEMODE 0x2003
218 #include "mozilla/gfx/DeviceManagerDx.h"
219 #include "mozilla/layers/APZInputBridge.h"
220 #include "mozilla/layers/InputAPZContext.h"
221 #include "mozilla/layers/KnowsCompositor.h"
222 #include "InputData.h"
224 #include "mozilla/TaskController.h"
225 #include "mozilla/Telemetry.h"
226 #include "mozilla/webrender/WebRenderAPI.h"
227 #include "mozilla/layers/IAPZCTreeManager.h"
229 #include "DirectManipulationOwner.h"
231 using namespace mozilla
;
232 using namespace mozilla::dom
;
233 using namespace mozilla::gfx
;
234 using namespace mozilla::layers
;
235 using namespace mozilla::widget
;
236 using namespace mozilla::plugins
;
238 /**************************************************************
239 **************************************************************
243 ** nsWindow Class static initializations and global variables.
245 **************************************************************
246 **************************************************************/
248 /**************************************************************
250 * SECTION: nsWindow statics
252 **************************************************************/
253 static const wchar_t kUser32LibName
[] = L
"user32.dll";
255 uint32_t nsWindow::sInstanceCount
= 0;
256 bool nsWindow::sIsOleInitialized
= false;
257 nsIWidget::Cursor
nsWindow::sCurrentCursor
= {};
258 nsWindow
* nsWindow::sCurrentWindow
= nullptr;
259 bool nsWindow::sJustGotDeactivate
= false;
260 bool nsWindow::sJustGotActivate
= false;
261 bool nsWindow::sIsInMouseCapture
= false;
263 // Urgent-message reentrancy depth for the static `WindowProc` callback.
265 // Three unfortunate facts collide:
267 // 𝛼) Some messages must be processed promptly. If not, Windows will leave the
268 // receiving window in an intermediate, and potentially unusable, state until
269 // the WindowProc invocation that is handling it returns.
271 // 𝛽) Some messages have indefinitely long processing time. These are mostly
272 // messages which may cause us to enter a nested modal loop (via
273 // `SpinEventLoopUntil` or similar).
275 // 𝛾) Sometimes, messages skip the queue entirely. Our `WindowProc` may be
276 // reentrantly reinvoked from the kernel while we're blocking _on_ the
277 // kernel, even briefly, during processing of other messages. (Relevant
278 // search term: `KeUserModeCallback`.)
280 // The nightmare scenario, then, is that during processing of an 𝛼-message, we
281 // briefly become blocked (e.g., by calling `::SendMessageW()`), and the kernel
282 // takes that opportunity to use 𝛾 to hand us a 𝛽-message. (Concretely, see
285 // There is little we can do to prevent the first half of this scenario. 𝛼) and
286 // 𝛾) are effectively immutable facts of Windows, and we sometimes legitimately
287 // need to make blocking calls to process 𝛼-messages. (We may not even be aware
288 // that we're making such calls, if they're undocumented implementation details
291 // In an ideal world, WindowProc would always return promptly (or at least in
292 // bounded time), and 𝛽-messages would not _per se_ exist; long-running modal
293 // states would instead be implemented in async fashion. In practice, that's far
294 // easier said than done -- replacing existing uses of `SpinEventLoopUntil` _et
295 // al._ with asynchronous mechanisms is a collection of mostly-unrelated cross-
296 // cutting architectural tasks, each of potentially unbounded scope. For now,
297 // and for the foreseeable future, we're stuck with them.
299 // We therefore simply punt. More specifically: if a known 𝛽-message jumps the
300 // queue to come in while we're in the middle of processing a known 𝛼-message,
302 // * properly queue the message for processing later;
303 // * respond to the 𝛽-message as though we actually had processed it; and
304 // * just hope that it can wait until we get around to it.
306 // The word "known" requires a bit of justification. There is no canonical set
307 // of 𝛼-messages, nor is the set of 𝛽-messages fixed (or even demarcable). We
308 // can't safely assume that all messages are 𝛼-messages, as that could cause
309 // 𝛽-messages to be arbitrarily and surprisingly delayed whenever any nested
310 // event loop is active. We also can't assume all messages are 𝛽-messages,
311 // since one 𝛼-message jumping the queue while processing another 𝛼-message is
312 // part of normal and required operation for windowed Windows applications.
314 // So we simply add messages to those sets as we identify them. (Or, preferably,
315 // rework the 𝛽-message's handling to make it no longer 𝛽. But see above.)
319 // The actual value of `sDepth` is the number of active invocations of
320 // `WindowProc` that are processing known 𝛼-messages.
321 size_t nsWindow::WndProcUrgentInvocation::sDepth
= 0;
323 // Hook Data Members for Dropdowns. sProcessHook Tells the
324 // hook methods whether they should be processing the hook
326 HHOOK
nsWindow::sMsgFilterHook
= nullptr;
327 HHOOK
nsWindow::sCallProcHook
= nullptr;
328 HHOOK
nsWindow::sCallMouseHook
= nullptr;
329 bool nsWindow::sProcessHook
= false;
330 UINT
nsWindow::sRollupMsgId
= 0;
331 HWND
nsWindow::sRollupMsgWnd
= nullptr;
332 UINT
nsWindow::sHookTimerId
= 0;
334 // Used to prevent dispatching mouse events that do not originate from user
336 POINT
nsWindow::sLastMouseMovePoint
= {0};
338 bool nsWindow::sIsRestoringSession
= false;
340 bool nsWindow::sTouchInjectInitialized
= false;
341 InjectTouchInputPtr
nsWindow::sInjectTouchFuncPtr
;
343 static SystemTimeConverter
<DWORD
>& TimeConverter() {
344 static SystemTimeConverter
<DWORD
> timeConverterSingleton
;
345 return timeConverterSingleton
;
348 // Global event hook for window cloaking. Never deregistered.
349 // - `Nothing` if not yet set.
350 // - `Some(nullptr)` if no attempt should be made to set it.
351 static mozilla::Maybe
<HWINEVENTHOOK
> sWinCloakEventHook
= Nothing();
352 static mozilla::LazyLogModule
sCloakingLog("DWMCloaking");
356 class CurrentWindowsTimeGetter
{
358 explicit CurrentWindowsTimeGetter(HWND aWnd
) : mWnd(aWnd
) {}
360 DWORD
GetCurrentTime() const { return ::GetTickCount(); }
362 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp
& aNow
) {
363 DWORD currentTime
= GetCurrentTime();
364 if (sBackwardsSkewStamp
&& currentTime
== sLastPostTime
) {
365 // There's already one inflight with this timestamp. Don't
369 sBackwardsSkewStamp
= Some(aNow
);
370 sLastPostTime
= currentTime
;
371 static_assert(sizeof(WPARAM
) >= sizeof(DWORD
),
372 "Can't fit a DWORD in a WPARAM");
373 ::PostMessage(mWnd
, MOZ_WM_SKEWFIX
, sLastPostTime
, 0);
376 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime
,
377 TimeStamp
* aOutSkewStamp
) {
378 if (aPostTime
!= sLastPostTime
) {
379 // The SKEWFIX message is stale; we've sent a new one since then.
383 MOZ_ASSERT(sBackwardsSkewStamp
);
384 *aOutSkewStamp
= sBackwardsSkewStamp
.value();
385 sBackwardsSkewStamp
= Nothing();
390 static Maybe
<TimeStamp
> sBackwardsSkewStamp
;
391 static DWORD sLastPostTime
;
395 Maybe
<TimeStamp
> CurrentWindowsTimeGetter::sBackwardsSkewStamp
;
396 DWORD
CurrentWindowsTimeGetter::sLastPostTime
= 0;
398 } // namespace mozilla
400 /**************************************************************
402 * SECTION: globals variables
404 **************************************************************/
406 static const char* sScreenManagerContractID
=
407 "@mozilla.org/gfx/screenmanager;1";
409 extern mozilla::LazyLogModule gWindowsLog
;
411 static NS_DEFINE_CID(kCClipboardCID
, NS_CLIPBOARD_CID
);
413 // General purpose user32.dll hook object
414 static WindowsDllInterceptor sUser32Intercept
;
416 // When the client area is extended out into the default window frame area,
417 // this is the minimum amount of space along the edge of resizable windows
418 // we will always display a resize cursor in, regardless of the underlying
420 static const int32_t kResizableBorderMinSize
= 3;
422 // Getting this object from the window server can be expensive. Keep it
423 // around, also get it off the main thread. (See bug 1640852)
424 StaticRefPtr
<IVirtualDesktopManager
> gVirtualDesktopManager
;
425 static bool gInitializedVirtualDesktopManager
= false;
427 // We should never really try to accelerate windows bigger than this. In some
428 // cases this might lead to no D3D9 acceleration where we could have had it
429 // but D3D9 does not reliably report when it supports bigger windows. 8192
430 // is as safe as we can get, we know at least D3D10 hardware always supports
431 // this, other hardware we expect to report correctly in D3D9.
432 #define MAX_ACCELERATED_DIMENSION 8192
434 // On window open (as well as after), Windows has an unfortunate habit of
435 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
436 // to DOM target conversions for these, we cache responses for a given
437 // coordinate this many milliseconds:
438 #define HITTEST_CACHE_LIFETIME_MS 50
440 #if defined(ACCESSIBILITY)
445 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
446 * injecting tiptsf.dll. The touchscreen process then posts registered messages
447 * to our main thread. The tiptsf hook picks up those registered messages and
448 * uses them as commands, some of which call into UIA, which then calls into
449 * MSAA, which then sends WM_GETOBJECT to us.
451 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
452 * hook. Since thread-local hooks are called ahead of global hooks, we will
453 * see these registered messages before tiptsf does. At this point we can then
454 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
455 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
456 * flag by calling TIPMessageHandler::IsA11yBlocked().
458 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
459 * function that also calls into UIA.
461 class TIPMessageHandler
{
463 ~TIPMessageHandler() {
465 ::UnhookWindowsHookEx(mHook
);
469 static void Initialize() {
474 sInstance
= new TIPMessageHandler();
475 ClearOnShutdown(&sInstance
);
478 static bool IsA11yBlocked() {
483 return sInstance
->mA11yBlockCount
> 0;
487 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
488 MOZ_ASSERT(NS_IsMainThread());
490 // Registered messages used by tiptsf
491 mMessages
[0] = ::RegisterWindowMessage(L
"ImmersiveFocusNotification");
492 mMessages
[1] = ::RegisterWindowMessage(L
"TipCloseMenus");
493 mMessages
[2] = ::RegisterWindowMessage(L
"TabletInputPanelOpening");
494 mMessages
[3] = ::RegisterWindowMessage(L
"IHM Pen or Touch Event noticed");
495 mMessages
[4] = ::RegisterWindowMessage(L
"ProgrammabilityCaretVisibility");
496 mMessages
[5] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPHidden");
497 mMessages
[6] = ::RegisterWindowMessage(L
"CaretTrackingUpdateIPInfo");
499 mHook
= ::SetWindowsHookEx(WH_GETMESSAGE
, &TIPHook
, nullptr,
500 ::GetCurrentThreadId());
503 if (!sSendMessageTimeoutWStub
) {
504 sUser32Intercept
.Init("user32.dll");
505 DebugOnly
<bool> hooked
= sSendMessageTimeoutWStub
.Set(
506 sUser32Intercept
, "SendMessageTimeoutW", &SendMessageTimeoutWHook
);
511 class MOZ_RAII A11yInstantiationBlocker
{
513 A11yInstantiationBlocker() {
514 if (!TIPMessageHandler::sInstance
) {
517 ++TIPMessageHandler::sInstance
->mA11yBlockCount
;
520 ~A11yInstantiationBlocker() {
521 if (!TIPMessageHandler::sInstance
) {
524 MOZ_ASSERT(TIPMessageHandler::sInstance
->mA11yBlockCount
> 0);
525 --TIPMessageHandler::sInstance
->mA11yBlockCount
;
529 friend class A11yInstantiationBlocker
;
531 static LRESULT CALLBACK
TIPHook(int aCode
, WPARAM aWParam
, LPARAM aLParam
) {
532 if (aCode
< 0 || !sInstance
) {
533 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
536 MSG
* msg
= reinterpret_cast<MSG
*>(aLParam
);
537 UINT
& msgCode
= msg
->message
;
539 for (uint32_t i
= 0; i
< ArrayLength(sInstance
->mMessages
); ++i
) {
540 if (msgCode
== sInstance
->mMessages
[i
]) {
541 A11yInstantiationBlocker block
;
542 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
546 return ::CallNextHookEx(nullptr, aCode
, aWParam
, aLParam
);
549 static LRESULT WINAPI
SendMessageTimeoutWHook(HWND aHwnd
, UINT aMsgCode
,
550 WPARAM aWParam
, LPARAM aLParam
,
551 UINT aFlags
, UINT aTimeout
,
552 PDWORD_PTR aMsgResult
) {
553 // We don't want to handle this unless the message is a WM_GETOBJECT that we
554 // want to block, and the aHwnd is a nsWindow that belongs to the current
555 // (i.e., main) thread.
556 if (!aMsgResult
|| aMsgCode
!= WM_GETOBJECT
||
557 static_cast<LONG
>(aLParam
) != OBJID_CLIENT
|| !::NS_IsMainThread() ||
558 !WinUtils::GetNSWindowPtr(aHwnd
) || !IsA11yBlocked()) {
559 return sSendMessageTimeoutWStub(aHwnd
, aMsgCode
, aWParam
, aLParam
, aFlags
,
560 aTimeout
, aMsgResult
);
563 // In this case we want to fake the result that would happen if we had
564 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
565 // off to DefWindowProc to accomplish this.
566 *aMsgResult
= static_cast<DWORD_PTR
>(
567 ::DefWindowProcW(aHwnd
, aMsgCode
, aWParam
, aLParam
));
569 return static_cast<LRESULT
>(TRUE
);
572 static WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
573 sSendMessageTimeoutWStub
;
574 static StaticAutoPtr
<TIPMessageHandler
> sInstance
;
578 uint32_t mA11yBlockCount
;
581 WindowsDllInterceptor::FuncHookType
<decltype(&SendMessageTimeoutW
)>
582 TIPMessageHandler::sSendMessageTimeoutWStub
;
583 StaticAutoPtr
<TIPMessageHandler
> TIPMessageHandler::sInstance
;
585 } // namespace mozilla
587 #endif // defined(ACCESSIBILITY)
591 // This task will get the VirtualDesktopManager from the generic thread pool
592 // since doing this on the main thread on startup causes performance issues.
596 // This should be fine and should not require any locking, as when the main
597 // thread will access it, if it races with this function it will either find
598 // it to be null or to have a valid value.
599 class InitializeVirtualDesktopManagerTask
: public Task
{
601 InitializeVirtualDesktopManagerTask()
602 : Task(Kind::OffMainThreadOnly
, kDefaultPriorityValue
) {}
604 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
605 bool GetName(nsACString
& aName
) override
{
606 aName
.AssignLiteral("InitializeVirtualDesktopManagerTask");
611 virtual TaskResult
Run() override
{
612 RefPtr
<IVirtualDesktopManager
> desktopManager
;
613 HRESULT hr
= ::CoCreateInstance(
614 CLSID_VirtualDesktopManager
, NULL
, CLSCTX_INPROC_SERVER
,
615 __uuidof(IVirtualDesktopManager
), getter_AddRefs(desktopManager
));
617 return TaskResult::Complete
;
620 gVirtualDesktopManager
= desktopManager
;
621 return TaskResult::Complete
;
625 // Ground-truth query: does Windows claim the window is cloaked right now?
626 static bool IsCloaked(HWND hwnd
) {
628 HRESULT hr
= ::DwmGetWindowAttribute(hwnd
, DWMWA_CLOAKED
, &cloakedState
,
629 sizeof(cloakedState
));
632 MOZ_LOG(sCloakingLog
, LogLevel::Warning
,
633 ("failed (%08lX) to query cloaking state for HWND %p", hr
, hwnd
));
637 return cloakedState
!= 0;
640 } // namespace mozilla
642 /**************************************************************
643 **************************************************************
645 ** BLOCK: nsIWidget impl.
647 ** nsIWidget interface implementation, broken down into
650 **************************************************************
651 **************************************************************/
653 /**************************************************************
655 * SECTION: nsWindow construction and destruction
657 **************************************************************/
659 nsWindow::nsWindow(bool aIsChildWindow
)
660 : nsBaseWidget(BorderStyle::Default
),
661 mBrush(::CreateSolidBrush(NSRGB_2_COLOREF(::GetSysColor(COLOR_BTNFACE
)))),
662 mFrameState(std::in_place
, this),
663 mIsChildWindow(aIsChildWindow
),
664 mLastPaintEndTime(TimeStamp::Now()),
665 mCachedHitTestTime(TimeStamp::Now()),
666 mSizeConstraintsScale(GetDefaultScale().scale
),
667 mDesktopId("DesktopIdMutex") {
668 MOZ_ASSERT(mWindowType
== WindowType::Child
);
670 if (!gInitializedVirtualDesktopManager
) {
671 TaskController::Get()->AddTask(
672 MakeAndAddRef
<InitializeVirtualDesktopManagerTask
>());
673 gInitializedVirtualDesktopManager
= true;
676 // Global initialization
677 if (!sInstanceCount
) {
678 // Global app registration id for Win7 and up. See
679 // WinTaskbar.cpp for details.
680 // MSIX packages explicitly do not support setting the appid from within
681 // the app, as it is set in the package manifest instead.
682 if (!WinUtils::HasPackageIdentity()) {
683 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
685 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
686 #if defined(ACCESSIBILITY)
687 mozilla::TIPMessageHandler::Initialize();
688 #endif // defined(ACCESSIBILITY)
689 if (SUCCEEDED(::OleInitialize(nullptr))) {
690 sIsOleInitialized
= true;
692 NS_ASSERTION(sIsOleInitialized
, "***** OLE is not initialized!\n");
693 MouseScrollHandler::Initialize();
695 nsUXThemeData::UpdateNativeThemeInfo();
696 RedirectedKeyDownMessageManager::Forget();
702 nsWindow::~nsWindow() {
705 // If the widget was released without calling Destroy() then the native window
706 // still exists, and we need to destroy it. Destroy() will early-return if it
707 // was already called. In any case it is important to call it before
708 // destroying mPresentLock (cf. 1156182).
711 // Free app icon resources. This must happen after `OnDestroy` (see bug
713 if (mIconSmall
) ::DestroyIcon(mIconSmall
);
715 if (mIconBig
) ::DestroyIcon(mIconBig
);
720 if (sInstanceCount
== 0) {
721 IMEHandler::Terminate();
723 if (sIsOleInitialized
) {
724 ::OleFlushClipboard();
726 sIsOleInitialized
= false;
730 NS_IF_RELEASE(mNativeDragTarget
);
733 /**************************************************************
735 * SECTION: nsIWidget::Create, nsIWidget::Destroy
737 * Creating and destroying windows for this widget.
739 **************************************************************/
741 // Allow Derived classes to modify the height that is passed
742 // when the window is created or resized.
743 int32_t nsWindow::GetHeight(int32_t aProposedHeight
) { return aProposedHeight
; }
745 void nsWindow::SendAnAPZEvent(InputData
& aEvent
) {
746 LRESULT popupHandlingResult
;
747 if (DealWithPopups(mWnd
, MOZ_WM_DMANIP
, 0, 0, &popupHandlingResult
)) {
748 // We need to consume the event after using it to roll up the popup(s).
752 if (mSwipeTracker
&& aEvent
.mInputType
== PANGESTURE_INPUT
) {
753 // Give the swipe tracker a first pass at the event. If a new pan gesture
754 // has been started since the beginning of the swipe, the swipe tracker
755 // will know to ignore the event.
756 nsEventStatus status
=
757 mSwipeTracker
->ProcessEvent(aEvent
.AsPanGestureInput());
758 if (status
== nsEventStatus_eConsumeNoDefault
) {
763 APZEventResult result
;
765 result
= mAPZC
->InputBridge()->ReceiveInputEvent(aEvent
);
767 if (result
.GetStatus() == nsEventStatus_eConsumeNoDefault
) {
771 MOZ_ASSERT(aEvent
.mInputType
== PANGESTURE_INPUT
||
772 aEvent
.mInputType
== PINCHGESTURE_INPUT
);
774 if (aEvent
.mInputType
== PANGESTURE_INPUT
) {
775 PanGestureInput
& panInput
= aEvent
.AsPanGestureInput();
776 WidgetWheelEvent event
= panInput
.ToWidgetEvent(this);
778 if (MayStartSwipeForNonAPZ(panInput
)) {
782 event
= MayStartSwipeForAPZ(panInput
, result
);
785 ProcessUntransformedAPZEvent(&event
, result
);
790 PinchGestureInput
& pinchInput
= aEvent
.AsPinchGestureInput();
791 WidgetWheelEvent event
= pinchInput
.ToWidgetEvent(this);
792 ProcessUntransformedAPZEvent(&event
, result
);
795 void nsWindow::RecreateDirectManipulationIfNeeded() {
796 DestroyDirectManipulation();
798 if (mWindowType
!= WindowType::TopLevel
&& mWindowType
!= WindowType::Popup
) {
802 if (!(StaticPrefs::apz_allow_zooming() ||
803 StaticPrefs::apz_windows_use_direct_manipulation()) ||
804 StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
808 mDmOwner
= MakeUnique
<DirectManipulationOwner
>(this);
810 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
811 GetHeight(mBounds
.Height()));
812 mDmOwner
->Init(bounds
);
815 void nsWindow::ResizeDirectManipulationViewport() {
817 LayoutDeviceIntRect
bounds(mBounds
.X(), mBounds
.Y(), mBounds
.Width(),
818 GetHeight(mBounds
.Height()));
819 mDmOwner
->ResizeViewport(bounds
);
823 void nsWindow::DestroyDirectManipulation() {
830 // Create the proper widget
831 nsresult
nsWindow::Create(nsIWidget
* aParent
, nsNativeWidget aNativeParent
,
832 const LayoutDeviceIntRect
& aRect
,
833 widget::InitData
* aInitData
) {
834 // Historical note: there was once some belief and/or intent that nsWindows
835 // could be created on arbitrary threads, and this may still be reflected in
837 MOZ_ASSERT(NS_IsMainThread());
839 widget::InitData defaultInitData
;
840 if (!aInitData
) aInitData
= &defaultInitData
;
842 nsIWidget
* baseParent
=
843 aInitData
->mWindowType
== WindowType::Dialog
||
844 aInitData
->mWindowType
== WindowType::TopLevel
||
845 aInitData
->mWindowType
== WindowType::Invisible
849 mIsTopWidgetWindow
= (nullptr == baseParent
);
852 // Ensure that the toolkit is created.
853 nsToolkit::GetToolkit();
855 BaseCreate(baseParent
, aInitData
);
858 if (aParent
) { // has a nsIWidget parent
859 parent
= aParent
? (HWND
)aParent
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
861 } else { // has a nsNative parent
862 parent
= (HWND
)aNativeParent
;
864 aNativeParent
? WinUtils::GetNSWindowPtr((HWND
)aNativeParent
) : nullptr;
867 mIsRTL
= aInitData
->mRTL
;
868 mForMenupopupFrame
= aInitData
->mForMenupopupFrame
;
869 mOpeningAnimationSuppressed
= aInitData
->mIsAnimationSuppressed
;
870 mAlwaysOnTop
= aInitData
->mAlwaysOnTop
;
871 mResizable
= aInitData
->mResizable
;
873 DWORD style
= WindowStyle();
874 DWORD extendedStyle
= WindowExStyle();
876 if (mWindowType
== WindowType::Popup
) {
880 } else if (mWindowType
== WindowType::Invisible
) {
881 // Make sure CreateWindowEx succeeds at creating a toplevel window
882 style
&= ~0x40000000; // WS_CHILDWINDOW
884 // See if the caller wants to explictly set clip children and clip siblings
885 if (aInitData
->mClipChildren
) {
886 style
|= WS_CLIPCHILDREN
;
888 style
&= ~WS_CLIPCHILDREN
;
890 if (aInitData
->mClipSiblings
) {
891 style
|= WS_CLIPSIBLINGS
;
895 const wchar_t* className
= ChooseWindowClass(mWindowType
, mForMenupopupFrame
);
897 // Take specific actions when creating the first top-level window
898 static bool sFirstTopLevelWindowCreated
= false;
899 if (aInitData
->mWindowType
== WindowType::TopLevel
&& !aParent
&&
900 !sFirstTopLevelWindowCreated
) {
901 sFirstTopLevelWindowCreated
= true;
902 mWnd
= ConsumePreXULSkeletonUIHandle();
903 auto skeletonUIError
= GetPreXULSkeletonUIErrorReason();
904 if (skeletonUIError
) {
905 nsAutoString
errorString(
906 GetPreXULSkeletonUIErrorString(skeletonUIError
.value()));
907 Telemetry::ScalarSet(
908 Telemetry::ScalarID::STARTUP_SKELETON_UI_DISABLED_REASON
,
912 MOZ_ASSERT(style
== kPreXULSkeletonUIWindowStyle
,
913 "The skeleton UI window style should match the expected "
914 "style for the first window created");
915 MOZ_ASSERT(extendedStyle
== kPreXULSkeletonUIWindowStyleEx
,
916 "The skeleton UI window extended style should match the "
917 "expected extended style for the first window created");
919 ::GetWindowThreadProcessId(mWnd
, nullptr) == ::GetCurrentThreadId(),
920 "The skeleton UI window should be created on the same thread as "
922 mIsShowingPreXULSkeletonUI
= true;
924 // If we successfully consumed the pre-XUL skeleton UI, just update
925 // our internal state to match what is currently being displayed.
927 mIsCloaked
= mozilla::IsCloaked(mWnd
);
928 mFrameState
->ConsumePreXULSkeletonState(WasPreXULSkeletonUIMaximized());
930 // These match the margins set in browser-tabsintitlebar.js with
931 // default prefs on Windows. Bug 1673092 tracks lining this up with
932 // that more correctly instead of hard-coding it.
933 SetNonClientMargins(LayoutDeviceIntMargin(0, 2, 2, 2));
935 // Reset the WNDPROC for this window and its whole class, as we had
936 // to use our own WNDPROC when creating the the skeleton UI window.
937 ::SetWindowLongPtrW(mWnd
, GWLP_WNDPROC
,
938 reinterpret_cast<LONG_PTR
>(
939 WinUtils::NonClientDpiScalingDefWindowProcW
));
940 ::SetClassLongPtrW(mWnd
, GCLP_WNDPROC
,
941 reinterpret_cast<LONG_PTR
>(
942 WinUtils::NonClientDpiScalingDefWindowProcW
));
948 ::CreateWindowExW(extendedStyle
, className
, L
"", style
, aRect
.X(),
949 aRect
.Y(), aRect
.Width(), GetHeight(aRect
.Height()),
950 parent
, nullptr, nsToolkit::mDllInstance
, nullptr);
954 NS_WARNING("nsWindow CreateWindowEx failed.");
955 return NS_ERROR_FAILURE
;
958 if (!sWinCloakEventHook
) {
959 MOZ_LOG(sCloakingLog
, LogLevel::Info
, ("Registering cloaking event hook"));
961 // C++03 lambda approximation until P2173R1 is available (-std=c++2b)
962 struct StdcallLambda
{
963 static void CALLBACK
OnCloakUncloakHook(HWINEVENTHOOK hWinEventHook
,
964 DWORD event
, HWND hwnd
,
965 LONG idObject
, LONG idChild
,
967 DWORD dwmsEventTime
) {
968 const bool isCloaked
= event
== EVENT_OBJECT_CLOAKED
? true : false;
969 nsWindow::OnCloakEvent(hwnd
, isCloaked
);
973 const HWINEVENTHOOK hook
= ::SetWinEventHook(
974 EVENT_OBJECT_CLOAKED
, EVENT_OBJECT_UNCLOAKED
, HMODULE(nullptr),
975 &StdcallLambda::OnCloakUncloakHook
, ::GetCurrentProcessId(),
976 ::GetCurrentThreadId(), WINEVENT_OUTOFCONTEXT
);
977 sWinCloakEventHook
= Some(hook
);
980 const DWORD err
= ::GetLastError();
981 MOZ_LOG(sCloakingLog
, LogLevel::Error
,
982 ("Failed to register cloaking event hook! GLE = %lu (0x%lX)", err
,
987 if (aInitData
->mIsPrivate
) {
988 if (NimbusFeatures::GetBool("majorRelease2022"_ns
,
989 "feltPrivacyWindowSeparation"_ns
, true) &&
990 // Although permanent Private Browsing mode is indeed Private Browsing,
991 // we choose to make it look like regular Firefox in terms of the icon
992 // it uses (which also means we shouldn't use the Private Browsing
994 !StaticPrefs::browser_privatebrowsing_autostart()) {
995 RefPtr
<IPropertyStore
> pPropStore
;
996 if (!FAILED(SHGetPropertyStoreForWindow(mWnd
, IID_IPropertyStore
,
997 getter_AddRefs(pPropStore
)))) {
1000 // make sure we're using the private browsing AUMID so that taskbar
1001 // grouping works properly
1002 Unused
<< NS_WARN_IF(
1003 !mozilla::widget::WinTaskbar::GenerateAppUserModelID(aumid
, true));
1004 if (!FAILED(InitPropVariantFromString(aumid
.get(), &pv
))) {
1005 if (!FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
))) {
1006 pPropStore
->Commit();
1009 PropVariantClear(&pv
);
1012 HICON icon
= ::LoadIconW(::GetModuleHandleW(nullptr),
1013 MAKEINTRESOURCEW(IDI_PBMODE
));
1019 mDeviceNotifyHandle
= InputDeviceUtils::RegisterNotification(mWnd
);
1021 // If mDefaultScale is set before mWnd has been set, it will have the scale of
1022 // the primary monitor, rather than the monitor that the window is actually
1023 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
1024 // which resets mDefaultScale, but for popup windows we don't reset
1025 // mDefaultScale on that message. In order to ensure that popup windows
1026 // spawned on a non-primary monitor end up with the correct scale, we reset
1027 // mDefaultScale here so that it gets recomputed using the correct monitor now
1028 // that we have a mWnd.
1029 mDefaultScale
= -1.0;
1032 DWORD dwAttribute
= TRUE
;
1033 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1034 sizeof dwAttribute
);
1037 UpdateDarkModeToolbar();
1039 if (mOpeningAnimationSuppressed
) {
1040 SuppressAnimation(true);
1044 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
1045 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1048 if (mWindowType
!= WindowType::Invisible
&&
1049 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
1050 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
1052 // We create two zero-sized windows as descendants of the top-level window,
1055 // Top-level window (MozillaWindowClass)
1056 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
1057 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
1059 // We need to have the middle window, otherwise the Trackpoint driver
1060 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
1061 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
1062 // window hierarchy until they are handled by nsWindow::WindowProc.
1063 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
1064 // but these do not propagate automatically, so we have the window
1065 // procedure pretend that they were dispatched to the top-level window
1068 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
1069 // is given below so that it catches the Trackpoint driver's heuristics.
1070 HWND scrollContainerWnd
= ::CreateWindowW(
1071 className
, L
"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD
| WS_VISIBLE
, 0,
1072 0, 0, 0, mWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1073 HWND scrollableWnd
= ::CreateWindowW(
1074 className
, L
"FAKETRACKPOINTSCROLLABLE",
1075 WS_CHILD
| WS_VISIBLE
| WS_VSCROLL
| WS_TABSTOP
| 0x30, 0, 0, 0, 0,
1076 scrollContainerWnd
, nullptr, nsToolkit::mDllInstance
, nullptr);
1078 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
1079 // WindowProcInternal can distinguish it from the top-level window
1081 ::SetWindowLongPtrW(scrollableWnd
, GWLP_ID
, eFakeTrackPointScrollableID
);
1083 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
1084 // old window procedure in its "user data".
1085 WNDPROC oldWndProc
= (WNDPROC
)::SetWindowLongPtrW(
1086 scrollableWnd
, GWLP_WNDPROC
, (LONG_PTR
)nsWindow::WindowProc
);
1087 ::SetWindowLongPtrW(scrollableWnd
, GWLP_USERDATA
, (LONG_PTR
)oldWndProc
);
1090 // We will start receiving native events after associating with our native
1091 // window. We will also become the output of WinUtils::GetNSWindowPtr for that
1093 if (!AssociateWithNativeWindow()) {
1094 return NS_ERROR_FAILURE
;
1097 // Starting with Windows XP, a process always runs within a terminal services
1098 // session. In order to play nicely with RDP, fast user switching, and the
1099 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
1100 // our HWND in order to receive this message.
1101 DebugOnly
<BOOL
> wtsRegistered
=
1102 ::WTSRegisterSessionNotification(mWnd
, NOTIFY_FOR_THIS_SESSION
);
1103 NS_ASSERTION(wtsRegistered
, "WTSRegisterSessionNotification failed!\n");
1105 mDefaultIMC
.Init(this);
1106 IMEHandler::InitInputContext(this, mInputContext
);
1108 static bool a11yPrimed
= false;
1109 if (!a11yPrimed
&& mWindowType
== WindowType::TopLevel
) {
1111 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
1112 ::PostMessage(mWnd
, MOZ_WM_STARTA11Y
, 0, 0);
1116 RecreateDirectManipulationIfNeeded();
1121 void nsWindow::LocalesChanged() {
1122 bool isRTL
= intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1123 if (mIsRTL
!= isRTL
) {
1124 DWORD dwAttribute
= isRTL
;
1125 DwmSetWindowAttribute(mWnd
, DWMWA_NONCLIENT_RTL_LAYOUT
, &dwAttribute
,
1126 sizeof dwAttribute
);
1131 // Close this nsWindow
1132 void nsWindow::Destroy() {
1133 // WM_DESTROY has already fired, avoid calling it twice
1134 if (mOnDestroyCalled
) return;
1136 // Don't destroy windows that have file pickers open, we'll tear these down
1137 // later once the picker is closed.
1138 mDestroyCalled
= true;
1139 if (mPickerDisplayCount
) return;
1141 // During the destruction of all of our children, make sure we don't get
1143 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1145 DestroyDirectManipulation();
1148 * On windows the LayerManagerOGL destructor wants the widget to be around for
1149 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
1151 DestroyLayerManager();
1153 InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle
);
1154 mDeviceNotifyHandle
= nullptr;
1156 // The DestroyWindow function destroys the specified window. The function
1157 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
1158 // and remove the keyboard focus from it. The function also destroys the
1159 // window's menu, flushes the thread message queue, destroys timers, removes
1160 // clipboard ownership, and breaks the clipboard viewer chain (if the window
1161 // is at the top of the viewer chain).
1163 // If the specified window is a parent or owner window, DestroyWindow
1164 // automatically destroys the associated child or owned windows when it
1165 // destroys the parent or owner window. The function first destroys child or
1166 // owned windows, and then it destroys the parent or owner window.
1167 VERIFY(::DestroyWindow(mWnd
));
1169 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
1170 // OnDestroy() didn't get called, call it now.
1171 if (false == mOnDestroyCalled
) {
1172 MSGResult msgResult
;
1173 mWindowHook
.Notify(mWnd
, WM_DESTROY
, 0, 0, msgResult
);
1178 /**************************************************************
1180 * SECTION: Window class utilities
1182 * Utilities for calculating the proper window class name for
1185 **************************************************************/
1188 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName
,
1189 UINT aExtraStyle
, LPWSTR aIconID
) {
1191 if (::GetClassInfoW(nsToolkit::mDllInstance
, aClassName
, &wc
)) {
1192 // already registered
1196 wc
.style
= CS_DBLCLKS
| aExtraStyle
;
1197 wc
.lpfnWndProc
= WinUtils::NonClientDpiScalingDefWindowProcW
;
1200 wc
.hInstance
= nsToolkit::mDllInstance
;
1202 aIconID
? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID
) : nullptr;
1203 wc
.hCursor
= nullptr;
1204 wc
.hbrBackground
= nullptr;
1205 wc
.lpszMenuName
= nullptr;
1206 wc
.lpszClassName
= aClassName
;
1208 if (!::RegisterClassW(&wc
)) {
1209 // For older versions of Win32 (i.e., not XP), the registration may
1210 // fail with aExtraStyle, so we have to re-register without it.
1211 wc
.style
= CS_DBLCLKS
;
1212 ::RegisterClassW(&wc
);
1217 static LPWSTR
const gStockApplicationIcon
= MAKEINTRESOURCEW(32512);
1220 const wchar_t* nsWindow::ChooseWindowClass(WindowType aWindowType
,
1221 bool aForMenupopupFrame
) {
1222 MOZ_ASSERT_IF(aForMenupopupFrame
, aWindowType
== WindowType::Popup
);
1223 switch (aWindowType
) {
1224 case WindowType::Invisible
:
1225 return RegisterWindowClass(kClassNameHidden
, 0, gStockApplicationIcon
);
1226 case WindowType::Dialog
:
1227 return RegisterWindowClass(kClassNameDialog
, 0, 0);
1228 case WindowType::Popup
:
1229 if (aForMenupopupFrame
) {
1230 return RegisterWindowClass(kClassNameDropShadow
, CS_DROPSHADOW
,
1231 gStockApplicationIcon
);
1235 return RegisterWindowClass(GetMainWindowClass(), 0,
1236 gStockApplicationIcon
);
1240 /**************************************************************
1242 * SECTION: Window styles utilities
1244 * Return the proper windows styles and extended styles.
1246 **************************************************************/
1248 // Return nsWindow styles
1249 DWORD
nsWindow::WindowStyle() {
1252 switch (mWindowType
) {
1253 case WindowType::Child
:
1254 style
= WS_OVERLAPPED
;
1257 case WindowType::Dialog
:
1258 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
| DS_3DLOOK
|
1259 DS_MODALFRAME
| WS_CLIPCHILDREN
;
1260 if (mBorderStyle
!= BorderStyle::Default
)
1261 style
|= WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
;
1264 case WindowType::Popup
:
1265 style
= WS_POPUP
| WS_OVERLAPPED
;
1269 NS_ERROR("unknown border style");
1272 case WindowType::TopLevel
:
1273 case WindowType::Invisible
:
1274 style
= WS_OVERLAPPED
| WS_BORDER
| WS_DLGFRAME
| WS_SYSMENU
|
1275 WS_THICKFRAME
| WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
| WS_CLIPCHILDREN
;
1279 if (mBorderStyle
!= BorderStyle::Default
&&
1280 mBorderStyle
!= BorderStyle::All
) {
1281 if (mBorderStyle
== BorderStyle::None
||
1282 !(mBorderStyle
& BorderStyle::Border
))
1283 style
&= ~WS_BORDER
;
1285 if (mBorderStyle
== BorderStyle::None
||
1286 !(mBorderStyle
& BorderStyle::Title
)) {
1287 style
&= ~WS_DLGFRAME
;
1290 if (mBorderStyle
== BorderStyle::None
||
1291 !(mBorderStyle
& BorderStyle::Close
))
1293 // XXX The close box can only be removed by changing the window class,
1294 // as far as I know --- roc+moz@cs.cmu.edu
1296 if (mBorderStyle
== BorderStyle::None
||
1297 !(mBorderStyle
& (BorderStyle::Menu
| BorderStyle::Close
)))
1298 style
&= ~WS_SYSMENU
;
1299 // Looks like getting rid of the system menu also does away with the
1300 // close box. So, we only get rid of the system menu if you want neither it
1301 // nor the close box. How does the Windows "Dialog" window class get just
1302 // closebox and no sysmenu? Who knows.
1304 if (mBorderStyle
== BorderStyle::None
||
1305 !(mBorderStyle
& BorderStyle::ResizeH
))
1306 style
&= ~WS_THICKFRAME
;
1308 if (mBorderStyle
== BorderStyle::None
||
1309 !(mBorderStyle
& BorderStyle::Minimize
))
1310 style
&= ~WS_MINIMIZEBOX
;
1312 if (mBorderStyle
== BorderStyle::None
||
1313 !(mBorderStyle
& BorderStyle::Maximize
))
1314 style
&= ~WS_MAXIMIZEBOX
;
1316 if (IsPopupWithTitleBar()) {
1317 style
|= WS_CAPTION
;
1318 if (mBorderStyle
& BorderStyle::Close
) {
1319 style
|= WS_SYSMENU
;
1324 if (mIsChildWindow
) {
1325 style
|= WS_CLIPCHILDREN
;
1326 if (!(style
& WS_POPUP
)) {
1327 style
|= WS_CHILD
; // WS_POPUP and WS_CHILD are mutually exclusive.
1331 VERIFY_WINDOW_STYLE(style
);
1335 // Return nsWindow extended styles
1336 DWORD
nsWindow::WindowExStyle() {
1337 switch (mWindowType
) {
1338 case WindowType::Child
:
1341 case WindowType::Dialog
:
1342 return WS_EX_WINDOWEDGE
| WS_EX_DLGMODALFRAME
;
1344 case WindowType::Popup
: {
1345 DWORD extendedStyle
= WS_EX_TOOLWINDOW
;
1346 if (mPopupLevel
== PopupLevel::Top
) extendedStyle
|= WS_EX_TOPMOST
;
1347 return extendedStyle
;
1350 NS_ERROR("unknown border style");
1353 case WindowType::TopLevel
:
1354 case WindowType::Invisible
:
1355 return WS_EX_WINDOWEDGE
;
1359 /**************************************************************
1361 * SECTION: Native window association utilities
1363 * Used in Create and Destroy. A nsWindow can associate with its
1364 * underlying native window mWnd. Once a native window is
1365 * associated with a nsWindow, its native events will be handled
1366 * by the static member function nsWindow::WindowProc. Moreover,
1367 * the association will be registered in the WinUtils association
1368 * list, that is, calling WinUtils::GetNSWindowPtr on the native
1369 * window will return the associated nsWindow. This is used in
1370 * nsWindow::WindowProc to correctly dispatch native events to
1371 * the handler methods defined in nsWindow, even though it is a
1372 * static member function.
1374 * After dissociation, the native events of the native window will
1375 * no longer be handled by nsWindow::WindowProc, and will thus not
1376 * be dispatched to the nsWindow native event handler methods.
1377 * Moreover, the association will no longer be registered in the
1378 * WinUtils association list, so calling WinUtils::GetNSWindowPtr
1379 * on the native window will return nullptr.
1381 **************************************************************/
1383 bool nsWindow::AssociateWithNativeWindow() {
1384 if (!mWnd
|| !IsWindow(mWnd
)) {
1385 NS_ERROR("Invalid window handle");
1389 // Connect the this pointer to the native window handle.
1390 // This should be done before SetWindowLongPtrW, because nsWindow::WindowProc
1391 // uses WinUtils::GetNSWindowPtr internally.
1392 WinUtils::SetNSWindowPtr(mWnd
, this);
1394 ::SetLastError(ERROR_SUCCESS
);
1395 const auto prevWndProc
= reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1396 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(nsWindow::WindowProc
)));
1397 if (!prevWndProc
&& GetLastError() != ERROR_SUCCESS
) {
1398 NS_ERROR("Failure in SetWindowLongPtrW");
1399 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1403 mPrevWndProc
.emplace(prevWndProc
);
1407 void nsWindow::DissociateFromNativeWindow() {
1408 if (!mWnd
|| !IsWindow(mWnd
) || mPrevWndProc
.isNothing()) {
1412 DebugOnly
<WNDPROC
> wndProcBeforeDissociate
=
1413 reinterpret_cast<WNDPROC
>(::SetWindowLongPtrW(
1414 mWnd
, GWLP_WNDPROC
, reinterpret_cast<LONG_PTR
>(*mPrevWndProc
)));
1415 NS_ASSERTION(wndProcBeforeDissociate
== nsWindow::WindowProc
,
1416 "Unstacked an unexpected native window procedure");
1418 WinUtils::SetNSWindowPtr(mWnd
, nullptr);
1419 mPrevWndProc
.reset();
1422 /**************************************************************
1424 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1426 * Set or clear the parent widgets using window properties, and
1427 * handles calculating native parent handles.
1429 **************************************************************/
1431 // Get and set parent widgets
1432 void nsWindow::SetParent(nsIWidget
* aNewParent
) {
1433 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
1434 nsIWidget
* parent
= GetParent();
1436 parent
->RemoveChild(this);
1439 mParent
= aNewParent
;
1442 ReparentNativeWidget(aNewParent
);
1443 aNewParent
->AddChild(this);
1447 // If we have no parent, SetParent should return the desktop.
1448 VERIFY(::SetParent(mWnd
, nullptr));
1449 RecreateDirectManipulationIfNeeded();
1453 void nsWindow::ReparentNativeWidget(nsIWidget
* aNewParent
) {
1454 MOZ_ASSERT(aNewParent
, "null widget");
1456 mParent
= aNewParent
;
1457 if (mWindowType
== WindowType::Popup
) {
1460 HWND newParent
= (HWND
)aNewParent
->GetNativeData(NS_NATIVE_WINDOW
);
1461 NS_ASSERTION(newParent
, "Parent widget has a null native window handle");
1462 if (newParent
&& mWnd
) {
1463 ::SetParent(mWnd
, newParent
);
1464 RecreateDirectManipulationIfNeeded();
1468 nsIWidget
* nsWindow::GetParent(void) {
1469 if (mIsTopWidgetWindow
) {
1472 if (mInDtor
|| mOnDestroyCalled
) {
1478 static int32_t RoundDown(double aDouble
) {
1479 return aDouble
> 0 ? static_cast<int32_t>(floor(aDouble
))
1480 : static_cast<int32_t>(ceil(aDouble
));
1483 float nsWindow::GetDPI() {
1485 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
1487 screen
->GetDpi(&dpi
);
1492 double nsWindow::GetDefaultScaleInternal() {
1493 if (mDefaultScale
<= 0.0) {
1494 mDefaultScale
= WinUtils::LogToPhysFactor(mWnd
);
1496 return mDefaultScale
;
1499 int32_t nsWindow::LogToPhys(double aValue
) {
1500 return WinUtils::LogToPhys(
1501 ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTOPRIMARY
), aValue
);
1504 nsWindow
* nsWindow::GetParentWindow(bool aIncludeOwner
) {
1505 return static_cast<nsWindow
*>(GetParentWindowBase(aIncludeOwner
));
1508 nsWindow
* nsWindow::GetParentWindowBase(bool aIncludeOwner
) {
1509 if (mIsTopWidgetWindow
) {
1510 // Must use a flag instead of mWindowType to tell if the window is the
1511 // owned by the topmost widget, because a child window can be embedded
1512 // inside a HWND which is not associated with a nsIWidget.
1516 // If this widget has already been destroyed, pretend we have no parent.
1517 // This corresponds to code in Destroy which removes the destroyed
1518 // widget from its parent's child list.
1519 if (mInDtor
|| mOnDestroyCalled
) return nullptr;
1521 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1522 // root owner. aIncludeOwner set to false implies the search will stop at the
1523 // true parent (default).
1524 nsWindow
* widget
= nullptr;
1526 HWND parent
= nullptr;
1528 parent
= ::GetParent(mWnd
);
1530 parent
= ::GetAncestor(mWnd
, GA_PARENT
);
1533 widget
= WinUtils::GetNSWindowPtr(parent
);
1535 // If the widget is in the process of being destroyed then
1537 if (widget
->mInDtor
) {
1547 /**************************************************************
1549 * SECTION: nsIWidget::Show
1551 * Hide or show this component.
1553 **************************************************************/
1555 void nsWindow::Show(bool bState
) {
1556 if (bState
&& mIsShowingPreXULSkeletonUI
) {
1557 // The first time we decide to actually show the window is when we decide
1558 // that we've taken over the window from the skeleton UI, and we should
1559 // no longer treat resizes / moves specially.
1560 mIsShowingPreXULSkeletonUI
= false;
1561 #if defined(ACCESSIBILITY)
1562 // If our HWND has focus and the a11y engine hasn't started yet, fire a
1563 // focus win event. Windows already did this when the skeleton UI appeared,
1564 // but a11y wouldn't have been able to start at that point even if a client
1565 // responded. Firing this now gives clients the chance to respond with
1566 // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
1567 // this if the a11y engine has already started because it has probably
1568 // already fired focus on a descendant.
1569 if (::GetFocus() == mWnd
&& !GetAccService()) {
1570 ::NotifyWinEvent(EVENT_OBJECT_FOCUS
, mWnd
, OBJID_CLIENT
, CHILDID_SELF
);
1572 #endif // defined(ACCESSIBILITY)
1575 if (mForMenupopupFrame
) {
1576 MOZ_ASSERT(ChooseWindowClass(mWindowType
, mForMenupopupFrame
) ==
1577 kClassNameDropShadow
);
1578 const bool shouldUseDropShadow
=
1579 mTransparencyMode
!= TransparencyMode::Transparent
;
1581 static bool sShadowEnabled
= true;
1582 if (sShadowEnabled
!= shouldUseDropShadow
) {
1583 ::SetClassLongA(mWnd
, GCL_STYLE
, shouldUseDropShadow
? CS_DROPSHADOW
: 0);
1584 sShadowEnabled
= shouldUseDropShadow
;
1587 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1588 // some popup menus to become invisible.
1589 LONG_PTR exStyle
= ::GetWindowLongPtrW(mWnd
, GWL_EXSTYLE
);
1590 if (exStyle
& WS_EX_LAYERED
) {
1591 ::SetWindowLongPtrW(mWnd
, GWL_EXSTYLE
, exStyle
& ~WS_EX_COMPOSITED
);
1595 bool syncInvalidate
= false;
1597 bool wasVisible
= mIsVisible
;
1598 // Set the status now so that anyone asking during ShowWindow or
1599 // SetWindowPos would get the correct answer.
1600 mIsVisible
= bState
;
1602 // We may have cached an out of date visible state. This can happen
1603 // when session restore sets the full screen mode.
1605 mOldStyle
|= WS_VISIBLE
;
1607 mOldStyle
&= ~WS_VISIBLE
;
1611 if (!wasVisible
&& mWindowType
== WindowType::TopLevel
) {
1612 // speed up the initial paint after show for
1613 // top level windows:
1614 syncInvalidate
= true;
1616 // Set the cursor before showing the window to avoid the default wait
1618 SetCursor(Cursor
{eCursor_standard
});
1620 switch (mFrameState
->GetSizeMode()) {
1621 case nsSizeMode_Fullscreen
:
1622 ::ShowWindow(mWnd
, SW_SHOW
);
1624 case nsSizeMode_Maximized
:
1625 ::ShowWindow(mWnd
, SW_SHOWMAXIMIZED
);
1627 case nsSizeMode_Minimized
:
1628 ::ShowWindow(mWnd
, SW_SHOWMINIMIZED
);
1631 if (CanTakeFocus() && !mAlwaysOnTop
) {
1632 ::ShowWindow(mWnd
, SW_SHOWNORMAL
);
1634 ::ShowWindow(mWnd
, SW_SHOWNOACTIVATE
);
1635 // Don't flicker the window if we're restoring session
1636 if (!sIsRestoringSession
) {
1637 Unused
<< GetAttention(2);
1643 DWORD flags
= SWP_NOSIZE
| SWP_NOMOVE
| SWP_SHOWWINDOW
;
1644 if (wasVisible
) flags
|= SWP_NOZORDER
;
1645 if (mAlwaysOnTop
) flags
|= SWP_NOACTIVATE
;
1647 if (mWindowType
== WindowType::Popup
) {
1648 // ensure popups are the topmost of the TOPMOST
1649 // layer. Remember not to set the SWP_NOZORDER
1650 // flag as that might allow the taskbar to overlap
1652 flags
|= SWP_NOACTIVATE
;
1653 HWND owner
= ::GetWindow(mWnd
, GW_OWNER
);
1655 // PopupLevel::Top popups should be above all else. All other
1656 // types should be placed in front of their owner, without
1657 // changing the owner's z-level relative to other windows.
1658 if (mPopupLevel
!= PopupLevel::Top
) {
1659 ::SetWindowPos(mWnd
, owner
, 0, 0, 0, 0, flags
);
1660 ::SetWindowPos(owner
, mWnd
, 0, 0, 0, 0,
1661 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
1663 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1666 ::SetWindowPos(mWnd
, HWND_TOPMOST
, 0, 0, 0, 0, flags
);
1669 if (mWindowType
== WindowType::Dialog
&& !CanTakeFocus())
1670 flags
|= SWP_NOACTIVATE
;
1672 ::SetWindowPos(mWnd
, HWND_TOP
, 0, 0, 0, 0, flags
);
1676 // Clear contents to avoid ghosting of old content if we display
1677 // this window again.
1678 if (wasVisible
&& mTransparencyMode
== TransparencyMode::Transparent
) {
1679 if (mCompositorWidgetDelegate
) {
1680 mCompositorWidgetDelegate
->ClearTransparentWindow();
1683 if (mWindowType
!= WindowType::Dialog
) {
1684 ::ShowWindow(mWnd
, SW_HIDE
);
1686 ::SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
1687 SWP_HIDEWINDOW
| SWP_NOSIZE
| SWP_NOMOVE
| SWP_NOZORDER
|
1693 if (!wasVisible
&& bState
) {
1695 if (syncInvalidate
&& !mInDtor
&& !mOnDestroyCalled
) {
1696 ::UpdateWindow(mWnd
);
1700 if (mOpeningAnimationSuppressed
) {
1701 SuppressAnimation(false);
1705 /**************************************************************
1707 * SECTION: nsIWidget::IsVisible
1709 * Returns the visibility state.
1711 **************************************************************/
1713 // Return true if the component is visible, false otherwise.
1715 // This does not take cloaking into account.
1716 bool nsWindow::IsVisible() const { return mIsVisible
; }
1718 /**************************************************************
1720 * SECTION: Window clipping utilities
1722 * Used in Size and Move operations for setting the proper
1723 * window clipping regions for window transparency.
1725 **************************************************************/
1727 // XP and Vista visual styles sometimes require window clipping regions to be
1728 // applied for proper transparency. These routines are called on size and move
1730 // XXX this is apparently still needed in Windows 7 and later
1731 void nsWindow::ClearThemeRegion() {
1732 if (mWindowType
== WindowType::Popup
&& !IsPopupWithTitleBar() &&
1733 (mPopupType
== PopupType::Tooltip
|| mPopupType
== PopupType::Panel
)) {
1734 SetWindowRgn(mWnd
, nullptr, false);
1738 /**************************************************************
1740 * SECTION: Touch and APZ-related functions
1742 **************************************************************/
1744 void nsWindow::RegisterTouchWindow() {
1745 mTouchWindow
= true;
1746 ::RegisterTouchWindow(mWnd
, TWF_WANTPALM
);
1747 ::EnumChildWindows(mWnd
, nsWindow::RegisterTouchForDescendants
, 0);
1750 BOOL CALLBACK
nsWindow::RegisterTouchForDescendants(HWND aWnd
, LPARAM aMsg
) {
1751 nsWindow
* win
= WinUtils::GetNSWindowPtr(aWnd
);
1753 ::RegisterTouchWindow(aWnd
, TWF_WANTPALM
);
1758 void nsWindow::LockAspectRatio(bool aShouldLock
) {
1760 mAspectRatio
= (float)mBounds
.Width() / (float)mBounds
.Height();
1766 /**************************************************************
1768 * SECTION: nsIWidget::SetInputRegion
1770 * Sets whether the window should ignore mouse events.
1772 **************************************************************/
1773 void nsWindow::SetInputRegion(const InputRegion
& aInputRegion
) {
1774 mInputRegion
= aInputRegion
;
1777 /**************************************************************
1779 * SECTION: nsIWidget::Move, nsIWidget::Resize, nsIWidget::Size
1781 * Repositioning and sizing a window.
1783 **************************************************************/
1785 void nsWindow::SetSizeConstraints(const SizeConstraints
& aConstraints
) {
1786 SizeConstraints c
= aConstraints
;
1788 if (mWindowType
!= WindowType::Popup
&& mResizable
) {
1790 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK
)), c
.mMinSize
.width
);
1792 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK
)), c
.mMinSize
.height
);
1795 if (mMaxTextureSize
> 0) {
1796 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1797 // a window grow bigger as we won't be able to draw content there in
1799 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
1800 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
1803 mSizeConstraintsScale
= GetDefaultScale().scale
;
1805 nsBaseWidget::SetSizeConstraints(c
);
1808 const SizeConstraints
nsWindow::GetSizeConstraints() {
1809 double scale
= GetDefaultScale().scale
;
1810 if (mSizeConstraintsScale
== scale
|| mSizeConstraintsScale
== 0.0) {
1811 return mSizeConstraints
;
1813 scale
/= mSizeConstraintsScale
;
1814 SizeConstraints c
= mSizeConstraints
;
1815 if (c
.mMinSize
.width
!= NS_MAXSIZE
) {
1816 c
.mMinSize
.width
= NSToIntRound(c
.mMinSize
.width
* scale
);
1818 if (c
.mMinSize
.height
!= NS_MAXSIZE
) {
1819 c
.mMinSize
.height
= NSToIntRound(c
.mMinSize
.height
* scale
);
1821 if (c
.mMaxSize
.width
!= NS_MAXSIZE
) {
1822 c
.mMaxSize
.width
= NSToIntRound(c
.mMaxSize
.width
* scale
);
1824 if (c
.mMaxSize
.height
!= NS_MAXSIZE
) {
1825 c
.mMaxSize
.height
= NSToIntRound(c
.mMaxSize
.height
* scale
);
1830 // Move this component
1831 void nsWindow::Move(double aX
, double aY
) {
1832 if (mWindowType
== WindowType::TopLevel
||
1833 mWindowType
== WindowType::Dialog
) {
1834 SetSizeMode(nsSizeMode_Normal
);
1837 // for top-level windows only, convert coordinates from desktop pixels
1838 // (the "parent" coordinate space) to the window's device pixel space
1840 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1841 int32_t x
= NSToIntRound(aX
* scale
);
1842 int32_t y
= NSToIntRound(aY
* scale
);
1844 // Check to see if window needs to be moved first
1845 // to avoid a costly call to SetWindowPos. This check
1846 // can not be moved to the calling code in nsView, because
1847 // some platforms do not position child windows correctly
1849 // Only perform this check for non-popup windows, since the positioning can
1850 // in fact change even when the x/y do not. We always need to perform the
1851 // check. See bug #97805 for details.
1852 if (mWindowType
!= WindowType::Popup
&& mBounds
.IsEqualXY(x
, y
)) {
1853 // Nothing to do, since it is already positioned correctly.
1857 mBounds
.MoveTo(x
, y
);
1861 // complain if a window is moved offscreen (legal, but potentially
1863 if (mIsTopWidgetWindow
) { // only a problem for top-level windows
1864 // Make sure this window is actually on the screen before we move it
1865 // XXX: Needs multiple monitor support
1866 HDC dc
= ::GetDC(mWnd
);
1868 if (::GetDeviceCaps(dc
, TECHNOLOGY
) == DT_RASDISPLAY
) {
1870 ::SystemParametersInfo(SPI_GETWORKAREA
, 0, &workArea
, 0);
1871 // no annoying assertions. just mention the issue.
1872 if (x
< 0 || x
>= workArea
.right
|| y
< 0 || y
>= workArea
.bottom
) {
1873 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
1874 ("window moved to offscreen position\n"));
1877 ::ReleaseDC(mWnd
, dc
);
1882 // Normally, when the skeleton UI is disabled, we resize+move the window
1883 // before showing it in order to ensure that it restores to the correct
1884 // position when the user un-maximizes it. However, when we are using the
1885 // skeleton UI, this results in the skeleton UI window being moved around
1886 // undesirably before being locked back into the maximized position. To
1887 // avoid this, we simply set the placement to restore to via
1888 // SetWindowPlacement. It's a little bit more of a dance, though, since we
1889 // need to convert the workspace coords that SetWindowPlacement uses to the
1890 // screen space coordinates we normally use with SetWindowPos.
1891 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
1892 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
1893 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
1895 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
1896 if (NS_WARN_IF(!monitor
)) {
1899 MONITORINFO mi
= {sizeof(MONITORINFO
)};
1900 VERIFY(::GetMonitorInfo(monitor
, &mi
));
1903 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
1905 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
1906 pl
.rcNormalPosition
.left
+= deltaX
;
1907 pl
.rcNormalPosition
.right
+= deltaX
;
1908 pl
.rcNormalPosition
.top
+= deltaY
;
1909 pl
.rcNormalPosition
.bottom
+= deltaY
;
1910 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
1914 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOSIZE
;
1915 double oldScale
= mDefaultScale
;
1916 mResizeState
= IN_SIZEMOVE
;
1917 VERIFY(::SetWindowPos(mWnd
, nullptr, x
, y
, 0, 0, flags
));
1918 mResizeState
= NOT_RESIZING
;
1919 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
1924 ResizeDirectManipulationViewport();
1928 // Resize this component
1929 void nsWindow::Resize(double aWidth
, double aHeight
, bool aRepaint
) {
1930 // for top-level windows only, convert coordinates from desktop pixels
1931 // (the "parent" coordinate space) to the window's device pixel space
1933 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
1934 int32_t width
= NSToIntRound(aWidth
* scale
);
1935 int32_t height
= NSToIntRound(aHeight
* scale
);
1937 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
1938 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
1939 if (width
< 0 || height
< 0) {
1940 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << width
<< ", "
1941 << height
<< ") repaint: " << aRepaint
;
1944 ConstrainSize(&width
, &height
);
1946 // Avoid unnecessary resizing calls
1947 if (mBounds
.IsEqualSize(width
, height
)) {
1954 // Set cached value for lightweight and printing
1955 bool wasLocking
= mAspectRatio
!= 0.0;
1956 mBounds
.SizeTo(width
, height
);
1958 LockAspectRatio(true); // This causes us to refresh the mAspectRatio value
1962 // Refer to the comment above a similar check in nsWindow::Move
1963 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
1964 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
1965 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
1966 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
1967 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
1968 mResizeState
= RESIZING
;
1969 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
1970 mResizeState
= NOT_RESIZING
;
1972 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_NOMOVE
;
1975 flags
|= SWP_NOREDRAW
;
1979 double oldScale
= mDefaultScale
;
1980 mResizeState
= RESIZING
;
1982 ::SetWindowPos(mWnd
, nullptr, 0, 0, width
, GetHeight(height
), flags
));
1984 mResizeState
= NOT_RESIZING
;
1985 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
1990 ResizeDirectManipulationViewport();
1993 if (aRepaint
) Invalidate();
1996 // Resize this component
1997 void nsWindow::Resize(double aX
, double aY
, double aWidth
, double aHeight
,
1999 // for top-level windows only, convert coordinates from desktop pixels
2000 // (the "parent" coordinate space) to the window's device pixel space
2002 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale
: 1.0;
2003 int32_t x
= NSToIntRound(aX
* scale
);
2004 int32_t y
= NSToIntRound(aY
* scale
);
2005 int32_t width
= NSToIntRound(aWidth
* scale
);
2006 int32_t height
= NSToIntRound(aHeight
* scale
);
2008 NS_ASSERTION((width
>= 0), "Negative width passed to nsWindow::Resize");
2009 NS_ASSERTION((height
>= 0), "Negative height passed to nsWindow::Resize");
2010 if (width
< 0 || height
< 0) {
2011 gfxCriticalNoteOnce
<< "Negative passed to Resize(" << x
<< " ," << y
2012 << ", " << width
<< ", " << height
2013 << ") repaint: " << aRepaint
;
2016 ConstrainSize(&width
, &height
);
2018 // Avoid unnecessary resizing calls
2019 if (mBounds
.IsEqualRect(x
, y
, width
, height
)) {
2026 // Set cached value for lightweight and printing
2027 mBounds
.SetRect(x
, y
, width
, height
);
2030 // Refer to the comment above a similar check in nsWindow::Move
2031 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2032 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2033 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2035 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2036 if (NS_WARN_IF(!monitor
)) {
2039 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2040 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2043 x
+ mi
.rcWork
.left
- mi
.rcMonitor
.left
- pl
.rcNormalPosition
.left
;
2045 y
+ mi
.rcWork
.top
- mi
.rcMonitor
.top
- pl
.rcNormalPosition
.top
;
2046 pl
.rcNormalPosition
.left
+= deltaX
;
2047 pl
.rcNormalPosition
.right
= pl
.rcNormalPosition
.left
+ width
;
2048 pl
.rcNormalPosition
.top
+= deltaY
;
2049 pl
.rcNormalPosition
.bottom
= pl
.rcNormalPosition
.top
+ GetHeight(height
);
2050 VERIFY(::SetWindowPlacement(mWnd
, &pl
));
2052 UINT flags
= SWP_NOZORDER
| SWP_NOACTIVATE
;
2054 flags
|= SWP_NOREDRAW
;
2059 double oldScale
= mDefaultScale
;
2060 mResizeState
= RESIZING
;
2062 ::SetWindowPos(mWnd
, nullptr, x
, y
, width
, GetHeight(height
), flags
));
2063 mResizeState
= NOT_RESIZING
;
2064 if (WinUtils::LogToPhysFactor(mWnd
) != oldScale
) {
2068 if (mTransitionWnd
) {
2069 // If we have a fullscreen transition window, we need to make
2070 // it topmost again, otherwise the taskbar may be raised by
2071 // the system unexpectedly when we leave fullscreen state.
2072 ::SetWindowPos(mTransitionWnd
, HWND_TOPMOST
, 0, 0, 0, 0,
2073 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
);
2077 ResizeDirectManipulationViewport();
2080 if (aRepaint
) Invalidate();
2083 mozilla::Maybe
<bool> nsWindow::IsResizingNativeWidget() {
2084 if (mResizeState
== RESIZING
) {
2090 /**************************************************************
2092 * SECTION: Window Z-order and state.
2094 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
2095 * nsIWidget::ConstrainPosition
2097 * Z-order, positioning, restore, minimize, and maximize.
2099 **************************************************************/
2101 // Position the window behind the given window
2102 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement
,
2103 nsIWidget
* aWidget
, bool aActivate
) {
2104 HWND behind
= HWND_TOP
;
2105 if (aPlacement
== eZPlacementBottom
)
2106 behind
= HWND_BOTTOM
;
2107 else if (aPlacement
== eZPlacementBelow
&& aWidget
)
2108 behind
= (HWND
)aWidget
->GetNativeData(NS_NATIVE_WINDOW
);
2109 UINT flags
= SWP_NOMOVE
| SWP_NOREPOSITION
| SWP_NOSIZE
;
2110 if (!aActivate
) flags
|= SWP_NOACTIVATE
;
2112 if (!CanTakeFocus() && behind
== HWND_TOP
) {
2113 // Can't place the window to top so place it behind the foreground window
2114 // (as long as it is not topmost)
2115 HWND wndAfter
= ::GetForegroundWindow();
2117 behind
= HWND_BOTTOM
;
2118 else if (!(GetWindowLongPtrW(wndAfter
, GWL_EXSTYLE
) & WS_EX_TOPMOST
))
2120 flags
|= SWP_NOACTIVATE
;
2123 ::SetWindowPos(mWnd
, behind
, 0, 0, 0, 0, flags
);
2126 static UINT
GetCurrentShowCmd(HWND aWnd
) {
2128 pl
.length
= sizeof(pl
);
2129 ::GetWindowPlacement(aWnd
, &pl
);
2133 // Maximize, minimize or restore the window.
2134 void nsWindow::SetSizeMode(nsSizeMode aMode
) {
2135 // If we are still displaying a maximized pre-XUL skeleton UI, ignore the
2136 // noise of sizemode changes. Once we have "shown" the window for the first
2137 // time (called nsWindow::Show(true), even though the window is already
2138 // technically displayed), we will again accept sizemode changes.
2139 if (mIsShowingPreXULSkeletonUI
&& WasPreXULSkeletonUIMaximized()) {
2143 mFrameState
->EnsureSizeMode(aMode
);
2146 nsSizeMode
nsWindow::SizeMode() { return mFrameState
->GetSizeMode(); }
2148 void DoGetWorkspaceID(HWND aWnd
, nsAString
* aWorkspaceID
) {
2149 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2150 if (!desktopManager
|| !aWnd
) {
2155 HRESULT hr
= desktopManager
->GetWindowDesktopId(aWnd
, &desktop
);
2160 RPC_WSTR workspaceIDStr
= nullptr;
2161 if (UuidToStringW(&desktop
, &workspaceIDStr
) == RPC_S_OK
) {
2162 aWorkspaceID
->Assign((wchar_t*)workspaceIDStr
);
2163 RpcStringFreeW(&workspaceIDStr
);
2167 void nsWindow::GetWorkspaceID(nsAString
& workspaceID
) {
2168 // If we have a value cached, use that, but also make sure it is
2169 // scheduled to be updated. If we don't yet have a value, get
2170 // one synchronously.
2171 auto desktop
= mDesktopId
.Lock();
2172 if (desktop
->mID
.IsEmpty()) {
2173 DoGetWorkspaceID(mWnd
, &desktop
->mID
);
2174 desktop
->mUpdateIsQueued
= false;
2176 AsyncUpdateWorkspaceID(*desktop
);
2179 workspaceID
= desktop
->mID
;
2182 void nsWindow::AsyncUpdateWorkspaceID(Desktop
& aDesktop
) {
2183 struct UpdateWorkspaceIdTask
: public Task
{
2184 explicit UpdateWorkspaceIdTask(nsWindow
* aSelf
)
2185 : Task(Kind::OffMainThreadOnly
, EventQueuePriority::Normal
),
2188 TaskResult
Run() override
{
2189 auto desktop
= mSelf
->mDesktopId
.Lock();
2190 if (desktop
->mUpdateIsQueued
) {
2191 DoGetWorkspaceID(mSelf
->mWnd
, &desktop
->mID
);
2192 desktop
->mUpdateIsQueued
= false;
2194 return TaskResult::Complete
;
2197 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
2198 bool GetName(nsACString
& aName
) override
{
2199 aName
.AssignLiteral("UpdateWorkspaceIdTask");
2204 RefPtr
<nsWindow
> mSelf
;
2207 if (aDesktop
.mUpdateIsQueued
) {
2211 aDesktop
.mUpdateIsQueued
= true;
2212 TaskController::Get()->AddTask(MakeAndAddRef
<UpdateWorkspaceIdTask
>(this));
2215 void nsWindow::MoveToWorkspace(const nsAString
& workspaceID
) {
2216 RefPtr
<IVirtualDesktopManager
> desktopManager
= gVirtualDesktopManager
;
2217 if (!desktopManager
) {
2222 const nsString flat
= PromiseFlatString(workspaceID
);
2223 RPC_WSTR workspaceIDStr
= reinterpret_cast<RPC_WSTR
>((wchar_t*)flat
.get());
2224 if (UuidFromStringW(workspaceIDStr
, &desktop
) == RPC_S_OK
) {
2225 if (SUCCEEDED(desktopManager
->MoveWindowToDesktop(mWnd
, desktop
))) {
2226 auto desktop
= mDesktopId
.Lock();
2227 desktop
->mID
= workspaceID
;
2232 void nsWindow::SuppressAnimation(bool aSuppress
) {
2233 DWORD dwAttribute
= aSuppress
? TRUE
: FALSE
;
2234 DwmSetWindowAttribute(mWnd
, DWMWA_TRANSITIONS_FORCEDISABLED
, &dwAttribute
,
2235 sizeof dwAttribute
);
2238 // Constrain a potential move to fit onscreen
2239 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2240 // except when using per-monitor DPI, in which case it's device pixels.
2241 void nsWindow::ConstrainPosition(DesktopIntPoint
& aPoint
) {
2242 if (!mIsTopWidgetWindow
) // only a problem for top-level windows
2245 double dpiScale
= GetDesktopToDeviceScale().scale
;
2247 // We need to use the window size in the kind of pixels used for window-
2248 // manipulation APIs.
2250 std::max
<int32_t>(NSToIntRound(mBounds
.Width() / dpiScale
), 1);
2252 std::max
<int32_t>(NSToIntRound(mBounds
.Height() / dpiScale
), 1);
2254 /* get our playing field. use the current screen, or failing that
2255 for any reason, use device caps for the default screen. */
2258 nsCOMPtr
<nsIScreenManager
> screenmgr
=
2259 do_GetService(sScreenManagerContractID
);
2263 nsCOMPtr
<nsIScreen
> screen
;
2264 int32_t left
, top
, width
, height
;
2266 screenmgr
->ScreenForRect(aPoint
.x
, aPoint
.y
, logWidth
, logHeight
,
2267 getter_AddRefs(screen
));
2268 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
2269 // For normalized windows, use the desktop work area.
2270 nsresult rv
= screen
->GetAvailRectDisplayPix(&left
, &top
, &width
, &height
);
2271 if (NS_FAILED(rv
)) {
2275 // For full screen windows, use the desktop.
2276 nsresult rv
= screen
->GetRectDisplayPix(&left
, &top
, &width
, &height
);
2277 if (NS_FAILED(rv
)) {
2281 screenRect
.left
= left
;
2282 screenRect
.right
= left
+ width
;
2283 screenRect
.top
= top
;
2284 screenRect
.bottom
= top
+ height
;
2286 if (aPoint
.x
< screenRect
.left
)
2287 aPoint
.x
= screenRect
.left
;
2288 else if (aPoint
.x
>= screenRect
.right
- logWidth
)
2289 aPoint
.x
= screenRect
.right
- logWidth
;
2291 if (aPoint
.y
< screenRect
.top
)
2292 aPoint
.y
= screenRect
.top
;
2293 else if (aPoint
.y
>= screenRect
.bottom
- logHeight
)
2294 aPoint
.y
= screenRect
.bottom
- logHeight
;
2297 /**************************************************************
2299 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2301 * Enabling and disabling the widget.
2303 **************************************************************/
2305 // Enable/disable this component
2306 void nsWindow::Enable(bool bState
) {
2308 ::EnableWindow(mWnd
, bState
);
2312 // Return the current enable state
2313 bool nsWindow::IsEnabled() const {
2314 return !mWnd
|| (::IsWindowEnabled(mWnd
) &&
2315 ::IsWindowEnabled(::GetAncestor(mWnd
, GA_ROOT
)));
2318 /**************************************************************
2320 * SECTION: nsIWidget::SetFocus
2322 * Give the focus to this widget.
2324 **************************************************************/
2326 void nsWindow::SetFocus(Raise aRaise
, mozilla::dom::CallerType aCallerType
) {
2328 #ifdef WINSTATE_DEBUG_OUTPUT
2329 if (mWnd
== WinUtils::GetTopLevelHWND(mWnd
)) {
2330 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2331 ("*** SetFocus: [ top] raise=%d\n", aRaise
== Raise::Yes
));
2333 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
2334 ("*** SetFocus: [child] raise=%d\n", aRaise
== Raise::Yes
));
2337 // Uniconify, if necessary
2338 HWND toplevelWnd
= WinUtils::GetTopLevelHWND(mWnd
);
2339 if (aRaise
== Raise::Yes
&& ::IsIconic(toplevelWnd
)) {
2340 ::ShowWindow(toplevelWnd
, SW_RESTORE
);
2346 /**************************************************************
2350 * GetBounds, GetClientBounds, GetScreenBounds,
2351 * GetRestoredBounds, GetClientOffset, SetNonClientMargins
2353 * Bound calculations.
2355 **************************************************************/
2357 // Return the window's full dimensions in screen coordinates.
2358 // If the window has a parent, converts the origin to an offset
2359 // of the parent's screen origin.
2360 LayoutDeviceIntRect
nsWindow::GetBounds() {
2366 VERIFY(::GetWindowRect(mWnd
, &r
));
2368 LayoutDeviceIntRect rect
;
2371 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2373 // popup window bounds' are in screen coordinates, not relative to parent
2375 if (mWindowType
== WindowType::Popup
) {
2376 rect
.MoveTo(r
.left
, r
.top
);
2380 // chrome on parent:
2381 // ___ 5,5 (chrome start)
2382 // | ____ 10,10 (client start)
2383 // | | ____ 20,20 (child start)
2385 // 20,20 - 5,5 = 15,15 (??)
2386 // minus GetClientOffset:
2387 // 15,15 - 5,5 = 10,10
2389 // no chrome on parent:
2390 // ______ 10,10 (win start)
2391 // | ____ 20,20 (child start)
2393 // 20,20 - 10,10 = 10,10
2395 // walking the chain:
2396 // ___ 5,5 (chrome start)
2397 // | ___ 10,10 (client start)
2398 // | | ___ 20,20 (child start)
2399 // | | | __ 30,30 (child start)
2401 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2402 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2403 // minus GetClientOffset:
2404 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2406 // convert coordinates if parent exists
2407 HWND parent
= ::GetParent(mWnd
);
2410 VERIFY(::GetWindowRect(parent
, &pr
));
2413 // adjust for chrome
2414 nsWindow
* pWidget
= static_cast<nsWindow
*>(GetParent());
2415 if (pWidget
&& pWidget
->IsTopLevelWidget()) {
2416 LayoutDeviceIntPoint clientOffset
= pWidget
->GetClientOffset();
2417 r
.left
-= clientOffset
.x
;
2418 r
.top
-= clientOffset
.y
;
2421 rect
.MoveTo(r
.left
, r
.top
);
2422 if (mCompositorSession
&&
2423 !wr::WindowSizeSanityCheck(rect
.width
, rect
.height
)) {
2424 gfxCriticalNoteOnce
<< "Invalid size" << rect
<< " size mode "
2425 << mFrameState
->GetSizeMode();
2431 // Get this component dimension
2432 LayoutDeviceIntRect
nsWindow::GetClientBounds() {
2434 return LayoutDeviceIntRect(0, 0, 0, 0);
2438 if (!::GetClientRect(mWnd
, &r
)) {
2439 MOZ_ASSERT_UNREACHABLE("unexpected to be called");
2440 gfxCriticalNoteOnce
<< "GetClientRect failed " << ::GetLastError();
2444 LayoutDeviceIntRect bounds
= GetBounds();
2445 LayoutDeviceIntRect rect
;
2446 rect
.MoveTo(bounds
.TopLeft() + GetClientOffset());
2447 rect
.SizeTo(r
.right
- r
.left
, r
.bottom
- r
.top
);
2451 // Like GetBounds, but don't offset by the parent
2452 LayoutDeviceIntRect
nsWindow::GetScreenBounds() {
2458 VERIFY(::GetWindowRect(mWnd
, &r
));
2460 return LayoutDeviceIntRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2463 nsresult
nsWindow::GetRestoredBounds(LayoutDeviceIntRect
& aRect
) {
2464 if (SizeMode() == nsSizeMode_Normal
) {
2465 aRect
= GetScreenBounds();
2469 return NS_ERROR_FAILURE
;
2472 WINDOWPLACEMENT pl
= {sizeof(WINDOWPLACEMENT
)};
2473 VERIFY(::GetWindowPlacement(mWnd
, &pl
));
2474 const RECT
& r
= pl
.rcNormalPosition
;
2476 HMONITOR monitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONULL
);
2478 return NS_ERROR_FAILURE
;
2480 MONITORINFO mi
= {sizeof(MONITORINFO
)};
2481 VERIFY(::GetMonitorInfo(monitor
, &mi
));
2483 aRect
.SetRect(r
.left
, r
.top
, r
.right
- r
.left
, r
.bottom
- r
.top
);
2484 aRect
.MoveBy(mi
.rcWork
.left
- mi
.rcMonitor
.left
,
2485 mi
.rcWork
.top
- mi
.rcMonitor
.top
);
2489 // Return the x,y offset of the client area from the origin of the window. If
2490 // the window is borderless returns (0,0).
2491 LayoutDeviceIntPoint
nsWindow::GetClientOffset() {
2493 return LayoutDeviceIntPoint(0, 0);
2497 GetWindowRect(mWnd
, &r1
);
2498 LayoutDeviceIntPoint pt
= WidgetToScreenOffset();
2499 return LayoutDeviceIntPoint(pt
.x
- LayoutDeviceIntCoord(r1
.left
),
2500 pt
.y
- LayoutDeviceIntCoord(r1
.top
));
2503 void nsWindow::ResetLayout() {
2504 // This will trigger a frame changed event, triggering
2505 // nc calc size and a sizemode gecko event.
2506 SetWindowPos(mWnd
, 0, 0, 0, 0, 0,
2507 SWP_FRAMECHANGED
| SWP_NOACTIVATE
| SWP_NOMOVE
|
2508 SWP_NOOWNERZORDER
| SWP_NOSIZE
| SWP_NOZORDER
);
2510 // If hidden, just send the frame changed event for now.
2515 // Send a gecko size event to trigger reflow.
2516 RECT clientRc
= {0};
2517 GetClientRect(mWnd
, &clientRc
);
2518 OnResize(WinUtils::ToIntRect(clientRc
).Size());
2520 // Invalidate and update
2524 // Internally track the caption status via a window property. Required
2525 // due to our internal handling of WM_NCACTIVATE when custom client
2527 static const wchar_t kManageWindowInfoProperty
[] = L
"ManageWindowInfoProperty";
2528 typedef BOOL(WINAPI
* GetWindowInfoPtr
)(HWND hwnd
, PWINDOWINFO pwi
);
2529 static WindowsDllInterceptor::FuncHookType
<GetWindowInfoPtr
>
2530 sGetWindowInfoPtrStub
;
2532 BOOL WINAPI
GetWindowInfoHook(HWND hWnd
, PWINDOWINFO pwi
) {
2533 if (!sGetWindowInfoPtrStub
) {
2534 NS_ASSERTION(FALSE
, "Something is horribly wrong in GetWindowInfoHook!");
2538 reinterpret_cast<LONG_PTR
>(GetPropW(hWnd
, kManageWindowInfoProperty
));
2539 // No property set, return the default data.
2540 if (!windowStatus
) return sGetWindowInfoPtrStub(hWnd
, pwi
);
2541 // Call GetWindowInfo and update dwWindowStatus with our
2542 // internally tracked value.
2543 BOOL result
= sGetWindowInfoPtrStub(hWnd
, pwi
);
2545 pwi
->dwWindowStatus
= (windowStatus
== 1 ? 0 : WS_ACTIVECAPTION
);
2549 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption
) {
2552 sUser32Intercept
.Init("user32.dll");
2553 sGetWindowInfoPtrStub
.Set(sUser32Intercept
, "GetWindowInfo",
2554 &GetWindowInfoHook
);
2555 if (!sGetWindowInfoPtrStub
) {
2559 // Update our internally tracked caption status
2560 SetPropW(mWnd
, kManageWindowInfoProperty
,
2561 reinterpret_cast<HANDLE
>(static_cast<INT_PTR
>(aActiveCaption
) + 1));
2564 #define DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 19
2565 #define DWMWA_USE_IMMERSIVE_DARK_MODE 20
2567 void nsWindow::UpdateDarkModeToolbar() {
2568 LookAndFeel::EnsureColorSchemesInitialized();
2569 BOOL dark
= LookAndFeel::ColorSchemeForChrome() == ColorScheme::Dark
;
2570 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1
, &dark
,
2572 DwmSetWindowAttribute(mWnd
, DWMWA_USE_IMMERSIVE_DARK_MODE
, &dark
,
2576 LayoutDeviceIntMargin
nsWindow::NormalWindowNonClientOffset() const {
2577 LayoutDeviceIntMargin nonClientOffset
;
2579 // We're dealing with a "normal" window (not maximized, minimized, or
2580 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2583 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2584 // frame intact. Setting it to a value greater than 0 reduces the frame
2585 // size by that amount.
2587 if (mNonClientMargins
.top
> 0) {
2588 nonClientOffset
.top
= std::min(mCaptionHeight
, mNonClientMargins
.top
);
2589 } else if (mNonClientMargins
.top
== 0) {
2590 nonClientOffset
.top
= mCaptionHeight
;
2592 nonClientOffset
.top
= 0;
2595 if (mNonClientMargins
.bottom
> 0) {
2596 nonClientOffset
.bottom
=
2597 std::min(mVertResizeMargin
, mNonClientMargins
.bottom
);
2598 } else if (mNonClientMargins
.bottom
== 0) {
2599 nonClientOffset
.bottom
= mVertResizeMargin
;
2601 nonClientOffset
.bottom
= 0;
2604 if (mNonClientMargins
.left
> 0) {
2605 nonClientOffset
.left
= std::min(mHorResizeMargin
, mNonClientMargins
.left
);
2606 } else if (mNonClientMargins
.left
== 0) {
2607 nonClientOffset
.left
= mHorResizeMargin
;
2609 nonClientOffset
.left
= 0;
2612 if (mNonClientMargins
.right
> 0) {
2613 nonClientOffset
.right
= std::min(mHorResizeMargin
, mNonClientMargins
.right
);
2614 } else if (mNonClientMargins
.right
== 0) {
2615 nonClientOffset
.right
= mHorResizeMargin
;
2617 nonClientOffset
.right
= 0;
2619 return nonClientOffset
;
2623 * Called when the window layout changes: full screen mode transitions,
2624 * theme changes, and composition changes. Calculates the new non-client
2625 * margins and fires off a frame changed event, which triggers an nc calc
2626 * size windows event, kicking the changes in.
2628 * The offsets calculated here are based on the value of `mNonClientMargins`
2629 * which is specified in the "chromemargins" attribute of the window. For
2630 * each margin, the value specified has the following meaning:
2631 * -1 - leave the default frame in place
2632 * 0 - remove the frame
2633 * >0 - frame size equals min(0, (default frame size - margin value))
2635 * This function calculates and populates `mNonClientOffset`.
2636 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2637 * as (default frame size - offset). For example, if the left frame should
2638 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2641 * For maximized, fullscreen, and minimized windows, the values stored in
2642 * `mNonClientMargins` are ignored, and special processing takes place.
2644 * For non-glass windows, we only allow frames to be their default size
2645 * or removed entirely.
2647 bool nsWindow::UpdateNonClientMargins(bool aReflowWindow
) {
2648 if (!mCustomNonClient
) {
2652 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
2655 bool(mBorderStyle
& (BorderStyle::All
| BorderStyle::Title
|
2656 BorderStyle::Menu
| BorderStyle::Default
));
2658 float dpi
= GetDPI();
2660 // mCaptionHeight is the default size of the NC area at
2661 // the top of the window. If the window has a caption,
2662 // the size is calculated as the sum of:
2663 // SM_CYFRAME - The thickness of the sizing border
2664 // around a resizable window
2665 // SM_CXPADDEDBORDER - The amount of border padding
2666 // for captioned windows
2667 // SM_CYCAPTION - The height of the caption area
2669 // If the window does not have a caption, mCaptionHeight will be equal to
2670 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2672 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2673 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CYCAPTION
, dpi
) +
2674 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2676 if (!mUseResizeMarginOverrides
) {
2677 // mHorResizeMargin is the size of the default NC areas on the
2678 // left and right sides of our window. It is calculated as
2680 // SM_CXFRAME - The thickness of the sizing border
2681 // SM_CXPADDEDBORDER - The amount of border padding
2682 // for captioned windows
2684 // If the window does not have a caption, mHorResizeMargin will be equal to
2685 // `WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)`
2687 WinUtils::GetSystemMetricsForDpi(SM_CXFRAME
, dpi
) +
2688 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2691 // mVertResizeMargin is the size of the default NC area at the
2692 // bottom of the window. It is calculated as the sum of:
2693 // SM_CYFRAME - The thickness of the sizing border
2694 // SM_CXPADDEDBORDER - The amount of border padding
2695 // for captioned windows.
2697 // If the window does not have a caption, mVertResizeMargin will be equal to
2698 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2700 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2701 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2705 if (sizeMode
== nsSizeMode_Minimized
) {
2706 // Use default frame size for minimized windows
2707 mNonClientOffset
.top
= 0;
2708 mNonClientOffset
.left
= 0;
2709 mNonClientOffset
.right
= 0;
2710 mNonClientOffset
.bottom
= 0;
2711 } else if (sizeMode
== nsSizeMode_Fullscreen
) {
2712 // Remove the default frame from the top of our fullscreen window. This
2713 // makes the whole caption part of our client area, allowing us to draw
2714 // in the whole caption area. Additionally remove the default frame from
2715 // the left, right, and bottom.
2716 mNonClientOffset
.top
= mCaptionHeight
;
2717 mNonClientOffset
.bottom
= mVertResizeMargin
;
2718 mNonClientOffset
.left
= mHorResizeMargin
;
2719 mNonClientOffset
.right
= mHorResizeMargin
;
2720 } else if (sizeMode
== nsSizeMode_Maximized
) {
2721 // We make the entire frame part of the client area. We leave the default
2722 // frame sizes for left, right and bottom since Windows will automagically
2723 // position the edges "offscreen" for maximized windows.
2724 int verticalResize
=
2725 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME
, dpi
) +
2726 (hasCaption
? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER
, dpi
)
2729 mNonClientOffset
.top
= mCaptionHeight
- verticalResize
;
2730 mNonClientOffset
.bottom
= 0;
2731 mNonClientOffset
.left
= 0;
2732 mNonClientOffset
.right
= 0;
2734 mozilla::Maybe
<UINT
> maybeEdge
= GetHiddenTaskbarEdge();
2736 auto edge
= maybeEdge
.value();
2737 if (ABE_LEFT
== edge
) {
2738 mNonClientOffset
.left
-= kHiddenTaskbarSize
;
2739 } else if (ABE_RIGHT
== edge
) {
2740 mNonClientOffset
.right
-= kHiddenTaskbarSize
;
2741 } else if (ABE_BOTTOM
== edge
|| ABE_TOP
== edge
) {
2742 mNonClientOffset
.bottom
-= kHiddenTaskbarSize
;
2745 // When we are drawing the non-client region, we need
2746 // to clear the portion of the NC region that is exposed by the
2747 // hidden taskbar. As above, we clear the bottom of the NC region
2748 // when the taskbar is at the top of the screen.
2749 UINT clearEdge
= (edge
== ABE_TOP
) ? ABE_BOTTOM
: edge
;
2750 mClearNCEdge
= Some(clearEdge
);
2753 mNonClientOffset
= NormalWindowNonClientOffset();
2756 if (aReflowWindow
) {
2757 // Force a reflow of content based on the new client
2765 nsresult
nsWindow::SetNonClientMargins(const LayoutDeviceIntMargin
& margins
) {
2766 if (!mIsTopWidgetWindow
|| mBorderStyle
== BorderStyle::None
)
2767 return NS_ERROR_INVALID_ARG
;
2770 mFutureMarginsOnceChromeShows
= margins
;
2771 mFutureMarginsToUse
= true;
2774 mFutureMarginsToUse
= false;
2776 // Request for a reset
2777 if (margins
.top
== -1 && margins
.left
== -1 && margins
.right
== -1 &&
2778 margins
.bottom
== -1) {
2779 mCustomNonClient
= false;
2780 mNonClientMargins
= margins
;
2781 // Force a reflow of content based on the new client
2786 reinterpret_cast<LONG_PTR
>(GetPropW(mWnd
, kManageWindowInfoProperty
));
2788 ::SendMessageW(mWnd
, WM_NCACTIVATE
, 1 != windowStatus
, 0);
2794 if (margins
.top
< -1 || margins
.bottom
< -1 || margins
.left
< -1 ||
2796 return NS_ERROR_INVALID_ARG
;
2798 mNonClientMargins
= margins
;
2799 mCustomNonClient
= true;
2800 if (!UpdateNonClientMargins()) {
2801 NS_WARNING("UpdateNonClientMargins failed!");
2808 void nsWindow::SetResizeMargin(mozilla::LayoutDeviceIntCoord aResizeMargin
) {
2809 mUseResizeMarginOverrides
= true;
2810 mHorResizeMargin
= aResizeMargin
;
2811 mVertResizeMargin
= aResizeMargin
;
2812 UpdateNonClientMargins();
2815 void nsWindow::InvalidateNonClientRegion() {
2816 // +-+-----------------------+-+
2817 // | | app non-client chrome | |
2818 // | +-----------------------+ |
2819 // | | app client chrome | | }
2820 // | +-----------------------+ | }
2821 // | | app content | | } area we don't want to invalidate
2822 // | +-----------------------+ | }
2823 // | | app client chrome | | }
2824 // | +-----------------------+ |
2825 // +---------------------------+ <
2826 // ^ ^ windows non-client chrome
2827 // client area = app *
2829 GetWindowRect(mWnd
, &rect
);
2830 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2831 HRGN winRgn
= CreateRectRgnIndirect(&rect
);
2833 // Subtract app client chrome and app content leaving
2834 // windows non-client chrome and app non-client chrome
2836 GetWindowRect(mWnd
, &rect
);
2837 rect
.top
+= mCaptionHeight
;
2838 rect
.right
-= mHorResizeMargin
;
2839 rect
.bottom
-= mVertResizeMargin
;
2840 rect
.left
+= mHorResizeMargin
;
2841 MapWindowPoints(nullptr, mWnd
, (LPPOINT
)&rect
, 2);
2842 HRGN clientRgn
= CreateRectRgnIndirect(&rect
);
2843 CombineRgn(winRgn
, winRgn
, clientRgn
, RGN_DIFF
);
2844 DeleteObject(clientRgn
);
2846 // triggers ncpaint and paint events for the two areas
2847 RedrawWindow(mWnd
, nullptr, winRgn
, RDW_FRAME
| RDW_INVALIDATE
);
2848 DeleteObject(winRgn
);
2851 /**************************************************************
2853 * SECTION: nsIWidget::SetBackgroundColor
2855 * Sets the window background paint color.
2857 **************************************************************/
2859 void nsWindow::SetBackgroundColor(const nscolor
& aColor
) {
2860 if (mBrush
) ::DeleteObject(mBrush
);
2862 mBrush
= ::CreateSolidBrush(NSRGB_2_COLOREF(aColor
));
2863 if (mWnd
!= nullptr) {
2864 ::SetClassLongPtrW(mWnd
, GCLP_HBRBACKGROUND
, (LONG_PTR
)mBrush
);
2868 /**************************************************************
2870 * SECTION: nsIWidget::SetCursor
2872 * SetCursor and related utilities for manging cursor state.
2874 **************************************************************/
2876 // Set this component cursor
2877 static HCURSOR
CursorFor(nsCursor aCursor
) {
2879 case eCursor_select
:
2880 return ::LoadCursor(nullptr, IDC_IBEAM
);
2882 return ::LoadCursor(nullptr, IDC_WAIT
);
2883 case eCursor_hyperlink
:
2884 return ::LoadCursor(nullptr, IDC_HAND
);
2885 case eCursor_standard
:
2886 case eCursor_context_menu
: // XXX See bug 258960.
2887 return ::LoadCursor(nullptr, IDC_ARROW
);
2889 case eCursor_n_resize
:
2890 case eCursor_s_resize
:
2891 return ::LoadCursor(nullptr, IDC_SIZENS
);
2893 case eCursor_w_resize
:
2894 case eCursor_e_resize
:
2895 return ::LoadCursor(nullptr, IDC_SIZEWE
);
2897 case eCursor_nw_resize
:
2898 case eCursor_se_resize
:
2899 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
2901 case eCursor_ne_resize
:
2902 case eCursor_sw_resize
:
2903 return ::LoadCursor(nullptr, IDC_SIZENESW
);
2905 case eCursor_crosshair
:
2906 return ::LoadCursor(nullptr, IDC_CROSS
);
2909 return ::LoadCursor(nullptr, IDC_SIZEALL
);
2912 return ::LoadCursor(nullptr, IDC_HELP
);
2914 case eCursor_copy
: // CSS3
2915 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_COPY
));
2918 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ALIAS
));
2921 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_CELL
));
2923 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_GRAB
));
2925 case eCursor_grabbing
:
2926 return ::LoadCursor(nsToolkit::mDllInstance
,
2927 MAKEINTRESOURCE(IDC_GRABBING
));
2929 case eCursor_spinning
:
2930 return ::LoadCursor(nullptr, IDC_APPSTARTING
);
2932 case eCursor_zoom_in
:
2933 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_ZOOMIN
));
2935 case eCursor_zoom_out
:
2936 return ::LoadCursor(nsToolkit::mDllInstance
,
2937 MAKEINTRESOURCE(IDC_ZOOMOUT
));
2939 case eCursor_not_allowed
:
2940 case eCursor_no_drop
:
2941 return ::LoadCursor(nullptr, IDC_NO
);
2943 case eCursor_col_resize
:
2944 return ::LoadCursor(nsToolkit::mDllInstance
,
2945 MAKEINTRESOURCE(IDC_COLRESIZE
));
2947 case eCursor_row_resize
:
2948 return ::LoadCursor(nsToolkit::mDllInstance
,
2949 MAKEINTRESOURCE(IDC_ROWRESIZE
));
2951 case eCursor_vertical_text
:
2952 return ::LoadCursor(nsToolkit::mDllInstance
,
2953 MAKEINTRESOURCE(IDC_VERTICALTEXT
));
2955 case eCursor_all_scroll
:
2956 // XXX not 100% appropriate perhaps
2957 return ::LoadCursor(nullptr, IDC_SIZEALL
);
2959 case eCursor_nesw_resize
:
2960 return ::LoadCursor(nullptr, IDC_SIZENESW
);
2962 case eCursor_nwse_resize
:
2963 return ::LoadCursor(nullptr, IDC_SIZENWSE
);
2965 case eCursor_ns_resize
:
2966 return ::LoadCursor(nullptr, IDC_SIZENS
);
2968 case eCursor_ew_resize
:
2969 return ::LoadCursor(nullptr, IDC_SIZEWE
);
2972 return ::LoadCursor(nsToolkit::mDllInstance
, MAKEINTRESOURCE(IDC_NONE
));
2975 NS_ERROR("Invalid cursor type");
2980 static HCURSOR
CursorForImage(const nsIWidget::Cursor
& aCursor
,
2981 CSSToLayoutDeviceScale aScale
) {
2982 if (!aCursor
.IsCustom()) {
2986 nsIntSize size
= nsIWidget::CustomCursorSize(aCursor
);
2988 // Reject cursors greater than 128 pixels in either direction, to prevent
2990 // XXX ideally we should rescale. Also, we could modify the API to
2991 // allow trusted content to set larger cursors.
2992 if (size
.width
> 128 || size
.height
> 128) {
2996 LayoutDeviceIntSize layoutSize
=
2997 RoundedToInt(CSSIntSize(size
.width
, size
.height
) * aScale
);
2998 LayoutDeviceIntPoint hotspot
=
2999 RoundedToInt(CSSIntPoint(aCursor
.mHotspotX
, aCursor
.mHotspotY
) * aScale
);
3001 nsresult rv
= nsWindowGfx::CreateIcon(aCursor
.mContainer
, true, hotspot
,
3002 layoutSize
, &cursor
);
3003 if (NS_FAILED(rv
)) {
3010 void nsWindow::SetCursor(const Cursor
& aCursor
) {
3011 static HCURSOR sCurrentHCursor
= nullptr;
3012 static bool sCurrentHCursorIsCustom
= false;
3016 if (sCurrentCursor
== aCursor
&& sCurrentHCursor
&& !mUpdateCursor
) {
3017 // Cursors in windows are global, so even if our mUpdateCursor flag is
3018 // false we always need to make sure the Windows cursor is up-to-date,
3019 // since stuff like native drag and drop / resizers code can mutate it
3020 // outside of this method.
3021 ::SetCursor(sCurrentHCursor
);
3025 mUpdateCursor
= false;
3027 if (sCurrentHCursorIsCustom
) {
3028 ::DestroyIcon(sCurrentHCursor
);
3030 sCurrentHCursor
= nullptr;
3031 sCurrentHCursorIsCustom
= false;
3032 sCurrentCursor
= aCursor
;
3034 HCURSOR cursor
= CursorForImage(aCursor
, GetDefaultScale());
3035 bool custom
= false;
3039 cursor
= CursorFor(aCursor
.mDefaultCursor
);
3046 sCurrentHCursor
= cursor
;
3047 sCurrentHCursorIsCustom
= custom
;
3048 ::SetCursor(cursor
);
3051 /**************************************************************
3053 * SECTION: nsIWidget::Get/SetTransparencyMode
3055 * Manage the transparency mode of the window containing this
3056 * widget. Only works for popup and dialog windows when the
3057 * Desktop Window Manager compositor is not enabled.
3059 **************************************************************/
3061 TransparencyMode
nsWindow::GetTransparencyMode() {
3062 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
3065 void nsWindow::SetTransparencyMode(TransparencyMode aMode
) {
3066 nsWindow
* window
= GetTopLevelWindow(true);
3069 if (!window
|| window
->DestroyCalled()) {
3073 window
->SetWindowTranslucencyInner(aMode
);
3076 /**************************************************************
3078 * SECTION: nsIWidget::UpdateWindowDraggingRegion
3080 * For setting the draggable titlebar region from CSS
3081 * with -moz-window-dragging: drag.
3083 **************************************************************/
3085 void nsWindow::UpdateWindowDraggingRegion(
3086 const LayoutDeviceIntRegion
& aRegion
) {
3087 if (mDraggableRegion
!= aRegion
) {
3088 mDraggableRegion
= aRegion
;
3092 /**************************************************************
3094 * SECTION: nsIWidget::HideWindowChrome
3096 * Show or hide window chrome.
3098 **************************************************************/
3100 void nsWindow::HideWindowChrome(bool aShouldHide
) {
3101 HWND hwnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
3102 if (!WinUtils::GetNSWindowPtr(hwnd
)) {
3103 NS_WARNING("Trying to hide window decorations in an embedded context");
3107 if (mHideChrome
== aShouldHide
) return;
3109 DWORD_PTR style
, exStyle
;
3110 mHideChrome
= aShouldHide
;
3112 DWORD_PTR tempStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3113 DWORD_PTR tempExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3115 style
= tempStyle
& ~(WS_CAPTION
| WS_THICKFRAME
);
3116 exStyle
= tempExStyle
& ~(WS_EX_DLGMODALFRAME
| WS_EX_WINDOWEDGE
|
3117 WS_EX_CLIENTEDGE
| WS_EX_STATICEDGE
);
3119 mOldStyle
= tempStyle
;
3120 mOldExStyle
= tempExStyle
;
3122 if (!mOldStyle
|| !mOldExStyle
) {
3123 mOldStyle
= ::GetWindowLongPtrW(hwnd
, GWL_STYLE
);
3124 mOldExStyle
= ::GetWindowLongPtrW(hwnd
, GWL_EXSTYLE
);
3128 exStyle
= mOldExStyle
;
3129 if (mFutureMarginsToUse
) {
3130 SetNonClientMargins(mFutureMarginsOnceChromeShows
);
3134 VERIFY_WINDOW_STYLE(style
);
3135 ::SetWindowLongPtrW(hwnd
, GWL_STYLE
, style
);
3136 ::SetWindowLongPtrW(hwnd
, GWL_EXSTYLE
, exStyle
);
3139 /**************************************************************
3141 * SECTION: nsWindow::Invalidate
3143 * Invalidate an area of the client for painting.
3145 **************************************************************/
3147 // Invalidate this component visible area
3148 void nsWindow::Invalidate(bool aEraseBackground
, bool aUpdateNCArea
,
3149 bool aIncludeChildren
) {
3154 #ifdef WIDGET_DEBUG_OUTPUT
3155 debug_DumpInvalidate(stdout
, this, nullptr, "noname", (int32_t)mWnd
);
3156 #endif // WIDGET_DEBUG_OUTPUT
3158 DWORD flags
= RDW_INVALIDATE
;
3159 if (aEraseBackground
) {
3162 if (aUpdateNCArea
) {
3165 if (aIncludeChildren
) {
3166 flags
|= RDW_ALLCHILDREN
;
3169 VERIFY(::RedrawWindow(mWnd
, nullptr, nullptr, flags
));
3172 // Invalidate this component visible area
3173 void nsWindow::Invalidate(const LayoutDeviceIntRect
& aRect
) {
3175 #ifdef WIDGET_DEBUG_OUTPUT
3176 debug_DumpInvalidate(stdout
, this, &aRect
, "noname", (int32_t)mWnd
);
3177 #endif // WIDGET_DEBUG_OUTPUT
3181 rect
.left
= aRect
.X();
3182 rect
.top
= aRect
.Y();
3183 rect
.right
= aRect
.XMost();
3184 rect
.bottom
= aRect
.YMost();
3186 VERIFY(::InvalidateRect(mWnd
, &rect
, FALSE
));
3190 static LRESULT CALLBACK
FullscreenTransitionWindowProc(HWND hWnd
, UINT uMsg
,
3194 case WM_FULLSCREEN_TRANSITION_BEFORE
:
3195 case WM_FULLSCREEN_TRANSITION_AFTER
: {
3196 DWORD duration
= (DWORD
)lParam
;
3197 DWORD flags
= AW_BLEND
;
3198 if (uMsg
== WM_FULLSCREEN_TRANSITION_AFTER
) {
3201 ::AnimateWindow(hWnd
, duration
, flags
);
3202 // The message sender should have added ref for us.
3203 NS_DispatchToMainThread(
3204 already_AddRefed
<nsIRunnable
>((nsIRunnable
*)wParam
));
3208 ::PostQuitMessage(0);
3211 return ::DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
3216 struct FullscreenTransitionInitData
{
3217 LayoutDeviceIntRect mBounds
;
3222 FullscreenTransitionInitData()
3223 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3225 ~FullscreenTransitionInitData() {
3227 ::CloseHandle(mSemaphore
);
3230 ::CloseHandle(mThread
);
3235 static DWORD WINAPI
FullscreenTransitionThreadProc(LPVOID lpParam
) {
3236 // Initialize window class
3237 static bool sInitialized
= false;
3238 if (!sInitialized
) {
3240 wc
.lpfnWndProc
= ::FullscreenTransitionWindowProc
;
3241 wc
.hInstance
= nsToolkit::mDllInstance
;
3242 wc
.hbrBackground
= ::CreateSolidBrush(RGB(0, 0, 0));
3243 wc
.lpszClassName
= kClassNameTransition
;
3244 ::RegisterClassW(&wc
);
3245 sInitialized
= true;
3248 auto data
= static_cast<FullscreenTransitionInitData
*>(lpParam
);
3249 HWND wnd
= ::CreateWindowW(kClassNameTransition
, L
"", 0, 0, 0, 0, 0, nullptr,
3250 nullptr, nsToolkit::mDllInstance
, nullptr);
3252 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3256 // Since AnimateWindow blocks the thread of the transition window,
3257 // we need to hide the cursor for that window, otherwise the system
3258 // would show the busy pointer to the user.
3259 ::ShowCursor(false);
3260 ::SetWindowLongW(wnd
, GWL_STYLE
, 0);
3263 WS_EX_LAYERED
| WS_EX_TRANSPARENT
| WS_EX_TOOLWINDOW
| WS_EX_NOACTIVATE
);
3264 ::SetWindowPos(wnd
, HWND_TOPMOST
, data
->mBounds
.X(), data
->mBounds
.Y(),
3265 data
->mBounds
.Width(), data
->mBounds
.Height(), 0);
3267 ::ReleaseSemaphore(data
->mSemaphore
, 1, nullptr);
3268 // The initialization data may no longer be valid
3269 // after we release the semaphore.
3273 while (::GetMessageW(&msg
, nullptr, 0, 0)) {
3274 ::TranslateMessage(&msg
);
3275 ::DispatchMessage(&msg
);
3278 ::DestroyWindow(wnd
);
3282 class FullscreenTransitionData final
: public nsISupports
{
3286 explicit FullscreenTransitionData(HWND aWnd
) : mWnd(aWnd
) {
3287 MOZ_ASSERT(NS_IsMainThread(),
3288 "FullscreenTransitionData "
3289 "should be constructed in the main thread");
3295 ~FullscreenTransitionData() {
3296 MOZ_ASSERT(NS_IsMainThread(),
3297 "FullscreenTransitionData "
3298 "should be deconstructed in the main thread");
3299 ::PostMessageW(mWnd
, WM_DESTROY
, 0, 0);
3303 NS_IMPL_ISUPPORTS0(FullscreenTransitionData
)
3306 bool nsWindow::PrepareForFullscreenTransition(nsISupports
** aData
) {
3307 FullscreenTransitionInitData initData
;
3308 nsCOMPtr
<nsIScreen
> screen
= GetWidgetScreen();
3309 const DesktopIntRect rect
= screen
->GetRectDisplayPix();
3310 MOZ_ASSERT(BoundsUseDesktopPixels(),
3311 "Should only be called on top-level window");
3313 LayoutDeviceIntRect::Round(rect
* GetDesktopToDeviceScale());
3315 // Create a semaphore for synchronizing the window handle which will
3316 // be created by the transition thread and used by the main thread for
3317 // posting the transition messages.
3318 initData
.mSemaphore
= ::CreateSemaphore(nullptr, 0, 1, nullptr);
3319 if (initData
.mSemaphore
) {
3320 initData
.mThread
= ::CreateThread(
3321 nullptr, 0, FullscreenTransitionThreadProc
, &initData
, 0, nullptr);
3322 if (initData
.mThread
) {
3323 ::WaitForSingleObject(initData
.mSemaphore
, INFINITE
);
3326 if (!initData
.mWnd
) {
3330 mTransitionWnd
= initData
.mWnd
;
3332 auto data
= new FullscreenTransitionData(initData
.mWnd
);
3339 void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage
,
3342 nsIRunnable
* aCallback
) {
3343 auto data
= static_cast<FullscreenTransitionData
*>(aData
);
3344 nsCOMPtr
<nsIRunnable
> callback
= aCallback
;
3345 UINT msg
= aStage
== eBeforeFullscreenToggle
? WM_FULLSCREEN_TRANSITION_BEFORE
3346 : WM_FULLSCREEN_TRANSITION_AFTER
;
3347 WPARAM wparam
= (WPARAM
)callback
.forget().take();
3348 ::PostMessage(data
->mWnd
, msg
, wparam
, (LPARAM
)aDuration
);
3352 void nsWindow::CleanupFullscreenTransition() {
3353 MOZ_ASSERT(NS_IsMainThread(),
3354 "CleanupFullscreenTransition "
3355 "should only run on the main thread");
3357 mTransitionWnd
= nullptr;
3360 void nsWindow::TryDwmResizeHack() {
3361 // The "DWM resize hack", aka the "fullscreen resize hack", is a workaround
3362 // for DWM's occasional and not-entirely-predictable failure to update its
3363 // internal state when the client area of a window changes without changing
3364 // the window size. The effect of this is that DWM will clip the content of
3365 // the window to its former client area.
3367 // It is not known under what circumstances the bug will trigger. Windows 11
3368 // is known to be required, but many Windows 11 machines do not exhibit the
3369 // issue. Even machines that _do_ exhibit it will sometimes not do so when
3370 // apparently-irrelevant changes are made to the configuration. (See bug
3373 // The bug is triggered by Firefox when a maximized window (which has window
3374 // decorations) becomes fullscreen (which doesn't). To work around this, if we
3375 // think it may occur, we "flicker-resize" the relevant window -- that is, we
3376 // reduce its height by 1px, then restore it. This causes DWM to acquire the
3377 // new client-area metrics.
3379 // Note that, in particular, this bug will not occur when using a separate
3380 // compositor window, as our compositor windows never have any nonclient area.
3382 // This is admittedly a sledgehammer where a screwdriver should suffice.
3384 // ---------------------------------------------------------------------------
3386 // Regardless of preferences or heuristics, only apply the hack if this is the
3387 // first time we've entered fullscreen across the entire Firefox session.
3388 // (Subsequent transitions to fullscreen, even with different windows, don't
3389 // appear to induce the bug.)
3391 // (main thread only; `atomic` not needed)
3392 static bool sIsFirstFullscreenEntry
= true;
3393 bool isFirstFullscreenEntry
= sIsFirstFullscreenEntry
;
3394 sIsFirstFullscreenEntry
= false;
3395 if (MOZ_LIKELY(!isFirstFullscreenEntry
)) {
3398 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3399 ("%s: first fullscreen entry", __PRETTY_FUNCTION__
));
3402 // Check whether to try to apply the DWM resize hack, based on the override
3403 // pref and/or some internal heuristics.
3405 const auto hackApplicationHeuristics
= [&]() -> bool {
3406 // The bug has only been seen under Windows 11. (At time of writing, this
3407 // is the latest version of Windows.)
3408 if (!IsWin11OrLater()) {
3412 KnowsCompositor
const* const kc
= mWindowRenderer
->AsKnowsCompositor();
3413 // This should never happen...
3415 // ... so if it does, we are in uncharted territory: don't apply the hack.
3420 // The bug doesn't occur when we're using a separate compositor window
3421 // (since the compositor window always comprises exactly its client area,
3422 // with no non-client border).
3423 if (kc
->GetUseCompositorWnd()) {
3427 // Otherwise, apply the hack.
3431 // Figure out whether or not we should perform the hack, and -- arguably
3432 // more importantly -- log that decision.
3433 bool const shouldApplyHack
= [&]() {
3434 enum Reason
: bool { Pref
, Heuristics
};
3435 auto const msg
= [&](bool decision
, Reason reason
) -> bool {
3436 MOZ_LOG(gWindowsLog
, LogLevel::Verbose
,
3437 ("%s %s per %s", decision
? "applying" : "skipping",
3438 "DWM resize hack", reason
== Pref
? "pref" : "heuristics"));
3441 switch (StaticPrefs::widget_windows_apply_dwm_resize_hack()) {
3443 return msg(false, Pref
);
3445 return msg(true, Pref
);
3446 default: // treat all other values as `auto`
3447 return msg(hackApplicationHeuristics(), Heuristics
);
3451 if (!shouldApplyHack
) {
3456 // The DWM bug is believed to involve a race condition: some users have
3457 // reported that setting a custom theme or adding unused command-line
3458 // parameters sometimes causes the bug to vanish.
3460 // Out of an abundance of caution, we therefore apply the hack in a later
3461 // event, rather than inline.
3462 NS_DispatchToMainThread(NS_NewRunnableFunction(
3463 "nsWindow::TryFullscreenResizeHack", [self
= RefPtr(this)]() {
3464 HWND
const hwnd
= self
->GetWindowHandle();
3466 if (self
->mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) {
3467 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Info
,
3468 ("DWM resize hack: window no longer fullscreen; aborting"));
3473 if (!::GetWindowRect(hwnd
, &origRect
)) {
3474 MOZ_LOG(gWindowsLog
, mozilla::LogLevel::Error
,
3475 ("DWM resize hack: could not get window size?!"));
3478 LONG
const x
= origRect
.left
;
3479 LONG
const y
= origRect
.top
;
3480 LONG
const width
= origRect
.right
- origRect
.left
;
3481 LONG
const height
= origRect
.bottom
- origRect
.top
;
3483 MOZ_DIAGNOSTIC_ASSERT(!self
->mIsPerformingDwmFlushHack
);
3485 MakeScopeExit([&, oldVal
= self
->mIsPerformingDwmFlushHack
]() {
3486 self
->mIsPerformingDwmFlushHack
= oldVal
;
3488 self
->mIsPerformingDwmFlushHack
= true;
3490 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3491 ("beginning DWM resize hack for HWND %08" PRIXPTR
,
3493 ::MoveWindow(hwnd
, x
, y
, width
, height
- 1, FALSE
);
3494 ::MoveWindow(hwnd
, x
, y
, width
, height
, TRUE
);
3495 MOZ_LOG(gWindowsLog
, LogLevel::Debug
,
3496 ("concluded DWM resize hack for HWND %08" PRIXPTR
,
3501 void nsWindow::OnFullscreenChanged(nsSizeMode aOldSizeMode
, bool aFullScreen
) {
3502 MOZ_ASSERT((aOldSizeMode
!= nsSizeMode_Fullscreen
) == aFullScreen
);
3504 // HACK: Potentially flicker-resize the window, to force DWM to get the right
3505 // client-area information.
3510 // Hide chrome and reposition window. Note this will also cache dimensions for
3511 // restoration, so it should only be called once per fullscreen request.
3513 // Don't do this when minimized, since our bounds make no sense then, nor when
3514 // coming back from that state.
3515 const bool toOrFromMinimized
=
3516 mFrameState
->GetSizeMode() == nsSizeMode_Minimized
||
3517 aOldSizeMode
== nsSizeMode_Minimized
;
3518 if (!toOrFromMinimized
) {
3519 InfallibleMakeFullScreen(aFullScreen
);
3522 // Possibly notify the taskbar that we have changed our fullscreen mode.
3523 TaskbarConcealer::OnFullscreenChanged(this, aFullScreen
);
3526 nsresult
nsWindow::MakeFullScreen(bool aFullScreen
) {
3527 mFrameState
->EnsureFullscreenMode(aFullScreen
);
3531 /**************************************************************
3533 * SECTION: Native data storage
3535 * nsIWidget::GetNativeData
3536 * nsIWidget::FreeNativeData
3538 * Set or clear native data based on a constant.
3540 **************************************************************/
3542 // Return some native data according to aDataType
3543 void* nsWindow::GetNativeData(uint32_t aDataType
) {
3544 switch (aDataType
) {
3545 case NS_NATIVE_WIDGET
:
3546 case NS_NATIVE_WINDOW
:
3547 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID
:
3549 case NS_NATIVE_GRAPHIC
:
3550 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3552 case NS_RAW_NATIVE_IME_CONTEXT
: {
3553 void* pseudoIMEContext
= GetPseudoIMEContext();
3554 if (pseudoIMEContext
) {
3555 return pseudoIMEContext
;
3559 case NS_NATIVE_TSF_THREAD_MGR
:
3560 case NS_NATIVE_TSF_CATEGORY_MGR
:
3561 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR
:
3562 return IMEHandler::GetNativeData(this, aDataType
);
3571 // Free some native data according to aDataType
3572 void nsWindow::FreeNativeData(void* data
, uint32_t aDataType
) {
3573 switch (aDataType
) {
3574 case NS_NATIVE_GRAPHIC
:
3575 case NS_NATIVE_WIDGET
:
3576 case NS_NATIVE_WINDOW
:
3583 /**************************************************************
3585 * SECTION: nsIWidget::SetTitle
3587 * Set the main windows title text.
3589 **************************************************************/
3591 nsresult
nsWindow::SetTitle(const nsAString
& aTitle
) {
3592 const nsString
& strTitle
= PromiseFlatString(aTitle
);
3593 AutoRestore
<bool> sendingText(mSendingSetText
);
3594 mSendingSetText
= true;
3595 ::SendMessageW(mWnd
, WM_SETTEXT
, (WPARAM
)0, (LPARAM
)(LPCWSTR
)strTitle
.get());
3599 /**************************************************************
3601 * SECTION: nsIWidget::SetIcon
3603 * Set the main windows icon.
3605 **************************************************************/
3607 void nsWindow::SetBigIcon(HICON aIcon
) {
3609 (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_BIG
, (LPARAM
)aIcon
);
3611 ::DestroyIcon(icon
);
3617 void nsWindow::SetSmallIcon(HICON aIcon
) {
3618 HICON icon
= (HICON
)::SendMessageW(mWnd
, WM_SETICON
, (WPARAM
)ICON_SMALL
,
3621 ::DestroyIcon(icon
);
3627 void nsWindow::SetIcon(const nsAString
& aIconSpec
) {
3628 // Assume the given string is a local identifier for an icon file.
3630 nsCOMPtr
<nsIFile
> iconFile
;
3631 ResolveIconName(aIconSpec
, u
".ico"_ns
, getter_AddRefs(iconFile
));
3632 if (!iconFile
) return;
3634 nsAutoString iconPath
;
3635 iconFile
->GetPath(iconPath
);
3637 // XXX this should use MZLU (see bug 239279)
3642 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3643 ::GetSystemMetrics(SM_CXICON
),
3644 ::GetSystemMetrics(SM_CYICON
), LR_LOADFROMFILE
);
3646 (HICON
)::LoadImageW(nullptr, (LPCWSTR
)iconPath
.get(), IMAGE_ICON
,
3647 ::GetSystemMetrics(SM_CXSMICON
),
3648 ::GetSystemMetrics(SM_CYSMICON
), LR_LOADFROMFILE
);
3651 SetBigIcon(bigIcon
);
3653 #ifdef DEBUG_SetIcon
3655 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3656 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3657 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3662 SetSmallIcon(smallIcon
);
3664 #ifdef DEBUG_SetIcon
3666 NS_LossyConvertUTF16toASCII
cPath(iconPath
);
3667 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
3668 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath
.get(),
3674 void nsWindow::SetBigIconNoData() {
3676 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3677 SetBigIcon(bigIcon
);
3680 void nsWindow::SetSmallIconNoData() {
3682 ::LoadIconW(::GetModuleHandleW(nullptr), gStockApplicationIcon
);
3683 SetSmallIcon(smallIcon
);
3686 /**************************************************************
3688 * SECTION: nsIWidget::WidgetToScreenOffset
3690 * Return this widget's origin in screen coordinates.
3692 **************************************************************/
3694 LayoutDeviceIntPoint
nsWindow::WidgetToScreenOffset() {
3698 ::ClientToScreen(mWnd
, &point
);
3699 return LayoutDeviceIntPoint(point
.x
, point
.y
);
3702 LayoutDeviceIntMargin
nsWindow::ClientToWindowMargin() {
3703 if (mWindowType
== WindowType::Popup
&& !IsPopupWithTitleBar()) {
3707 if (mCustomNonClient
) {
3708 return NonClientSizeMargin(NormalWindowNonClientOffset());
3711 // Just use a dummy 200x200 at (200, 200) client rect as the rect.
3713 clientRect
.left
= 200;
3714 clientRect
.top
= 200;
3715 clientRect
.right
= 400;
3716 clientRect
.bottom
= 400;
3718 auto ToRect
= [](const RECT
& aRect
) -> LayoutDeviceIntRect
{
3719 return {aRect
.left
, aRect
.top
, aRect
.right
- aRect
.left
,
3720 aRect
.bottom
- aRect
.top
};
3723 RECT windowRect
= clientRect
;
3724 ::AdjustWindowRectEx(&windowRect
, WindowStyle(), false, WindowExStyle());
3726 return ToRect(windowRect
) - ToRect(clientRect
);
3729 /**************************************************************
3731 * SECTION: nsIWidget::EnableDragDrop
3733 * Enables/Disables drag and drop of files on this widget.
3735 **************************************************************/
3737 void nsWindow::EnableDragDrop(bool aEnable
) {
3739 // Return early if the window already closed
3744 if (!mNativeDragTarget
) {
3745 mNativeDragTarget
= new nsNativeDragTarget(this);
3746 mNativeDragTarget
->AddRef();
3747 ::RegisterDragDrop(mWnd
, (LPDROPTARGET
)mNativeDragTarget
);
3750 if (mWnd
&& mNativeDragTarget
) {
3751 ::RevokeDragDrop(mWnd
);
3752 mNativeDragTarget
->DragCancel();
3753 NS_RELEASE(mNativeDragTarget
);
3758 /**************************************************************
3760 * SECTION: nsIWidget::CaptureMouse
3762 * Enables/Disables system mouse capture.
3764 **************************************************************/
3766 void nsWindow::CaptureMouse(bool aCapture
) {
3767 TRACKMOUSEEVENT mTrack
;
3768 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
3769 mTrack
.dwHoverTime
= 0;
3770 mTrack
.hwndTrack
= mWnd
;
3772 mTrack
.dwFlags
= TME_CANCEL
| TME_LEAVE
;
3775 mTrack
.dwFlags
= TME_LEAVE
;
3778 sIsInMouseCapture
= aCapture
;
3779 TrackMouseEvent(&mTrack
);
3782 /**************************************************************
3784 * SECTION: nsIWidget::CaptureRollupEvents
3786 * Dealing with event rollup on destroy for popups. Enables &
3787 * Disables system capture of any and all events that would
3788 * cause a dropdown to be rolled up.
3790 **************************************************************/
3792 void nsWindow::CaptureRollupEvents(bool aDoCapture
) {
3794 if (!sMsgFilterHook
&& !sCallProcHook
&& !sCallMouseHook
) {
3795 RegisterSpecialDropdownHooks();
3797 sProcessHook
= true;
3799 sProcessHook
= false;
3800 UnregisterSpecialDropdownHooks();
3804 /**************************************************************
3806 * SECTION: nsIWidget::GetAttention
3808 * Bring this window to the user's attention.
3810 **************************************************************/
3812 // Draw user's attention to this window until it comes to foreground.
3813 nsresult
nsWindow::GetAttention(int32_t aCycleCount
) {
3815 if (!mWnd
) return NS_ERROR_NOT_INITIALIZED
;
3817 HWND flashWnd
= WinUtils::GetTopLevelHWND(mWnd
, false, false);
3818 HWND fgWnd
= ::GetForegroundWindow();
3819 // Don't flash if the flash count is 0 or if the foreground window is our
3820 // window handle or that of our owned-most window.
3821 if (aCycleCount
== 0 || flashWnd
== fgWnd
||
3822 flashWnd
== WinUtils::GetTopLevelHWND(fgWnd
, false, false)) {
3826 DWORD defaultCycleCount
= 0;
3827 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT
, 0, &defaultCycleCount
, 0);
3829 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_ALL
,
3830 aCycleCount
> 0 ? aCycleCount
: defaultCycleCount
, 0};
3831 ::FlashWindowEx(&flashInfo
);
3836 void nsWindow::StopFlashing() {
3837 HWND flashWnd
= mWnd
;
3838 while (HWND ownerWnd
= ::GetWindow(flashWnd
, GW_OWNER
)) {
3839 flashWnd
= ownerWnd
;
3842 FLASHWINFO flashInfo
= {sizeof(FLASHWINFO
), flashWnd
, FLASHW_STOP
, 0, 0};
3843 ::FlashWindowEx(&flashInfo
);
3846 /**************************************************************
3848 * SECTION: nsIWidget::HasPendingInputEvent
3850 * Ask whether there user input events pending. All input events are
3851 * included, including those not targeted at this nsIwidget instance.
3853 **************************************************************/
3855 bool nsWindow::HasPendingInputEvent() {
3856 // If there is pending input or the user is currently
3857 // moving the window then return true.
3858 // Note: When the user is moving the window WIN32 spins
3859 // a separate event loop and input events are not
3860 // reported to the application.
3861 if (HIWORD(GetQueueStatus(QS_INPUT
))) return true;
3862 GUITHREADINFO guiInfo
;
3863 guiInfo
.cbSize
= sizeof(GUITHREADINFO
);
3864 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo
)) return false;
3865 return GUI_INMOVESIZE
== (guiInfo
.flags
& GUI_INMOVESIZE
);
3868 /**************************************************************
3870 * SECTION: nsIWidget::GetWindowRenderer
3872 * Get the window renderer associated with this widget.
3874 **************************************************************/
3876 WindowRenderer
* nsWindow::GetWindowRenderer() {
3877 if (mWindowRenderer
) {
3878 return mWindowRenderer
;
3881 if (!mLocalesChangedObserver
) {
3882 mLocalesChangedObserver
= new LocalesChangedObserver(this);
3886 if (!mWindowRenderer
&& ShouldUseOffMainThreadCompositing()) {
3887 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
3891 if (!mWindowRenderer
) {
3892 MOZ_ASSERT(!mCompositorSession
&& !mCompositorBridgeChild
);
3893 MOZ_ASSERT(!mCompositorWidgetDelegate
);
3895 // Ensure we have a widget proxy even if we're not using the compositor,
3896 // since all our transparent window handling lives there.
3897 WinCompositorWidgetInitData
initData(
3898 reinterpret_cast<uintptr_t>(mWnd
),
3899 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
3900 mTransparencyMode
, mFrameState
->GetSizeMode());
3901 // If we're not using the compositor, the options don't actually matter.
3902 CompositorOptions
options(false, false);
3903 mBasicLayersSurface
=
3904 new InProcessWinCompositorWidget(initData
, options
, this);
3905 mCompositorWidgetDelegate
= mBasicLayersSurface
;
3906 mWindowRenderer
= CreateFallbackRenderer();
3909 NS_ASSERTION(mWindowRenderer
, "Couldn't provide a valid window renderer.");
3911 if (mWindowRenderer
) {
3912 // Update the size constraints now that the layer manager has been
3914 KnowsCompositor
* knowsCompositor
= mWindowRenderer
->AsKnowsCompositor();
3915 if (knowsCompositor
) {
3916 SizeConstraints c
= mSizeConstraints
;
3917 mMaxTextureSize
= knowsCompositor
->GetMaxTextureSize();
3918 c
.mMaxSize
.width
= std::min(c
.mMaxSize
.width
, mMaxTextureSize
);
3919 c
.mMaxSize
.height
= std::min(c
.mMaxSize
.height
, mMaxTextureSize
);
3920 nsBaseWidget::SetSizeConstraints(c
);
3924 return mWindowRenderer
;
3927 /**************************************************************
3929 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
3931 * Called to connect the nsWindow to the delegate providing
3932 * platform compositing API access.
3934 **************************************************************/
3936 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate
* delegate
) {
3938 mCompositorWidgetDelegate
= delegate
->AsPlatformSpecificDelegate();
3939 MOZ_ASSERT(mCompositorWidgetDelegate
,
3940 "nsWindow::SetCompositorWidgetDelegate called with a "
3941 "non-PlatformCompositorWidgetDelegate");
3943 mCompositorWidgetDelegate
= nullptr;
3947 /**************************************************************
3949 * SECTION: nsIWidget::OnDefaultButtonLoaded
3951 * Called after the dialog is loaded and it has a default button.
3953 **************************************************************/
3955 nsresult
nsWindow::OnDefaultButtonLoaded(
3956 const LayoutDeviceIntRect
& aButtonRect
) {
3957 if (aButtonRect
.IsEmpty()) return NS_OK
;
3959 // Don't snap when we are not active.
3960 HWND activeWnd
= ::GetActiveWindow();
3961 if (activeWnd
!= ::GetForegroundWindow() ||
3962 WinUtils::GetTopLevelHWND(mWnd
, true) !=
3963 WinUtils::GetTopLevelHWND(activeWnd
, true)) {
3967 bool isAlwaysSnapCursor
=
3968 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
3970 if (!isAlwaysSnapCursor
) {
3971 BOOL snapDefaultButton
;
3972 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON
, 0, &snapDefaultButton
,
3978 LayoutDeviceIntRect widgetRect
= GetScreenBounds();
3979 LayoutDeviceIntRect
buttonRect(aButtonRect
+ widgetRect
.TopLeft());
3981 LayoutDeviceIntPoint
centerOfButton(buttonRect
.X() + buttonRect
.Width() / 2,
3982 buttonRect
.Y() + buttonRect
.Height() / 2);
3983 // The center of the button can be outside of the widget.
3984 // E.g., it could be hidden by scrolling.
3985 if (!widgetRect
.Contains(centerOfButton
)) {
3989 if (!::SetCursorPos(centerOfButton
.x
, centerOfButton
.y
)) {
3990 NS_ERROR("SetCursorPos failed");
3991 return NS_ERROR_FAILURE
;
3996 uint32_t nsWindow::GetMaxTouchPoints() const {
3997 return WinUtils::GetMaxTouchPoints();
4000 void nsWindow::SetWindowClass(const nsAString
& xulWinType
,
4001 const nsAString
& xulWinClass
,
4002 const nsAString
& xulWinName
) {
4003 mIsEarlyBlankWindow
= xulWinType
.EqualsLiteral("navigator:blank");
4006 /**************************************************************
4007 **************************************************************
4009 ** BLOCK: Moz Events
4011 ** Moz GUI event management.
4013 **************************************************************
4014 **************************************************************/
4016 /**************************************************************
4018 * SECTION: Mozilla event initialization
4020 * Helpers for initializing moz events.
4022 **************************************************************/
4024 // Event initialization
4025 void nsWindow::InitEvent(WidgetGUIEvent
& event
, LayoutDeviceIntPoint
* aPoint
) {
4026 if (nullptr == aPoint
) { // use the point from the event
4027 // get the message position in client coordinates
4028 if (mWnd
!= nullptr) {
4029 DWORD pos
= ::GetMessagePos();
4032 cpos
.x
= GET_X_LPARAM(pos
);
4033 cpos
.y
= GET_Y_LPARAM(pos
);
4035 ::ScreenToClient(mWnd
, &cpos
);
4036 event
.mRefPoint
= LayoutDeviceIntPoint(cpos
.x
, cpos
.y
);
4038 event
.mRefPoint
= LayoutDeviceIntPoint(0, 0);
4041 // use the point override if provided
4042 event
.mRefPoint
= *aPoint
;
4045 event
.AssignEventTime(CurrentMessageWidgetEventTime());
4048 WidgetEventTime
nsWindow::CurrentMessageWidgetEventTime() const {
4049 LONG messageTime
= ::GetMessageTime();
4050 return WidgetEventTime(GetMessageTimeStamp(messageTime
));
4053 /**************************************************************
4055 * SECTION: Moz event dispatch helpers
4057 * Helpers for dispatching different types of moz events.
4059 **************************************************************/
4061 // Main event dispatch. Invokes callback and ProcessEvent method on
4062 // Event Listener object. Part of nsIWidget.
4063 nsresult
nsWindow::DispatchEvent(WidgetGUIEvent
* event
,
4064 nsEventStatus
& aStatus
) {
4065 #ifdef WIDGET_DEBUG_OUTPUT
4066 debug_DumpEvent(stdout
, event
->mWidget
, event
, "something", (int32_t)mWnd
);
4067 #endif // WIDGET_DEBUG_OUTPUT
4069 aStatus
= nsEventStatus_eIgnore
;
4071 // Top level windows can have a view attached which requires events be sent
4072 // to the underlying base window and the view. Added when we combined the
4073 // base chrome window with the main content child for nc client area (title
4075 if (mAttachedWidgetListener
) {
4076 aStatus
= mAttachedWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4077 } else if (mWidgetListener
) {
4078 aStatus
= mWidgetListener
->HandleEvent(event
, mUseAttachedEvents
);
4081 // the window can be destroyed during processing of seemingly innocuous events
4082 // like, say, mousedowns due to the magic of scripting. mousedowns will return
4083 // nsEventStatus_eIgnore, which causes problems with the deleted window.
4085 if (mOnDestroyCalled
) aStatus
= nsEventStatus_eConsumeNoDefault
;
4089 bool nsWindow::DispatchStandardEvent(EventMessage aMsg
) {
4090 WidgetGUIEvent
event(true, aMsg
, this);
4093 bool result
= DispatchWindowEvent(event
);
4097 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent
* event
) {
4098 nsEventStatus status
= DispatchInputEvent(event
).mContentStatus
;
4099 return ConvertStatus(status
);
4102 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent
* aEvent
) {
4103 nsEventStatus status
;
4104 DispatchEvent(aEvent
, status
);
4105 return ConvertStatus(status
);
4108 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent
* aEvent
) {
4109 nsEventStatus status
=
4110 DispatchInputEvent(aEvent
->AsInputEvent()).mContentStatus
;
4111 return ConvertStatus(status
);
4114 // Recursively dispatch synchronous paints for nsIWidget
4115 // descendants with invalidated rectangles.
4116 BOOL CALLBACK
nsWindow::DispatchStarvedPaints(HWND aWnd
, LPARAM aMsg
) {
4117 LONG_PTR proc
= ::GetWindowLongPtrW(aWnd
, GWLP_WNDPROC
);
4118 if (proc
== (LONG_PTR
)&nsWindow::WindowProc
) {
4119 // its one of our windows so check to see if it has a
4120 // invalidated rect. If it does. Dispatch a synchronous
4122 if (GetUpdateRect(aWnd
, nullptr, FALSE
)) VERIFY(::UpdateWindow(aWnd
));
4127 // Check for pending paints and dispatch any pending paint
4128 // messages for any nsIWidget which is a descendant of the
4129 // top-level window that *this* window is embedded within.
4131 // Note: We do not dispatch pending paint messages for non
4132 // nsIWidget managed windows.
4133 void nsWindow::DispatchPendingEvents() {
4134 // We need to ensure that reflow events do not get starved.
4135 // At the same time, we don't want to recurse through here
4136 // as that would prevent us from dispatching starved paints.
4137 static int recursionBlocker
= 0;
4138 if (recursionBlocker
++ == 0) {
4139 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4143 // Quickly check to see if there are any paint events pending,
4144 // but only dispatch them if it has been long enough since the
4145 // last paint completed.
4146 if (::GetQueueStatus(QS_PAINT
) &&
4147 ((TimeStamp::Now() - mLastPaintEndTime
).ToMilliseconds() >= 50)) {
4148 // Find the top level window.
4149 HWND topWnd
= WinUtils::GetTopLevelHWND(mWnd
);
4151 // Dispatch pending paints for topWnd and all its descendant windows.
4152 // Note: EnumChildWindows enumerates all descendant windows not just
4153 // the children (but not the window itself).
4154 nsWindow::DispatchStarvedPaints(topWnd
, 0);
4155 ::EnumChildWindows(topWnd
, nsWindow::DispatchStarvedPaints
, 0);
4159 void nsWindow::DispatchCustomEvent(const nsString
& eventName
) {
4160 if (Document
* doc
= GetDocument()) {
4161 if (nsPIDOMWindowOuter
* win
= doc
->GetWindow()) {
4162 win
->DispatchCustomEvent(eventName
, ChromeOnlyDispatch::eYes
);
4167 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage
,
4168 LayoutDeviceIntPoint aEventPoint
) {
4169 // Allow users to start dragging by double-tapping.
4170 if (aEventMessage
== eMouseDoubleClick
) {
4174 // In chrome UI, allow touchdownstartsdrag attributes
4175 // to cause any touchdown event to trigger a drag.
4176 if (aEventMessage
== eMouseDown
) {
4177 WidgetMouseEvent
hittest(true, eMouseHitTest
, this,
4178 WidgetMouseEvent::eReal
);
4179 hittest
.mRefPoint
= aEventPoint
;
4180 hittest
.mIgnoreRootScrollFrame
= true;
4181 hittest
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
4182 DispatchInputEvent(&hittest
);
4184 if (EventTarget
* target
= hittest
.GetDOMEventTarget()) {
4185 if (nsIContent
* content
= nsIContent::FromEventTarget(target
)) {
4186 // Check if the element or any parent element has the
4187 // attribute we're looking for.
4188 for (Element
* element
= content
->GetAsElementOrParentElement(); element
;
4189 element
= element
->GetParentElement()) {
4190 nsAutoString startDrag
;
4191 element
->GetAttribute(u
"touchdownstartsdrag"_ns
, startDrag
);
4192 if (!startDrag
.IsEmpty()) {
4203 // Deal with all sort of mouse event
4204 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage
, WPARAM wParam
,
4205 LPARAM lParam
, bool aIsContextMenuKey
,
4206 int16_t aButton
, uint16_t aInputSource
,
4207 WinPointerInfo
* aPointerInfo
,
4209 ContextMenuPreventer
contextMenuPreventer(this);
4210 bool result
= false;
4214 if (!mWidgetListener
) {
4218 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
4219 LayoutDeviceIntPoint mpScreen
= eventPoint
+ WidgetToScreenOffset();
4221 // Suppress mouse moves caused by widget creation. Make sure to do this early
4222 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4224 if (aEventMessage
== eMouseMove
) {
4225 if ((sLastMouseMovePoint
.x
== mpScreen
.x
.value
) &&
4226 (sLastMouseMovePoint
.y
== mpScreen
.y
.value
)) {
4229 sLastMouseMovePoint
.x
= mpScreen
.x
;
4230 sLastMouseMovePoint
.y
= mpScreen
.y
;
4233 if (!aIgnoreAPZ
&& WinUtils::GetIsMouseFromTouch(aEventMessage
)) {
4235 // If mTouchWindow is true, then we must have APZ enabled and be
4236 // feeding it raw touch events. In that case we only want to
4237 // send touch-generated mouse events to content if they should
4238 // start a touch-based drag-and-drop gesture, such as on
4239 // double-tapping or when tapping elements marked with the
4240 // touchdownstartsdrag attribute in chrome UI.
4242 if (TouchEventShouldStartDrag(aEventMessage
, eventPoint
)) {
4243 aEventMessage
= eMouseTouchDrag
;
4250 uint32_t pointerId
=
4251 aPointerInfo
? aPointerInfo
->pointerId
: MOUSE_POINTERID();
4253 switch (aEventMessage
) {
4258 // eMouseMove and eMouseExitFromWidget are here because we need to make
4259 // sure capture flag isn't left on after a drag where we wouldn't see a
4260 // button up message (see bug 324131).
4263 case eMouseExitFromWidget
:
4264 if (!(wParam
& (MK_LBUTTON
| MK_MBUTTON
| MK_RBUTTON
)) &&
4266 CaptureMouse(false);
4274 WidgetMouseEvent
event(true, aEventMessage
, this, WidgetMouseEvent::eReal
,
4275 aIsContextMenuKey
? WidgetMouseEvent::eContextMenuKey
4276 : WidgetMouseEvent::eNormal
);
4277 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
) {
4278 LayoutDeviceIntPoint
zero(0, 0);
4279 InitEvent(event
, &zero
);
4281 InitEvent(event
, &eventPoint
);
4284 ModifierKeyState modifierKeyState
;
4285 modifierKeyState
.InitInputEvent(event
);
4287 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4288 // event in the web content for blocking web content to prevent its default.
4289 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4290 // this should not block web page to prevent its default. I.e., it should
4291 // behave same as ContextMenu key without Shift key.
4292 // XXX Should we allow to block web page to prevent its default with
4293 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4294 if (aEventMessage
== eContextMenu
&& aIsContextMenuKey
&& event
.IsShift() &&
4295 NativeKey::LastKeyOrCharMSG().message
== WM_SYSKEYDOWN
&&
4296 NativeKey::LastKeyOrCharMSG().wParam
== VK_F10
) {
4297 event
.mModifiers
&= ~MODIFIER_SHIFT
;
4300 event
.mButton
= aButton
;
4301 event
.mInputSource
= aInputSource
;
4303 // Mouse events from Windows WM_POINTER*. Fill more information in
4304 // WidgetMouseEvent.
4305 event
.AssignPointerHelperData(*aPointerInfo
);
4306 event
.mPressure
= aPointerInfo
->mPressure
;
4307 event
.mButtons
= aPointerInfo
->mButtons
;
4309 // If we get here the mouse events must be from non-touch sources, so
4310 // convert it to pointer events as well
4311 event
.convertToPointer
= true;
4312 event
.pointerId
= pointerId
;
4315 // Static variables used to distinguish simple-, double- and triple-clicks.
4316 static POINT sLastMousePoint
= {0};
4317 static LONG sLastMouseDownTime
= 0L;
4318 static LONG sLastClickCount
= 0L;
4319 static BYTE sLastMouseButton
= 0;
4321 bool insideMovementThreshold
=
4322 (DeprecatedAbs(sLastMousePoint
.x
- eventPoint
.x
.value
) <
4323 (short)::GetSystemMetrics(SM_CXDOUBLECLK
)) &&
4324 (DeprecatedAbs(sLastMousePoint
.y
- eventPoint
.y
.value
) <
4325 (short)::GetSystemMetrics(SM_CYDOUBLECLK
));
4329 case MouseButton::ePrimary
:
4330 eventButton
= VK_LBUTTON
;
4332 case MouseButton::eMiddle
:
4333 eventButton
= VK_MBUTTON
;
4335 case MouseButton::eSecondary
:
4336 eventButton
= VK_RBUTTON
;
4343 // Doubleclicks are used to set the click count, then changed to mousedowns
4344 // We're going to time double-clicks from mouse *up* to next mouse *down*
4345 LONG curMsgTime
= ::GetMessageTime();
4347 switch (aEventMessage
) {
4348 case eMouseDoubleClick
:
4349 event
.mMessage
= eMouseDown
;
4350 event
.mButton
= aButton
;
4351 sLastClickCount
= 2;
4352 sLastMouseDownTime
= curMsgTime
;
4355 // remember when this happened for the next mouse down
4356 sLastMousePoint
.x
= eventPoint
.x
;
4357 sLastMousePoint
.y
= eventPoint
.y
;
4358 sLastMouseButton
= eventButton
;
4361 // now look to see if we want to convert this to a double- or triple-click
4362 if (((curMsgTime
- sLastMouseDownTime
) < (LONG
)::GetDoubleClickTime()) &&
4363 insideMovementThreshold
&& eventButton
== sLastMouseButton
) {
4366 // reset the click count, to count *this* click
4367 sLastClickCount
= 1;
4369 // Set last Click time on MouseDown only
4370 sLastMouseDownTime
= curMsgTime
;
4373 if (!insideMovementThreshold
) {
4374 sLastClickCount
= 0;
4377 case eMouseExitFromWidget
:
4379 Some(IsTopLevelMouseExit(mWnd
) ? WidgetMouseEvent::ePlatformTopLevel
4380 : WidgetMouseEvent::ePlatformChild
);
4385 event
.mClickCount
= sLastClickCount
;
4388 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
4389 ("Msg Time: %d Click Count: %d\n", curMsgTime
, event
.mClickCount
));
4392 // call the event callback
4393 if (mWidgetListener
) {
4394 if (aEventMessage
== eMouseMove
) {
4395 LayoutDeviceIntRect rect
= GetBounds();
4398 if (rect
.Contains(event
.mRefPoint
)) {
4399 if (sCurrentWindow
== nullptr || sCurrentWindow
!= this) {
4400 if ((nullptr != sCurrentWindow
) && (!sCurrentWindow
->mInDtor
)) {
4401 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4402 sCurrentWindow
->DispatchMouseEvent(
4403 eMouseExitFromWidget
, wParam
, pos
, false, MouseButton::ePrimary
,
4404 aInputSource
, aPointerInfo
);
4406 sCurrentWindow
= this;
4408 LPARAM pos
= sCurrentWindow
->lParamToClient(lParamToScreen(lParam
));
4409 sCurrentWindow
->DispatchMouseEvent(
4410 eMouseEnterIntoWidget
, wParam
, pos
, false,
4411 MouseButton::ePrimary
, aInputSource
, aPointerInfo
);
4415 } else if (aEventMessage
== eMouseExitFromWidget
) {
4416 if (sCurrentWindow
== this) {
4417 sCurrentWindow
= nullptr;
4421 nsIWidget::ContentAndAPZEventStatus eventStatus
=
4422 DispatchInputEvent(&event
);
4423 contextMenuPreventer
.Update(event
, eventStatus
);
4424 return ConvertStatus(eventStatus
.mContentStatus
);
4430 HWND
nsWindow::GetTopLevelForFocus(HWND aCurWnd
) {
4431 // retrieve the toplevel window or dialogue
4432 HWND toplevelWnd
= nullptr;
4434 toplevelWnd
= aCurWnd
;
4435 nsWindow
* win
= WinUtils::GetNSWindowPtr(aCurWnd
);
4437 if (win
->mWindowType
== WindowType::TopLevel
||
4438 win
->mWindowType
== WindowType::Dialog
) {
4443 aCurWnd
= ::GetParent(aCurWnd
); // Parent or owner (if has no parent)
4448 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate
) {
4450 sJustGotActivate
= false;
4452 sJustGotDeactivate
= false;
4453 mLastKillFocusWindow
= nullptr;
4455 HWND toplevelWnd
= GetTopLevelForFocus(mWnd
);
4458 nsWindow
* win
= WinUtils::GetNSWindowPtr(toplevelWnd
);
4459 if (win
&& win
->mWidgetListener
) {
4461 win
->mWidgetListener
->WindowActivated();
4463 win
->mWidgetListener
->WindowDeactivated();
4469 HWND
nsWindow::WindowAtMouse() {
4470 DWORD pos
= ::GetMessagePos();
4472 mp
.x
= GET_X_LPARAM(pos
);
4473 mp
.y
= GET_Y_LPARAM(pos
);
4474 return ::WindowFromPoint(mp
);
4477 bool nsWindow::IsTopLevelMouseExit(HWND aWnd
) {
4478 HWND mouseWnd
= WindowAtMouse();
4480 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4481 // (which includes the non-client area). If the mouse has moved into
4482 // the non-client area, we should treat it as a top-level exit.
4483 HWND mouseTopLevel
= WinUtils::GetTopLevelHWND(mouseWnd
);
4484 if (mouseWnd
== mouseTopLevel
) return true;
4486 return WinUtils::GetTopLevelHWND(aWnd
) != mouseTopLevel
;
4489 /**************************************************************
4493 * IPC related helpers.
4495 **************************************************************/
4498 bool nsWindow::IsAsyncResponseEvent(UINT aMsg
, LRESULT
& aResult
) {
4503 case WM_WINDOWPOSCHANGING
:
4504 case WM_WINDOWPOSCHANGED
:
4505 case WM_PARENTNOTIFY
:
4506 case WM_ACTIVATEAPP
:
4509 case WM_CHILDACTIVATE
:
4510 case WM_IME_SETCONTEXT
:
4514 case WM_MOUSEACTIVATE
:
4515 case WM_CONTEXTMENU
:
4519 case WM_SETTINGCHANGE
:
4527 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4535 void nsWindow::IPCWindowProcHandler(UINT
& msg
, WPARAM
& wParam
, LPARAM
& lParam
) {
4537 msg
!= WM_GETOBJECT
,
4538 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4539 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4541 // Modal UI being displayed in windowless plugins.
4542 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4543 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4545 if (IsAsyncResponseEvent(msg
, res
)) {
4551 // Handle certain sync plugin events sent to the parent which
4552 // trigger ipc calls that result in deadlocks.
4555 bool handled
= false;
4558 // Windowless flash sending WM_ACTIVATE events to the main window
4559 // via calls to ShowWindow.
4561 if (lParam
!= 0 && LOWORD(wParam
) == WA_ACTIVE
&&
4562 IsWindow((HWND
)lParam
)) {
4563 // Check for Adobe Reader X sync activate message from their
4564 // helper window and ignore. Fixes an annoying focus problem.
4565 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) ==
4567 wchar_t szClass
[10];
4568 HWND focusWnd
= (HWND
)lParam
;
4569 if (IsWindowVisible(focusWnd
) &&
4570 GetClassNameW(focusWnd
, szClass
,
4571 sizeof(szClass
) / sizeof(char16_t
)) &&
4572 !wcscmp(szClass
, L
"Edit") &&
4573 !WinUtils::IsOurProcessWindow(focusWnd
)) {
4580 // Plugins taking or losing focus triggering focus app messages.
4583 // Windowed plugins that pass sys key events to defwndproc generate
4584 // WM_SYSCOMMAND events to the main window.
4586 // Windowed plugins that fire context menu selection events to parent
4588 case WM_CONTEXTMENU
:
4589 // IME events fired as a result of synchronous focus changes
4590 case WM_IME_SETCONTEXT
:
4596 (InSendMessageEx(nullptr) & (ISMEX_REPLIED
| ISMEX_SEND
)) == ISMEX_SEND
) {
4597 ReplyMessage(dwResult
);
4601 /**************************************************************
4602 **************************************************************
4604 ** BLOCK: Native events
4606 ** Main Windows message handlers and OnXXX handlers for
4607 ** Windows event handling.
4609 **************************************************************
4610 **************************************************************/
4612 /**************************************************************
4614 * SECTION: Wind proc.
4616 * The main Windows event procedures and associated
4617 * message processing methods.
4619 **************************************************************/
4621 static bool DisplaySystemMenu(HWND hWnd
, nsSizeMode sizeMode
, bool isRtl
,
4622 int32_t x
, int32_t y
) {
4623 HMENU hMenu
= GetSystemMenu(hWnd
, FALSE
);
4626 mii
.cbSize
= sizeof(MENUITEMINFO
);
4627 mii
.fMask
= MIIM_STATE
;
4630 // update the options
4631 mii
.fState
= MF_ENABLED
;
4632 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4633 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4634 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4635 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4636 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4638 mii
.fState
= MF_GRAYED
;
4640 case nsSizeMode_Fullscreen
:
4641 // intentional fall through
4642 case nsSizeMode_Maximized
:
4643 SetMenuItemInfo(hMenu
, SC_SIZE
, FALSE
, &mii
);
4644 SetMenuItemInfo(hMenu
, SC_MOVE
, FALSE
, &mii
);
4645 SetMenuItemInfo(hMenu
, SC_MAXIMIZE
, FALSE
, &mii
);
4647 case nsSizeMode_Minimized
:
4648 SetMenuItemInfo(hMenu
, SC_MINIMIZE
, FALSE
, &mii
);
4650 case nsSizeMode_Normal
:
4651 SetMenuItemInfo(hMenu
, SC_RESTORE
, FALSE
, &mii
);
4653 case nsSizeMode_Invalid
:
4654 NS_ASSERTION(false, "Did the argument come from invalid IPC?");
4657 MOZ_ASSERT_UNREACHABLE("Unhnalded nsSizeMode value detected");
4660 LPARAM cmd
= TrackPopupMenu(
4662 (TPM_LEFTBUTTON
| TPM_RIGHTBUTTON
| TPM_RETURNCMD
| TPM_TOPALIGN
|
4663 (isRtl
? TPM_RIGHTALIGN
: TPM_LEFTALIGN
)),
4664 x
, y
, 0, hWnd
, nullptr);
4666 PostMessage(hWnd
, WM_SYSCOMMAND
, cmd
, 0);
4673 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4674 // SEH exceptions and passes the real work to WindowProcInternal. See bug 587406
4675 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
4676 LRESULT CALLBACK
nsWindow::WindowProc(HWND hWnd
, UINT msg
, WPARAM wParam
,
4678 mozilla::ipc::CancelCPOWs();
4680 BackgroundHangMonitor().NotifyActivity();
4682 return mozilla::CallWindowProcCrashProtected(WindowProcInternal
, hWnd
, msg
,
4686 LRESULT CALLBACK
nsWindow::WindowProcInternal(HWND hWnd
, UINT msg
,
4687 WPARAM wParam
, LPARAM lParam
) {
4688 if (::GetWindowLongPtrW(hWnd
, GWLP_ID
) == eFakeTrackPointScrollableID
) {
4689 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4690 if (msg
== WM_HSCROLL
) {
4691 // Route WM_HSCROLL messages to the main window.
4692 hWnd
= ::GetParent(::GetParent(hWnd
));
4694 // Handle all other messages with its original window procedure.
4695 WNDPROC prevWindowProc
= (WNDPROC
)::GetWindowLongPtr(hWnd
, GWLP_USERDATA
);
4696 return ::CallWindowProcW(prevWindowProc
, hWnd
, msg
, wParam
, lParam
);
4700 if (msg
== MOZ_WM_TRACE
) {
4701 // This is a tracer event for measuring event loop latency.
4702 // See WidgetTraceEvent.cpp for more details.
4703 mozilla::SignalTracerThread();
4707 // Get the window which caused the event and ask it to process the message
4708 nsWindow
* targetWindow
= WinUtils::GetNSWindowPtr(hWnd
);
4709 NS_ASSERTION(targetWindow
, "nsWindow* is null!");
4710 if (!targetWindow
) return ::DefWindowProcW(hWnd
, msg
, wParam
, lParam
);
4712 // Hold the window for the life of this method, in case it gets
4713 // destroyed during processing, unless we're in the dtor already.
4714 nsCOMPtr
<nsIWidget
> kungFuDeathGrip
;
4715 if (!targetWindow
->mInDtor
) kungFuDeathGrip
= targetWindow
;
4717 targetWindow
->IPCWindowProcHandler(msg
, wParam
, lParam
);
4719 // Create this here so that we store the last rolled up popup until after
4720 // the event has been processed.
4721 nsAutoRollup autoRollup
;
4723 LRESULT popupHandlingResult
;
4724 if (DealWithPopups(hWnd
, msg
, wParam
, lParam
, &popupHandlingResult
))
4725 return popupHandlingResult
;
4727 // Call ProcessMessage
4729 if (targetWindow
->ProcessMessage(msg
, wParam
, lParam
, &retValue
)) {
4733 LRESULT res
= ::CallWindowProcW(targetWindow
->GetPrevWindowProc(), hWnd
, msg
,
4739 const char16_t
* GetQuitType() {
4740 if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART
, false)) {
4741 DWORD cchCmdLine
= 0;
4742 HRESULT rc
= ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
4743 &cchCmdLine
, nullptr);
4745 return u
"os-restart";
4751 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage
, WPARAM
& aWParam
,
4753 MSGResult
& aResult
) {
4754 if (mWindowHook
.Notify(mWnd
, aMessage
, aWParam
, aLParam
, aResult
)) {
4758 if (IMEHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
, aResult
)) {
4762 if (MouseScrollHandler::ProcessMessage(this, aMessage
, aWParam
, aLParam
,
4770 // The main windows message processing method. Wraps ProcessMessageInternal so
4771 // we can log aRetValue.
4772 bool nsWindow::ProcessMessage(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4773 LRESULT
* aRetValue
) {
4774 // For some events we might change the parameter values, so log
4775 // before and after we process them.
4776 NativeEventLogger
eventLogger("nsWindow", mWnd
, msg
, wParam
, lParam
);
4777 bool result
= ProcessMessageInternal(msg
, wParam
, lParam
, aRetValue
);
4778 eventLogger
.SetResult(*aRetValue
, result
);
4783 // The main windows message processing method. Called by ProcessMessage.
4784 bool nsWindow::ProcessMessageInternal(UINT msg
, WPARAM
& wParam
, LPARAM
& lParam
,
4785 LRESULT
* aRetValue
) {
4786 MSGResult
msgResult(aRetValue
);
4787 if (ExternalHandlerProcessMessage(msg
, wParam
, lParam
, msgResult
)) {
4788 return (msgResult
.mConsumed
|| !mWnd
);
4791 bool result
= false; // call the default nsWindow proc
4794 // The DWM resize hack (see bug 1763981) causes us to process a number of
4795 // messages, notably including some WM_WINDOWPOSCHANG{ING,ED} messages which
4796 // would ordinarily result in a whole lot of internal state being updated.
4798 // Since we're supposed to end in the same state we started in (and since the
4799 // content shouldn't know about any of this nonsense), just discard any
4800 // messages synchronously dispatched from within the hack.
4801 if (MOZ_UNLIKELY(mIsPerformingDwmFlushHack
)) {
4805 // Glass hit testing w/custom transparent margins.
4807 // FIXME(emilio): is this needed? We deal with titlebar buttons non-natively
4809 LRESULT dwmHitResult
;
4810 if (mCustomNonClient
&&
4811 DwmDefWindowProc(mWnd
, msg
, wParam
, lParam
, &dwmHitResult
)) {
4812 *aRetValue
= dwmHitResult
;
4816 // The preference whether to use a different keyboard layout for each
4817 // window is cached, and updating it will not take effect until the
4818 // next restart. We read the preference here and not upon WM_ACTIVATE to make
4819 // sure that this behavior is consistent. Otherwise, if the user changed the
4820 // preference before having ever lowered the window, the preference would take
4821 // effect immediately.
4822 static const bool sSwitchKeyboardLayout
=
4823 Preferences::GetBool("intl.keyboard.per_window_layout", false);
4824 AppShutdownReason shutdownReason
= AppShutdownReason::Unknown
;
4826 // (Large blocks of code should be broken out into OnEvent handlers.)
4828 // WM_QUERYENDSESSION must be handled by all windows.
4829 // Otherwise Windows thinks the window can just be killed at will.
4830 case WM_QUERYENDSESSION
: {
4831 // Ask around if it's ok to quit.
4832 nsCOMPtr
<nsIObserverService
> obsServ
=
4833 mozilla::services::GetObserverService();
4834 nsCOMPtr
<nsISupportsPRBool
> cancelQuitWrapper
=
4835 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID
);
4836 cancelQuitWrapper
->SetData(false);
4838 const char16_t
* quitType
= GetQuitType();
4839 obsServ
->NotifyObservers(cancelQuitWrapper
, "quit-application-requested",
4842 bool shouldCancelQuit
;
4843 cancelQuitWrapper
->GetData(&shouldCancelQuit
);
4844 *aRetValue
= !shouldCancelQuit
;
4848 case MOZ_WM_STARTA11Y
:
4849 #if defined(ACCESSIBILITY)
4850 Unused
<< GetAccessible();
4857 case WM_ENDSESSION
: {
4858 // For WM_ENDSESSION, wParam indicates whether we need to shutdown
4859 // (TRUE) or not (FALSE).
4864 // According to WM_ENDSESSION lParam documentation:
4865 // 0 -> OS shutdown or restart (no way to distinguish)
4866 // ENDSESSION_LOGOFF -> User is logging off
4867 // ENDSESSION_CLOSEAPP -> Application must shutdown
4868 // ENDSESSION_CRITICAL -> Application is forced to shutdown
4869 // The difference of the last two is not very clear.
4871 shutdownReason
= AppShutdownReason::OSShutdown
;
4872 } else if (lParam
& ENDSESSION_LOGOFF
) {
4873 shutdownReason
= AppShutdownReason::OSSessionEnd
;
4874 } else if (lParam
& (ENDSESSION_CLOSEAPP
| ENDSESSION_CRITICAL
)) {
4875 shutdownReason
= AppShutdownReason::OSForceClose
;
4877 MOZ_DIAGNOSTIC_ASSERT(false,
4878 "Received WM_ENDSESSION with unknown flags.");
4879 shutdownReason
= AppShutdownReason::OSForceClose
;
4883 case MOZ_WM_APP_QUIT
: {
4884 if (shutdownReason
== AppShutdownReason::Unknown
) {
4885 // TODO: We do not expect that these days anybody sends us
4886 // MOZ_WM_APP_QUIT, see bug 1827807.
4887 shutdownReason
= AppShutdownReason::WinUnexpectedMozQuit
;
4889 // Let's fake a shutdown sequence without actually closing windows etc.
4890 // to avoid Windows killing us in the middle. A proper shutdown would
4891 // require having a chance to pump some messages. Unfortunately
4892 // Windows won't let us do that. Bug 212316.
4893 nsCOMPtr
<nsIObserverService
> obsServ
=
4894 mozilla::services::GetObserverService();
4895 const char16_t
* syncShutdown
= u
"syncShutdown";
4896 const char16_t
* quitType
= GetQuitType();
4898 AppShutdown::Init(AppShutdownMode::Normal
, 0, shutdownReason
);
4900 obsServ
->NotifyObservers(nullptr, "quit-application-granted",
4902 obsServ
->NotifyObservers(nullptr, "quit-application-forced", nullptr);
4904 AppShutdown::OnShutdownConfirmed();
4906 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed
,
4908 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown
,
4910 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown
,
4912 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown
, nullptr);
4913 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM
, nullptr);
4914 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry
,
4917 AppShutdown::DoImmediateExit();
4918 MOZ_ASSERT_UNREACHABLE("Our process was supposed to exit.");
4921 case WM_SYSCOLORCHANGE
:
4922 // No need to invalidate layout for system color changes, but we need to
4923 // invalidate style.
4924 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
4927 case WM_THEMECHANGED
: {
4928 // Update non-client margin offsets
4929 UpdateNonClientMargins();
4930 nsUXThemeData::UpdateNativeThemeInfo();
4932 // We assume pretty much everything could've changed here.
4933 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout
);
4935 UpdateDarkModeToolbar();
4937 // Invalidate the window so that the repaint will
4938 // pick up the new theme.
4939 Invalidate(true, true, true);
4942 case WM_WTSSESSION_CHANGE
: {
4944 case WTS_CONSOLE_CONNECT
:
4945 case WTS_REMOTE_CONNECT
:
4946 case WTS_SESSION_UNLOCK
:
4947 // When a session becomes visible, we should invalidate.
4948 Invalidate(true, true, true);
4955 case WM_FONTCHANGE
: {
4956 // We only handle this message for the hidden window,
4957 // as we only need to update the (global) font list once
4958 // for any given change, not once per window!
4959 if (mWindowType
!= WindowType::Invisible
) {
4963 // update the global font list
4964 gfxPlatform::GetPlatform()->UpdateFontList();
4967 case WM_SETTINGCHANGE
: {
4968 if (wParam
== SPI_SETCLIENTAREAANIMATION
||
4969 wParam
== SPI_SETKEYBOARDDELAY
|| wParam
== SPI_SETMOUSEVANISH
) {
4970 // These need to update LookAndFeel cached values.
4971 // They affect reduced motion settings / caret blink count / show
4972 // pointer while typing, so no need to invalidate style / layout.
4973 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
4976 if (wParam
== SPI_SETFONTSMOOTHING
||
4977 wParam
== SPI_SETFONTSMOOTHINGTYPE
) {
4978 gfxDWriteFont::UpdateSystemTextVars();
4981 if (wParam
== SPI_SETWORKAREA
) {
4982 // NB: We also refresh screens on WM_DISPLAYCHANGE but the rcWork
4983 // values are sometimes wrong at that point. This message then
4984 // arrives soon afterward, when we can get the right rcWork values.
4985 ScreenHelperWin::RefreshScreens();
4988 if (auto lParamString
= reinterpret_cast<const wchar_t*>(lParam
)) {
4989 if (!wcscmp(lParamString
, L
"ImmersiveColorSet")) {
4990 // This affects system colors (-moz-win-accentcolor), so gotta pass
4992 NotifyThemeChanged(widget::ThemeChangeKind::Style
);
4996 // UserInteractionMode, ConvertibleSlateMode, SystemDockMode may cause
4997 // @media(pointer) queries to change, which layout needs to know about
4999 // (WM_SETTINGCHANGE will be sent to all top-level windows, so we
5000 // only respond to the hidden top-level window to avoid hammering
5001 // layout with a bunch of NotifyThemeChanged() calls)
5003 if (mWindowType
== WindowType::Invisible
) {
5004 if (!wcscmp(lParamString
, L
"UserInteractionMode") ||
5005 !wcscmp(lParamString
, L
"ConvertibleSlateMode") ||
5006 !wcscmp(lParamString
, L
"SystemDockMode")) {
5007 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5008 WindowsUIUtils::UpdateInTabletMode();
5014 case WM_DEVICECHANGE
: {
5015 if (wParam
== DBT_DEVICEARRIVAL
|| wParam
== DBT_DEVICEREMOVECOMPLETE
) {
5016 DEV_BROADCAST_HDR
* hdr
= reinterpret_cast<DEV_BROADCAST_HDR
*>(lParam
);
5017 // Check dbch_devicetype explicitly since we will get other device types
5018 // (e.g. DBT_DEVTYP_VOLUME) for some reasons even if we specify
5019 // DBT_DEVTYP_DEVICEINTERFACE in the filter for
5020 // RegisterDeviceNotification.
5021 if (hdr
->dbch_devicetype
== DBT_DEVTYP_DEVICEINTERFACE
) {
5022 // This can only change media queries (any-hover/any-pointer).
5023 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly
);
5028 case WM_NCCALCSIZE
: {
5029 // NOTE: the following block is mirrored in PreXULSkeletonUI.cpp, and
5030 // will need to be kept in sync.
5031 if (mCustomNonClient
) {
5032 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5033 // the proposed window rectangle for our window. During our
5034 // processing of the `WM_NCCALCSIZE` message, we are expected to
5035 // modify the `RECT` that `lParam` points to, so that its value upon
5036 // our return is the new client area. We must return 0 if `wParam`
5039 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5040 // struct. This struct contains an array of 3 `RECT`s, the first of
5041 // which has the exact same meaning as the `RECT` that is pointed to
5042 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5043 // conjunction with our return value, can
5044 // be used to specify portions of the source and destination window
5045 // rectangles that are valid and should be preserved. We opt not to
5046 // implement an elaborate client-area preservation technique, and
5047 // simply return 0, which means "preserve the entire old client area
5048 // and align it with the upper-left corner of our new client area".
5050 wParam
? &(reinterpret_cast<NCCALCSIZE_PARAMS
*>(lParam
))->rgrc
[0]
5051 : (reinterpret_cast<RECT
*>(lParam
));
5052 auto margin
= NonClientSizeMargin();
5053 clientRect
->top
+= margin
.top
;
5054 clientRect
->left
+= margin
.left
;
5055 clientRect
->right
-= margin
.right
;
5056 clientRect
->bottom
-= margin
.bottom
;
5057 // Make client rect's width and height more than 0 to
5058 // avoid problems of webrender and angle.
5059 clientRect
->right
= std::max(clientRect
->right
, clientRect
->left
+ 1);
5060 clientRect
->bottom
= std::max(clientRect
->bottom
, clientRect
->top
+ 1);
5068 case WM_NCHITTEST
: {
5069 if (mInputRegion
.mFullyTransparent
) {
5070 // Treat this window as transparent.
5071 *aRetValue
= HTTRANSPARENT
;
5076 if (mInputRegion
.mMargin
) {
5077 const LayoutDeviceIntPoint
screenPoint(GET_X_LPARAM(lParam
),
5078 GET_Y_LPARAM(lParam
));
5079 LayoutDeviceIntRect screenRect
= GetScreenBounds();
5080 screenRect
.Deflate(mInputRegion
.mMargin
);
5081 if (!screenRect
.Contains(screenPoint
)) {
5082 *aRetValue
= HTTRANSPARENT
;
5089 * If an nc client area margin has been moved, we are responsible
5090 * for calculating where the resize margins are and returning the
5091 * appropriate set of hit test constants. DwmDefWindowProc (above)
5092 * will handle hit testing on it's command buttons if we are on a
5093 * composited desktop.
5096 if (!mCustomNonClient
) {
5101 ClientMarginHitTestPoint(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5108 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5109 * custom titlebar we paint ourselves, or if we're the ones
5110 * sending the message with an updated title
5113 if (mSendingSetText
|| !mCustomNonClient
|| mNonClientMargins
.top
== -1)
5117 // From msdn, the way around this is to disable the visible state
5118 // temporarily. We need the text to be set but we don't want the
5119 // redraw to occur. However, we need to make sure that we don't
5120 // do this at the same time that a Present is happening.
5122 // To do this we take mPresentLock in nsWindow::PreRender and
5123 // if that lock is taken we wait before doing WM_SETTEXT
5124 if (mCompositorWidgetDelegate
) {
5125 mCompositorWidgetDelegate
->EnterPresentLock();
5127 DWORD style
= GetWindowLong(mWnd
, GWL_STYLE
);
5128 SetWindowLong(mWnd
, GWL_STYLE
, style
& ~WS_VISIBLE
);
5130 CallWindowProcW(GetPrevWindowProc(), mWnd
, msg
, wParam
, lParam
);
5131 SetWindowLong(mWnd
, GWL_STYLE
, style
);
5132 if (mCompositorWidgetDelegate
) {
5133 mCompositorWidgetDelegate
->LeavePresentLock();
5139 case WM_NCACTIVATE
: {
5141 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5142 * through WM_NCPAINT via InvalidateNonClientRegion.
5144 UpdateGetWindowInfoCaptionStatus(FALSE
!= wParam
);
5146 if (!mCustomNonClient
) {
5150 // There is a case that rendered result is not kept. Bug 1237617
5151 if (wParam
== TRUE
&& !gfxEnv::MOZ_DISABLE_FORCE_PRESENT()) {
5152 NS_DispatchToMainThread(NewRunnableMethod(
5153 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent
));
5156 // let the dwm handle nc painting on glass
5157 // Never allow native painting if we are on fullscreen
5158 if (mFrameState
->GetSizeMode() != nsSizeMode_Fullscreen
) break;
5160 if (wParam
== TRUE
) {
5162 *aRetValue
= FALSE
; // ignored
5164 // invalidate to trigger a paint
5165 InvalidateNonClientRegion();
5169 *aRetValue
= TRUE
; // go ahead and deactive
5171 // invalidate to trigger a paint
5172 InvalidateNonClientRegion();
5179 * ClearType changes often don't send a WM_SETTINGCHANGE message. But they
5180 * do seem to always send a WM_NCPAINT message, so let's update on that.
5182 gfxDWriteFont::UpdateSystemTextVars();
5185 case WM_POWERBROADCAST
:
5187 case PBT_APMSUSPEND
:
5188 PostSleepWakeNotification(true);
5190 case PBT_APMRESUMEAUTOMATIC
:
5191 case PBT_APMRESUMECRITICAL
:
5192 case PBT_APMRESUMESUSPEND
:
5193 PostSleepWakeNotification(false);
5198 case WM_CLOSE
: // close request
5199 if (mWidgetListener
) mWidgetListener
->RequestWindowClose(this);
5200 result
= true; // abort window closure
5205 DestroyLayerManager();
5211 *aRetValue
= (int)OnPaint(0);
5216 result
= OnHotKey(wParam
, lParam
);
5221 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5222 result
= ProcessCharMessage(nativeMsg
, nullptr);
5223 DispatchPendingEvents();
5228 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5229 nativeMsg
.time
= ::GetMessageTime();
5230 result
= ProcessKeyUpMessage(nativeMsg
, nullptr);
5231 DispatchPendingEvents();
5236 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5237 result
= ProcessKeyDownMessage(nativeMsg
, nullptr);
5238 DispatchPendingEvents();
5241 // Say we've dealt with erasing the background. (This is actually handled in
5242 // WM_PAINT, where necessary.)
5243 case WM_ERASEBKGND
: {
5248 case WM_MOUSEMOVE
: {
5249 LPARAM lParamScreen
= lParamToScreen(lParam
);
5250 mSimulatedClientArea
= IsSimulatedClientArea(GET_X_LPARAM(lParamScreen
),
5251 GET_Y_LPARAM(lParamScreen
));
5253 if (!mMousePresent
&& !sIsInMouseCapture
) {
5254 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5255 TRACKMOUSEEVENT mTrack
;
5256 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5257 mTrack
.dwFlags
= TME_LEAVE
;
5258 mTrack
.dwHoverTime
= 0;
5259 mTrack
.hwndTrack
= mWnd
;
5260 TrackMouseEvent(&mTrack
);
5262 mMousePresent
= true;
5264 // Suppress dispatch of pending events
5265 // when mouse moves are generated by widget
5266 // creation instead of user input.
5268 mp
.x
= GET_X_LPARAM(lParamScreen
);
5269 mp
.y
= GET_Y_LPARAM(lParamScreen
);
5270 bool userMovedMouse
= false;
5271 if ((sLastMouseMovePoint
.x
!= mp
.x
) || (sLastMouseMovePoint
.y
!= mp
.y
)) {
5272 userMovedMouse
= true;
5275 if (userMovedMouse
) {
5276 result
= DispatchMouseEvent(
5277 eMouseMove
, wParam
, lParam
, false, MouseButton::ePrimary
,
5278 MOUSE_INPUT_SOURCE(),
5279 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5280 DispatchPendingEvents();
5284 case WM_NCMOUSEMOVE
: {
5285 LPARAM lParamClient
= lParamToClient(lParam
);
5286 if (IsSimulatedClientArea(GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
))) {
5287 if (!sIsInMouseCapture
) {
5288 TRACKMOUSEEVENT mTrack
;
5289 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5290 mTrack
.dwFlags
= TME_LEAVE
| TME_NONCLIENT
;
5291 mTrack
.dwHoverTime
= 0;
5292 mTrack
.hwndTrack
= mWnd
;
5293 TrackMouseEvent(&mTrack
);
5295 // If we noticed the mouse moving in our draggable region, forward the
5296 // message as a normal WM_MOUSEMOVE.
5297 SendMessage(mWnd
, WM_MOUSEMOVE
, 0, lParamClient
);
5299 // We've transitioned from a draggable area to somewhere else within
5300 // the non-client area - perhaps one of the edges of the window for
5302 mSimulatedClientArea
= false;
5305 if (mMousePresent
&& !sIsInMouseCapture
&& !mSimulatedClientArea
) {
5306 SendMessage(mWnd
, WM_MOUSELEAVE
, 0, 0);
5310 case WM_LBUTTONDOWN
: {
5312 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5313 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5314 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5315 DispatchPendingEvents();
5318 case WM_LBUTTONUP
: {
5320 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5321 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE(),
5322 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5323 DispatchPendingEvents();
5326 case WM_NCMOUSELEAVE
: {
5327 mSimulatedClientArea
= false;
5329 if (EventIsInsideWindow(this)) {
5330 // If we're handling WM_NCMOUSELEAVE and the mouse is still over the
5331 // window, then by process of elimination, the mouse has moved from the
5332 // non-client to client area, so no need to fall-through to the
5333 // WM_MOUSELEAVE handler. We also need to re-register for the
5334 // WM_MOUSELEAVE message, since according to the documentation at [1],
5335 // all tracking requested via TrackMouseEvent is cleared once
5336 // WM_NCMOUSELEAVE or WM_MOUSELEAVE fires.
5338 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-trackmouseevent
5339 TRACKMOUSEEVENT mTrack
;
5340 mTrack
.cbSize
= sizeof(TRACKMOUSEEVENT
);
5341 mTrack
.dwFlags
= TME_LEAVE
;
5342 mTrack
.dwHoverTime
= 0;
5343 mTrack
.hwndTrack
= mWnd
;
5344 TrackMouseEvent(&mTrack
);
5347 // We've transitioned from non-client to outside of the window, so
5348 // fall-through to the WM_MOUSELEAVE handler.
5351 case WM_MOUSELEAVE
: {
5352 if (!mMousePresent
) break;
5353 if (mSimulatedClientArea
) break;
5354 mMousePresent
= false;
5356 // Check if the mouse is over the fullscreen transition window, if so
5357 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5358 // transition window disappears will not be ignored, even if the mouse
5360 if (mTransitionWnd
&& WindowAtMouse() == mTransitionWnd
) {
5361 sLastMouseMovePoint
= {0};
5364 // We need to check mouse button states and put them in for
5366 WPARAM mouseState
= (GetKeyState(VK_LBUTTON
) ? MK_LBUTTON
: 0) |
5367 (GetKeyState(VK_MBUTTON
) ? MK_MBUTTON
: 0) |
5368 (GetKeyState(VK_RBUTTON
) ? MK_RBUTTON
: 0);
5369 // Synthesize an event position because we don't get one from
5371 LPARAM pos
= lParamToClient(::GetMessagePos());
5372 DispatchMouseEvent(eMouseExitFromWidget
, mouseState
, pos
, false,
5373 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5376 case WM_CONTEXTMENU
: {
5377 // If the context menu is brought up by a touch long-press, then
5378 // the APZ code is responsible for dealing with this, so we don't
5379 // need to do anything.
5381 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
5382 MOZ_ASSERT(mAPZC
); // since mTouchWindow is true, APZ must be enabled
5387 // If this WM_CONTEXTMENU is triggered by a mouse's secondary button up
5388 // event in overscroll gutter, we shouldn't open context menu.
5389 if (MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
5390 mNeedsToPreventContextMenu
) {
5395 // if the context menu is brought up from the keyboard, |lParam|
5398 bool contextMenukey
= false;
5400 contextMenukey
= true;
5401 pos
= lParamToClient(GetMessagePos());
5403 pos
= lParamToClient(lParam
);
5406 result
= DispatchMouseEvent(
5407 eContextMenu
, wParam
, pos
, contextMenukey
,
5408 contextMenukey
? MouseButton::ePrimary
: MouseButton::eSecondary
,
5409 MOUSE_INPUT_SOURCE());
5410 if (lParam
!= -1 && !result
&& mCustomNonClient
&&
5411 mDraggableRegion
.Contains(GET_X_LPARAM(pos
), GET_Y_LPARAM(pos
))) {
5412 // Blank area hit, throw up the system menu.
5413 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5414 GET_X_LPARAM(lParam
), GET_Y_LPARAM(lParam
));
5419 case WM_POINTERLEAVE
:
5420 case WM_POINTERDOWN
:
5422 case WM_POINTERUPDATE
:
5423 result
= OnPointerEvents(msg
, wParam
, lParam
);
5425 DispatchPendingEvents();
5429 case DM_POINTERHITTEST
:
5431 UINT contactId
= GET_POINTERID_WPARAM(wParam
);
5432 POINTER_INPUT_TYPE pointerType
;
5433 if (mPointerEvents
.GetPointerType(contactId
, &pointerType
) &&
5434 pointerType
== PT_TOUCHPAD
) {
5435 mDmOwner
->SetContact(contactId
);
5440 case WM_LBUTTONDBLCLK
:
5441 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5442 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5443 DispatchPendingEvents();
5446 case WM_MBUTTONDOWN
:
5447 result
= DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5448 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5449 DispatchPendingEvents();
5453 result
= DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5454 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5455 DispatchPendingEvents();
5458 case WM_MBUTTONDBLCLK
:
5459 result
= DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5460 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5461 DispatchPendingEvents();
5464 case WM_NCMBUTTONDOWN
:
5465 result
= DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5466 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5467 DispatchPendingEvents();
5470 case WM_NCMBUTTONUP
:
5471 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5472 MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5473 DispatchPendingEvents();
5476 case WM_NCMBUTTONDBLCLK
:
5478 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5479 false, MouseButton::eMiddle
, MOUSE_INPUT_SOURCE());
5480 DispatchPendingEvents();
5483 case WM_RBUTTONDOWN
:
5485 DispatchMouseEvent(eMouseDown
, wParam
, lParam
, false,
5486 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5487 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5488 DispatchPendingEvents();
5493 DispatchMouseEvent(eMouseUp
, wParam
, lParam
, false,
5494 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE(),
5495 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5496 DispatchPendingEvents();
5499 case WM_RBUTTONDBLCLK
:
5501 DispatchMouseEvent(eMouseDoubleClick
, wParam
, lParam
, false,
5502 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5503 DispatchPendingEvents();
5506 case WM_NCRBUTTONDOWN
:
5508 DispatchMouseEvent(eMouseDown
, 0, lParamToClient(lParam
), false,
5509 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5510 DispatchPendingEvents();
5513 case WM_NCRBUTTONUP
:
5515 DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5516 MouseButton::eSecondary
, MOUSE_INPUT_SOURCE());
5517 DispatchPendingEvents();
5520 case WM_NCRBUTTONDBLCLK
:
5521 result
= DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
),
5522 false, MouseButton::eSecondary
,
5523 MOUSE_INPUT_SOURCE());
5524 DispatchPendingEvents();
5527 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5528 // of mouse. If 5-button mouse works with standard mouse deriver of
5529 // Windows, users cannot disable 4th button (browser back) nor 5th button
5530 // (browser forward). We should allow to do it with our prefs since we can
5531 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5532 // messages are not sent to DefWindowProc.
5533 case WM_XBUTTONDOWN
:
5535 case WM_NCXBUTTONDOWN
:
5536 case WM_NCXBUTTONUP
:
5538 switch (GET_XBUTTON_WPARAM(wParam
)) {
5540 result
= !Preferences::GetBool("mousebutton.4th.enabled", true);
5543 result
= !Preferences::GetBool("mousebutton.5th.enabled", true);
5551 if (mAspectRatio
> 0) {
5552 LPRECT rect
= (LPRECT
)lParam
;
5553 int32_t newWidth
, newHeight
;
5555 // The following conditions and switch statement borrow heavily from the
5556 // Chromium source code from
5557 // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
5558 if (wParam
== WMSZ_LEFT
|| wParam
== WMSZ_RIGHT
||
5559 wParam
== WMSZ_TOPLEFT
|| wParam
== WMSZ_BOTTOMLEFT
) {
5560 newWidth
= rect
->right
- rect
->left
;
5561 newHeight
= newWidth
/ mAspectRatio
;
5562 if (newHeight
< mSizeConstraints
.mMinSize
.height
) {
5563 newHeight
= mSizeConstraints
.mMinSize
.height
;
5564 newWidth
= newHeight
* mAspectRatio
;
5565 } else if (newHeight
> mSizeConstraints
.mMaxSize
.height
) {
5566 newHeight
= mSizeConstraints
.mMaxSize
.height
;
5567 newWidth
= newHeight
* mAspectRatio
;
5570 newHeight
= rect
->bottom
- rect
->top
;
5571 newWidth
= newHeight
* mAspectRatio
;
5572 if (newWidth
< mSizeConstraints
.mMinSize
.width
) {
5573 newWidth
= mSizeConstraints
.mMinSize
.width
;
5574 newHeight
= newWidth
/ mAspectRatio
;
5575 } else if (newWidth
> mSizeConstraints
.mMaxSize
.width
) {
5576 newWidth
= mSizeConstraints
.mMaxSize
.width
;
5577 newHeight
= newWidth
/ mAspectRatio
;
5584 rect
->right
= newWidth
+ rect
->left
;
5585 rect
->bottom
= rect
->top
+ newHeight
;
5588 rect
->right
= newWidth
+ rect
->left
;
5589 rect
->top
= rect
->bottom
- newHeight
;
5593 rect
->left
= rect
->right
- newWidth
;
5594 rect
->top
= rect
->bottom
- newHeight
;
5597 rect
->right
= rect
->left
+ newWidth
;
5598 rect
->top
= rect
->bottom
- newHeight
;
5600 case WMSZ_BOTTOMLEFT
:
5601 rect
->left
= rect
->right
- newWidth
;
5602 rect
->bottom
= rect
->top
+ newHeight
;
5604 case WMSZ_BOTTOMRIGHT
:
5605 rect
->right
= rect
->left
+ newWidth
;
5606 rect
->bottom
= rect
->top
+ newHeight
;
5611 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5612 // resize or move event. Instead we wait for first VM_SIZING message
5613 // within a ENTERSIZEMOVE to consider this a live resize event.
5614 if (mResizeState
== IN_SIZEMOVE
) {
5615 mResizeState
= RESIZING
;
5616 NotifyLiveResizeStarted();
5622 FinishLiveResizing(MOVING
);
5623 if (WinUtils::IsPerMonitorDPIAware()) {
5624 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5625 // a window around. Therefore, call ChangedDPI and ResetLayout here
5626 // if it appears that the window's scaling is not what we expect.
5627 // This causes the prescontext and appshell window management code to
5628 // check the appUnitsPerDevPixel value and current widget size, and
5629 // refresh them if necessary. If nothing has changed, these calls will
5630 // return without actually triggering any extra reflow or painting.
5631 if (WinUtils::LogToPhysFactor(mWnd
) != mDefaultScale
) {
5634 if (mWidgetListener
) {
5635 mWidgetListener
->UIResolutionChanged();
5641 case WM_ENTERSIZEMOVE
: {
5642 if (mResizeState
== NOT_RESIZING
) {
5643 mResizeState
= IN_SIZEMOVE
;
5648 case WM_EXITSIZEMOVE
: {
5649 FinishLiveResizing(NOT_RESIZING
);
5651 if (!sIsInMouseCapture
) {
5652 NotifySizeMoveDone();
5655 // Windows spins a separate hidden event loop when moving a window so we
5656 // don't hear mouse events during this time and WM_EXITSIZEMOVE is fired
5657 // when the hidden event loop exits. We set mDraggingWindowWithMouse to
5658 // true in WM_NCLBUTTONDOWN when we started moving the window with the
5659 // mouse so we know that if mDraggingWindowWithMouse is true, we can send
5660 // a mouse up event.
5661 if (mDraggingWindowWithMouse
) {
5662 mDraggingWindowWithMouse
= false;
5663 result
= DispatchMouseEvent(
5664 eMouseUp
, wParam
, lParam
, false, MouseButton::ePrimary
,
5665 MOUSE_INPUT_SOURCE(),
5666 mPointerEvents
.GetCachedPointerInfo(msg
, wParam
));
5672 case WM_DISPLAYCHANGE
: {
5673 ScreenHelperWin::RefreshScreens();
5674 if (mWidgetListener
) {
5675 mWidgetListener
->UIResolutionChanged();
5680 case WM_NCLBUTTONDBLCLK
:
5681 DispatchMouseEvent(eMouseDoubleClick
, 0, lParamToClient(lParam
), false,
5682 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5683 result
= DispatchMouseEvent(eMouseUp
, 0, lParamToClient(lParam
), false,
5684 MouseButton::ePrimary
, MOUSE_INPUT_SOURCE());
5685 DispatchPendingEvents();
5688 case WM_NCLBUTTONDOWN
: {
5689 // Dispatch a custom event when this happens in the draggable region, so
5690 // that non-popup-based panels can react to it. This doesn't send an
5691 // actual mousedown event because that would break dragging or interfere
5692 // with other mousedown handling in the caption area.
5693 if (ClientMarginHitTestPoint(GET_X_LPARAM(lParam
),
5694 GET_Y_LPARAM(lParam
)) == HTCAPTION
) {
5695 DispatchCustomEvent(u
"draggableregionleftmousedown"_ns
);
5696 mDraggingWindowWithMouse
= true;
5699 if (IsWindowButton(wParam
) && mCustomNonClient
) {
5700 DispatchMouseEvent(eMouseDown
, wParamFromGlobalMouseState(),
5701 lParamToClient(lParam
), false, MouseButton::ePrimary
,
5702 MOUSE_INPUT_SOURCE(), nullptr, true);
5703 DispatchPendingEvents();
5709 case WM_APPCOMMAND
: {
5710 MSG nativeMsg
= WinUtils::InitMSG(msg
, wParam
, lParam
, mWnd
);
5711 result
= HandleAppCommandMsg(nativeMsg
, aRetValue
);
5715 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5716 // and the loword of wParam specifies which. But we don't want to tell
5717 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5718 // events are fired. Instead, set either the sJustGotActivate or
5719 // gJustGotDeactivate flags and activate/deactivate once the focus
5722 int32_t fActive
= LOWORD(wParam
);
5723 if (mWidgetListener
) {
5724 if (WA_INACTIVE
== fActive
) {
5725 // when minimizing a window, the deactivation and focus events will
5726 // be fired in the reverse order. Instead, just deactivate right away.
5727 // This can also happen when a modal system dialog is opened, so check
5728 // if the last window to receive the WM_KILLFOCUS message was this one
5729 // or a child of this one.
5730 if (HIWORD(wParam
) ||
5731 (mLastKillFocusWindow
&&
5732 (GetTopLevelForFocus(mLastKillFocusWindow
) == mWnd
))) {
5733 DispatchFocusToTopLevelWindow(false);
5735 sJustGotDeactivate
= true;
5737 if (mIsTopWidgetWindow
) {
5738 mLastKeyboardLayout
= KeyboardLayout::GetInstance()->GetLayout();
5743 sJustGotActivate
= true;
5744 WidgetMouseEvent
event(true, eMouseActivate
, this,
5745 WidgetMouseEvent::eReal
);
5747 ModifierKeyState modifierKeyState
;
5748 modifierKeyState
.InitInputEvent(event
);
5749 DispatchInputEvent(&event
);
5750 if (sSwitchKeyboardLayout
&& mLastKeyboardLayout
)
5751 ActivateKeyboardLayout(mLastKeyboardLayout
, 0);
5753 #ifdef ACCESSIBILITY
5754 a11y::LazyInstantiator::ResetUiaDetectionCache();
5760 case WM_ACTIVATEAPP
: {
5761 // Bug 1851991: Sometimes this can be called before gfxPlatform::Init
5762 // when a window is created very early. In that case we just forego
5763 // setting this and accept the GPU process might briefly run at a lower
5765 if (GPUProcessManager::Get()) {
5766 GPUProcessManager::Get()->SetAppInForeground(wParam
);
5770 case WM_MOUSEACTIVATE
:
5771 // A popup with a parent owner should not be activated when clicked but
5772 // should still allow the mouse event to be fired, so the return value
5773 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
5774 // just use default processing so that the window is activated.
5775 if (IsPopup() && IsOwnerForegroundWindow()) {
5776 *aRetValue
= MA_NOACTIVATE
;
5781 case WM_WINDOWPOSCHANGING
: {
5782 LPWINDOWPOS info
= (LPWINDOWPOS
)lParam
;
5783 OnWindowPosChanging(info
);
5787 // Workaround for race condition in explorer.exe.
5788 case MOZ_WM_FULLSCREEN_STATE_UPDATE
: {
5789 TaskbarConcealer::OnAsyncStateUpdateRequest(mWnd
);
5793 case WM_GETMINMAXINFO
: {
5794 MINMAXINFO
* mmi
= (MINMAXINFO
*)lParam
;
5795 // Set the constraints. The minimum size should also be constrained to the
5796 // default window maximum size so that it fits on screen.
5797 mmi
->ptMinTrackSize
.x
=
5798 std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
5799 std::max((int32_t)mmi
->ptMinTrackSize
.x
,
5800 mSizeConstraints
.mMinSize
.width
));
5801 mmi
->ptMinTrackSize
.y
=
5802 std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
5803 std::max((int32_t)mmi
->ptMinTrackSize
.y
,
5804 mSizeConstraints
.mMinSize
.height
));
5805 mmi
->ptMaxTrackSize
.x
= std::min((int32_t)mmi
->ptMaxTrackSize
.x
,
5806 mSizeConstraints
.mMaxSize
.width
);
5807 mmi
->ptMaxTrackSize
.y
= std::min((int32_t)mmi
->ptMaxTrackSize
.y
,
5808 mSizeConstraints
.mMaxSize
.height
);
5812 WndProcUrgentInvocation::Marker _marker
;
5814 // If previous focused window isn't ours, it must have received the
5815 // redirected message. So, we should forget it.
5816 if (!WinUtils::IsOurProcessWindow(HWND(wParam
))) {
5817 RedirectedKeyDownMessageManager::Forget();
5819 if (sJustGotActivate
) {
5820 DispatchFocusToTopLevelWindow(true);
5822 TaskbarConcealer::OnFocusAcquired(this);
5826 if (sJustGotDeactivate
) {
5827 DispatchFocusToTopLevelWindow(false);
5829 mLastKillFocusWindow
= mWnd
;
5833 case WM_WINDOWPOSCHANGED
: {
5834 WINDOWPOS
* wp
= (LPWINDOWPOS
)lParam
;
5835 OnWindowPosChanged(wp
);
5836 TaskbarConcealer::OnWindowPosChanged(this);
5840 case WM_INPUTLANGCHANGEREQUEST
:
5845 case WM_INPUTLANGCHANGE
:
5846 KeyboardLayout::GetInstance()->OnLayoutChange(
5847 reinterpret_cast<HKL
>(lParam
));
5848 nsBidiKeyboard::OnLayoutChange();
5849 result
= false; // always pass to child window
5852 case WM_DESTROYCLIPBOARD
: {
5853 nsIClipboard
* clipboard
;
5854 nsresult rv
= CallGetService(kCClipboardCID
, &clipboard
);
5855 if (NS_SUCCEEDED(rv
)) {
5856 clipboard
->EmptyClipboard(nsIClipboard::kGlobalClipboard
);
5857 NS_RELEASE(clipboard
);
5861 #ifdef ACCESSIBILITY
5862 case WM_GETOBJECT
: {
5864 // Do explicit casting to make it working on 64bit systems (see bug 649236
5866 int32_t objId
= static_cast<DWORD
>(lParam
);
5867 if (objId
== OBJID_CLIENT
) { // oleacc.dll will be loaded dynamically
5868 RefPtr
<IAccessible
> root(
5869 a11y::LazyInstantiator::GetRootAccessible(mWnd
));
5871 *aRetValue
= LresultFromObject(IID_IAccessible
, wParam
, root
);
5872 a11y::LazyInstantiator::EnableBlindAggregation(mWnd
);
5879 case WM_SYSCOMMAND
: {
5880 WPARAM
const filteredWParam
= (wParam
& 0xFFF0);
5882 // SC_CLOSE may trigger a synchronous confirmation prompt. If we're in the
5883 // middle of something important, put off responding to it.
5884 if (filteredWParam
== SC_CLOSE
&& WndProcUrgentInvocation::IsActive()) {
5885 ::PostMessageW(mWnd
, msg
, wParam
, lParam
);
5890 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
5891 filteredWParam
== SC_RESTORE
&&
5892 GetCurrentShowCmd(mWnd
) != SW_SHOWMINIMIZED
) {
5893 mFrameState
->EnsureFullscreenMode(false);
5897 // Handle the system menu manually when we're in full screen mode
5898 // so we can set the appropriate options.
5899 if (filteredWParam
== SC_KEYMENU
&& lParam
== VK_SPACE
&&
5900 mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
) {
5901 DisplaySystemMenu(mWnd
, mFrameState
->GetSizeMode(), mIsRTL
,
5902 MOZ_SYSCONTEXT_X_POS
, MOZ_SYSCONTEXT_Y_POS
);
5907 case WM_DPICHANGED
: {
5908 LPRECT rect
= (LPRECT
)lParam
;
5909 OnDPIChanged(rect
->left
, rect
->top
, rect
->right
- rect
->left
,
5910 rect
->bottom
- rect
->top
);
5914 /* Gesture support events */
5915 case WM_TABLET_QUERYSYSTEMGESTURESTATUS
:
5916 // According to MS samples, this must be handled to enable
5917 // rotational support in multi-touch drivers.
5919 *aRetValue
= TABLET_ROTATE_GESTURE_ENABLE
;
5923 result
= OnTouch(wParam
, lParam
);
5930 result
= OnGesture(wParam
, lParam
);
5933 case WM_GESTURENOTIFY
: {
5934 if (mWindowType
!= WindowType::Invisible
) {
5935 // A GestureNotify event is dispatched to decide which single-finger
5936 // panning direction should be active (including none) and if pan
5937 // feedback should be displayed. Java and plugin windows can make their
5940 GESTURENOTIFYSTRUCT
* gestureinfo
= (GESTURENOTIFYSTRUCT
*)lParam
;
5941 nsPointWin touchPoint
;
5942 touchPoint
= gestureinfo
->ptsLocation
;
5943 touchPoint
.ScreenToClient(mWnd
);
5944 WidgetGestureNotifyEvent
gestureNotifyEvent(true, eGestureNotify
, this);
5945 gestureNotifyEvent
.mRefPoint
=
5946 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint
);
5947 nsEventStatus status
;
5948 DispatchEvent(&gestureNotifyEvent
, status
);
5949 mDisplayPanFeedback
= gestureNotifyEvent
.mDisplayPanFeedback
;
5951 mGesture
.SetWinGestureSupport(mWnd
, gestureNotifyEvent
.mPanDirection
);
5953 result
= false; // should always bubble to DefWindowProc
5957 WidgetContentCommandEvent
command(true, eContentCommandDelete
, this);
5958 DispatchWindowEvent(command
);
5963 WidgetContentCommandEvent
command(true, eContentCommandCut
, this);
5964 DispatchWindowEvent(command
);
5969 WidgetContentCommandEvent
command(true, eContentCommandCopy
, this);
5970 DispatchWindowEvent(command
);
5975 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this);
5976 DispatchWindowEvent(command
);
5981 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this);
5982 DispatchWindowEvent(command
);
5983 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
5988 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this);
5989 DispatchWindowEvent(command
);
5990 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
5995 // Support EM_CANPASTE message only when wParam isn't specified or
5996 // is plain text format.
5997 if (wParam
== 0 || wParam
== CF_TEXT
|| wParam
== CF_UNICODETEXT
) {
5998 WidgetContentCommandEvent
command(true, eContentCommandPaste
, this,
6000 DispatchWindowEvent(command
);
6001 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6007 WidgetContentCommandEvent
command(true, eContentCommandUndo
, this, true);
6008 DispatchWindowEvent(command
);
6009 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6014 WidgetContentCommandEvent
command(true, eContentCommandRedo
, this, true);
6015 DispatchWindowEvent(command
);
6016 *aRetValue
= (LRESULT
)(command
.mSucceeded
&& command
.mIsEnabled
);
6020 case MOZ_WM_SKEWFIX
: {
6021 TimeStamp skewStamp
;
6022 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam
,
6024 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
6030 if (msg
== nsAppShell::GetTaskbarButtonCreatedMessage()) {
6031 SetHasTaskbarIconBeenCreated();
6036 //*aRetValue = result;
6040 // Events which caused mWnd destruction and aren't consumed
6041 // will crash during the Windows default processing.
6046 void nsWindow::FinishLiveResizing(ResizeState aNewState
) {
6047 if (mResizeState
== RESIZING
) {
6048 NotifyLiveResizeStopped();
6050 mResizeState
= aNewState
;
6054 /**************************************************************
6056 * SECTION: Event processing helpers
6058 * Special processing for certain event types and
6059 * synthesized events.
6061 **************************************************************/
6063 LayoutDeviceIntMargin
nsWindow::NonClientSizeMargin(
6064 const LayoutDeviceIntMargin
& aNonClientOffset
) const {
6065 return LayoutDeviceIntMargin(mCaptionHeight
- aNonClientOffset
.top
,
6066 mHorResizeMargin
- aNonClientOffset
.right
,
6067 mVertResizeMargin
- aNonClientOffset
.bottom
,
6068 mHorResizeMargin
- aNonClientOffset
.left
);
6071 int32_t nsWindow::ClientMarginHitTestPoint(int32_t aX
, int32_t aY
) {
6072 const nsSizeMode sizeMode
= mFrameState
->GetSizeMode();
6073 if (sizeMode
== nsSizeMode_Minimized
|| sizeMode
== nsSizeMode_Fullscreen
) {
6077 // Calculations are done in screen coords
6078 const LayoutDeviceIntRect winRect
= GetScreenBounds();
6079 const LayoutDeviceIntPoint
point(aX
, aY
);
6081 // hit return constants:
6082 // HTBORDER - non-resizable border
6083 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
6084 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
6085 // HTTOPLEFT, HTTOPRIGHT - resizable corner
6086 // HTCAPTION - general title bar area
6087 // HTCLIENT - area considered the client
6088 // HTCLOSE - hovering over the close button
6089 // HTMAXBUTTON - maximize button
6090 // HTMINBUTTON - minimize button
6092 int32_t testResult
= HTCLIENT
;
6093 const bool isResizable
=
6094 sizeMode
!= nsSizeMode_Maximized
&&
6096 (BorderStyle::All
| BorderStyle::ResizeH
| BorderStyle::Default
));
6098 LayoutDeviceIntMargin nonClientSizeMargin
= NonClientSizeMargin();
6100 // Ensure being accessible to borders of window. Even if contents are in
6101 // this area, the area must behave as border.
6102 nonClientSizeMargin
.EnsureAtLeast(
6103 LayoutDeviceIntMargin(kResizableBorderMinSize
, kResizableBorderMinSize
,
6104 kResizableBorderMinSize
, kResizableBorderMinSize
));
6106 LayoutDeviceIntRect clientRect
= winRect
;
6107 clientRect
.Deflate(nonClientSizeMargin
);
6109 const bool allowContentOverride
=
6110 sizeMode
== nsSizeMode_Maximized
|| clientRect
.Contains(point
);
6112 // The border size. If there is no content under mouse cursor, the border
6113 // size should be larger than the values in system settings. Otherwise,
6114 // contents under the mouse cursor should be able to override the behavior.
6115 // E.g., user must expect that Firefox button always opens the popup menu
6116 // even when the user clicks on the above edge of it.
6117 LayoutDeviceIntMargin borderSize
= nonClientSizeMargin
;
6118 borderSize
.EnsureAtLeast(
6119 LayoutDeviceIntMargin(mVertResizeMargin
, mHorResizeMargin
,
6120 mVertResizeMargin
, mHorResizeMargin
));
6123 bool bottom
= false;
6127 if (point
.y
>= winRect
.y
&& point
.y
< winRect
.y
+ borderSize
.top
) {
6129 } else if (point
.y
<= winRect
.YMost() &&
6130 point
.y
> winRect
.YMost() - borderSize
.bottom
) {
6134 // (the 2x case here doubles the resize area for corners)
6135 int multiplier
= (top
|| bottom
) ? 2 : 1;
6136 if (point
.x
>= winRect
.x
&&
6137 point
.x
< winRect
.x
+ (multiplier
* borderSize
.left
)) {
6139 } else if (point
.x
<= winRect
.XMost() &&
6140 point
.x
> winRect
.XMost() - (multiplier
* borderSize
.right
)) {
6144 bool inResizeRegion
= false;
6149 testResult
= HTTOPLEFT
;
6151 testResult
= HTTOPRIGHT
;
6153 } else if (bottom
) {
6154 testResult
= HTBOTTOM
;
6156 testResult
= HTBOTTOMLEFT
;
6158 testResult
= HTBOTTOMRIGHT
;
6162 testResult
= HTLEFT
;
6165 testResult
= HTRIGHT
;
6168 inResizeRegion
= (testResult
!= HTCLIENT
);
6171 testResult
= HTCAPTION
;
6172 } else if (bottom
|| left
|| right
) {
6173 testResult
= HTBORDER
;
6177 if (!sIsInMouseCapture
&& allowContentOverride
) {
6179 POINT pt
= {aX
, aY
};
6180 ::ScreenToClient(mWnd
, &pt
);
6182 if (pt
.x
== mCachedHitTestPoint
.x
.value
&&
6183 pt
.y
== mCachedHitTestPoint
.y
.value
&&
6184 TimeStamp::Now() - mCachedHitTestTime
<
6185 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS
)) {
6186 return mCachedHitTestResult
;
6189 mCachedHitTestPoint
= {pt
.x
, pt
.y
};
6190 mCachedHitTestTime
= TimeStamp::Now();
6193 auto pt
= mCachedHitTestPoint
;
6195 if (mWindowBtnRect
[WindowButtonType::Minimize
].Contains(pt
)) {
6196 testResult
= HTMINBUTTON
;
6197 } else if (mWindowBtnRect
[WindowButtonType::Maximize
].Contains(pt
)) {
6198 testResult
= HTMAXBUTTON
;
6199 } else if (mWindowBtnRect
[WindowButtonType::Close
].Contains(pt
)) {
6200 testResult
= HTCLOSE
;
6201 } else if (!inResizeRegion
) {
6202 // If we're in the resize region, avoid overriding that with either a
6203 // drag or a client result; resize takes priority over either (but not
6204 // over the window controls, which is why we check this after those).
6205 if (mDraggableRegion
.Contains(pt
)) {
6206 testResult
= HTCAPTION
;
6208 testResult
= HTCLIENT
;
6212 mCachedHitTestResult
= testResult
;
6218 bool nsWindow::IsSimulatedClientArea(int32_t screenX
, int32_t screenY
) {
6219 int32_t testResult
= ClientMarginHitTestPoint(screenX
, screenY
);
6220 return testResult
== HTCAPTION
|| IsWindowButton(testResult
);
6223 bool nsWindow::IsWindowButton(int32_t hitTestResult
) {
6224 return hitTestResult
== HTMINBUTTON
|| hitTestResult
== HTMAXBUTTON
||
6225 hitTestResult
== HTCLOSE
;
6228 TimeStamp
nsWindow::GetMessageTimeStamp(LONG aEventTime
) const {
6229 CurrentWindowsTimeGetter
getCurrentTime(mWnd
);
6230 return TimeConverter().GetTimeStampFromSystemTime(aEventTime
, getCurrentTime
);
6233 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode
) {
6234 // Retain the previous mode that was notified to observers
6235 static bool sWasSleepMode
= false;
6237 // Only notify observers if mode changed
6238 if (aIsSleepMode
== sWasSleepMode
) return;
6240 sWasSleepMode
= aIsSleepMode
;
6242 nsCOMPtr
<nsIObserverService
> observerService
=
6243 mozilla::services::GetObserverService();
6244 if (observerService
)
6245 observerService
->NotifyObservers(nullptr,
6247 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6248 : NS_WIDGET_WAKE_OBSERVER_TOPIC
,
6252 LRESULT
nsWindow::ProcessCharMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6253 if (IMEHandler::IsComposingOn(this)) {
6254 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION
);
6256 // These must be checked here too as a lone WM_CHAR could be received
6257 // if a child window didn't handle it (for example Alt+Space in a content
6259 ModifierKeyState modKeyState
;
6260 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6261 return static_cast<LRESULT
>(nativeKey
.HandleCharMessage(aEventDispatched
));
6264 LRESULT
nsWindow::ProcessKeyUpMessage(const MSG
& aMsg
, bool* aEventDispatched
) {
6265 ModifierKeyState modKeyState
;
6266 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6267 bool result
= nativeKey
.HandleKeyUpMessage(aEventDispatched
);
6268 if (aMsg
.wParam
== VK_F10
) {
6269 // Bug 1382199: Windows default behavior will trigger the System menu bar
6270 // when F10 is released. Among other things, this causes the System menu bar
6271 // to appear when a web page overrides the contextmenu event. We *never*
6272 // want this default behavior, so eat this key (never pass it to Windows).
6278 LRESULT
nsWindow::ProcessKeyDownMessage(const MSG
& aMsg
,
6279 bool* aEventDispatched
) {
6280 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6281 // must clean up the redirected message information itself. For more
6282 // information, see above comment of
6283 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6284 // KeyboardLayout.h.
6285 RedirectedKeyDownMessageManager::AutoFlusher
redirectedMsgFlusher(this, aMsg
);
6287 ModifierKeyState modKeyState
;
6289 NativeKey
nativeKey(this, aMsg
, modKeyState
);
6291 static_cast<LRESULT
>(nativeKey
.HandleKeyDownMessage(aEventDispatched
));
6292 // HandleKeyDownMessage cleaned up the redirected message information
6293 // itself, so, we should do nothing.
6294 redirectedMsgFlusher
.Cancel();
6296 if (aMsg
.wParam
== VK_MENU
||
6297 (aMsg
.wParam
== VK_F10
&& !modKeyState
.IsShift())) {
6298 // We need to let Windows handle this keypress,
6299 // by returning false, if there's a native menu
6300 // bar somewhere in our containing window hierarchy.
6301 // Otherwise we handle the keypress and don't pass
6302 // it on to Windows, by returning true.
6303 bool hasNativeMenu
= false;
6306 if (::GetMenu(hWnd
)) {
6307 hasNativeMenu
= true;
6310 hWnd
= ::GetParent(hWnd
);
6312 result
= !hasNativeMenu
;
6318 nsresult
nsWindow::SynthesizeNativeKeyEvent(
6319 int32_t aNativeKeyboardLayout
, int32_t aNativeKeyCode
,
6320 uint32_t aModifierFlags
, const nsAString
& aCharacters
,
6321 const nsAString
& aUnmodifiedCharacters
, nsIObserver
* aObserver
) {
6322 AutoObserverNotifier
notifier(aObserver
, "keyevent");
6324 KeyboardLayout
* keyboardLayout
= KeyboardLayout::GetInstance();
6325 return keyboardLayout
->SynthesizeNativeKeyEvent(
6326 this, aNativeKeyboardLayout
, aNativeKeyCode
, aModifierFlags
, aCharacters
,
6327 aUnmodifiedCharacters
);
6330 nsresult
nsWindow::SynthesizeNativeMouseEvent(
6331 LayoutDeviceIntPoint aPoint
, NativeMouseMessage aNativeMessage
,
6332 MouseButton aButton
, nsIWidget::Modifiers aModifierFlags
,
6333 nsIObserver
* aObserver
) {
6334 AutoObserverNotifier
notifier(aObserver
, "mouseevent");
6337 memset(&input
, 0, sizeof(input
));
6339 // TODO (bug 1693240):
6340 // Now, we synthesize native mouse events asynchronously since we want to
6341 // synthesize the event on the front window at the point. However, Windows
6342 // does not provide a way to set modifier only while a mouse message is
6343 // being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
6344 // need a trick for handling it.
6346 switch (aNativeMessage
) {
6347 case NativeMouseMessage::Move
:
6348 input
.mi
.dwFlags
= MOUSEEVENTF_MOVE
;
6349 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6350 // to the position it's already at, we still dispatch a mousemove
6351 // event, because the callers of this function expect that.
6352 sLastMouseMovePoint
= {0};
6354 case NativeMouseMessage::ButtonDown
:
6355 case NativeMouseMessage::ButtonUp
: {
6356 const bool isDown
= aNativeMessage
== NativeMouseMessage::ButtonDown
;
6358 case MouseButton::ePrimary
:
6359 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_LEFTDOWN
: MOUSEEVENTF_LEFTUP
;
6361 case MouseButton::eMiddle
:
6363 isDown
? MOUSEEVENTF_MIDDLEDOWN
: MOUSEEVENTF_MIDDLEUP
;
6365 case MouseButton::eSecondary
:
6367 isDown
? MOUSEEVENTF_RIGHTDOWN
: MOUSEEVENTF_RIGHTUP
;
6369 case MouseButton::eX1
:
6370 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6371 input
.mi
.mouseData
= XBUTTON1
;
6373 case MouseButton::eX2
:
6374 input
.mi
.dwFlags
= isDown
? MOUSEEVENTF_XDOWN
: MOUSEEVENTF_XUP
;
6375 input
.mi
.mouseData
= XBUTTON2
;
6378 return NS_ERROR_INVALID_ARG
;
6382 case NativeMouseMessage::EnterWindow
:
6383 case NativeMouseMessage::LeaveWindow
:
6384 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Windows");
6385 return NS_ERROR_INVALID_ARG
;
6388 input
.type
= INPUT_MOUSE
;
6389 ::SetCursorPos(aPoint
.x
, aPoint
.y
);
6390 ::SendInput(1, &input
, sizeof(INPUT
));
6395 nsresult
nsWindow::SynthesizeNativeMouseScrollEvent(
6396 LayoutDeviceIntPoint aPoint
, uint32_t aNativeMessage
, double aDeltaX
,
6397 double aDeltaY
, double aDeltaZ
, uint32_t aModifierFlags
,
6398 uint32_t aAdditionalFlags
, nsIObserver
* aObserver
) {
6399 AutoObserverNotifier
notifier(aObserver
, "mousescrollevent");
6400 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6401 this, aPoint
, aNativeMessage
,
6402 (aNativeMessage
== WM_MOUSEWHEEL
|| aNativeMessage
== WM_VSCROLL
)
6403 ? static_cast<int32_t>(aDeltaY
)
6404 : static_cast<int32_t>(aDeltaX
),
6405 aModifierFlags
, aAdditionalFlags
);
6408 nsresult
nsWindow::SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase
,
6409 LayoutDeviceIntPoint aPoint
,
6410 double aDeltaX
, double aDeltaY
,
6411 int32_t aModifierFlags
,
6412 nsIObserver
* aObserver
) {
6413 AutoObserverNotifier
notifier(aObserver
, "touchpadpanevent");
6414 DirectManipulationOwner::SynthesizeNativeTouchpadPan(
6415 this, aEventPhase
, aPoint
, aDeltaX
, aDeltaY
, aModifierFlags
);
6419 static void MaybeLogPosChanged(HWND aWnd
, WINDOWPOS
* wp
) {
6420 #ifdef WINSTATE_DEBUG_OUTPUT
6421 if (aWnd
== WinUtils::GetTopLevelHWND(aWnd
)) {
6422 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [ top] "));
6424 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** OnWindowPosChanged: [child] "));
6426 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("WINDOWPOS flags:"));
6427 if (wp
->flags
& SWP_FRAMECHANGED
) {
6428 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_FRAMECHANGED "));
6430 if (wp
->flags
& SWP_SHOWWINDOW
) {
6431 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_SHOWWINDOW "));
6433 if (wp
->flags
& SWP_NOSIZE
) {
6434 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOSIZE "));
6436 if (wp
->flags
& SWP_HIDEWINDOW
) {
6437 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_HIDEWINDOW "));
6439 if (wp
->flags
& SWP_NOZORDER
) {
6440 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOZORDER "));
6442 if (wp
->flags
& SWP_NOACTIVATE
) {
6443 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("SWP_NOACTIVATE "));
6445 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("\n"));
6449 /**************************************************************
6451 * SECTION: OnXXX message handlers
6453 * For message handlers that need to be broken out or
6454 * implemented in specific platform code.
6456 **************************************************************/
6458 void nsWindow::OnWindowPosChanged(WINDOWPOS
* wp
) {
6463 MaybeLogPosChanged(mWnd
, wp
);
6465 // Handle window size mode changes
6466 if (wp
->flags
& SWP_FRAMECHANGED
) {
6467 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6468 // windows when fullscreen games disable desktop composition. If we're
6469 // minimized and not being activated, ignore the event and let windows
6471 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
&&
6472 (wp
->flags
& SWP_NOACTIVATE
)) {
6476 mFrameState
->OnFrameChanged();
6478 if (mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
6479 // Skip window size change events below on minimization.
6484 // Notify visibility change when window is activated.
6485 if (!(wp
->flags
& SWP_NOACTIVATE
) && NeedsToTrackWindowOcclusionState()) {
6486 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
6487 this, mFrameState
->GetSizeMode() != nsSizeMode_Minimized
);
6490 // Handle window position changes
6491 if (!(wp
->flags
& SWP_NOMOVE
)) {
6492 mBounds
.MoveTo(wp
->x
, wp
->y
);
6493 NotifyWindowMoved(wp
->x
, wp
->y
);
6496 // Handle window size changes
6497 if (!(wp
->flags
& SWP_NOSIZE
)) {
6499 int32_t newWidth
, newHeight
;
6501 ::GetWindowRect(mWnd
, &r
);
6503 newWidth
= r
.right
- r
.left
;
6504 newHeight
= r
.bottom
- r
.top
;
6506 if (newWidth
> mLastSize
.width
) {
6510 drect
.left
= wp
->x
+ mLastSize
.width
;
6512 drect
.right
= drect
.left
+ (newWidth
- mLastSize
.width
);
6513 drect
.bottom
= drect
.top
+ newHeight
;
6515 ::RedrawWindow(mWnd
, &drect
, nullptr,
6516 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6517 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6519 if (newHeight
> mLastSize
.height
) {
6524 drect
.top
= wp
->y
+ mLastSize
.height
;
6525 drect
.right
= drect
.left
+ newWidth
;
6526 drect
.bottom
= drect
.top
+ (newHeight
- mLastSize
.height
);
6528 ::RedrawWindow(mWnd
, &drect
, nullptr,
6529 RDW_INVALIDATE
| RDW_NOERASE
| RDW_NOINTERNALPAINT
|
6530 RDW_ERASENOW
| RDW_ALLCHILDREN
);
6533 mBounds
.SizeTo(newWidth
, newHeight
);
6534 mLastSize
.width
= newWidth
;
6535 mLastSize
.height
= newHeight
;
6537 #ifdef WINSTATE_DEBUG_OUTPUT
6538 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
6539 ("*** Resize window: %d x %d x %d x %d\n", wp
->x
, wp
->y
, newWidth
,
6543 if (mAspectRatio
> 0) {
6544 // It's possible (via Windows Aero Snap) that the size of the window
6545 // has changed such that it violates the aspect ratio constraint. If so,
6546 // queue up an event to enforce the aspect ratio constraint and repaint.
6547 // When resized with Windows Aero Snap, we are in the NOT_RESIZING state.
6548 float newAspectRatio
= (float)newWidth
/ newHeight
;
6549 if (mResizeState
== NOT_RESIZING
&& mAspectRatio
!= newAspectRatio
) {
6550 // Hold a reference to self alive and pass it into the lambda to make
6551 // sure this nsIWidget stays alive long enough to run this function.
6552 nsCOMPtr
<nsIWidget
> self(this);
6553 NS_DispatchToMainThread(NS_NewRunnableFunction(
6554 "EnforceAspectRatio", [self
, this, newWidth
]() -> void {
6556 Resize(newWidth
, newWidth
/ mAspectRatio
, true);
6562 // If a maximized window is resized, recalculate the non-client margins.
6563 if (mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
6564 if (UpdateNonClientMargins(true)) {
6565 // gecko resize event already sent by UpdateNonClientMargins.
6571 // Notify the widget listener for size change of client area for gecko
6572 // events. This needs to be done when either window size is changed,
6573 // or window frame is changed. They may not happen together.
6574 // However, we don't invoke that for popup when window frame changes,
6575 // because popups may trigger frame change before size change via
6576 // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
6577 // code below call OnResize with a wrong client size first, which can
6578 // lead to flickerling for some popups.
6579 if (!(wp
->flags
& SWP_NOSIZE
) ||
6580 ((wp
->flags
& SWP_FRAMECHANGED
) && !IsPopup())) {
6582 LayoutDeviceIntSize clientSize
;
6583 if (::GetClientRect(mWnd
, &r
)) {
6584 clientSize
= WinUtils::ToIntRect(r
).Size();
6586 clientSize
= mBounds
.Size();
6588 // Send a gecko resize event
6589 OnResize(clientSize
);
6593 void nsWindow::OnWindowPosChanging(WINDOWPOS
* info
) {
6594 // Update non-client margins if the frame size is changing, and let the
6595 // browser know we are changing size modes, so alternative css can kick in.
6596 // If we're going into fullscreen mode, ignore this, since it'll reset
6597 // margins to normal mode.
6598 if (info
->flags
& SWP_FRAMECHANGED
&& !(info
->flags
& SWP_NOSIZE
)) {
6599 mFrameState
->OnFrameChanging();
6602 // Force fullscreen. This works around a bug in Windows 10 1809 where
6603 // using fullscreen when a window is "snapped" causes a spurious resize
6604 // smaller than the full screen, see bug 1482920.
6605 if (mFrameState
->GetSizeMode() == nsSizeMode_Fullscreen
&&
6606 !(info
->flags
& SWP_NOMOVE
) && !(info
->flags
& SWP_NOSIZE
)) {
6607 nsCOMPtr
<nsIScreenManager
> screenmgr
=
6608 do_GetService(sScreenManagerContractID
);
6610 LayoutDeviceIntRect
bounds(info
->x
, info
->y
, info
->cx
, info
->cy
);
6611 DesktopIntRect deskBounds
=
6612 RoundedToInt(bounds
/ GetDesktopToDeviceScale());
6613 nsCOMPtr
<nsIScreen
> screen
;
6614 screenmgr
->ScreenForRect(deskBounds
.X(), deskBounds
.Y(),
6615 deskBounds
.Width(), deskBounds
.Height(),
6616 getter_AddRefs(screen
));
6619 auto rect
= screen
->GetRect();
6622 info
->cx
= rect
.width
;
6623 info
->cy
= rect
.height
;
6628 // enforce local z-order rules
6629 if (!(info
->flags
& SWP_NOZORDER
)) {
6630 HWND hwndAfter
= info
->hwndInsertAfter
;
6632 nsWindow
* aboveWindow
= 0;
6633 nsWindowZ placement
;
6635 if (hwndAfter
== HWND_BOTTOM
)
6636 placement
= nsWindowZBottom
;
6637 else if (hwndAfter
== HWND_TOP
|| hwndAfter
== HWND_TOPMOST
||
6638 hwndAfter
== HWND_NOTOPMOST
)
6639 placement
= nsWindowZTop
;
6641 placement
= nsWindowZRelative
;
6642 aboveWindow
= WinUtils::GetNSWindowPtr(hwndAfter
);
6645 if (mWidgetListener
) {
6646 nsCOMPtr
<nsIWidget
> actualBelow
= nullptr;
6647 if (mWidgetListener
->ZLevelChanged(false, &placement
, aboveWindow
,
6648 getter_AddRefs(actualBelow
))) {
6649 if (placement
== nsWindowZBottom
)
6650 info
->hwndInsertAfter
= HWND_BOTTOM
;
6651 else if (placement
== nsWindowZTop
)
6652 info
->hwndInsertAfter
= HWND_TOP
;
6654 info
->hwndInsertAfter
=
6655 (HWND
)actualBelow
->GetNativeData(NS_NATIVE_WINDOW
);
6660 // prevent rude external programs from making hidden window visible
6661 if (mWindowType
== WindowType::Invisible
) info
->flags
&= ~SWP_SHOWWINDOW
;
6663 // When waking from sleep or switching out of tablet mode, Windows 10
6664 // Version 1809 will reopen popup windows that should be hidden. Detect
6665 // this case and refuse to show the window.
6666 static bool sDWMUnhidesPopups
= IsWin10Sep2018UpdateOrLater();
6667 if (sDWMUnhidesPopups
&& (info
->flags
& SWP_SHOWWINDOW
) &&
6668 mWindowType
== WindowType::Popup
&& mWidgetListener
&&
6669 mWidgetListener
->ShouldNotBeVisible()) {
6670 info
->flags
&= ~SWP_SHOWWINDOW
;
6674 void nsWindow::UserActivity() {
6675 // Check if we have the idle service, if not we try to get it.
6676 if (!mIdleService
) {
6677 mIdleService
= do_GetService("@mozilla.org/widget/useridleservice;1");
6680 // Check that we now have the idle service.
6682 mIdleService
->ResetIdleTimeOut(0);
6686 // Helper function for TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT,
6688 static bool TouchDeviceNeedsPanGestureConversion(HANDLE aSource
) {
6689 std::string deviceName
;
6691 // The first call just queries how long the name string will be.
6692 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, nullptr, &dataSize
);
6693 if (!dataSize
|| dataSize
> 0x10000) {
6696 deviceName
.resize(dataSize
);
6697 // The second call actually populates the string.
6698 UINT result
= GetRawInputDeviceInfoA(aSource
, RIDI_DEVICENAME
, &deviceName
[0],
6700 if (result
== UINT_MAX
) {
6703 // The affected device name is "\\?\VIRTUAL_DIGITIZER", but each backslash
6704 // needs to be escaped with another one.
6705 std::string expectedDeviceName
= "\\\\?\\VIRTUAL_DIGITIZER";
6706 // For some reason, the dataSize returned by the first call is double the
6707 // actual length of the device name (as if it were returning the size of a
6708 // wide-character string in bytes) even though we are using the narrow
6709 // version of the API. For the comparison against the expected device name
6710 // to pass, we truncate the buffer to be no longer tha the expected device
6712 if (deviceName
.substr(0, expectedDeviceName
.length()) != expectedDeviceName
) {
6716 RID_DEVICE_INFO deviceInfo
;
6717 deviceInfo
.cbSize
= sizeof(deviceInfo
);
6718 dataSize
= sizeof(deviceInfo
);
6720 GetRawInputDeviceInfoA(aSource
, RIDI_DEVICEINFO
, &deviceInfo
, &dataSize
);
6721 if (result
== UINT_MAX
) {
6724 // The device identifiers that we check for here come from bug 1355162
6725 // comment 1 (see also bug 1511901 comment 35).
6726 return deviceInfo
.dwType
== RIM_TYPEHID
&& deviceInfo
.hid
.dwVendorId
== 0 &&
6727 deviceInfo
.hid
.dwProductId
== 0 &&
6728 deviceInfo
.hid
.dwVersionNumber
== 1 &&
6729 deviceInfo
.hid
.usUsagePage
== 13 && deviceInfo
.hid
.usUsage
== 4;
6732 // Determine if the touch device that originated |aOSEvent| needs to have
6733 // touch events representing a two-finger gesture converted to pan
6735 // We only do this for touch devices with a specific name and identifiers.
6736 static bool TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent
,
6737 uint32_t aTouchCount
) {
6738 if (!StaticPrefs::apz_windows_check_for_pan_gesture_conversion()) {
6741 if (aTouchCount
== 0) {
6744 HANDLE source
= aOSEvent
[0].hSource
;
6746 // Cache the result of this computation for each touch device.
6747 // Touch devices are identified by the HANDLE stored in the hSource
6748 // field of TOUCHINPUT.
6749 static std::map
<HANDLE
, bool> sResultCache
;
6750 auto [iter
, inserted
] = sResultCache
.emplace(source
, false);
6752 iter
->second
= TouchDeviceNeedsPanGestureConversion(source
);
6754 return iter
->second
;
6757 Maybe
<PanGestureInput
> nsWindow::ConvertTouchToPanGesture(
6758 const MultiTouchInput
& aTouchInput
, PTOUCHINPUT aOSEvent
) {
6759 // Checks if the touch device that originated the touch event is one
6760 // for which we want to convert the touch events to pang gesture events.
6761 bool shouldConvert
= TouchDeviceNeedsPanGestureConversion(
6762 aOSEvent
, aTouchInput
.mTouches
.Length());
6763 if (!shouldConvert
) {
6767 // Only two-finger gestures need conversion.
6768 if (aTouchInput
.mTouches
.Length() != 2) {
6772 PanGestureInput::PanGestureType eventType
= PanGestureInput::PANGESTURE_PAN
;
6773 if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_START
) {
6774 eventType
= PanGestureInput::PANGESTURE_START
;
6775 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_END
) {
6776 eventType
= PanGestureInput::PANGESTURE_END
;
6777 } else if (aTouchInput
.mType
== MultiTouchInput::MULTITOUCH_CANCEL
) {
6778 eventType
= PanGestureInput::PANGESTURE_CANCELLED
;
6781 // Use the midpoint of the two touches as the start point of the pan gesture.
6782 ScreenPoint focusPoint
= (aTouchInput
.mTouches
[0].mScreenPoint
+
6783 aTouchInput
.mTouches
[1].mScreenPoint
) /
6785 // To compute the displacement of the pan gesture, we keep track of the
6786 // location of the previous event.
6787 ScreenPoint displacement
= (eventType
== PanGestureInput::PANGESTURE_START
)
6789 : (focusPoint
- mLastPanGestureFocus
);
6790 mLastPanGestureFocus
= focusPoint
;
6792 // We need to negate the displacement because for a touch event, moving the
6793 // fingers down results in scrolling up, but for a touchpad gesture, we want
6794 // moving the fingers down to result in scrolling down.
6795 PanGestureInput
result(eventType
, aTouchInput
.mTimeStamp
, focusPoint
,
6796 -displacement
, aTouchInput
.modifiers
);
6797 result
.mSimulateMomentum
= true;
6799 return Some(result
);
6802 // Dispatch an event that originated as an OS touch event.
6803 // Usually, we want to dispatch it as a touch event, but some touchpads
6804 // produce touch events for two-finger scrolling, which need to be converted
6805 // to pan gesture events for correct behaviour.
6806 void nsWindow::DispatchTouchOrPanGestureInput(MultiTouchInput
& aTouchInput
,
6807 PTOUCHINPUT aOSEvent
) {
6808 if (Maybe
<PanGestureInput
> panInput
=
6809 ConvertTouchToPanGesture(aTouchInput
, aOSEvent
)) {
6810 DispatchPanGestureInput(*panInput
);
6814 DispatchTouchInput(aTouchInput
);
6817 bool nsWindow::OnTouch(WPARAM wParam
, LPARAM lParam
) {
6818 uint32_t cInputs
= LOWORD(wParam
);
6819 PTOUCHINPUT pInputs
= new TOUCHINPUT
[cInputs
];
6821 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, pInputs
,
6822 sizeof(TOUCHINPUT
))) {
6823 MultiTouchInput touchInput
, touchEndInput
;
6825 // Walk across the touch point array processing each contact point.
6826 for (uint32_t i
= 0; i
< cInputs
; i
++) {
6827 bool addToEvent
= false, addToEndEvent
= false;
6829 // N.B.: According with MS documentation
6830 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
6831 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
6832 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
6833 // TOUCHEVENTF_UP can be combined together.
6835 if (pInputs
[i
].dwFlags
& (TOUCHEVENTF_DOWN
| TOUCHEVENTF_MOVE
)) {
6836 if (touchInput
.mTimeStamp
.IsNull()) {
6837 // Initialize a touch event to send.
6838 touchInput
.mType
= MultiTouchInput::MULTITOUCH_MOVE
;
6839 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6840 ModifierKeyState modifierKeyState
;
6841 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
6843 // Pres shell expects this event to be a eTouchStart
6844 // if any new contact points have been added since the last event sent.
6845 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_DOWN
) {
6846 touchInput
.mType
= MultiTouchInput::MULTITOUCH_START
;
6850 if (pInputs
[i
].dwFlags
& TOUCHEVENTF_UP
) {
6851 // Pres shell expects removed contacts points to be delivered in a
6852 // separate eTouchEnd event containing only the contact points that were
6854 if (touchEndInput
.mTimeStamp
.IsNull()) {
6855 // Initialize a touch event to send.
6856 touchEndInput
.mType
= MultiTouchInput::MULTITOUCH_END
;
6857 touchEndInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6858 ModifierKeyState modifierKeyState
;
6859 touchEndInput
.modifiers
= modifierKeyState
.GetModifiers();
6861 addToEndEvent
= true;
6863 if (!addToEvent
&& !addToEndEvent
) {
6864 // Filter out spurious Windows events we don't understand, like palm
6869 // Setup the touch point we'll append to the touch event array.
6870 nsPointWin touchPoint
;
6871 touchPoint
.x
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].x
);
6872 touchPoint
.y
= TOUCH_COORD_TO_PIXEL(pInputs
[i
].y
);
6873 touchPoint
.ScreenToClient(mWnd
);
6875 // Initialize the touch data.
6876 SingleTouchData
touchData(
6877 pInputs
[i
].dwID
, // aIdentifier
6878 ScreenIntPoint::FromUnknownPoint(touchPoint
), // aScreenPoint
6879 // The contact area info cannot be trusted even when
6880 // TOUCHINPUTMASKF_CONTACTAREA is set when the input source is pen,
6881 // which somehow violates the API docs. (bug 1710509) Ultimately the
6882 // dwFlags check will become redundant since we want to migrate to
6883 // WM_POINTER for pens. (bug 1707075)
6884 (pInputs
[i
].dwMask
& TOUCHINPUTMASKF_CONTACTAREA
) &&
6885 !(pInputs
[i
].dwFlags
& TOUCHEVENTF_PEN
)
6886 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs
[i
].cxContact
) / 2,
6887 TOUCH_COORD_TO_PIXEL(pInputs
[i
].cyContact
) / 2)
6888 : ScreenSize(1, 1), // aRadius
6889 0.0f
, // aRotationAngle
6892 // Append touch data to the appropriate event.
6894 touchInput
.mTouches
.AppendElement(touchData
);
6896 if (addToEndEvent
) {
6897 touchEndInput
.mTouches
.AppendElement(touchData
);
6901 // Dispatch touch start and touch move event if we have one.
6902 if (!touchInput
.mTimeStamp
.IsNull()) {
6903 DispatchTouchOrPanGestureInput(touchInput
, pInputs
);
6905 // Dispatch touch end event if we have one.
6906 if (!touchEndInput
.mTimeStamp
.IsNull()) {
6907 DispatchTouchOrPanGestureInput(touchEndInput
, pInputs
);
6912 CloseTouchInputHandle((HTOUCHINPUT
)lParam
);
6916 // Gesture event processing. Handles WM_GESTURE events.
6917 bool nsWindow::OnGesture(WPARAM wParam
, LPARAM lParam
) {
6918 // Treatment for pan events which translate into scroll events:
6919 if (mGesture
.IsPanEvent(lParam
)) {
6920 if (!mGesture
.ProcessPanMessage(mWnd
, wParam
, lParam
))
6921 return false; // ignore
6923 nsEventStatus status
;
6925 WidgetWheelEvent
wheelEvent(true, eWheel
, this);
6927 ModifierKeyState modifierKeyState
;
6928 modifierKeyState
.InitInputEvent(wheelEvent
);
6930 wheelEvent
.mButton
= 0;
6931 wheelEvent
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6932 wheelEvent
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
6934 bool endFeedback
= true;
6936 if (mGesture
.PanDeltaToPixelScroll(wheelEvent
)) {
6937 DispatchEvent(&wheelEvent
, status
);
6940 if (mDisplayPanFeedback
) {
6941 mGesture
.UpdatePanFeedbackX(
6942 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaX
)),
6944 mGesture
.UpdatePanFeedbackY(
6945 mWnd
, DeprecatedAbs(RoundDown(wheelEvent
.mOverflowDeltaY
)),
6947 mGesture
.PanFeedbackFinalize(mWnd
, endFeedback
);
6950 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
6955 // Other gestures translate into simple gesture events:
6956 WidgetSimpleGestureEvent
event(true, eVoidEvent
, this);
6957 if (!mGesture
.ProcessGestureMessage(mWnd
, wParam
, lParam
, event
)) {
6958 return false; // fall through to DefWndProc
6961 // Polish up and send off the new event
6962 ModifierKeyState modifierKeyState
;
6963 modifierKeyState
.InitInputEvent(event
);
6965 event
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
6966 event
.mInputSource
= MouseEvent_Binding::MOZ_SOURCE_TOUCH
;
6968 nsEventStatus status
;
6969 DispatchEvent(&event
, status
);
6970 if (status
== nsEventStatus_eIgnore
) {
6971 return false; // Ignored, fall through
6974 // Only close this if we process and return true.
6975 CloseGestureInfoHandle((HGESTUREINFO
)lParam
);
6977 return true; // Handled
6980 // WM_DESTROY event handler
6981 void nsWindow::OnDestroy() {
6982 mOnDestroyCalled
= true;
6984 // If this is a toplevel window, notify the taskbar concealer to clean up any
6987 TaskbarConcealer::OnWindowDestroyed(mWnd
);
6990 // Make sure we don't get destroyed in the process of tearing down.
6991 nsCOMPtr
<nsIWidget
> kungFuDeathGrip(this);
6993 // Dispatch the destroy notification.
6994 if (!mInDtor
) NotifyWindowDestroyed();
6996 // Prevent the widget from sending additional events.
6997 mWidgetListener
= nullptr;
6998 mAttachedWidgetListener
= nullptr;
7000 DestroyDirectManipulation();
7002 if (mWnd
== mLastKillFocusWindow
) {
7003 mLastKillFocusWindow
= nullptr;
7005 // Unregister notifications from terminal services
7006 ::WTSUnRegisterSessionNotification(mWnd
);
7008 // We will stop receiving native events after dissociating from our native
7009 // window. We will also disappear from the output of WinUtils::GetNSWindowPtr
7011 DissociateFromNativeWindow();
7013 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
7014 // can be cleared. (It's used in tracking windows for mouse events.)
7015 if (sCurrentWindow
== this) sCurrentWindow
= nullptr;
7017 // Disconnects us from our parent, will call our GetParent().
7018 nsBaseWidget::Destroy();
7020 // Release references to children, device context, toolkit, and app shell.
7021 nsBaseWidget::OnDestroy();
7023 // Clear our native parent handle.
7024 // XXX Windows will take care of this in the proper order, and
7025 // SetParent(nullptr)'s remove child on the parent already took place in
7026 // nsBaseWidget's Destroy call above.
7027 // SetParent(nullptr);
7030 // We have to destroy the native drag target before we null out our window
7032 EnableDragDrop(false);
7034 // If we're going away and for some reason we're still the rollup widget,
7035 // rollup and turn off capture.
7036 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7037 nsCOMPtr
<nsIWidget
> rollupWidget
;
7038 if (rollupListener
) {
7039 rollupWidget
= rollupListener
->GetRollupWidget();
7041 if (this == rollupWidget
) {
7042 rollupListener
->Rollup({});
7043 CaptureRollupEvents(false);
7046 IMEHandler::OnDestroyWindow(this);
7048 // Free GDI window class objects
7050 VERIFY(::DeleteObject(mBrush
));
7054 // Destroy any custom cursor resources.
7055 if (mCursor
.IsCustom()) {
7056 SetCursor(Cursor
{eCursor_standard
});
7059 if (mCompositorWidgetDelegate
) {
7060 mCompositorWidgetDelegate
->OnDestroyWindow();
7062 mBasicLayersSurface
= nullptr;
7064 // Finalize panning feedback to possibly restore window displacement
7065 mGesture
.PanFeedbackFinalize(mWnd
, true);
7067 // Clear the main HWND.
7071 // Send a resize message to the listener
7072 bool nsWindow::OnResize(const LayoutDeviceIntSize
& aSize
) {
7073 if (mCompositorWidgetDelegate
&&
7074 !mCompositorWidgetDelegate
->OnWindowResize(aSize
)) {
7078 bool result
= false;
7079 if (mWidgetListener
) {
7080 result
= mWidgetListener
->WindowResized(this, aSize
.width
, aSize
.height
);
7083 // If there is an attached view, inform it as well as the normal widget
7085 if (mAttachedWidgetListener
) {
7086 return mAttachedWidgetListener
->WindowResized(this, aSize
.width
,
7093 void nsWindow::OnSizeModeChange() {
7094 const nsSizeMode mode
= mFrameState
->GetSizeMode();
7096 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7097 ("nsWindow::OnSizeModeChange() sizeMode %d", mode
));
7099 if (NeedsToTrackWindowOcclusionState()) {
7100 WinWindowOcclusionTracker::Get()->OnWindowVisibilityChanged(
7101 this, mode
!= nsSizeMode_Minimized
);
7103 wr::DebugFlags flags
{0};
7104 flags
._0
= gfx::gfxVars::WebRenderDebugFlags();
7105 bool debugEnabled
= bool(flags
& wr::DebugFlags::WINDOW_VISIBILITY_DBG
);
7106 if (debugEnabled
&& mCompositorWidgetDelegate
) {
7107 mCompositorWidgetDelegate
->NotifyVisibilityUpdated(mode
,
7112 if (mCompositorWidgetDelegate
) {
7113 mCompositorWidgetDelegate
->OnWindowModeChange(mode
);
7116 if (mWidgetListener
) {
7117 mWidgetListener
->SizeModeChanged(mode
);
7121 bool nsWindow::OnHotKey(WPARAM wParam
, LPARAM lParam
) { return true; }
7123 bool nsWindow::IsPopup() { return mWindowType
== WindowType::Popup
; }
7125 bool nsWindow::ShouldUseOffMainThreadCompositing() {
7126 if (mWindowType
== WindowType::Popup
&& mPopupType
== PopupType::Tooltip
) {
7130 // Content rendering of popup is always done by child window.
7131 // See nsDocumentViewer::ShouldAttachToTopLevel().
7132 if (mWindowType
== WindowType::Popup
&& !mIsChildWindow
) {
7133 MOZ_ASSERT(!mParent
);
7137 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
7140 void nsWindow::WindowUsesOMTC() {
7141 ULONG_PTR style
= ::GetClassLongPtr(mWnd
, GCL_STYLE
);
7143 NS_WARNING("Could not get window class style");
7146 style
|= CS_HREDRAW
| CS_VREDRAW
;
7147 DebugOnly
<ULONG_PTR
> result
= ::SetClassLongPtr(mWnd
, GCL_STYLE
, style
);
7148 NS_WARNING_ASSERTION(result
, "Could not reset window class style");
7151 void nsWindow::OnDPIChanged(int32_t x
, int32_t y
, int32_t width
,
7153 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
7154 // they remain tied to their original parent's resolution.
7155 if (mWindowType
== WindowType::Popup
) {
7158 if (StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
7161 mDefaultScale
= -1.0; // force recomputation of scale factor
7163 if (mResizeState
!= RESIZING
&&
7164 mFrameState
->GetSizeMode() == nsSizeMode_Normal
) {
7165 // Limit the position (if not in the middle of a drag-move) & size,
7166 // if it would overflow the destination screen
7167 nsCOMPtr
<nsIScreenManager
> sm
= do_GetService(sScreenManagerContractID
);
7169 nsCOMPtr
<nsIScreen
> screen
;
7170 sm
->ScreenForRect(x
, y
, width
, height
, getter_AddRefs(screen
));
7172 int32_t availLeft
, availTop
, availWidth
, availHeight
;
7173 screen
->GetAvailRect(&availLeft
, &availTop
, &availWidth
, &availHeight
);
7174 if (mResizeState
!= MOVING
) {
7175 x
= std::max(x
, availLeft
);
7176 y
= std::max(y
, availTop
);
7178 width
= std::min(width
, availWidth
);
7179 height
= std::min(height
, availHeight
);
7183 Resize(x
, y
, width
, height
, true);
7185 UpdateNonClientMargins();
7190 // Callback to generate OnCloakChanged pseudo-events.
7192 void nsWindow::OnCloakEvent(HWND aWnd
, bool aCloaked
) {
7193 MOZ_ASSERT(NS_IsMainThread());
7195 const char* const kEventName
= aCloaked
? "CLOAKED" : "UNCLOAKED";
7196 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(aWnd
);
7199 sCloakingLog
, LogLevel::Debug
,
7200 ("Received %s event for HWND %p (not an nsWindow)", kEventName
, aWnd
));
7204 const char* const kWasCloakedStr
= pWin
->mIsCloaked
? "cloaked" : "uncloaked";
7205 if (mozilla::IsCloaked(aWnd
) == pWin
->mIsCloaked
) {
7206 MOZ_LOG(sCloakingLog
, LogLevel::Debug
,
7207 ("Received redundant %s event for %s HWND %p; discarding",
7208 kEventName
, kWasCloakedStr
, aWnd
));
7213 sCloakingLog
, LogLevel::Info
,
7214 ("Received %s event for %s HWND %p", kEventName
, kWasCloakedStr
, aWnd
));
7216 // Cloaking events like the one we've just received are sent asynchronously.
7217 // Rather than process them one-by-one, we jump the gun a bit and perform
7218 // updates on all newly cloaked/uncloaked nsWindows at once. This also lets us
7219 // batch operations that consider more than one window's state.
7224 nsTArray
<Item
> changedWindows
;
7226 mozilla::EnumerateThreadWindows([&](HWND hwnd
) {
7227 nsWindow
* pWin
= WinUtils::GetNSWindowPtr(hwnd
);
7232 const bool isCloaked
= mozilla::IsCloaked(hwnd
);
7233 if (isCloaked
!= pWin
->mIsCloaked
) {
7234 changedWindows
.AppendElement(Item
{pWin
, isCloaked
});
7238 if (changedWindows
.IsEmpty()) {
7242 for (const Item
& item
: changedWindows
) {
7243 item
.win
->OnCloakChanged(item
.nowCloaked
);
7246 nsWindow::TaskbarConcealer::OnCloakChanged();
7249 void nsWindow::OnCloakChanged(bool aCloaked
) {
7250 MOZ_LOG(sCloakingLog
, LogLevel::Info
,
7251 ("Calling OnCloakChanged(): HWND %p, aCloaked %s", mWnd
,
7252 aCloaked
? "true" : "false"));
7253 mIsCloaked
= aCloaked
;
7256 /**************************************************************
7257 **************************************************************
7259 ** BLOCK: IME management and accessibility
7261 ** Handles managing IME input and accessibility.
7263 **************************************************************
7264 **************************************************************/
7266 void nsWindow::SetInputContext(const InputContext
& aContext
,
7267 const InputContextAction
& aAction
) {
7268 InputContext newInputContext
= aContext
;
7269 IMEHandler::SetInputContext(this, newInputContext
, aAction
);
7270 mInputContext
= newInputContext
;
7273 InputContext
nsWindow::GetInputContext() {
7274 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7275 if (WinUtils::IsIMEEnabled(mInputContext
) && IMEHandler::GetOpenState(this)) {
7276 mInputContext
.mIMEState
.mOpen
= IMEState::OPEN
;
7278 mInputContext
.mIMEState
.mOpen
= IMEState::CLOSED
;
7280 return mInputContext
;
7283 TextEventDispatcherListener
* nsWindow::GetNativeTextEventDispatcherListener() {
7284 return IMEHandler::GetNativeTextEventDispatcherListener();
7287 #ifdef ACCESSIBILITY
7289 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7290 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7292 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: " \
7294 aHwnd, ::GetParent(aHwnd), aWnd); \
7295 printf(" acc: %p", aAcc); \
7297 nsAutoString name; \
7299 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7305 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7308 a11y::LocalAccessible
* nsWindow::GetAccessible() {
7309 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7310 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled
)
7313 if (mInDtor
|| mOnDestroyCalled
|| mWindowType
== WindowType::Invisible
) {
7317 // In case of popup window return a popup accessible.
7318 nsView
* view
= nsView::GetViewFor(this);
7320 nsIFrame
* frame
= view
->GetFrame();
7321 if (frame
&& nsLayoutUtils::IsPopup(frame
)) {
7322 nsAccessibilityService
* accService
= GetOrCreateAccService();
7324 a11y::DocAccessible
* docAcc
=
7325 GetAccService()->GetDocAccessible(frame
->PresShell());
7329 docAcc
->GetAccessibleOrDescendant(frame
->GetContent()));
7330 return docAcc
->GetAccessibleOrDescendant(frame
->GetContent());
7336 // otherwise root document accessible.
7337 NS_LOG_WMGETOBJECT(this, mWnd
, GetRootAccessible());
7338 return GetRootAccessible();
7342 /**************************************************************
7343 **************************************************************
7345 ** BLOCK: Transparency
7347 ** Window transparency helpers.
7349 **************************************************************
7350 **************************************************************/
7352 void nsWindow::SetWindowTranslucencyInner(TransparencyMode aMode
) {
7353 if (aMode
== mTransparencyMode
) return;
7355 // stop on dialogs and popups!
7356 HWND hWnd
= WinUtils::GetTopLevelHWND(mWnd
, true);
7357 nsWindow
* parent
= WinUtils::GetNSWindowPtr(hWnd
);
7360 NS_WARNING("Trying to use transparent chrome in an embedded context");
7364 if (parent
!= this) {
7366 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7369 if (aMode
== TransparencyMode::Transparent
) {
7370 // If we're switching to the use of a transparent window, hide the chrome
7372 HideWindowChrome(true);
7373 } else if (mHideChrome
&&
7374 mTransparencyMode
== TransparencyMode::Transparent
) {
7375 // if we're switching out of transparent, re-enable our parent's chrome.
7376 HideWindowChrome(false);
7379 LONG_PTR style
= ::GetWindowLongPtrW(hWnd
, GWL_STYLE
),
7380 exStyle
= ::GetWindowLongPtr(hWnd
, GWL_EXSTYLE
);
7382 if (parent
->mIsVisible
) {
7383 style
|= WS_VISIBLE
;
7384 if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Maximized
) {
7385 style
|= WS_MAXIMIZE
;
7386 } else if (parent
->mFrameState
->GetSizeMode() == nsSizeMode_Minimized
) {
7387 style
|= WS_MINIMIZE
;
7391 if (aMode
== TransparencyMode::Transparent
)
7392 exStyle
|= WS_EX_LAYERED
;
7394 exStyle
&= ~WS_EX_LAYERED
;
7396 VERIFY_WINDOW_STYLE(style
);
7397 ::SetWindowLongPtrW(hWnd
, GWL_STYLE
, style
);
7398 ::SetWindowLongPtrW(hWnd
, GWL_EXSTYLE
, exStyle
);
7400 mTransparencyMode
= aMode
;
7402 if (mCompositorWidgetDelegate
) {
7403 mCompositorWidgetDelegate
->UpdateTransparency(aMode
);
7407 /**************************************************************
7408 **************************************************************
7410 ** BLOCK: Popup rollup hooks
7412 ** Deals with CaptureRollup on popup windows.
7414 **************************************************************
7415 **************************************************************/
7417 // Schedules a timer for a window, so we can rollup after processing the hook
7419 void nsWindow::ScheduleHookTimer(HWND aWnd
, UINT aMsgId
) {
7420 // In some cases multiple hooks may be scheduled
7421 // so ignore any other requests once one timer is scheduled
7422 if (sHookTimerId
== 0) {
7423 // Remember the window handle and the message ID to be used later
7424 sRollupMsgId
= aMsgId
;
7425 sRollupMsgWnd
= aWnd
;
7426 // Schedule native timer for doing the rollup after
7427 // this event is done being processed
7428 sHookTimerId
= ::SetTimer(nullptr, 0, 0, (TIMERPROC
)HookTimerForPopups
);
7429 NS_ASSERTION(sHookTimerId
, "Timer couldn't be created.");
7433 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7434 int gLastMsgCode
= 0;
7435 extern MSGFEventMsgInfo gMSGFEvents
[];
7438 // Process Menu messages, rollup when popup is clicked.
7439 LRESULT CALLBACK
nsWindow::MozSpecialMsgFilter(int code
, WPARAM wParam
,
7441 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7443 MSG
* pMsg
= (MSG
*)lParam
;
7446 while (gMSGFEvents
[inx
].mId
!= code
&& gMSGFEvents
[inx
].mStr
!= nullptr) {
7449 if (code
!= gLastMsgCode
) {
7450 if (gMSGFEvents
[inx
].mId
== code
) {
7452 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7453 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code
,
7454 gMSGFEvents
[inx
].mStr
, pMsg
->hwnd
));
7458 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7459 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code
,
7460 gMSGFEvents
[inx
].mId
, pMsg
->hwnd
));
7463 gLastMsgCode
= code
;
7465 PrintEvent(pMsg
->message
, FALSE
, FALSE
);
7467 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7469 if (sProcessHook
&& code
== MSGF_MENU
) {
7470 MSG
* pMsg
= (MSG
*)lParam
;
7471 ScheduleHookTimer(pMsg
->hwnd
, pMsg
->message
);
7474 return ::CallNextHookEx(sMsgFilterHook
, code
, wParam
, lParam
);
7477 // Process all mouse messages. Roll up when a click is in a native window
7478 // that doesn't have an nsIWidget.
7479 LRESULT CALLBACK
nsWindow::MozSpecialMouseProc(int code
, WPARAM wParam
,
7482 switch (WinUtils::GetNativeMessage(wParam
)) {
7483 case WM_LBUTTONDOWN
:
7484 case WM_RBUTTONDOWN
:
7485 case WM_MBUTTONDOWN
:
7487 case WM_MOUSEHWHEEL
: {
7488 MOUSEHOOKSTRUCT
* ms
= (MOUSEHOOKSTRUCT
*)lParam
;
7489 nsIWidget
* mozWin
= WinUtils::GetNSWindowPtr(ms
->hwnd
);
7491 ScheduleHookTimer(ms
->hwnd
, (UINT
)wParam
);
7497 return ::CallNextHookEx(sCallMouseHook
, code
, wParam
, lParam
);
7500 // Process all messages. Roll up when the window is moving, or
7501 // is resizing or when maximized or mininized.
7502 LRESULT CALLBACK
nsWindow::MozSpecialWndProc(int code
, WPARAM wParam
,
7504 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7506 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7507 PrintEvent(cwpt
->message
, FALSE
, FALSE
);
7512 CWPSTRUCT
* cwpt
= (CWPSTRUCT
*)lParam
;
7513 if (cwpt
->message
== WM_MOVING
|| cwpt
->message
== WM_SIZING
||
7514 cwpt
->message
== WM_GETMINMAXINFO
) {
7515 ScheduleHookTimer(cwpt
->hwnd
, (UINT
)cwpt
->message
);
7519 return ::CallNextHookEx(sCallProcHook
, code
, wParam
, lParam
);
7522 // Register the special "hooks" for dropdown processing.
7523 void nsWindow::RegisterSpecialDropdownHooks() {
7524 NS_ASSERTION(!sMsgFilterHook
, "sMsgFilterHook must be NULL!");
7525 NS_ASSERTION(!sCallProcHook
, "sCallProcHook must be NULL!");
7527 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7529 // Install msg hook for moving the window and resizing
7530 if (!sMsgFilterHook
) {
7531 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7532 sMsgFilterHook
= SetWindowsHookEx(WH_MSGFILTER
, MozSpecialMsgFilter
,
7533 nullptr, GetCurrentThreadId());
7534 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7535 if (!sMsgFilterHook
) {
7536 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7537 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7542 // Install msg hook for menus
7543 if (!sCallProcHook
) {
7544 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7545 sCallProcHook
= SetWindowsHookEx(WH_CALLWNDPROC
, MozSpecialWndProc
, nullptr,
7546 GetCurrentThreadId());
7547 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7548 if (!sCallProcHook
) {
7550 gWindowsLog
, LogLevel::Info
,
7551 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7556 // Install msg hook for the mouse
7557 if (!sCallMouseHook
) {
7558 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7559 sCallMouseHook
= SetWindowsHookEx(WH_MOUSE
, MozSpecialMouseProc
, nullptr,
7560 GetCurrentThreadId());
7561 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7562 if (!sCallMouseHook
) {
7563 MOZ_LOG(gWindowsLog
, LogLevel::Info
,
7564 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7570 // Unhook special message hooks for dropdowns.
7571 void nsWindow::UnregisterSpecialDropdownHooks() {
7573 "***************** De-installing Msg Hooks ***************\n");
7575 if (sCallProcHook
) {
7576 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7577 if (!::UnhookWindowsHookEx(sCallProcHook
)) {
7578 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7580 sCallProcHook
= nullptr;
7583 if (sMsgFilterHook
) {
7584 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7585 if (!::UnhookWindowsHookEx(sMsgFilterHook
)) {
7586 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7588 sMsgFilterHook
= nullptr;
7591 if (sCallMouseHook
) {
7592 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7593 if (!::UnhookWindowsHookEx(sCallMouseHook
)) {
7594 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7596 sCallMouseHook
= nullptr;
7600 // This timer is designed to only fire one time at most each time a "hook"
7601 // function is used to rollup the dropdown. In some cases, the timer may be
7602 // scheduled from the hook, but that hook event or a subsequent event may roll
7603 // up the dropdown before this timer function is executed.
7605 // For example, if an MFC control takes focus, the combobox will lose focus and
7606 // rollup before this function fires.
7607 VOID CALLBACK
nsWindow::HookTimerForPopups(HWND hwnd
, UINT uMsg
, UINT idEvent
,
7609 if (sHookTimerId
!= 0) {
7610 // if the window is nullptr then we need to use the ID to kill the timer
7611 DebugOnly
<BOOL
> status
= ::KillTimer(nullptr, sHookTimerId
);
7612 NS_ASSERTION(status
, "Hook Timer was not killed.");
7616 if (sRollupMsgId
!= 0) {
7617 // Note: DealWithPopups does the check to make sure that the rollup widget
7619 LRESULT popupHandlingResult
;
7620 nsAutoRollup autoRollup
;
7621 DealWithPopups(sRollupMsgWnd
, sRollupMsgId
, 0, 0, &popupHandlingResult
);
7623 sRollupMsgWnd
= nullptr;
7627 static bool IsDifferentThreadWindow(HWND aWnd
) {
7628 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd
, nullptr);
7632 bool nsWindow::EventIsInsideWindow(nsWindow
* aWindow
,
7633 Maybe
<POINT
> aEventPoint
) {
7635 ::GetWindowRect(aWindow
->mWnd
, &r
);
7640 DWORD pos
= ::GetMessagePos();
7641 mp
.x
= GET_X_LPARAM(pos
);
7642 mp
.y
= GET_Y_LPARAM(pos
);
7645 auto margin
= aWindow
->mInputRegion
.mMargin
;
7653 // was the event inside this window?
7654 return static_cast<bool>(::PtInRect(&r
, mp
));
7658 bool nsWindow::GetPopupsToRollup(nsIRollupListener
* aRollupListener
,
7659 uint32_t* aPopupsToRollup
,
7660 Maybe
<POINT
> aEventPoint
) {
7661 // If we're dealing with menus, we probably have submenus and we don't want
7662 // to rollup some of them if the click is in a parent menu of the current
7664 *aPopupsToRollup
= UINT32_MAX
;
7665 AutoTArray
<nsIWidget
*, 5> widgetChain
;
7666 uint32_t sameTypeCount
= aRollupListener
->GetSubmenuWidgetChain(&widgetChain
);
7667 for (uint32_t i
= 0; i
< widgetChain
.Length(); ++i
) {
7668 nsIWidget
* widget
= widgetChain
[i
];
7669 if (EventIsInsideWindow(static_cast<nsWindow
*>(widget
), aEventPoint
)) {
7670 // Don't roll up if the mouse event occurred within a menu of the
7671 // same type. If the mouse event occurred in a menu higher than that,
7672 // roll up, but pass the number of popups to Rollup so that only those
7673 // of the same type close up.
7674 if (i
< sameTypeCount
) {
7678 *aPopupsToRollup
= sameTypeCount
;
7686 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd
) {
7687 // While popup is open, popup window might be activated by other application.
7688 // At this time, we need to take back focus to the previous window but it
7689 // causes flickering its nonclient area because WM_NCACTIVATE comes before
7690 // WM_ACTIVATE and we cannot know which window will take focus at receiving
7691 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
7693 // If non-popup window receives WM_NCACTIVATE at deactivating, default
7694 // wndproc shouldn't handle it as deactivating. Instead, at receiving
7695 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
7696 // This returns true if the window needs to handle WM_NCACTIVATE later.
7698 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7699 return window
&& !window
->IsPopup();
7702 static bool IsTouchSupportEnabled(HWND aWnd
) {
7703 nsWindow
* topWindow
=
7704 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd
, true));
7705 return topWindow
? topWindow
->IsTouchWindow() : false;
7708 static Maybe
<POINT
> GetSingleTouch(WPARAM wParam
, LPARAM lParam
) {
7710 uint32_t cInputs
= LOWORD(wParam
);
7715 if (GetTouchInputInfo((HTOUCHINPUT
)lParam
, cInputs
, &input
,
7716 sizeof(TOUCHINPUT
))) {
7718 ret
->x
= TOUCH_COORD_TO_PIXEL(input
.x
);
7719 ret
->y
= TOUCH_COORD_TO_PIXEL(input
.y
);
7721 // Note that we don't call CloseTouchInputHandle here because we need
7722 // to read the touch input info again in OnTouch later.
7727 bool nsWindow::DealWithPopups(HWND aWnd
, UINT aMessage
, WPARAM aWParam
,
7728 LPARAM aLParam
, LRESULT
* aResult
) {
7729 NS_ASSERTION(aResult
, "Bad outResult");
7731 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
7732 *aResult
= MA_NOACTIVATE
;
7734 if (!::IsWindowVisible(aWnd
)) {
7738 if (MOZ_UNLIKELY(aMessage
== WM_KILLFOCUS
)) {
7739 // NOTE: We deal with this here rather than on the switch below because we
7740 // want to do this even if there are no menus to rollup (tooltips don't set
7741 // the rollup listener etc).
7742 if (RefPtr pm
= nsXULPopupManager::GetInstance()) {
7743 pm
->RollupTooltips();
7747 nsIRollupListener
* rollupListener
= nsBaseWidget::GetActiveRollupListener();
7748 NS_ENSURE_TRUE(rollupListener
, false);
7750 nsCOMPtr
<nsIWidget
> popup
= rollupListener
->GetRollupWidget();
7755 static bool sSendingNCACTIVATE
= false;
7756 static bool sPendingNCACTIVATE
= false;
7757 uint32_t popupsToRollup
= UINT32_MAX
;
7759 bool consumeRollupEvent
= false;
7760 Maybe
<POINT
> touchPoint
; // In screen coords.
7762 // If we rollup with animations but get occluded right away, we might not
7763 // advance the refresh driver enough for the animation to finish.
7764 auto allowAnimations
= nsIRollupListener::AllowAnimations::Yes
;
7765 nsWindow
* popupWindow
= static_cast<nsWindow
*>(popup
.get());
7766 UINT nativeMessage
= WinUtils::GetNativeMessage(aMessage
);
7767 switch (nativeMessage
) {
7769 if (!IsTouchSupportEnabled(aWnd
)) {
7770 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
7771 // compatibility mouse events will do it instead.
7774 touchPoint
= GetSingleTouch(aWParam
, aLParam
);
7779 case WM_LBUTTONDOWN
:
7780 case WM_RBUTTONDOWN
:
7781 case WM_MBUTTONDOWN
:
7782 case WM_NCLBUTTONDOWN
:
7783 case WM_NCRBUTTONDOWN
:
7784 case WM_NCMBUTTONDOWN
:
7785 if (nativeMessage
!= WM_TOUCH
&& IsTouchSupportEnabled(aWnd
) &&
7786 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH
) {
7787 // If any of these mouse events are really compatibility events that
7788 // Windows is sending for touch inputs, then don't allow them to dismiss
7789 // popups when APZ is enabled (instead we do the dismissing as part of
7790 // WM_TOUCH handling which is more correct).
7791 // If we don't do this, then when the user lifts their finger after a
7792 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
7793 // us will dismiss the contextmenu popup that we displayed as part of
7794 // handling the long-tap-up.
7797 if (!EventIsInsideWindow(popupWindow
, touchPoint
) &&
7798 GetPopupsToRollup(rollupListener
, &popupsToRollup
, touchPoint
)) {
7802 case WM_POINTERDOWN
: {
7803 WinPointerEvents pointerEvents
;
7804 if (!pointerEvents
.ShouldRollupOnPointerEvent(nativeMessage
, aWParam
)) {
7808 pt
.x
= GET_X_LPARAM(aLParam
);
7809 pt
.y
= GET_Y_LPARAM(aLParam
);
7810 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
7813 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
7814 // Don't roll up if the event is inside the popup window.
7818 case MOZ_WM_DMANIP
: {
7820 ::GetCursorPos(&pt
);
7821 if (!GetPopupsToRollup(rollupListener
, &popupsToRollup
, Some(pt
))) {
7824 if (EventIsInsideWindow(popupWindow
, Some(pt
))) {
7825 // Don't roll up if the event is inside the popup window
7830 case WM_MOUSEHWHEEL
:
7831 // We need to check if the popup thinks that it should cause closing
7832 // itself when mouse wheel events are fired outside the rollup widget.
7833 if (!EventIsInsideWindow(popupWindow
)) {
7834 // Check if we should consume this event even if we don't roll-up:
7835 consumeRollupEvent
= rollupListener
->ShouldConsumeOnMouseWheelEvent();
7836 *aResult
= MA_ACTIVATE
;
7837 if (rollupListener
->ShouldRollupOnMouseWheelEvent() &&
7838 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7842 return consumeRollupEvent
;
7844 case WM_ACTIVATEAPP
:
7845 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7849 WndProcUrgentInvocation::Marker _marker
;
7851 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
7852 // because we cannot distinguish it's caused by mouse or not.
7853 if (LOWORD(aWParam
) == WA_ACTIVE
&& aLParam
) {
7854 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7855 if (window
&& window
->IsPopup()) {
7856 // Cancel notifying widget listeners of deactivating the previous
7857 // active window (see WM_KILLFOCUS case in ProcessMessage()).
7858 sJustGotDeactivate
= false;
7859 // Reactivate the window later.
7860 ::PostMessageW(aWnd
, MOZ_WM_REACTIVATE
, aWParam
, aLParam
);
7863 // Don't rollup the popup when focus moves back to the parent window
7864 // from a popup because such case is caused by strange mouse drivers.
7865 nsWindow
* prevWindow
=
7866 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
7867 if (prevWindow
&& prevWindow
->IsPopup()) {
7868 // Consume this message here since previous window must not have
7869 // been inactivated since we've already stopped accepting the
7870 // inactivation below.
7873 } else if (LOWORD(aWParam
) == WA_INACTIVE
) {
7874 nsWindow
* activeWindow
=
7875 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND
>(aLParam
));
7876 if (sPendingNCACTIVATE
&& NeedsToHandleNCActivateDelayed(aWnd
)) {
7877 // If focus moves to non-popup widget or focusable popup, the window
7878 // needs to update its nonclient area.
7879 if (!activeWindow
|| !activeWindow
->IsPopup()) {
7880 sSendingNCACTIVATE
= true;
7881 ::SendMessageW(aWnd
, WM_NCACTIVATE
, false, 0);
7882 sSendingNCACTIVATE
= false;
7884 sPendingNCACTIVATE
= false;
7886 // If focus moves from/to popup, we don't need to rollup the popup
7887 // because such case is caused by strange mouse drivers. And in
7888 // such case, we should consume the message here since we need to
7889 // hide this odd focus move from our content. (If we didn't consume
7890 // the message here, ProcessMessage() will notify widget listener of
7891 // inactivation and that causes unnecessary reflow for supporting
7892 // -moz-window-inactive pseudo class.
7894 if (activeWindow
->IsPopup()) {
7897 nsWindow
* deactiveWindow
= WinUtils::GetNSWindowPtr(aWnd
);
7898 if (deactiveWindow
&& deactiveWindow
->IsPopup()) {
7902 } else if (LOWORD(aWParam
) == WA_CLICKACTIVE
) {
7903 // If the WM_ACTIVATE message is caused by a click in a popup,
7904 // we should not rollup any popups.
7905 nsWindow
* window
= WinUtils::GetNSWindowPtr(aWnd
);
7906 if ((window
&& window
->IsPopup()) ||
7907 !GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7911 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7914 case MOZ_WM_REACTIVATE
:
7915 // The previous active window should take back focus.
7916 if (::IsWindow(reinterpret_cast<HWND
>(aLParam
))) {
7917 // FYI: Even without this API call, you see expected result (e.g., the
7918 // owner window of the popup keeps active without flickering
7919 // the non-client area). And also this causes initializing
7920 // TSF and it causes using CPU time a lot. However, even if we
7921 // consume WM_ACTIVE messages, native focus change has already
7922 // been occurred. I.e., a popup window is active now. Therefore,
7923 // you'll see some odd behavior if we don't reactivate the owner
7924 // window here. For example, if you do:
7925 // 1. Turn wheel on a bookmark panel.
7926 // 2. Turn wheel on another window.
7927 // then, you'll see that the another window becomes active but the
7928 // owner window of the bookmark panel looks still active and the
7929 // bookmark panel keeps open. The reason is that the first wheel
7930 // operation gives focus to the bookmark panel. Therefore, when
7931 // the next operation gives focus to the another window, previous
7932 // focus window is the bookmark panel (i.e., a popup window).
7933 // So, in this case, our hack around here prevents to inactivate
7934 // the owner window and roll up the bookmark panel.
7935 ::SetForegroundWindow(reinterpret_cast<HWND
>(aLParam
));
7940 if (!aWParam
&& !sSendingNCACTIVATE
&&
7941 NeedsToHandleNCActivateDelayed(aWnd
)) {
7942 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
7943 // nonclient area state change.
7944 ::DefWindowProcW(aWnd
, aMessage
, TRUE
, aLParam
);
7945 // Accept the deactivating because it's necessary to receive following
7948 sPendingNCACTIVATE
= true;
7953 case WM_MOUSEACTIVATE
:
7954 if (!EventIsInsideWindow(popupWindow
) &&
7955 GetPopupsToRollup(rollupListener
, &popupsToRollup
)) {
7956 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
7957 // of TweakUI is enabled. Then, check if the popup should be rolled up
7958 // with rollup listener. If not, just consume the message.
7959 if (HIWORD(aLParam
) == WM_MOUSEMOVE
&&
7960 !rollupListener
->ShouldRollupOnMouseActivate()) {
7963 // Otherwise, it should be handled by wndproc.
7967 // Prevent the click inside the popup from causing a change in window
7968 // activation. Since the popup is shown non-activated, we need to eat any
7969 // requests to activate the window while it is displayed. Windows will
7970 // automatically activate the popup on the mousedown otherwise.
7974 // If the window is being minimized, close popups.
7975 if (aLParam
== SW_PARENTCLOSING
) {
7976 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7982 // If focus moves to other window created in different process/thread,
7983 // e.g., a plugin window, popups should be rolled up.
7984 if (IsDifferentThreadWindow(reinterpret_cast<HWND
>(aWParam
))) {
7985 allowAnimations
= nsIRollupListener::AllowAnimations::No
;
7998 // Only need to deal with the last rollup for left mouse down events.
7999 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
8001 nsIRollupListener::RollupOptions rollupOptions
{
8003 nsIRollupListener::FlushViews::Yes
,
8004 /* mPoint = */ nullptr,
8008 if (nativeMessage
== WM_TOUCH
|| nativeMessage
== WM_LBUTTONDOWN
||
8009 nativeMessage
== WM_POINTERDOWN
) {
8010 LayoutDeviceIntPoint pos
;
8011 if (nativeMessage
== WM_TOUCH
) {
8012 pos
.x
= touchPoint
->x
;
8013 pos
.y
= touchPoint
->y
;
8016 pt
.x
= GET_X_LPARAM(aLParam
);
8017 pt
.y
= GET_Y_LPARAM(aLParam
);
8018 // POINTERDOWN is already in screen coords.
8019 if (nativeMessage
== WM_LBUTTONDOWN
) {
8020 ::ClientToScreen(aWnd
, &pt
);
8022 pos
= LayoutDeviceIntPoint(pt
.x
, pt
.y
);
8025 rollupOptions
.mPoint
= &pos
;
8026 nsIContent
* lastRollup
= nullptr;
8027 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
, &lastRollup
);
8028 nsAutoRollup::SetLastRollup(lastRollup
);
8030 consumeRollupEvent
= rollupListener
->Rollup(rollupOptions
);
8033 // Tell hook to stop processing messages
8034 sProcessHook
= false;
8036 sRollupMsgWnd
= nullptr;
8038 // If we are NOT supposed to be consuming events, let it go through
8039 if (consumeRollupEvent
&& nativeMessage
!= WM_RBUTTONDOWN
) {
8040 *aResult
= MA_ACTIVATE
;
8047 /**************************************************************
8048 **************************************************************
8050 ** BLOCK: Misc. utility methods and functions.
8054 **************************************************************
8055 **************************************************************/
8057 // Note that the result of GetTopLevelWindow method can be different from the
8058 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8059 // window. Because our top level window may be contained in another window
8060 // which is not managed by us.
8061 nsWindow
* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup
) {
8062 nsWindow
* curWindow
= this;
8065 if (aStopOnDialogOrPopup
) {
8066 switch (curWindow
->mWindowType
) {
8067 case WindowType::Dialog
:
8068 case WindowType::Popup
:
8075 // Retrieve the top level parent or owner window
8076 nsWindow
* parentWindow
= curWindow
->GetParentWindow(true);
8078 if (!parentWindow
) return curWindow
;
8080 curWindow
= parentWindow
;
8084 // Set a flag if hwnd is a (non-popup) visible window from this process,
8085 // and bail out of the enumeration. Otherwise leave the flag unmodified
8086 // and continue the enumeration.
8087 // lParam must be a bool* pointing at the flag to be set.
8088 static BOOL CALLBACK
EnumVisibleWindowsProc(HWND hwnd
, LPARAM lParam
) {
8090 ::GetWindowThreadProcessId(hwnd
, &pid
);
8091 if (pid
== ::GetCurrentProcessId() && ::IsWindowVisible(hwnd
)) {
8092 // Don't count popups as visible windows, since they don't take focus,
8093 // in case we only have a popup visible (see bug 1554490 where the gfx
8094 // test window is an offscreen popup).
8095 nsWindow
* window
= WinUtils::GetNSWindowPtr(hwnd
);
8096 if (!window
|| !window
->IsPopup()) {
8097 bool* windowsVisible
= reinterpret_cast<bool*>(lParam
);
8098 *windowsVisible
= true;
8105 // Determine if it would be ok to activate a window, taking focus.
8106 // We want to avoid stealing focus from another app (bug 225305).
8107 bool nsWindow::CanTakeFocus() {
8108 HWND fgWnd
= ::GetForegroundWindow();
8110 // There is no foreground window, so don't worry about stealing focus.
8113 // We can take focus if the current foreground window is already from
8116 ::GetWindowThreadProcessId(fgWnd
, &pid
);
8117 if (pid
== ::GetCurrentProcessId()) {
8121 bool windowsVisible
= false;
8122 ::EnumWindows(EnumVisibleWindowsProc
,
8123 reinterpret_cast<LPARAM
>(&windowsVisible
));
8125 if (!windowsVisible
) {
8126 // We're probably creating our first visible window, allow that to
8133 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
8134 static const wchar_t* sMainWindowClass
= nullptr;
8135 if (!sMainWindowClass
) {
8136 nsAutoString className
;
8137 Preferences::GetString("ui.window_class_override", className
);
8138 if (!className
.IsEmpty()) {
8139 sMainWindowClass
= wcsdup(className
.get());
8141 sMainWindowClass
= kClassNameGeneral
;
8144 return sMainWindowClass
;
8147 LPARAM
nsWindow::lParamToScreen(LPARAM lParam
) {
8149 pt
.x
= GET_X_LPARAM(lParam
);
8150 pt
.y
= GET_Y_LPARAM(lParam
);
8151 ::ClientToScreen(mWnd
, &pt
);
8152 return MAKELPARAM(pt
.x
, pt
.y
);
8155 LPARAM
nsWindow::lParamToClient(LPARAM lParam
) {
8157 pt
.x
= GET_X_LPARAM(lParam
);
8158 pt
.y
= GET_Y_LPARAM(lParam
);
8159 ::ScreenToClient(mWnd
, &pt
);
8160 return MAKELPARAM(pt
.x
, pt
.y
);
8163 WPARAM
nsWindow::wParamFromGlobalMouseState() {
8166 if (!!::GetKeyState(VK_CONTROL
)) {
8167 result
|= MK_CONTROL
;
8170 if (!!::GetKeyState(VK_SHIFT
)) {
8174 if (!!::GetKeyState(VK_LBUTTON
)) {
8175 result
|= MK_LBUTTON
;
8178 if (!!::GetKeyState(VK_MBUTTON
)) {
8179 result
|= MK_MBUTTON
;
8182 if (!!::GetKeyState(VK_RBUTTON
)) {
8183 result
|= MK_RBUTTON
;
8186 if (!!::GetKeyState(VK_XBUTTON1
)) {
8187 result
|= MK_XBUTTON1
;
8190 if (!!::GetKeyState(VK_XBUTTON2
)) {
8191 result
|= MK_XBUTTON2
;
8197 void nsWindow::PickerOpen() { mPickerDisplayCount
++; }
8199 void nsWindow::PickerClosed() {
8200 NS_ASSERTION(mPickerDisplayCount
> 0, "mPickerDisplayCount out of sync!");
8201 if (!mPickerDisplayCount
) return;
8202 mPickerDisplayCount
--;
8203 if (!mPickerDisplayCount
&& mDestroyCalled
) {
8208 bool nsWindow::WidgetTypeSupportsAcceleration() {
8209 // We don't currently support using an accelerated layer manager with
8210 // transparent windows so don't even try. I'm also not sure if we even
8211 // want to support this case. See bug 593471.
8213 // Windows' support for transparent accelerated surfaces isn't great.
8214 // Some possible approaches:
8215 // - Readback the data and update it using
8216 // UpdateLayeredWindow/UpdateLayeredWindowIndirect
8217 // This is what WPF does. See
8218 // CD3DDeviceLevel1::PresentWithGDI/CD3DSwapChainWithSwDC in WpfGfx. The
8219 // rationale for not using IDirect3DSurface9::GetDC is explained here:
8220 // https://web.archive.org/web/20160521191104/https://blogs.msdn.microsoft.com/dwayneneed/2008/09/08/transparent-windows-in-wpf/
8221 // - Use D3D11_RESOURCE_MISC_GDI_COMPATIBLE, IDXGISurface1::GetDC(),
8222 // and UpdateLayeredWindowIndirect.
8223 // This is suggested here:
8224 // https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/december/windows-with-c-layered-windows-with-direct2d
8225 // but might have the same problem that IDirect3DSurface9::GetDC has.
8226 // - Creating the window with the WS_EX_NOREDIRECTIONBITMAP flag and use
8227 // DirectComposition.
8228 // Not supported on Win7.
8229 // - Using DwmExtendFrameIntoClientArea with negative margins and something
8230 // to turn off the glass effect.
8231 // This doesn't work when the DWM is not running (Win7)
8233 // Also see bug 1150376, D3D11 composition can cause issues on some devices
8234 // on Windows 7 where presentation fails randomly for windows with drop
8236 return mTransparencyMode
!= TransparencyMode::Transparent
&&
8237 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
8240 bool nsWindow::DispatchTouchEventFromWMPointer(
8241 UINT msg
, LPARAM aLParam
, const WinPointerInfo
& aPointerInfo
,
8242 mozilla::MouseButton aButton
) {
8243 MultiTouchInput::MultiTouchType touchType
;
8245 case WM_POINTERDOWN
:
8246 touchType
= MultiTouchInput::MULTITOUCH_START
;
8248 case WM_POINTERUPDATE
:
8249 if (aPointerInfo
.mPressure
== 0) {
8250 return false; // hover
8252 touchType
= MultiTouchInput::MULTITOUCH_MOVE
;
8255 touchType
= MultiTouchInput::MULTITOUCH_END
;
8261 nsPointWin touchPoint
;
8262 touchPoint
.x
= GET_X_LPARAM(aLParam
);
8263 touchPoint
.y
= GET_Y_LPARAM(aLParam
);
8264 touchPoint
.ScreenToClient(mWnd
);
8266 SingleTouchData
touchData(static_cast<int32_t>(aPointerInfo
.pointerId
),
8267 ScreenIntPoint::FromUnknownPoint(touchPoint
),
8268 ScreenSize(1, 1), // pixel size radius for pen
8269 0.0f
, // no radius rotation
8270 aPointerInfo
.mPressure
);
8271 touchData
.mTiltX
= aPointerInfo
.tiltX
;
8272 touchData
.mTiltY
= aPointerInfo
.tiltY
;
8273 touchData
.mTwist
= aPointerInfo
.twist
;
8275 MultiTouchInput touchInput
;
8276 touchInput
.mType
= touchType
;
8277 touchInput
.mTimeStamp
= GetMessageTimeStamp(::GetMessageTime());
8278 touchInput
.mTouches
.AppendElement(touchData
);
8279 touchInput
.mButton
= aButton
;
8280 touchInput
.mButtons
= aPointerInfo
.mButtons
;
8282 // POINTER_INFO.dwKeyStates can't be used as it only supports Shift and Ctrl
8283 ModifierKeyState modifierKeyState
;
8284 touchInput
.modifiers
= modifierKeyState
.GetModifiers();
8286 DispatchTouchInput(touchInput
, MouseEvent_Binding::MOZ_SOURCE_PEN
);
8290 static MouseButton
PenFlagsToMouseButton(PEN_FLAGS aPenFlags
) {
8291 // Theoretically flags can be set together but they do not
8292 if (aPenFlags
& PEN_FLAG_BARREL
) {
8293 return MouseButton::eSecondary
;
8295 if (aPenFlags
& PEN_FLAG_ERASER
) {
8296 return MouseButton::eEraser
;
8298 return MouseButton::ePrimary
;
8301 bool nsWindow::OnPointerEvents(UINT msg
, WPARAM aWParam
, LPARAM aLParam
) {
8303 // APZ is not available on context menu. Follow the behavior of touch input
8304 // which fallbacks to WM_LBUTTON* and WM_GESTURE, to keep consistency.
8307 if (!mPointerEvents
.ShouldHandleWinPointerMessages(msg
, aWParam
)) {
8310 if (!mPointerEvents
.ShouldFirePointerEventByWinPointerMessages()) {
8311 // We have to handle WM_POINTER* to fetch and cache pen related information
8312 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
8313 // handler. This is because Windows doesn't support ::DoDragDrop in the
8314 // touch or pen message handlers.
8315 mPointerEvents
.ConvertAndCachePointerInfo(msg
, aWParam
);
8316 // Don't consume the Windows WM_POINTER* messages
8320 uint32_t pointerId
= mPointerEvents
.GetPointerId(aWParam
);
8321 POINTER_PEN_INFO penInfo
{};
8322 if (!mPointerEvents
.GetPointerPenInfo(pointerId
, &penInfo
)) {
8326 // When dispatching mouse events with pen, there may be some
8327 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
8328 // small movements. Those events will reset sLastMousePoint and reset
8329 // sLastClickCount. To prevent that, we keep the last pen down position
8330 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
8331 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
8332 // eMouseMove for WM_POINTERUPDATE.
8333 static POINT sLastPointerDownPoint
= {0};
8335 // We don't support chorded buttons for pen. Keep the button at
8337 static mozilla::MouseButton sLastPenDownButton
= MouseButton::ePrimary
;
8338 static bool sPointerDown
= false;
8340 EventMessage message
;
8341 mozilla::MouseButton button
= MouseButton::ePrimary
;
8343 case WM_POINTERDOWN
: {
8344 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8345 GET_Y_LPARAM(aLParam
));
8346 sLastPointerDownPoint
.x
= eventPoint
.x
;
8347 sLastPointerDownPoint
.y
= eventPoint
.y
;
8348 message
= eMouseDown
;
8349 button
= PenFlagsToMouseButton(penInfo
.penFlags
);
8350 sLastPenDownButton
= button
;
8351 sPointerDown
= true;
8355 MOZ_ASSERT(sPointerDown
, "receive WM_POINTERUP w/o WM_POINTERDOWN");
8356 button
= sPointerDown
? sLastPenDownButton
: MouseButton::ePrimary
;
8357 sPointerDown
= false;
8359 case WM_POINTERUPDATE
:
8360 message
= eMouseMove
;
8362 LayoutDeviceIntPoint
eventPoint(GET_X_LPARAM(aLParam
),
8363 GET_Y_LPARAM(aLParam
));
8364 int32_t movementX
= sLastPointerDownPoint
.x
> eventPoint
.x
8365 ? sLastPointerDownPoint
.x
- eventPoint
.x
.value
8366 : eventPoint
.x
.value
- sLastPointerDownPoint
.x
;
8367 int32_t movementY
= sLastPointerDownPoint
.y
> eventPoint
.y
8368 ? sLastPointerDownPoint
.y
- eventPoint
.y
.value
8369 : eventPoint
.y
.value
- sLastPointerDownPoint
.y
;
8370 bool insideMovementThreshold
=
8371 movementX
< (int32_t)::GetSystemMetrics(SM_CXDRAG
) &&
8372 movementY
< (int32_t)::GetSystemMetrics(SM_CYDRAG
);
8374 if (insideMovementThreshold
) {
8375 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
8376 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8379 button
= sLastPenDownButton
;
8382 case WM_POINTERLEAVE
:
8383 message
= eMouseExitFromWidget
;
8389 // Windows defines the pen pressure is normalized to a range between 0 and
8390 // 1024. Convert it to float.
8391 float pressure
= penInfo
.pressure
? (float)penInfo
.pressure
/ 1024 : 0;
8392 int16_t buttons
= sPointerDown
8393 ? nsContentUtils::GetButtonsFlagForButton(button
)
8394 : MouseButtonsFlag::eNoButtons
;
8395 WinPointerInfo
pointerInfo(pointerId
, penInfo
.tiltX
, penInfo
.tiltY
, pressure
,
8398 // https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-pointer_pen_info,
8399 // the rotation is normalized in a range of 0 to 359.
8400 MOZ_ASSERT(penInfo
.rotation
<= 359);
8401 pointerInfo
.twist
= (int32_t)penInfo
.rotation
;
8403 // Fire touch events but not when the barrel button is pressed.
8404 if (button
!= MouseButton::eSecondary
&&
8405 StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
8406 DispatchTouchEventFromWMPointer(msg
, aLParam
, pointerInfo
, button
)) {
8410 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8412 LPARAM newLParam
= lParamToClient(aLParam
);
8413 DispatchMouseEvent(message
, aWParam
, newLParam
, false, button
,
8414 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8416 if (button
== MouseButton::eSecondary
&& message
== eMouseUp
) {
8417 // Fire eContextMenu manually since consuming WM_POINTER* blocks
8419 DispatchMouseEvent(eContextMenu
, aWParam
, newLParam
, false, button
,
8420 MouseEvent_Binding::MOZ_SOURCE_PEN
, &pointerInfo
);
8422 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8427 void nsWindow::GetCompositorWidgetInitData(
8428 mozilla::widget::CompositorWidgetInitData
* aInitData
) {
8429 *aInitData
= WinCompositorWidgetInitData(
8430 reinterpret_cast<uintptr_t>(mWnd
),
8431 reinterpret_cast<uintptr_t>(static_cast<nsIWidget
*>(this)),
8432 mTransparencyMode
, mFrameState
->GetSizeMode());
8435 bool nsWindow::SynchronouslyRepaintOnResize() { return false; }
8437 void nsWindow::MaybeDispatchInitialFocusEvent() {
8438 if (mIsShowingPreXULSkeletonUI
&& ::GetActiveWindow() == mWnd
) {
8439 DispatchFocusToTopLevelWindow(true);
8443 already_AddRefed
<nsIWidget
> nsIWidget::CreateTopLevelWindow() {
8444 nsCOMPtr
<nsIWidget
> window
= new nsWindow();
8445 return window
.forget();
8448 already_AddRefed
<nsIWidget
> nsIWidget::CreateChildWindow() {
8449 nsCOMPtr
<nsIWidget
> window
= new nsWindow(true);
8450 return window
.forget();
8454 bool nsWindow::InitTouchInjection() {
8455 if (!sTouchInjectInitialized
) {
8456 // Initialize touch injection on the first call
8457 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8462 InitializeTouchInjectionPtr func
=
8463 (InitializeTouchInjectionPtr
)GetProcAddress(hMod
,
8464 "InitializeTouchInjection");
8466 WinUtils::Log("InitializeTouchInjection not available.");
8470 if (!func(TOUCH_INJECT_MAX_POINTS
, TOUCH_FEEDBACK_DEFAULT
)) {
8471 WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d",
8476 sInjectTouchFuncPtr
=
8477 (InjectTouchInputPtr
)GetProcAddress(hMod
, "InjectTouchInput");
8478 if (!sInjectTouchFuncPtr
) {
8479 WinUtils::Log("InjectTouchInput not available.");
8482 sTouchInjectInitialized
= true;
8487 bool nsWindow::InjectTouchPoint(uint32_t aId
, LayoutDeviceIntPoint
& aPoint
,
8488 POINTER_FLAGS aFlags
, uint32_t aPressure
,
8489 uint32_t aOrientation
) {
8490 if (aId
> TOUCH_INJECT_MAX_POINTS
) {
8491 WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
8495 POINTER_TOUCH_INFO info
{};
8497 info
.touchFlags
= TOUCH_FLAG_NONE
;
8499 TOUCH_MASK_CONTACTAREA
| TOUCH_MASK_ORIENTATION
| TOUCH_MASK_PRESSURE
;
8500 info
.pressure
= aPressure
;
8501 info
.orientation
= aOrientation
;
8503 info
.pointerInfo
.pointerFlags
= aFlags
;
8504 info
.pointerInfo
.pointerType
= PT_TOUCH
;
8505 info
.pointerInfo
.pointerId
= aId
;
8506 info
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8507 info
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8509 info
.rcContact
.top
= info
.pointerInfo
.ptPixelLocation
.y
- 2;
8510 info
.rcContact
.bottom
= info
.pointerInfo
.ptPixelLocation
.y
+ 2;
8511 info
.rcContact
.left
= info
.pointerInfo
.ptPixelLocation
.x
- 2;
8512 info
.rcContact
.right
= info
.pointerInfo
.ptPixelLocation
.x
+ 2;
8514 for (int i
= 0; i
< 3; i
++) {
8515 if (sInjectTouchFuncPtr(1, &info
)) {
8518 DWORD error
= GetLastError();
8519 if (error
== ERROR_NOT_READY
&& i
< 2) {
8520 // We sent it too quickly after the previous injection (see bug 1535140
8521 // comment 10). On the first loop iteration we just yield (via Sleep(0))
8522 // and try again. If it happens again on the second loop iteration we
8523 // explicitly Sleep(1) and try again. If that doesn't work either we just
8528 WinUtils::Log("InjectTouchInput failure. GetLastError=%d", error
);
8534 void nsWindow::ChangedDPI() {
8535 if (mWidgetListener
) {
8536 if (PresShell
* presShell
= mWidgetListener
->GetPresShell()) {
8537 presShell
->BackingScaleFactorChanged();
8542 static Result
<POINTER_FLAGS
, nsresult
> PointerStateToFlag(
8543 nsWindow::TouchPointerState aPointerState
, bool isUpdate
) {
8544 bool hover
= aPointerState
& nsWindow::TOUCH_HOVER
;
8545 bool contact
= aPointerState
& nsWindow::TOUCH_CONTACT
;
8546 bool remove
= aPointerState
& nsWindow::TOUCH_REMOVE
;
8547 bool cancel
= aPointerState
& nsWindow::TOUCH_CANCEL
;
8549 POINTER_FLAGS flags
;
8551 // We know about this pointer, send an update
8552 flags
= POINTER_FLAG_UPDATE
;
8554 flags
|= POINTER_FLAG_INRANGE
;
8555 } else if (contact
) {
8556 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_INRANGE
;
8557 } else if (remove
) {
8558 flags
= POINTER_FLAG_UP
;
8562 flags
|= POINTER_FLAG_CANCELED
;
8565 // Missing init state, error out
8566 if (remove
|| cancel
) {
8567 return Err(NS_ERROR_INVALID_ARG
);
8570 // Create a new pointer
8571 flags
= POINTER_FLAG_INRANGE
;
8573 flags
|= POINTER_FLAG_INCONTACT
| POINTER_FLAG_DOWN
;
8579 nsresult
nsWindow::SynthesizeNativeTouchPoint(
8580 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8581 LayoutDeviceIntPoint aPoint
, double aPointerPressure
,
8582 uint32_t aPointerOrientation
, nsIObserver
* aObserver
) {
8583 AutoObserverNotifier
notifier(aObserver
, "touchpoint");
8585 if (StaticPrefs::apz_test_fails_with_native_injection() ||
8586 !InitTouchInjection()) {
8587 // If we don't have touch injection from the OS, or if we are running a test
8588 // that cannot properly inject events to satisfy the OS requirements (see
8589 // bug 1313170) we can just fake it and synthesize the events from here.
8590 MOZ_ASSERT(NS_IsMainThread());
8591 if (aPointerState
== TOUCH_HOVER
) {
8592 return NS_ERROR_UNEXPECTED
;
8595 if (!mSynthesizedTouchInput
) {
8596 mSynthesizedTouchInput
= MakeUnique
<MultiTouchInput
>();
8599 WidgetEventTime time
= CurrentMessageWidgetEventTime();
8600 LayoutDeviceIntPoint pointInWindow
= aPoint
- WidgetToScreenOffset();
8601 MultiTouchInput inputToDispatch
= UpdateSynthesizedTouchState(
8602 mSynthesizedTouchInput
.get(), time
.mTimeStamp
, aPointerId
,
8603 aPointerState
, pointInWindow
, aPointerPressure
, aPointerOrientation
);
8604 DispatchTouchInput(inputToDispatch
);
8608 // win api expects a value from 0 to 1024. aPointerPressure is a value
8610 uint32_t pressure
= (uint32_t)ceil(aPointerPressure
* 1024);
8612 // If we already know about this pointer id get it's record
8613 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8614 POINTER_FLAGS flags
;
8615 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8616 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8617 if (result
.isOk()) {
8618 flags
= result
.unwrap();
8620 return result
.unwrapErr();
8624 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8625 PointerInfo::PointerType::TOUCH
));
8627 if (entry
.Data()->mType
!= PointerInfo::PointerType::TOUCH
) {
8628 return NS_ERROR_UNEXPECTED
;
8630 if (aPointerState
& TOUCH_REMOVE
) {
8631 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8632 // so shouldn't leak.
8637 return !InjectTouchPoint(aPointerId
, aPoint
, flags
, pressure
,
8638 aPointerOrientation
)
8639 ? NS_ERROR_UNEXPECTED
8644 nsresult
nsWindow::ClearNativeTouchSequence(nsIObserver
* aObserver
) {
8645 AutoObserverNotifier
notifier(aObserver
, "cleartouch");
8646 if (!sTouchInjectInitialized
) {
8650 // cancel all input points
8651 for (auto iter
= mActivePointers
.Iter(); !iter
.Done(); iter
.Next()) {
8652 auto* info
= iter
.UserData();
8653 if (info
->mType
!= PointerInfo::PointerType::TOUCH
) {
8656 InjectTouchPoint(info
->mPointerId
, info
->mPosition
, POINTER_FLAG_CANCELED
);
8660 nsBaseWidget::ClearNativeTouchSequence(nullptr);
8665 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8666 static CreateSyntheticPointerDevicePtr CreateSyntheticPointerDevice
;
8667 static DestroySyntheticPointerDevicePtr DestroySyntheticPointerDevice
;
8668 static InjectSyntheticPointerInputPtr InjectSyntheticPointerInput
;
8670 static HSYNTHETICPOINTERDEVICE sSyntheticPenDevice
;
8672 static bool InitPenInjection() {
8673 if (sSyntheticPenDevice
) {
8676 #if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
8677 HMODULE hMod
= LoadLibraryW(kUser32LibName
);
8681 CreateSyntheticPointerDevice
=
8682 (CreateSyntheticPointerDevicePtr
)GetProcAddress(
8683 hMod
, "CreateSyntheticPointerDevice");
8684 if (!CreateSyntheticPointerDevice
) {
8685 WinUtils::Log("CreateSyntheticPointerDevice not available.");
8688 DestroySyntheticPointerDevice
=
8689 (DestroySyntheticPointerDevicePtr
)GetProcAddress(
8690 hMod
, "DestroySyntheticPointerDevice");
8691 if (!DestroySyntheticPointerDevice
) {
8692 WinUtils::Log("DestroySyntheticPointerDevice not available.");
8695 InjectSyntheticPointerInput
= (InjectSyntheticPointerInputPtr
)GetProcAddress(
8696 hMod
, "InjectSyntheticPointerInput");
8697 if (!InjectSyntheticPointerInput
) {
8698 WinUtils::Log("InjectSyntheticPointerInput not available.");
8702 sSyntheticPenDevice
=
8703 CreateSyntheticPointerDevice(PT_PEN
, 1, POINTER_FEEDBACK_DEFAULT
);
8704 return !!sSyntheticPenDevice
;
8707 nsresult
nsWindow::SynthesizeNativePenInput(
8708 uint32_t aPointerId
, nsIWidget::TouchPointerState aPointerState
,
8709 LayoutDeviceIntPoint aPoint
, double aPressure
, uint32_t aRotation
,
8710 int32_t aTiltX
, int32_t aTiltY
, int32_t aButton
, nsIObserver
* aObserver
) {
8711 AutoObserverNotifier
notifier(aObserver
, "peninput");
8712 if (!InitPenInjection()) {
8713 return NS_ERROR_UNEXPECTED
;
8716 // win api expects a value from 0 to 1024. aPointerPressure is a value
8718 uint32_t pressure
= (uint32_t)ceil(aPressure
* 1024);
8720 // If we already know about this pointer id get it's record
8721 return mActivePointers
.WithEntryHandle(aPointerId
, [&](auto&& entry
) {
8722 POINTER_FLAGS flags
;
8723 // Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
8724 auto result
= PointerStateToFlag(aPointerState
, !!entry
);
8725 if (result
.isOk()) {
8726 flags
= result
.unwrap();
8728 return result
.unwrapErr();
8732 entry
.Insert(MakeUnique
<PointerInfo
>(aPointerId
, aPoint
,
8733 PointerInfo::PointerType::PEN
));
8735 if (entry
.Data()->mType
!= PointerInfo::PointerType::PEN
) {
8736 return NS_ERROR_UNEXPECTED
;
8738 if (aPointerState
& TOUCH_REMOVE
) {
8739 // Remove the pointer from our tracking list. This is UniquePtr wrapped,
8740 // so shouldn't leak.
8745 POINTER_TYPE_INFO info
{};
8748 info
.penInfo
.pointerInfo
.pointerType
= PT_PEN
;
8749 info
.penInfo
.pointerInfo
.pointerFlags
= flags
;
8750 info
.penInfo
.pointerInfo
.pointerId
= aPointerId
;
8751 info
.penInfo
.pointerInfo
.ptPixelLocation
.x
= aPoint
.x
;
8752 info
.penInfo
.pointerInfo
.ptPixelLocation
.y
= aPoint
.y
;
8754 info
.penInfo
.penFlags
= PEN_FLAG_NONE
;
8755 // PEN_FLAG_ERASER is not supported this way, unfortunately.
8757 info
.penInfo
.penFlags
|= PEN_FLAG_BARREL
;
8759 info
.penInfo
.penMask
= PEN_MASK_PRESSURE
| PEN_MASK_ROTATION
|
8760 PEN_MASK_TILT_X
| PEN_MASK_TILT_Y
;
8761 info
.penInfo
.pressure
= pressure
;
8762 info
.penInfo
.rotation
= aRotation
;
8763 info
.penInfo
.tiltX
= aTiltX
;
8764 info
.penInfo
.tiltY
= aTiltY
;
8766 return InjectSyntheticPointerInput(sSyntheticPenDevice
, &info
, 1)
8768 : NS_ERROR_UNEXPECTED
;
8772 bool nsWindow::HandleAppCommandMsg(const MSG
& aAppCommandMsg
,
8773 LRESULT
* aRetValue
) {
8774 ModifierKeyState modKeyState
;
8775 NativeKey
nativeKey(this, aAppCommandMsg
, modKeyState
);
8776 bool consumed
= nativeKey
.HandleAppCommandMessage();
8777 *aRetValue
= consumed
? 1 : 0;
8782 nsresult
nsWindow::SetHiDPIMode(bool aHiDPI
) {
8783 return WinUtils::SetHiDPIMode(aHiDPI
);
8786 nsresult
nsWindow::RestoreHiDPIMode() { return WinUtils::RestoreHiDPIMode(); }
8789 mozilla::Maybe
<UINT
> nsWindow::GetHiddenTaskbarEdge() {
8790 HMONITOR windowMonitor
= ::MonitorFromWindow(mWnd
, MONITOR_DEFAULTTONEAREST
);
8792 // Check all four sides of our monitor for an appbar. Skip any that aren't
8793 // the system taskbar.
8795 mi
.cbSize
= sizeof(MONITORINFO
);
8796 ::GetMonitorInfo(windowMonitor
, &mi
);
8798 APPBARDATA appBarData
;
8799 appBarData
.cbSize
= sizeof(appBarData
);
8800 appBarData
.rc
= mi
.rcMonitor
;
8801 const auto kEdges
= {ABE_BOTTOM
, ABE_TOP
, ABE_LEFT
, ABE_RIGHT
};
8802 for (auto edge
: kEdges
) {
8803 appBarData
.uEdge
= edge
;
8804 HWND appBarHwnd
= (HWND
)SHAppBarMessage(ABM_GETAUTOHIDEBAREX
, &appBarData
);
8806 nsAutoString className
;
8807 if (WinUtils::GetClassName(appBarHwnd
, className
)) {
8808 if (className
.Equals(L
"Shell_TrayWnd") ||
8809 className
.Equals(L
"Shell_SecondaryTrayWnd")) {
8819 static nsSizeMode
GetSizeModeForWindowFrame(HWND aWnd
, bool aFullscreenMode
) {
8821 pl
.length
= sizeof(pl
);
8822 ::GetWindowPlacement(aWnd
, &pl
);
8824 if (pl
.showCmd
== SW_SHOWMINIMIZED
) {
8825 return nsSizeMode_Minimized
;
8826 } else if (aFullscreenMode
) {
8827 return nsSizeMode_Fullscreen
;
8828 } else if (pl
.showCmd
== SW_SHOWMAXIMIZED
) {
8829 return nsSizeMode_Maximized
;
8831 return nsSizeMode_Normal
;
8835 static void ShowWindowWithMode(HWND aWnd
, nsSizeMode aMode
) {
8836 // This will likely cause a callback to
8837 // nsWindow::FrameState::{OnFrameChanging() and OnFrameChanged()}
8839 case nsSizeMode_Fullscreen
:
8840 ::ShowWindow(aWnd
, SW_SHOW
);
8843 case nsSizeMode_Maximized
:
8844 ::ShowWindow(aWnd
, SW_MAXIMIZE
);
8847 case nsSizeMode_Minimized
:
8848 ::ShowWindow(aWnd
, SW_MINIMIZE
);
8852 // Don't call ::ShowWindow if we're trying to "restore" a window that is
8853 // already in a normal state. Prevents a bug where snapping to one side
8854 // of the screen and then minimizing would cause Windows to forget our
8855 // window's correct restored position/size.
8856 if (GetCurrentShowCmd(aWnd
) != SW_SHOWNORMAL
) {
8857 ::ShowWindow(aWnd
, SW_RESTORE
);
8862 nsWindow::FrameState::FrameState(nsWindow
* aWindow
) : mWindow(aWindow
) {}
8864 nsSizeMode
nsWindow::FrameState::GetSizeMode() const { return mSizeMode
; }
8866 void nsWindow::FrameState::CheckInvariant() const {
8867 MOZ_ASSERT(mSizeMode
>= 0 && mSizeMode
< nsSizeMode_Invalid
);
8868 MOZ_ASSERT(mLastSizeMode
>= 0 && mLastSizeMode
< nsSizeMode_Invalid
);
8869 MOZ_ASSERT(mPreFullscreenSizeMode
>= 0 &&
8870 mPreFullscreenSizeMode
< nsSizeMode_Invalid
);
8871 MOZ_ASSERT(mWindow
);
8873 // We should never observe fullscreen sizemode unless fullscreen is enabled
8874 MOZ_ASSERT_IF(mSizeMode
== nsSizeMode_Fullscreen
, mFullscreenMode
);
8875 MOZ_ASSERT_IF(!mFullscreenMode
, mSizeMode
!= nsSizeMode_Fullscreen
);
8877 // Something went wrong if we somehow saved fullscreen mode when we are
8878 // changing into fullscreen mode
8879 MOZ_ASSERT(mPreFullscreenSizeMode
!= nsSizeMode_Fullscreen
);
8882 void nsWindow::FrameState::ConsumePreXULSkeletonState(bool aWasMaximized
) {
8883 mSizeMode
= aWasMaximized
? nsSizeMode_Maximized
: nsSizeMode_Normal
;
8886 void nsWindow::FrameState::EnsureSizeMode(nsSizeMode aMode
,
8887 DoShowWindow aDoShowWindow
) {
8888 if (mSizeMode
== aMode
) {
8892 if (StaticPrefs::widget_windows_fullscreen_remind_taskbar()) {
8893 // If we're unminimizing a window, asynchronously notify the taskbar after
8894 // the message has been processed. This redundant notification works around
8895 // a race condition in explorer.exe. (See bug 1835851, or comments in
8896 // TaskbarConcealer.)
8898 // Note that we notify regardless of `aMode`: unminimizing a non-fullscreen
8899 // window can also affect the correct taskbar state, yet fail to affect the
8900 // current taskbar state.
8901 if (mSizeMode
== nsSizeMode_Minimized
) {
8902 ::PostMessage(mWindow
->mWnd
, MOZ_WM_FULLSCREEN_STATE_UPDATE
, 0, 0);
8906 if (aMode
== nsSizeMode_Fullscreen
) {
8907 EnsureFullscreenMode(true, aDoShowWindow
);
8908 MOZ_ASSERT(mSizeMode
== nsSizeMode_Fullscreen
);
8909 } else if (mSizeMode
== nsSizeMode_Fullscreen
&& aMode
== nsSizeMode_Normal
) {
8910 // If we are in fullscreen mode, minimize should work like normal and
8911 // return us to fullscreen mode when unminimized. Maximize isn't really
8912 // available and won't do anything. "Restore" should do the same thing as
8913 // requesting to end fullscreen.
8914 EnsureFullscreenMode(false, aDoShowWindow
);
8916 SetSizeModeInternal(aMode
, aDoShowWindow
);
8920 void nsWindow::FrameState::EnsureFullscreenMode(bool aFullScreen
,
8921 DoShowWindow aDoShowWindow
) {
8922 const bool changed
= aFullScreen
!= mFullscreenMode
;
8923 if (changed
&& aFullScreen
) {
8924 // Save the size mode from before fullscreen.
8925 mPreFullscreenSizeMode
= mSizeMode
;
8927 mFullscreenMode
= aFullScreen
;
8928 if (changed
|| aFullScreen
) {
8929 // NOTE(emilio): When minimizing a fullscreen window we remain with
8930 // mFullscreenMode = true, but mSizeMode = nsSizeMode_Minimized. We need to
8931 // make sure to call SetSizeModeInternal even if mFullscreenMode didn't
8932 // change, to ensure we actually end up with a fullscreen sizemode when
8933 // restoring a window from that state.
8934 SetSizeModeInternal(
8935 aFullScreen
? nsSizeMode_Fullscreen
: mPreFullscreenSizeMode
,
8940 void nsWindow::FrameState::OnFrameChanging() {
8941 const nsSizeMode newSizeMode
=
8942 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
8943 EnsureSizeMode(newSizeMode
);
8944 mWindow
->UpdateNonClientMargins(false);
8947 void nsWindow::FrameState::OnFrameChanged() {
8948 // We don't want to perform the ShowWindow ourselves if we're on the frame
8949 // changed message. Windows has done the frame change for us, and we take care
8950 // of activating as needed. We also don't want to potentially trigger
8951 // more focus / restore. Among other things, this addresses a bug on Win7
8952 // related to window docking. (bug 489258)
8953 const auto newSizeMode
=
8954 GetSizeModeForWindowFrame(mWindow
->mWnd
, mFullscreenMode
);
8955 EnsureSizeMode(newSizeMode
, DoShowWindow::No
);
8957 // If window was restored, activate the window now to get correct attributes.
8958 if (mWindow
->mIsVisible
&& mWindow
->IsForegroundWindow() &&
8959 mLastSizeMode
== nsSizeMode_Minimized
&&
8960 mSizeMode
!= nsSizeMode_Minimized
) {
8961 mWindow
->DispatchFocusToTopLevelWindow(true);
8963 mLastSizeMode
= mSizeMode
;
8966 static void MaybeLogSizeMode(nsSizeMode aMode
) {
8967 #ifdef WINSTATE_DEBUG_OUTPUT
8968 MOZ_LOG(gWindowsLog
, LogLevel::Info
, ("*** SizeMode: %d\n", int(aMode
)));
8972 void nsWindow::FrameState::SetSizeModeInternal(nsSizeMode aMode
,
8973 DoShowWindow aDoShowWindow
) {
8974 if (mSizeMode
== aMode
) {
8978 const auto oldSizeMode
= mSizeMode
;
8979 const bool fullscreenChange
=
8980 mSizeMode
== nsSizeMode_Fullscreen
|| aMode
== nsSizeMode_Fullscreen
;
8981 const bool fullscreen
= aMode
== nsSizeMode_Fullscreen
;
8983 mLastSizeMode
= mSizeMode
;
8986 MaybeLogSizeMode(mSizeMode
);
8988 if (bool(aDoShowWindow
) && mWindow
->mIsVisible
) {
8989 ShowWindowWithMode(mWindow
->mWnd
, aMode
);
8992 mWindow
->UpdateNonClientMargins(false);
8994 if (fullscreenChange
) {
8995 mWindow
->OnFullscreenChanged(oldSizeMode
, fullscreen
);
8998 mWindow
->OnSizeModeChange();
9001 void nsWindow::ContextMenuPreventer::Update(
9002 const WidgetMouseEvent
& aEvent
,
9003 const nsIWidget::ContentAndAPZEventStatus
& aEventStatus
) {
9004 mNeedsToPreventContextMenu
=
9005 aEvent
.mMessage
== eMouseUp
&&
9006 aEvent
.mButton
== MouseButton::eSecondary
&&
9007 aEvent
.mInputSource
== MouseEvent_Binding::MOZ_SOURCE_MOUSE
&&
9008 aEventStatus
.mApzStatus
== nsEventStatus_eConsumeNoDefault
;