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