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/. */
6
7 /*
8 * nsWindow - Native window management and event handling.
9 *
10 * nsWindow is organized into a set of major blocks and
11 * block subsections. The layout is as follows:
12 *
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.
31 *
32 * Search for "BLOCK:" to find major blocks.
33 * Search for "SECTION:" to find specific sections.
34 *
35 * Blocks should be split out into separate files if they
36 * become unmanageable.
37 *
38 * Related source:
39 *
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.
44 *
45 */
46
47 /**************************************************************
48 **************************************************************
49 **
50 ** BLOCK: Includes
51 **
52 ** Include headers.
53 **
54 **************************************************************
55 **************************************************************/
56
57 #include "gfx2DGlue.h"
58 #include "gfxEnv.h"
59 #include "gfxPlatform.h"
60
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/ScopeExit.h"
69 #include "mozilla/TouchEvents.h"
70 #include "mozilla/TimeStamp.h"
71
72 #include "mozilla/ipc/MessageChannel.h"
73 #include <algorithm>
74 #include <limits>
75
76 #include "nsWindow.h"
77 #include "nsAppRunner.h"
78
79 #include <shellapi.h>
80 #include <windows.h>
81 #include <wtsapi32.h>
82 #include <process.h>
83 #include <commctrl.h>
84 #include <dbt.h>
85 #include <unknwn.h>
86 #include <psapi.h>
87 #include <rpc.h>
88
89 #include "mozilla/Logging.h"
90 #include "prtime.h"
91 #include "prenv.h"
92
93 #include "mozilla/WidgetTraceEvent.h"
94 #include "nsContentUtils.h"
95 #include "nsISupportsPrimitives.h"
96 #include "nsITheme.h"
97 #include "nsIObserverService.h"
98 #include "nsIScreenManager.h"
99 #include "imgIContainer.h"
100 #include "nsIFile.h"
101 #include "nsIRollupListener.h"
102 #include "nsIClipboard.h"
103 #include "WinMouseScrollHandler.h"
104 #include "nsFontMetrics.h"
105 #include "nsIFontEnumerator.h"
106 #include "nsFont.h"
107 #include "nsRect.h"
108 #include "nsThreadUtils.h"
109 #include "nsNativeCharsetUtils.h"
110 #include "nsGkAtoms.h"
111 #include "nsCRT.h"
112 #include "nsAppDirectoryServiceDefs.h"
113 #include "nsWidgetsCID.h"
114 #include "nsTHashtable.h"
115 #include "nsHashKeys.h"
116 #include "nsString.h"
117 #include "mozilla/Components.h"
118 #include "nsNativeThemeWin.h"
119 #include "nsWindowsDllInterceptor.h"
120 #include "nsLayoutUtils.h"
121 #include "nsView.h"
122 #include "nsWindowGfx.h"
123 #include "gfxWindowsPlatform.h"
124 #include "gfxDWriteFonts.h"
125 #include "Layers.h"
126 #include "nsPrintfCString.h"
127 #include "mozilla/Preferences.h"
128 #include "SystemTimeConverter.h"
129 #include "WinTaskbar.h"
130 #include "WidgetUtils.h"
131 #include "WinContentSystemParameters.h"
132 #include "nsIWidgetListener.h"
133 #include "mozilla/dom/Document.h"
134 #include "mozilla/dom/MouseEventBinding.h"
135 #include "mozilla/dom/Touch.h"
136 #include "mozilla/gfx/2D.h"
137 #include "mozilla/gfx/GPUProcessManager.h"
138 #include "mozilla/intl/LocaleService.h"
139 #include "mozilla/WindowsVersion.h"
140 #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
141 #include "mozilla/TextEventDispatcherListener.h"
142 #include "mozilla/widget/nsAutoRollup.h"
143 #include "mozilla/widget/PlatformWidgetTypes.h"
144 #include "nsStyleConsts.h"
145 #include "nsBidiKeyboard.h"
146 #include "nsStyleConsts.h"
147 #include "gfxConfig.h"
148 #include "InProcessWinCompositorWidget.h"
149 #include "InputDeviceUtils.h"
150 #include "ScreenHelperWin.h"
151 #include "mozilla/StaticPrefs_apz.h"
152 #include "mozilla/StaticPrefs_dom.h"
153 #include "mozilla/StaticPrefs_gfx.h"
154 #include "mozilla/StaticPrefs_layout.h"
155
156 #include "nsIGfxInfo.h"
157 #include "nsUXThemeConstants.h"
158 #include "KeyboardLayout.h"
159 #include "nsNativeDragTarget.h"
160 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
161 #include <zmouse.h>
162 #include <richedit.h>
163
164 #if defined(ACCESSIBILITY)
165
166 # ifdef DEBUG
167 # include "mozilla/a11y/Logging.h"
168 # endif
169
170 # include "oleidl.h"
171 # include <winuser.h>
172 # include "nsAccessibilityService.h"
173 # include "mozilla/PresShell.h"
174 # include "mozilla/a11y/DocAccessible.h"
175 # include "mozilla/a11y/LazyInstantiator.h"
176 # include "mozilla/a11y/Platform.h"
177 # if !defined(WINABLEAPI)
178 # include <winable.h>
179 # endif // !defined(WINABLEAPI)
180 #endif // defined(ACCESSIBILITY)
181
182 #include "nsIWinTaskbar.h"
183 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
184
185 #include "nsIWindowsUIUtils.h"
186
187 #include "nsWindowDefs.h"
188
189 #include "nsCrashOnException.h"
190
191 #include "nsIContent.h"
192
193 #include "mozilla/BackgroundHangMonitor.h"
194 #include "WinIMEHandler.h"
195
196 #include "npapi.h"
197
198 #include <d3d11.h>
199
200 #include "InkCollector.h"
201
202 // ERROR from wingdi.h (below) gets undefined by some code.
203 // #define ERROR 0
204 // #define RGN_ERROR ERROR
205 #define ERROR 0
206
207 #if !defined(SM_CONVERTIBLESLATEMODE)
208 # define SM_CONVERTIBLESLATEMODE 0x2003
209 #endif
210
211 #if !defined(WM_DPICHANGED)
212 # define WM_DPICHANGED 0x02E0
213 #endif
214
215 #include "mozilla/gfx/DeviceManagerDx.h"
216 #include "mozilla/layers/APZInputBridge.h"
217 #include "mozilla/layers/InputAPZContext.h"
218 #include "mozilla/layers/KnowsCompositor.h"
219 #include "InputData.h"
220
221 #include "mozilla/TaskController.h"
222 #include "mozilla/Telemetry.h"
223 #include "mozilla/webrender/WebRenderAPI.h"
224 #include "mozilla/layers/IAPZCTreeManager.h"
225
226 #include "DirectManipulationOwner.h"
227
228 using namespace mozilla;
229 using namespace mozilla::dom;
230 using namespace mozilla::gfx;
231 using namespace mozilla::layers;
232 using namespace mozilla::widget;
233 using namespace mozilla::plugins;
234
235 /**************************************************************
236 **************************************************************
237 **
238 ** BLOCK: Variables
239 **
240 ** nsWindow Class static initializations and global variables.
241 **
242 **************************************************************
243 **************************************************************/
244
245 /**************************************************************
246 *
247 * SECTION: nsWindow statics
248 *
249 **************************************************************/
250
251 bool nsWindow::sDropShadowEnabled = true;
252 uint32_t nsWindow::sInstanceCount = 0;
253 bool nsWindow::sSwitchKeyboardLayout = false;
254 BOOL nsWindow::sIsOleInitialized = FALSE;
255 HCURSOR nsWindow::sCustomHCursor = nullptr;
256 nsIWidget::Cursor nsWindow::sCurrentCursor = {};
257 nsWindow* nsWindow::sCurrentWindow = nullptr;
258 bool nsWindow::sJustGotDeactivate = false;
259 bool nsWindow::sJustGotActivate = false;
260 bool nsWindow::sIsInMouseCapture = false;
261
262 // imported in nsWidgetFactory.cpp
263 TriStateBool nsWindow::sCanQuit = TRI_UNKNOWN;
264
265 // Hook Data Memebers for Dropdowns. sProcessHook Tells the
266 // hook methods whether they should be processing the hook
267 // messages.
268 HHOOK nsWindow::sMsgFilterHook = nullptr;
269 HHOOK nsWindow::sCallProcHook = nullptr;
270 HHOOK nsWindow::sCallMouseHook = nullptr;
271 bool nsWindow::sProcessHook = false;
272 UINT nsWindow::sRollupMsgId = 0;
273 HWND nsWindow::sRollupMsgWnd = nullptr;
274 UINT nsWindow::sHookTimerId = 0;
275
276 // Mouse Clicks - static variable definitions for figuring
277 // out 1 - 3 Clicks.
278 POINT nsWindow::sLastMousePoint = {0};
279 POINT nsWindow::sLastMouseMovePoint = {0};
280 LONG nsWindow::sLastMouseDownTime = 0L;
281 LONG nsWindow::sLastClickCount = 0L;
282 BYTE nsWindow::sLastMouseButton = 0;
283
284 bool nsWindow::sHaveInitializedPrefs = false;
285 bool nsWindow::sIsRestoringSession = false;
286
287 bool nsWindow::sFirstTopLevelWindowCreated = false;
288
289 TriStateBool nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor = TRI_UNKNOWN;
290
TimeConverter()291 static SystemTimeConverter<DWORD>& TimeConverter() {
292 static SystemTimeConverter<DWORD> timeConverterSingleton;
293 return timeConverterSingleton;
294 }
295
296 namespace mozilla {
297
298 class CurrentWindowsTimeGetter {
299 public:
CurrentWindowsTimeGetter(HWND aWnd)300 explicit CurrentWindowsTimeGetter(HWND aWnd) : mWnd(aWnd) {}
301
GetCurrentTime() const302 DWORD GetCurrentTime() const { return ::GetTickCount(); }
303
GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp & aNow)304 void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow) {
305 DWORD currentTime = GetCurrentTime();
306 if (sBackwardsSkewStamp && currentTime == sLastPostTime) {
307 // There's already one inflight with this timestamp. Don't
308 // send a duplicate.
309 return;
310 }
311 sBackwardsSkewStamp = Some(aNow);
312 sLastPostTime = currentTime;
313 static_assert(sizeof(WPARAM) >= sizeof(DWORD),
314 "Can't fit a DWORD in a WPARAM");
315 ::PostMessage(mWnd, MOZ_WM_SKEWFIX, sLastPostTime, 0);
316 }
317
GetAndClearBackwardsSkewStamp(DWORD aPostTime,TimeStamp * aOutSkewStamp)318 static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime,
319 TimeStamp* aOutSkewStamp) {
320 if (aPostTime != sLastPostTime) {
321 // The SKEWFIX message is stale; we've sent a new one since then.
322 // Ignore this one.
323 return false;
324 }
325 MOZ_ASSERT(sBackwardsSkewStamp);
326 *aOutSkewStamp = sBackwardsSkewStamp.value();
327 sBackwardsSkewStamp = Nothing();
328 return true;
329 }
330
331 private:
332 static Maybe<TimeStamp> sBackwardsSkewStamp;
333 static DWORD sLastPostTime;
334 HWND mWnd;
335 };
336
337 Maybe<TimeStamp> CurrentWindowsTimeGetter::sBackwardsSkewStamp;
338 DWORD CurrentWindowsTimeGetter::sLastPostTime = 0;
339
340 } // namespace mozilla
341
342 /**************************************************************
343 *
344 * SECTION: globals variables
345 *
346 **************************************************************/
347
348 static const char* sScreenManagerContractID =
349 "@mozilla.org/gfx/screenmanager;1";
350
351 extern mozilla::LazyLogModule gWindowsLog;
352
353 // True if we have sent a notification that we are suspending/sleeping.
354 static bool gIsSleepMode = false;
355
356 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
357
358 // General purpose user32.dll hook object
359 static WindowsDllInterceptor sUser32Intercept;
360
361 // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
362 // the default window border Windows paints. Glass will be extended inward
363 // this distance to remove the border.
364 static const int32_t kGlassMarginAdjustment = 2;
365
366 // When the client area is extended out into the default window frame area,
367 // this is the minimum amount of space along the edge of resizable windows
368 // we will always display a resize cursor in, regardless of the underlying
369 // content.
370 static const int32_t kResizableBorderMinSize = 3;
371
372 // Getting this object from the window server can be expensive. Keep it
373 // around, also get it off the main thread. (See bug 1640852)
374 StaticRefPtr<IVirtualDesktopManager> gVirtualDesktopManager;
375 static bool gInitializedVirtualDesktopManager = false;
376
377 // We should never really try to accelerate windows bigger than this. In some
378 // cases this might lead to no D3D9 acceleration where we could have had it
379 // but D3D9 does not reliably report when it supports bigger windows. 8192
380 // is as safe as we can get, we know at least D3D10 hardware always supports
381 // this, other hardware we expect to report correctly in D3D9.
382 #define MAX_ACCELERATED_DIMENSION 8192
383
384 // On window open (as well as after), Windows has an unfortunate habit of
385 // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
386 // to DOM target conversions for these, we cache responses for a given
387 // coordinate this many milliseconds:
388 #define HITTEST_CACHE_LIFETIME_MS 50
389
390 #if defined(ACCESSIBILITY)
391
392 namespace mozilla {
393
394 /**
395 * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
396 * injecting tiptsf.dll. The touchscreen process then posts registered messages
397 * to our main thread. The tiptsf hook picks up those registered messages and
398 * uses them as commands, some of which call into UIA, which then calls into
399 * MSAA, which then sends WM_GETOBJECT to us.
400 *
401 * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
402 * hook. Since thread-local hooks are called ahead of global hooks, we will
403 * see these registered messages before tiptsf does. At this point we can then
404 * raise a flag that blocks a11y before invoking CallNextHookEx which will then
405 * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
406 * flag by calling TIPMessageHandler::IsA11yBlocked().
407 *
408 * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
409 * function that also calls into UIA.
410 */
411 class TIPMessageHandler {
412 public:
~TIPMessageHandler()413 ~TIPMessageHandler() {
414 if (mHook) {
415 ::UnhookWindowsHookEx(mHook);
416 }
417 }
418
Initialize()419 static void Initialize() {
420 if (!IsWin8OrLater()) {
421 return;
422 }
423
424 if (sInstance) {
425 return;
426 }
427
428 sInstance = new TIPMessageHandler();
429 ClearOnShutdown(&sInstance);
430 }
431
IsA11yBlocked()432 static bool IsA11yBlocked() {
433 if (!sInstance) {
434 return false;
435 }
436
437 return sInstance->mA11yBlockCount > 0;
438 }
439
440 private:
TIPMessageHandler()441 TIPMessageHandler() : mHook(nullptr), mA11yBlockCount(0) {
442 MOZ_ASSERT(NS_IsMainThread());
443
444 // Registered messages used by tiptsf
445 mMessages[0] = ::RegisterWindowMessage(L"ImmersiveFocusNotification");
446 mMessages[1] = ::RegisterWindowMessage(L"TipCloseMenus");
447 mMessages[2] = ::RegisterWindowMessage(L"TabletInputPanelOpening");
448 mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed");
449 mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility");
450 mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden");
451 mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo");
452
453 mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr,
454 ::GetCurrentThreadId());
455 MOZ_ASSERT(mHook);
456
457 // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
458 // first initialized.
459 if (!IsWin10OrLater() && GetModuleHandle(L"tiptsf.dll") &&
460 !sProcessCaretEventsStub) {
461 sTipTsfInterceptor.Init("tiptsf.dll");
462 DebugOnly<bool> ok = sProcessCaretEventsStub.Set(
463 sTipTsfInterceptor, "ProcessCaretEvents", &ProcessCaretEventsHook);
464 MOZ_ASSERT(ok);
465 }
466
467 if (!sSendMessageTimeoutWStub) {
468 sUser32Intercept.Init("user32.dll");
469 DebugOnly<bool> hooked = sSendMessageTimeoutWStub.Set(
470 sUser32Intercept, "SendMessageTimeoutW", &SendMessageTimeoutWHook);
471 MOZ_ASSERT(hooked);
472 }
473 }
474
475 class MOZ_RAII A11yInstantiationBlocker {
476 public:
A11yInstantiationBlocker()477 A11yInstantiationBlocker() {
478 if (!TIPMessageHandler::sInstance) {
479 return;
480 }
481 ++TIPMessageHandler::sInstance->mA11yBlockCount;
482 }
483
~A11yInstantiationBlocker()484 ~A11yInstantiationBlocker() {
485 if (!TIPMessageHandler::sInstance) {
486 return;
487 }
488 MOZ_ASSERT(TIPMessageHandler::sInstance->mA11yBlockCount > 0);
489 --TIPMessageHandler::sInstance->mA11yBlockCount;
490 }
491 };
492
493 friend class A11yInstantiationBlocker;
494
TIPHook(int aCode,WPARAM aWParam,LPARAM aLParam)495 static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam) {
496 if (aCode < 0 || !sInstance) {
497 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
498 }
499
500 MSG* msg = reinterpret_cast<MSG*>(aLParam);
501 UINT& msgCode = msg->message;
502
503 for (uint32_t i = 0; i < ArrayLength(sInstance->mMessages); ++i) {
504 if (msgCode == sInstance->mMessages[i]) {
505 A11yInstantiationBlocker block;
506 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
507 }
508 }
509
510 return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
511 }
512
ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,DWORD aEvent,HWND aHwnd,LONG aObjectId,LONG aChildId,DWORD aGeneratingTid,DWORD aEventTime)513 static void CALLBACK ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,
514 DWORD aEvent, HWND aHwnd,
515 LONG aObjectId, LONG aChildId,
516 DWORD aGeneratingTid,
517 DWORD aEventTime) {
518 A11yInstantiationBlocker block;
519 sProcessCaretEventsStub(aWinEventHook, aEvent, aHwnd, aObjectId, aChildId,
520 aGeneratingTid, aEventTime);
521 }
522
SendMessageTimeoutWHook(HWND aHwnd,UINT aMsgCode,WPARAM aWParam,LPARAM aLParam,UINT aFlags,UINT aTimeout,PDWORD_PTR aMsgResult)523 static LRESULT WINAPI SendMessageTimeoutWHook(HWND aHwnd, UINT aMsgCode,
524 WPARAM aWParam, LPARAM aLParam,
525 UINT aFlags, UINT aTimeout,
526 PDWORD_PTR aMsgResult) {
527 // We don't want to handle this unless the message is a WM_GETOBJECT that we
528 // want to block, and the aHwnd is a nsWindow that belongs to the current
529 // thread.
530 if (!aMsgResult || aMsgCode != WM_GETOBJECT ||
531 static_cast<DWORD>(aLParam) != OBJID_CLIENT ||
532 !WinUtils::GetNSWindowPtr(aHwnd) ||
533 ::GetWindowThreadProcessId(aHwnd, nullptr) != ::GetCurrentThreadId() ||
534 !IsA11yBlocked()) {
535 return sSendMessageTimeoutWStub(aHwnd, aMsgCode, aWParam, aLParam, aFlags,
536 aTimeout, aMsgResult);
537 }
538
539 // In this case we want to fake the result that would happen if we had
540 // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
541 // off to DefWindowProc to accomplish this.
542 *aMsgResult = static_cast<DWORD_PTR>(
543 ::DefWindowProcW(aHwnd, aMsgCode, aWParam, aLParam));
544
545 return static_cast<LRESULT>(TRUE);
546 }
547
548 static WindowsDllInterceptor sTipTsfInterceptor;
549 static WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
550 sProcessCaretEventsStub;
551 static WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
552 sSendMessageTimeoutWStub;
553 static StaticAutoPtr<TIPMessageHandler> sInstance;
554
555 HHOOK mHook;
556 UINT mMessages[7];
557 uint32_t mA11yBlockCount;
558 };
559
560 WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
561 WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
562 TIPMessageHandler::sProcessCaretEventsStub;
563 WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
564 TIPMessageHandler::sSendMessageTimeoutWStub;
565 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
566
567 } // namespace mozilla
568
569 #endif // defined(ACCESSIBILITY)
570
571 namespace mozilla {
572
573 // This task will get the VirtualDesktopManager from the generic thread pool
574 // since doing this on the main thread on startup causes performance issues.
575 //
576 // See bug 1640852.
577 //
578 // This should be fine and should not require any locking, as when the main
579 // thread will access it, if it races with this function it will either find
580 // it to be null or to have a valid value.
581 class InitializeVirtualDesktopManagerTask : public Task {
582 public:
InitializeVirtualDesktopManagerTask()583 InitializeVirtualDesktopManagerTask() : Task(false, kDefaultPriorityValue) {}
584
Run()585 virtual bool Run() override {
586 #ifndef __MINGW32__
587 if (!IsWin10OrLater()) {
588 return true;
589 }
590
591 RefPtr<IVirtualDesktopManager> desktopManager;
592 HRESULT hr = ::CoCreateInstance(
593 CLSID_VirtualDesktopManager, NULL, CLSCTX_INPROC_SERVER,
594 __uuidof(IVirtualDesktopManager), getter_AddRefs(desktopManager));
595 if (FAILED(hr)) {
596 return true;
597 }
598
599 gVirtualDesktopManager = desktopManager;
600 #endif
601 return true;
602 }
603 };
604
605 } // namespace mozilla
606
607 /**************************************************************
608 **************************************************************
609 **
610 ** BLOCK: nsIWidget impl.
611 **
612 ** nsIWidget interface implementation, broken down into
613 ** sections.
614 **
615 **************************************************************
616 **************************************************************/
617
618 /**************************************************************
619 *
620 * SECTION: nsWindow construction and destruction
621 *
622 **************************************************************/
623
nsWindow(bool aIsChildWindow)624 nsWindow::nsWindow(bool aIsChildWindow)
625 : nsWindowBase(),
626 mResizeState(NOT_RESIZING),
627 mIsChildWindow(aIsChildWindow) {
628 if (!gInitializedVirtualDesktopManager) {
629 TaskController::Get()->AddTask(
630 MakeAndAddRef<InitializeVirtualDesktopManagerTask>());
631 gInitializedVirtualDesktopManager = true;
632 }
633
634 mIconSmall = nullptr;
635 mIconBig = nullptr;
636 mWnd = nullptr;
637 mLastKillFocusWindow = nullptr;
638 mTransitionWnd = nullptr;
639 mPaintDC = nullptr;
640 mPrevWndProc = nullptr;
641 mNativeDragTarget = nullptr;
642 mDeviceNotifyHandle = nullptr;
643 mInDtor = false;
644 mIsVisible = false;
645 mIsTopWidgetWindow = false;
646 mDisplayPanFeedback = false;
647 mTouchWindow = false;
648 mFutureMarginsToUse = false;
649 mCustomNonClient = false;
650 mHideChrome = false;
651 mFullscreenMode = false;
652 mMousePresent = false;
653 mMouseInDraggableArea = false;
654 mDestroyCalled = false;
655 mIsEarlyBlankWindow = false;
656 mIsShowingPreXULSkeletonUI = false;
657 mResizable = false;
658 mHasTaskbarIconBeenCreated = false;
659 mMouseTransparent = false;
660 mPickerDisplayCount = 0;
661 mWindowType = eWindowType_child;
662 mBorderStyle = eBorderStyle_default;
663 mOldSizeMode = nsSizeMode_Normal;
664 mLastSizeMode = nsSizeMode_Normal;
665 mLastSize.width = 0;
666 mLastSize.height = 0;
667 mOldStyle = 0;
668 mOldExStyle = 0;
669 mPainting = 0;
670 mLastKeyboardLayout = 0;
671 mLastPaintEndTime = TimeStamp::Now();
672 mCachedHitTestPoint.x = 0;
673 mCachedHitTestPoint.y = 0;
674 mCachedHitTestTime = TimeStamp::Now();
675 mCachedHitTestResult = 0;
676 #ifdef MOZ_XUL
677 mTransparencyMode = eTransparencyOpaque;
678 memset(&mGlassMargins, 0, sizeof mGlassMargins);
679 #endif
680 DWORD background = ::GetSysColor(COLOR_BTNFACE);
681 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
682 mSendingSetText = false;
683 mDefaultScale = -1.0; // not yet set, will be calculated on first use
684 mAspectRatio = 0.0; // not yet set, will be calculated on first use
685
686 mTaskbarPreview = nullptr;
687
688 mCompositorWidgetDelegate = nullptr;
689
690 // Global initialization
691 if (!sInstanceCount) {
692 // Global app registration id for Win7 and up. See
693 // WinTaskbar.cpp for details.
694 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
695 KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
696 #if defined(ACCESSIBILITY)
697 mozilla::TIPMessageHandler::Initialize();
698 #endif // defined(ACCESSIBILITY)
699 if (SUCCEEDED(::OleInitialize(nullptr))) {
700 sIsOleInitialized = TRUE;
701 }
702 NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
703 MouseScrollHandler::Initialize();
704 // Init theme data
705 nsUXThemeData::UpdateNativeThemeInfo();
706 RedirectedKeyDownMessageManager::Forget();
707 if (mPointerEvents.ShouldEnableInkCollector()) {
708 InkCollector::sInkCollector = new InkCollector();
709 }
710 } // !sInstanceCount
711
712 mIdleService = nullptr;
713
714 mSizeConstraintsScale = GetDefaultScale().scale;
715 mMaxTextureSize = -1; // Will be calculated when layer manager is created.
716
717 mRequestFxrOutputPending = false;
718
719 sInstanceCount++;
720 }
721
~nsWindow()722 nsWindow::~nsWindow() {
723 mInDtor = true;
724
725 // If the widget was released without calling Destroy() then the native window
726 // still exists, and we need to destroy it. Destroy() will early-return if it
727 // was already called. In any case it is important to call it before
728 // destroying mPresentLock (cf. 1156182).
729 Destroy();
730
731 // Free app icon resources. This must happen after `OnDestroy` (see bug
732 // 708033).
733 if (mIconSmall) ::DestroyIcon(mIconSmall);
734
735 if (mIconBig) ::DestroyIcon(mIconBig);
736
737 sInstanceCount--;
738
739 // Global shutdown
740 if (sInstanceCount == 0) {
741 if (InkCollector::sInkCollector) {
742 InkCollector::sInkCollector->Shutdown();
743 InkCollector::sInkCollector = nullptr;
744 }
745 IMEHandler::Terminate();
746 sCurrentCursor = {};
747 if (sIsOleInitialized) {
748 ::OleFlushClipboard();
749 ::OleUninitialize();
750 sIsOleInitialized = FALSE;
751 }
752 }
753
754 NS_IF_RELEASE(mNativeDragTarget);
755 }
756
757 /**************************************************************
758 *
759 * SECTION: nsIWidget::Create, nsIWidget::Destroy
760 *
761 * Creating and destroying windows for this widget.
762 *
763 **************************************************************/
764
765 // Allow Derived classes to modify the height that is passed
766 // when the window is created or resized.
GetHeight(int32_t aProposedHeight)767 int32_t nsWindow::GetHeight(int32_t aProposedHeight) { return aProposedHeight; }
768
ShouldCacheTitleBarInfo(nsWindowType aWindowType,nsBorderStyle aBorderStyle)769 static bool ShouldCacheTitleBarInfo(nsWindowType aWindowType,
770 nsBorderStyle aBorderStyle) {
771 return (aWindowType == eWindowType_toplevel) &&
772 (aBorderStyle == eBorderStyle_default ||
773 aBorderStyle == eBorderStyle_all) &&
774 (!nsUXThemeData::sTitlebarInfoPopulatedThemed ||
775 !nsUXThemeData::sTitlebarInfoPopulatedAero);
776 }
777
SendAnAPZEvent(InputData & aEvent)778 void nsWindow::SendAnAPZEvent(InputData& aEvent) {
779 LRESULT popupHandlingResult;
780 if (DealWithPopups(mWnd, MOZ_WM_DMANIP, 0, 0, &popupHandlingResult)) {
781 // We need to consume the event after using it to roll up the popup(s).
782 return;
783 }
784
785 APZEventResult result;
786 if (mAPZC) {
787 result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
788 }
789 if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
790 return;
791 }
792
793 MOZ_ASSERT(aEvent.mInputType == PANGESTURE_INPUT ||
794 aEvent.mInputType == PINCHGESTURE_INPUT);
795
796 if (aEvent.mInputType == PANGESTURE_INPUT) {
797 PanGestureInput& panInput = aEvent.AsPanGestureInput();
798 WidgetWheelEvent event = panInput.ToWidgetEvent(this);
799 ProcessUntransformedAPZEvent(&event, result);
800
801 return;
802 }
803
804 PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
805 WidgetWheelEvent event = pinchInput.ToWidgetEvent(this);
806 ProcessUntransformedAPZEvent(&event, result);
807 }
808
RecreateDirectManipulationIfNeeded()809 void nsWindow::RecreateDirectManipulationIfNeeded() {
810 DestroyDirectManipulation();
811
812 if (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_popup) {
813 return;
814 }
815
816 if (!(StaticPrefs::apz_allow_zooming() ||
817 StaticPrefs::apz_windows_use_direct_manipulation()) ||
818 StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
819 return;
820 }
821
822 if (!IsWin10OrLater()) {
823 // Chrome source said the Windows Direct Manipulation implementation had
824 // important bugs until Windows 10 (although IE on Windows 8.1 seems to use
825 // Direct Manipulation).
826 return;
827 }
828
829 mDmOwner = MakeUnique<DirectManipulationOwner>(this);
830
831 LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
832 GetHeight(mBounds.Height()));
833 mDmOwner->Init(bounds);
834 }
835
ResizeDirectManipulationViewport()836 void nsWindow::ResizeDirectManipulationViewport() {
837 if (mDmOwner) {
838 LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
839 GetHeight(mBounds.Height()));
840 mDmOwner->ResizeViewport(bounds);
841 }
842 }
843
DestroyDirectManipulation()844 void nsWindow::DestroyDirectManipulation() {
845 if (mDmOwner) {
846 mDmOwner->Destroy();
847 mDmOwner.reset();
848 }
849 }
850
851 // Create the proper widget
Create(nsIWidget * aParent,nsNativeWidget aNativeParent,const LayoutDeviceIntRect & aRect,nsWidgetInitData * aInitData)852 nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
853 const LayoutDeviceIntRect& aRect,
854 nsWidgetInitData* aInitData) {
855 nsWidgetInitData defaultInitData;
856 if (!aInitData) aInitData = &defaultInitData;
857
858 nsIWidget* baseParent =
859 aInitData->mWindowType == eWindowType_dialog ||
860 aInitData->mWindowType == eWindowType_toplevel ||
861 aInitData->mWindowType == eWindowType_invisible
862 ? nullptr
863 : aParent;
864
865 mIsTopWidgetWindow = (nullptr == baseParent);
866 mBounds = aRect;
867
868 // Ensure that the toolkit is created.
869 nsToolkit::GetToolkit();
870
871 BaseCreate(baseParent, aInitData);
872
873 HWND parent;
874 if (aParent) { // has a nsIWidget parent
875 parent = aParent ? (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
876 mParent = aParent;
877 } else { // has a nsNative parent
878 parent = (HWND)aNativeParent;
879 mParent =
880 aNativeParent ? WinUtils::GetNSWindowPtr((HWND)aNativeParent) : nullptr;
881 }
882
883 mIsRTL = aInitData->mRTL;
884 mOpeningAnimationSuppressed = aInitData->mIsAnimationSuppressed;
885 mAlwaysOnTop = aInitData->mAlwaysOnTop;
886 mResizable = aInitData->mResizable;
887
888 DWORD style = WindowStyle();
889 DWORD extendedStyle = WindowExStyle();
890
891 // When window is PiP window on Windows7, WS_EX_COMPOSITED is set to suppress
892 // flickering during resizing with hardware acceleration.
893 bool isPIPWindow = aInitData && aInitData->mPIPWindow;
894 if (isPIPWindow && !IsWin8OrLater() &&
895 gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
896 WidgetTypeSupportsAcceleration()) {
897 extendedStyle |= WS_EX_COMPOSITED;
898 }
899
900 if (mWindowType == eWindowType_popup) {
901 if (!aParent) {
902 parent = nullptr;
903 }
904
905 if (!IsWin8OrLater() && HasBogusPopupsDropShadowOnMultiMonitor() &&
906 ShouldUseOffMainThreadCompositing()) {
907 extendedStyle |= WS_EX_COMPOSITED;
908 }
909
910 if (aInitData->mMouseTransparent) {
911 // This flag makes the window transparent to mouse events
912 mMouseTransparent = true;
913 extendedStyle |= WS_EX_TRANSPARENT;
914 }
915 } else if (mWindowType == eWindowType_invisible) {
916 // Make sure CreateWindowEx succeeds at creating a toplevel window
917 style &= ~0x40000000; // WS_CHILDWINDOW
918 } else {
919 // See if the caller wants to explictly set clip children and clip siblings
920 if (aInitData->clipChildren) {
921 style |= WS_CLIPCHILDREN;
922 } else {
923 style &= ~WS_CLIPCHILDREN;
924 }
925 if (aInitData->clipSiblings) {
926 style |= WS_CLIPSIBLINGS;
927 }
928 }
929
930 const wchar_t* className;
931 if (aInitData->mDropShadow) {
932 className = GetWindowPopupClass();
933 } else {
934 className = GetWindowClass();
935 }
936 // Plugins are created in the disabled state so that they can't
937 // steal focus away from our main window. This is especially
938 // important if the plugin has loaded in a background tab.
939 if (aInitData->mWindowType == eWindowType_plugin ||
940 aInitData->mWindowType == eWindowType_plugin_ipc_chrome ||
941 aInitData->mWindowType == eWindowType_plugin_ipc_content) {
942 style |= WS_DISABLED;
943 }
944
945 if (aInitData->mWindowType == eWindowType_toplevel && !aParent &&
946 !sFirstTopLevelWindowCreated) {
947 sFirstTopLevelWindowCreated = true;
948 mWnd = ConsumePreXULSkeletonUIHandle();
949 auto skeletonUIError = GetPreXULSkeletonUIErrorReason();
950 if (skeletonUIError) {
951 nsAutoString errorString(
952 GetPreXULSkeletonUIErrorString(skeletonUIError.value()));
953 Telemetry::ScalarSet(
954 Telemetry::ScalarID::STARTUP_SKELETON_UI_DISABLED_REASON,
955 errorString);
956 }
957 if (mWnd) {
958 MOZ_ASSERT(style == kPreXULSkeletonUIWindowStyle,
959 "The skeleton UI window style should match the expected "
960 "style for the first window created");
961 MOZ_ASSERT(extendedStyle == kPreXULSkeletonUIWindowStyleEx,
962 "The skeleton UI window extended style should match the "
963 "expected extended style for the first window created");
964 mIsShowingPreXULSkeletonUI = true;
965
966 // If we successfully consumed the pre-XUL skeleton UI, just update
967 // our internal state to match what is currently being displayed.
968 mIsVisible = true;
969 mSizeMode = WasPreXULSkeletonUIMaximized() ? nsSizeMode_Maximized
970 : nsSizeMode_Normal;
971
972 // These match the margins set in browser-tabsintitlebar.js with
973 // default prefs on Windows. Bug 1673092 tracks lining this up with
974 // that more correctly instead of hard-coding it.
975 LayoutDeviceIntMargin margins(0, 2, 2, 2);
976 SetNonClientMargins(margins);
977
978 // Reset the WNDPROC for this window and its whole class, as we had
979 // to use our own WNDPROC when creating the the skeleton UI window.
980 ::SetWindowLongPtrW(mWnd, GWLP_WNDPROC,
981 reinterpret_cast<LONG_PTR>(
982 WinUtils::NonClientDpiScalingDefWindowProcW));
983 ::SetClassLongPtrW(mWnd, GCLP_WNDPROC,
984 reinterpret_cast<LONG_PTR>(
985 WinUtils::NonClientDpiScalingDefWindowProcW));
986 }
987 }
988
989 if (!mWnd) {
990 mWnd =
991 ::CreateWindowExW(extendedStyle, className, L"", style, aRect.X(),
992 aRect.Y(), aRect.Width(), GetHeight(aRect.Height()),
993 parent, nullptr, nsToolkit::mDllInstance, nullptr);
994 }
995
996 if (!mWnd) {
997 NS_WARNING("nsWindow CreateWindowEx failed.");
998 return NS_ERROR_FAILURE;
999 }
1000
1001 mDeviceNotifyHandle = InputDeviceUtils::RegisterNotification(mWnd);
1002
1003 // If mDefaultScale is set before mWnd has been set, it will have the scale of
1004 // the primary monitor, rather than the monitor that the window is actually
1005 // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
1006 // which resets mDefaultScale, but for popup windows we don't reset
1007 // mDefaultScale on that message. In order to ensure that popup windows
1008 // spawned on a non-primary monitor end up with the correct scale, we reset
1009 // mDefaultScale here so that it gets recomputed using the correct monitor now
1010 // that we have a mWnd.
1011 mDefaultScale = -1.0;
1012
1013 if (mIsRTL) {
1014 DWORD dwAttribute = TRUE;
1015 DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute,
1016 sizeof dwAttribute);
1017 }
1018
1019 if (mOpeningAnimationSuppressed) {
1020 SuppressAnimation(true);
1021 }
1022
1023 if (mAlwaysOnTop) {
1024 ::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0,
1025 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1026 }
1027
1028 if (!IsPlugin() && mWindowType != eWindowType_invisible &&
1029 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
1030 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
1031 //
1032 // We create two zero-sized windows as descendants of the top-level window,
1033 // like so:
1034 //
1035 // Top-level window (MozillaWindowClass)
1036 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
1037 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
1038 //
1039 // We need to have the middle window, otherwise the Trackpoint driver
1040 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
1041 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
1042 // window hierarchy until they are handled by nsWindow::WindowProc.
1043 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
1044 // but these do not propagate automatically, so we have the window
1045 // procedure pretend that they were dispatched to the top-level window
1046 // instead.
1047 //
1048 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
1049 // is given below so that it catches the Trackpoint driver's heuristics.
1050 HWND scrollContainerWnd = ::CreateWindowW(
1051 className, L"FAKETRACKPOINTSCROLLCONTAINER", WS_CHILD | WS_VISIBLE, 0,
1052 0, 0, 0, mWnd, nullptr, nsToolkit::mDllInstance, nullptr);
1053 HWND scrollableWnd = ::CreateWindowW(
1054 className, L"FAKETRACKPOINTSCROLLABLE",
1055 WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | 0x30, 0, 0, 0, 0,
1056 scrollContainerWnd, nullptr, nsToolkit::mDllInstance, nullptr);
1057
1058 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
1059 // WindowProcInternal can distinguish it from the top-level window
1060 // easily.
1061 ::SetWindowLongPtrW(scrollableWnd, GWLP_ID, eFakeTrackPointScrollableID);
1062
1063 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
1064 // old window procedure in its "user data".
1065 WNDPROC oldWndProc = (WNDPROC)::SetWindowLongPtrW(
1066 scrollableWnd, GWLP_WNDPROC, (LONG_PTR)nsWindow::WindowProc);
1067 ::SetWindowLongPtrW(scrollableWnd, GWLP_USERDATA, (LONG_PTR)oldWndProc);
1068 }
1069
1070 SubclassWindow(TRUE);
1071
1072 // Starting with Windows XP, a process always runs within a terminal services
1073 // session. In order to play nicely with RDP, fast user switching, and the
1074 // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
1075 // our HWND in order to receive this message.
1076 DebugOnly<BOOL> wtsRegistered =
1077 ::WTSRegisterSessionNotification(mWnd, NOTIFY_FOR_THIS_SESSION);
1078 NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n");
1079
1080 mDefaultIMC.Init(this);
1081 IMEHandler::InitInputContext(this, mInputContext);
1082
1083 // Do some initialization work, but only if (a) it hasn't already been done,
1084 // and (b) this is the hidden window (which is conveniently created before
1085 // any visible windows but after the profile has been initialized).
1086 if (!sHaveInitializedPrefs && mWindowType == eWindowType_invisible) {
1087 sSwitchKeyboardLayout =
1088 Preferences::GetBool("intl.keyboard.per_window_layout", false);
1089 sHaveInitializedPrefs = true;
1090 }
1091
1092 // Query for command button metric data for rendering the titlebar. We
1093 // only do this once on the first window that has an actual titlebar
1094 if (ShouldCacheTitleBarInfo(mWindowType, mBorderStyle)) {
1095 nsUXThemeData::UpdateTitlebarInfo(mWnd);
1096 }
1097
1098 static bool a11yPrimed = false;
1099 if (!a11yPrimed && mWindowType == eWindowType_toplevel) {
1100 a11yPrimed = true;
1101 if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
1102 ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0);
1103 }
1104 }
1105
1106 RecreateDirectManipulationIfNeeded();
1107
1108 return NS_OK;
1109 }
1110
LocalesChanged()1111 void nsWindow::LocalesChanged() {
1112 bool isRTL = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
1113 if (mIsRTL != isRTL) {
1114 DWORD dwAttribute = isRTL;
1115 DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute,
1116 sizeof dwAttribute);
1117 mIsRTL = isRTL;
1118 }
1119 }
1120
1121 // Close this nsWindow
Destroy()1122 void nsWindow::Destroy() {
1123 // WM_DESTROY has already fired, avoid calling it twice
1124 if (mOnDestroyCalled) return;
1125
1126 // Don't destroy windows that have file pickers open, we'll tear these down
1127 // later once the picker is closed.
1128 mDestroyCalled = true;
1129 if (mPickerDisplayCount) return;
1130
1131 // During the destruction of all of our children, make sure we don't get
1132 // deleted.
1133 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
1134
1135 DestroyDirectManipulation();
1136
1137 /**
1138 * On windows the LayerManagerOGL destructor wants the widget to be around for
1139 * cleanup. It also would like to have the HWND intact, so we nullptr it here.
1140 */
1141 DestroyLayerManager();
1142
1143 /* We should clear our cached resources now and not wait for the GC to
1144 * delete the nsWindow. */
1145 ClearCachedResources();
1146
1147 InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle);
1148 mDeviceNotifyHandle = nullptr;
1149
1150 // The DestroyWindow function destroys the specified window. The function
1151 // sends WM_DESTROY and WM_NCDESTROY messages to the window to deactivate it
1152 // and remove the keyboard focus from it. The function also destroys the
1153 // window's menu, flushes the thread message queue, destroys timers, removes
1154 // clipboard ownership, and breaks the clipboard viewer chain (if the window
1155 // is at the top of the viewer chain).
1156 //
1157 // If the specified window is a parent or owner window, DestroyWindow
1158 // automatically destroys the associated child or owned windows when it
1159 // destroys the parent or owner window. The function first destroys child or
1160 // owned windows, and then it destroys the parent or owner window.
1161 VERIFY(::DestroyWindow(mWnd));
1162
1163 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If
1164 // OnDestroy() didn't get called, call it now.
1165 if (false == mOnDestroyCalled) {
1166 MSGResult msgResult;
1167 mWindowHook.Notify(mWnd, WM_DESTROY, 0, 0, msgResult);
1168 OnDestroy();
1169 }
1170 }
1171
1172 /**************************************************************
1173 *
1174 * SECTION: Window class utilities
1175 *
1176 * Utilities for calculating the proper window class name for
1177 * Create window.
1178 *
1179 **************************************************************/
1180
RegisterWindowClass(const wchar_t * aClassName,UINT aExtraStyle,LPWSTR aIconID) const1181 const wchar_t* nsWindow::RegisterWindowClass(const wchar_t* aClassName,
1182 UINT aExtraStyle,
1183 LPWSTR aIconID) const {
1184 WNDCLASSW wc;
1185 if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) {
1186 // already registered
1187 return aClassName;
1188 }
1189
1190 wc.style = CS_DBLCLKS | aExtraStyle;
1191 wc.lpfnWndProc = WinUtils::NonClientDpiScalingDefWindowProcW;
1192 wc.cbClsExtra = 0;
1193 wc.cbWndExtra = 0;
1194 wc.hInstance = nsToolkit::mDllInstance;
1195 wc.hIcon =
1196 aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr;
1197 wc.hCursor = nullptr;
1198 wc.hbrBackground = mBrush;
1199 wc.lpszMenuName = nullptr;
1200 wc.lpszClassName = aClassName;
1201
1202 if (!::RegisterClassW(&wc)) {
1203 // For older versions of Win32 (i.e., not XP), the registration may
1204 // fail with aExtraStyle, so we have to re-register without it.
1205 wc.style = CS_DBLCLKS;
1206 ::RegisterClassW(&wc);
1207 }
1208 return aClassName;
1209 }
1210
1211 static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);
1212
1213 // Return the proper window class for everything except popups.
GetWindowClass() const1214 const wchar_t* nsWindow::GetWindowClass() const {
1215 switch (mWindowType) {
1216 case eWindowType_invisible:
1217 return RegisterWindowClass(kClassNameHidden, 0, gStockApplicationIcon);
1218 case eWindowType_dialog:
1219 return RegisterWindowClass(kClassNameDialog, 0, 0);
1220 default:
1221 return RegisterWindowClass(GetMainWindowClass(), 0,
1222 gStockApplicationIcon);
1223 }
1224 }
1225
1226 // Return the proper popup window class
GetWindowPopupClass() const1227 const wchar_t* nsWindow::GetWindowPopupClass() const {
1228 return RegisterWindowClass(kClassNameDropShadow, CS_XP_DROPSHADOW,
1229 gStockApplicationIcon);
1230 }
1231
1232 /**************************************************************
1233 *
1234 * SECTION: Window styles utilities
1235 *
1236 * Return the proper windows styles and extended styles.
1237 *
1238 **************************************************************/
1239
1240 // Return nsWindow styles
WindowStyle()1241 DWORD nsWindow::WindowStyle() {
1242 DWORD style;
1243
1244 switch (mWindowType) {
1245 case eWindowType_plugin:
1246 case eWindowType_plugin_ipc_chrome:
1247 case eWindowType_plugin_ipc_content:
1248 case eWindowType_child:
1249 style = WS_OVERLAPPED;
1250 break;
1251
1252 case eWindowType_dialog:
1253 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK |
1254 DS_MODALFRAME | WS_CLIPCHILDREN;
1255 if (mBorderStyle != eBorderStyle_default)
1256 style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
1257 break;
1258
1259 case eWindowType_popup:
1260 style = WS_POPUP;
1261 if (!HasGlass()) {
1262 style |= WS_OVERLAPPED;
1263 }
1264 break;
1265
1266 default:
1267 NS_ERROR("unknown border style");
1268 // fall through
1269
1270 case eWindowType_toplevel:
1271 case eWindowType_invisible:
1272 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU |
1273 WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN;
1274 break;
1275 }
1276
1277 if (mBorderStyle != eBorderStyle_default &&
1278 mBorderStyle != eBorderStyle_all) {
1279 if (mBorderStyle == eBorderStyle_none ||
1280 !(mBorderStyle & eBorderStyle_border))
1281 style &= ~WS_BORDER;
1282
1283 if (mBorderStyle == eBorderStyle_none ||
1284 !(mBorderStyle & eBorderStyle_title)) {
1285 style &= ~WS_DLGFRAME;
1286 style |= WS_POPUP;
1287 style &= ~WS_CHILD;
1288 }
1289
1290 if (mBorderStyle == eBorderStyle_none ||
1291 !(mBorderStyle & eBorderStyle_close))
1292 style &= ~0;
1293 // XXX The close box can only be removed by changing the window class,
1294 // as far as I know --- roc+moz@cs.cmu.edu
1295
1296 if (mBorderStyle == eBorderStyle_none ||
1297 !(mBorderStyle & (eBorderStyle_menu | eBorderStyle_close)))
1298 style &= ~WS_SYSMENU;
1299 // Looks like getting rid of the system menu also does away with the
1300 // close box. So, we only get rid of the system menu if you want neither it
1301 // nor the close box. How does the Windows "Dialog" window class get just
1302 // closebox and no sysmenu? Who knows.
1303
1304 if (mBorderStyle == eBorderStyle_none ||
1305 !(mBorderStyle & eBorderStyle_resizeh))
1306 style &= ~WS_THICKFRAME;
1307
1308 if (mBorderStyle == eBorderStyle_none ||
1309 !(mBorderStyle & eBorderStyle_minimize))
1310 style &= ~WS_MINIMIZEBOX;
1311
1312 if (mBorderStyle == eBorderStyle_none ||
1313 !(mBorderStyle & eBorderStyle_maximize))
1314 style &= ~WS_MAXIMIZEBOX;
1315
1316 if (IsPopupWithTitleBar()) {
1317 style |= WS_CAPTION;
1318 if (mBorderStyle & eBorderStyle_close) {
1319 style |= WS_SYSMENU;
1320 }
1321 }
1322 }
1323
1324 if (mIsChildWindow) {
1325 style |= WS_CLIPCHILDREN;
1326 if (!(style & WS_POPUP)) {
1327 style |= WS_CHILD; // WS_POPUP and WS_CHILD are mutually exclusive.
1328 }
1329 }
1330
1331 VERIFY_WINDOW_STYLE(style);
1332 return style;
1333 }
1334
1335 // Return nsWindow extended styles
WindowExStyle()1336 DWORD nsWindow::WindowExStyle() {
1337 switch (mWindowType) {
1338 case eWindowType_plugin:
1339 case eWindowType_plugin_ipc_chrome:
1340 case eWindowType_plugin_ipc_content:
1341 case eWindowType_child:
1342 return 0;
1343
1344 case eWindowType_dialog:
1345 return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
1346
1347 case eWindowType_popup: {
1348 DWORD extendedStyle = WS_EX_TOOLWINDOW;
1349 if (mPopupLevel == ePopupLevelTop) extendedStyle |= WS_EX_TOPMOST;
1350 return extendedStyle;
1351 }
1352 default:
1353 NS_ERROR("unknown border style");
1354 // fall through
1355
1356 case eWindowType_toplevel:
1357 case eWindowType_invisible:
1358 return WS_EX_WINDOWEDGE;
1359 }
1360 }
1361
1362 /**************************************************************
1363 *
1364 * SECTION: Window subclassing utilities
1365 *
1366 * Set or clear window subclasses on native windows. Used in
1367 * Create and Destroy.
1368 *
1369 **************************************************************/
1370
1371 // Subclass (or remove the subclass from) this component's nsWindow
SubclassWindow(BOOL bState)1372 void nsWindow::SubclassWindow(BOOL bState) {
1373 if (bState) {
1374 if (!mWnd || !IsWindow(mWnd)) {
1375 NS_ERROR("Invalid window handle");
1376 }
1377
1378 mPrevWndProc = reinterpret_cast<WNDPROC>(SetWindowLongPtrW(
1379 mWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
1380 NS_ASSERTION(mPrevWndProc, "Null standard window procedure");
1381 // connect the this pointer to the nsWindow handle
1382 WinUtils::SetNSWindowBasePtr(mWnd, this);
1383 } else {
1384 if (IsWindow(mWnd)) {
1385 SetWindowLongPtrW(mWnd, GWLP_WNDPROC,
1386 reinterpret_cast<LONG_PTR>(mPrevWndProc));
1387 }
1388 WinUtils::SetNSWindowBasePtr(mWnd, nullptr);
1389 mPrevWndProc = nullptr;
1390 }
1391 }
1392
1393 /**************************************************************
1394 *
1395 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
1396 *
1397 * Set or clear the parent widgets using window properties, and
1398 * handles calculating native parent handles.
1399 *
1400 **************************************************************/
1401
1402 // Get and set parent widgets
SetParent(nsIWidget * aNewParent)1403 void nsWindow::SetParent(nsIWidget* aNewParent) {
1404 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
1405 nsIWidget* parent = GetParent();
1406 if (parent) {
1407 parent->RemoveChild(this);
1408 }
1409
1410 mParent = aNewParent;
1411
1412 if (aNewParent) {
1413 ReparentNativeWidget(aNewParent);
1414 aNewParent->AddChild(this);
1415 return;
1416 }
1417 if (mWnd) {
1418 // If we have no parent, SetParent should return the desktop.
1419 VERIFY(::SetParent(mWnd, nullptr));
1420 RecreateDirectManipulationIfNeeded();
1421 }
1422 }
1423
ReparentNativeWidget(nsIWidget * aNewParent)1424 void nsWindow::ReparentNativeWidget(nsIWidget* aNewParent) {
1425 MOZ_ASSERT(aNewParent, "null widget");
1426
1427 mParent = aNewParent;
1428 if (mWindowType == eWindowType_popup) {
1429 return;
1430 }
1431 HWND newParent = (HWND)aNewParent->GetNativeData(NS_NATIVE_WINDOW);
1432 NS_ASSERTION(newParent, "Parent widget has a null native window handle");
1433 if (newParent && mWnd) {
1434 ::SetParent(mWnd, newParent);
1435 RecreateDirectManipulationIfNeeded();
1436 }
1437 }
1438
GetParent(void)1439 nsIWidget* nsWindow::GetParent(void) {
1440 if (mIsTopWidgetWindow) {
1441 return nullptr;
1442 }
1443 if (mInDtor || mOnDestroyCalled) {
1444 return nullptr;
1445 }
1446 return mParent;
1447 }
1448
RoundDown(double aDouble)1449 static int32_t RoundDown(double aDouble) {
1450 return aDouble > 0 ? static_cast<int32_t>(floor(aDouble))
1451 : static_cast<int32_t>(ceil(aDouble));
1452 }
1453
GetDPI()1454 float nsWindow::GetDPI() {
1455 float dpi = 96.0f;
1456 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
1457 if (screen) {
1458 screen->GetDpi(&dpi);
1459 }
1460 return dpi;
1461 }
1462
GetDefaultScaleInternal()1463 double nsWindow::GetDefaultScaleInternal() {
1464 if (mDefaultScale <= 0.0) {
1465 mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
1466 }
1467 return mDefaultScale;
1468 }
1469
LogToPhys(double aValue)1470 int32_t nsWindow::LogToPhys(double aValue) {
1471 return WinUtils::LogToPhys(
1472 ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTOPRIMARY), aValue);
1473 }
1474
GetParentWindow(bool aIncludeOwner)1475 nsWindow* nsWindow::GetParentWindow(bool aIncludeOwner) {
1476 return static_cast<nsWindow*>(GetParentWindowBase(aIncludeOwner));
1477 }
1478
GetParentWindowBase(bool aIncludeOwner)1479 nsWindowBase* nsWindow::GetParentWindowBase(bool aIncludeOwner) {
1480 if (mIsTopWidgetWindow) {
1481 // Must use a flag instead of mWindowType to tell if the window is the
1482 // owned by the topmost widget, because a child window can be embedded
1483 // inside a HWND which is not associated with a nsIWidget.
1484 return nullptr;
1485 }
1486
1487 // If this widget has already been destroyed, pretend we have no parent.
1488 // This corresponds to code in Destroy which removes the destroyed
1489 // widget from its parent's child list.
1490 if (mInDtor || mOnDestroyCalled) return nullptr;
1491
1492 // aIncludeOwner set to true implies walking the parent chain to retrieve the
1493 // root owner. aIncludeOwner set to false implies the search will stop at the
1494 // true parent (default).
1495 nsWindow* widget = nullptr;
1496 if (mWnd) {
1497 HWND parent = nullptr;
1498 if (aIncludeOwner)
1499 parent = ::GetParent(mWnd);
1500 else
1501 parent = ::GetAncestor(mWnd, GA_PARENT);
1502
1503 if (parent) {
1504 widget = WinUtils::GetNSWindowPtr(parent);
1505 if (widget) {
1506 // If the widget is in the process of being destroyed then
1507 // do NOT return it
1508 if (widget->mInDtor) {
1509 widget = nullptr;
1510 }
1511 }
1512 }
1513 }
1514
1515 return static_cast<nsWindowBase*>(widget);
1516 }
1517
EnumAllChildWindProc(HWND aWnd,LPARAM aParam)1518 BOOL CALLBACK nsWindow::EnumAllChildWindProc(HWND aWnd, LPARAM aParam) {
1519 nsWindow* wnd = WinUtils::GetNSWindowPtr(aWnd);
1520 if (wnd) {
1521 reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
1522 }
1523 return TRUE;
1524 }
1525
EnumAllThreadWindowProc(HWND aWnd,LPARAM aParam)1526 BOOL CALLBACK nsWindow::EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam) {
1527 nsWindow* wnd = WinUtils::GetNSWindowPtr(aWnd);
1528 if (wnd) {
1529 reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
1530 }
1531 EnumChildWindows(aWnd, EnumAllChildWindProc, aParam);
1532 return TRUE;
1533 }
1534
1535 /* static*/
EnumAllWindows()1536 nsTArray<nsWindow*> nsWindow::EnumAllWindows() {
1537 nsTArray<nsWindow*> windows;
1538 EnumThreadWindows(GetCurrentThreadId(), EnumAllThreadWindowProc,
1539 reinterpret_cast<LPARAM>(&windows));
1540 return windows;
1541 }
1542
CreateSourceSurfaceForGfxSurface(gfxASurface * aSurface)1543 static already_AddRefed<SourceSurface> CreateSourceSurfaceForGfxSurface(
1544 gfxASurface* aSurface) {
1545 MOZ_ASSERT(aSurface);
1546 return Factory::CreateSourceSurfaceForCairoSurface(
1547 aSurface->CairoSurface(), aSurface->GetSize(),
1548 aSurface->GetSurfaceFormat());
1549 }
1550
EnsureSnapshotSurface(ScrollSnapshot & aSnapshotData,const mozilla::gfx::IntSize & aSize)1551 nsWindow::ScrollSnapshot* nsWindow::EnsureSnapshotSurface(
1552 ScrollSnapshot& aSnapshotData, const mozilla::gfx::IntSize& aSize) {
1553 // If the surface doesn't exist or is the wrong size then create new one.
1554 if (!aSnapshotData.surface || aSnapshotData.surface->GetSize() != aSize) {
1555 aSnapshotData.surface = new gfxWindowsSurface(aSize, kScrollCaptureFormat);
1556 aSnapshotData.surfaceHasSnapshot = false;
1557 }
1558
1559 return &aSnapshotData;
1560 }
1561
CreateScrollSnapshot()1562 already_AddRefed<SourceSurface> nsWindow::CreateScrollSnapshot() {
1563 RECT clip = {0};
1564 int rgnType = ::GetWindowRgnBox(mWnd, &clip);
1565 if (rgnType == RGN_ERROR) {
1566 // We failed to get the clip assume that we need a full fallback.
1567 clip.left = 0;
1568 clip.top = 0;
1569 clip.right = mBounds.Width();
1570 clip.bottom = mBounds.Height();
1571 return GetFallbackScrollSnapshot(clip);
1572 }
1573
1574 // Check that the window is in a position to snapshot. We don't check for
1575 // clipped width as that doesn't currently matter for APZ scrolling.
1576 if (clip.top || clip.bottom != mBounds.Height()) {
1577 return GetFallbackScrollSnapshot(clip);
1578 }
1579
1580 HDC windowDC = ::GetDC(mWnd);
1581 if (!windowDC) {
1582 return GetFallbackScrollSnapshot(clip);
1583 }
1584 auto releaseDC = MakeScopeExit([&] { ::ReleaseDC(mWnd, windowDC); });
1585
1586 gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
1587 ScrollSnapshot* snapshot;
1588 if (clip.left || clip.right != mBounds.Width()) {
1589 // Can't do a full snapshot, so use the partial snapshot.
1590 snapshot = EnsureSnapshotSurface(mPartialSnapshot, snapshotSize);
1591 } else {
1592 snapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
1593 }
1594
1595 // Note that we know that the clip is full height.
1596 if (!::BitBlt(snapshot->surface->GetDC(), clip.left, 0,
1597 clip.right - clip.left, clip.bottom, windowDC, clip.left, 0,
1598 SRCCOPY)) {
1599 return GetFallbackScrollSnapshot(clip);
1600 }
1601 ::GdiFlush();
1602 snapshot->surface->Flush();
1603 snapshot->surfaceHasSnapshot = true;
1604 snapshot->clip = clip;
1605 mCurrentSnapshot = snapshot;
1606
1607 return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
1608 }
1609
GetFallbackScrollSnapshot(const RECT & aRequiredClip)1610 already_AddRefed<SourceSurface> nsWindow::GetFallbackScrollSnapshot(
1611 const RECT& aRequiredClip) {
1612 gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
1613
1614 // If the current snapshot is the correct size and covers the required clip,
1615 // just keep that by returning null.
1616 // Note: we know the clip is always full height.
1617 if (mCurrentSnapshot &&
1618 mCurrentSnapshot->surface->GetSize() == snapshotSize &&
1619 mCurrentSnapshot->clip.left <= aRequiredClip.left &&
1620 mCurrentSnapshot->clip.right >= aRequiredClip.right) {
1621 return nullptr;
1622 }
1623
1624 // Otherwise we'll use the full snapshot, making sure it is big enough first.
1625 mCurrentSnapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
1626
1627 // If there is no snapshot, create a default.
1628 if (!mCurrentSnapshot->surfaceHasSnapshot) {
1629 gfx::SurfaceFormat format = mCurrentSnapshot->surface->GetSurfaceFormat();
1630 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(
1631 mCurrentSnapshot->surface->CairoSurface(),
1632 mCurrentSnapshot->surface->GetSize(), &format);
1633
1634 DefaultFillScrollCapture(dt);
1635 }
1636
1637 return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
1638 }
1639
1640 /**************************************************************
1641 *
1642 * SECTION: nsIWidget::Show
1643 *
1644 * Hide or show this component.
1645 *
1646 **************************************************************/
1647
Show(bool bState)1648 void nsWindow::Show(bool bState) {
1649 if (bState && mIsShowingPreXULSkeletonUI) {
1650 // The first time we decide to actually show the window is when we decide
1651 // that we've taken over the window from the skeleton UI, and we should
1652 // no longer treat resizes / moves specially.
1653 mIsShowingPreXULSkeletonUI = false;
1654 // Initialize the UI state - this would normally happen below, but since
1655 // we're actually already showing, we won't hit it in the normal way.
1656 ::SendMessageW(mWnd, WM_CHANGEUISTATE,
1657 MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
1658 #if defined(ACCESSIBILITY)
1659 // If our HWND has focus and the a11y engine hasn't started yet, fire a
1660 // focus win event. Windows already did this when the skeleton UI appeared,
1661 // but a11y wouldn't have been able to start at that point even if a client
1662 // responded. Firing this now gives clients the chance to respond with
1663 // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
1664 // this if the a11y engine has already started because it has probably
1665 // already fired focus on a descendant.
1666 if (::GetFocus() == mWnd && !GetAccService()) {
1667 ::NotifyWinEvent(EVENT_OBJECT_FOCUS, mWnd, OBJID_CLIENT, CHILDID_SELF);
1668 }
1669 #endif // defined(ACCESSIBILITY)
1670 }
1671
1672 if (mWindowType == eWindowType_popup) {
1673 // See bug 603793. When we try to draw D3D9/10 windows with a drop shadow
1674 // without the DWM on a secondary monitor, windows fails to composite
1675 // our windows correctly. We therefor switch off the drop shadow for
1676 // pop-up windows when the DWM is disabled and two monitors are
1677 // connected.
1678 if (HasBogusPopupsDropShadowOnMultiMonitor() &&
1679 WinUtils::GetMonitorCount() > 1 &&
1680 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
1681 if (sDropShadowEnabled) {
1682 ::SetClassLongA(mWnd, GCL_STYLE, 0);
1683 sDropShadowEnabled = false;
1684 }
1685 } else {
1686 if (!sDropShadowEnabled) {
1687 ::SetClassLongA(mWnd, GCL_STYLE, CS_DROPSHADOW);
1688 sDropShadowEnabled = true;
1689 }
1690 }
1691
1692 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1693 // some popup menus to become invisible.
1694 LONG_PTR exStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
1695 if (exStyle & WS_EX_LAYERED) {
1696 ::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, exStyle & ~WS_EX_COMPOSITED);
1697 }
1698 }
1699
1700 bool syncInvalidate = false;
1701
1702 bool wasVisible = mIsVisible;
1703 // Set the status now so that anyone asking during ShowWindow or
1704 // SetWindowPos would get the correct answer.
1705 mIsVisible = bState;
1706
1707 // We may have cached an out of date visible state. This can happen
1708 // when session restore sets the full screen mode.
1709 if (mIsVisible)
1710 mOldStyle |= WS_VISIBLE;
1711 else
1712 mOldStyle &= ~WS_VISIBLE;
1713
1714 if (!mIsVisible && wasVisible) {
1715 ClearCachedResources();
1716 }
1717
1718 if (mWnd) {
1719 if (bState) {
1720 if (!wasVisible && mWindowType == eWindowType_toplevel) {
1721 // speed up the initial paint after show for
1722 // top level windows:
1723 syncInvalidate = true;
1724
1725 // Set the cursor before showing the window to avoid the default wait
1726 // cursor.
1727 SetCursor(Cursor{eCursor_standard});
1728
1729 switch (mSizeMode) {
1730 case nsSizeMode_Fullscreen:
1731 ::ShowWindow(mWnd, SW_SHOW);
1732 break;
1733 case nsSizeMode_Maximized:
1734 ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
1735 break;
1736 case nsSizeMode_Minimized:
1737 ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
1738 break;
1739 default:
1740 if (CanTakeFocus() && !mAlwaysOnTop) {
1741 ::ShowWindow(mWnd, SW_SHOWNORMAL);
1742 } else {
1743 ::ShowWindow(mWnd, SW_SHOWNOACTIVATE);
1744 // Don't flicker the window if we're restoring session
1745 if (!sIsRestoringSession) {
1746 Unused << GetAttention(2);
1747 }
1748 }
1749 break;
1750 }
1751 } else {
1752 DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
1753 if (wasVisible) flags |= SWP_NOZORDER;
1754 if (mAlwaysOnTop) flags |= SWP_NOACTIVATE;
1755
1756 if (mWindowType == eWindowType_popup) {
1757 // ensure popups are the topmost of the TOPMOST
1758 // layer. Remember not to set the SWP_NOZORDER
1759 // flag as that might allow the taskbar to overlap
1760 // the popup.
1761 flags |= SWP_NOACTIVATE;
1762 HWND owner = ::GetWindow(mWnd, GW_OWNER);
1763 if (owner) {
1764 // ePopupLevelTop popups should be above all else. All other
1765 // types should be placed in front of their owner, without
1766 // changing the owner's z-level relative to other windows.
1767 if (PopupLevel() != ePopupLevelTop) {
1768 ::SetWindowPos(mWnd, owner, 0, 0, 0, 0, flags);
1769 ::SetWindowPos(owner, mWnd, 0, 0, 0, 0,
1770 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
1771 } else {
1772 ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
1773 }
1774 } else {
1775 ::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0, flags);
1776 }
1777 } else {
1778 if (mWindowType == eWindowType_dialog && !CanTakeFocus())
1779 flags |= SWP_NOACTIVATE;
1780
1781 ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
1782 }
1783 }
1784
1785 if (!wasVisible && (mWindowType == eWindowType_toplevel ||
1786 mWindowType == eWindowType_dialog)) {
1787 // When a toplevel window or dialog is shown, initialize the UI state
1788 ::SendMessageW(mWnd, WM_CHANGEUISTATE,
1789 MAKEWPARAM(UIS_SET, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
1790 }
1791 } else {
1792 // Clear contents to avoid ghosting of old content if we display
1793 // this window again.
1794 if (wasVisible && mTransparencyMode == eTransparencyTransparent) {
1795 if (mCompositorWidgetDelegate) {
1796 mCompositorWidgetDelegate->ClearTransparentWindow();
1797 }
1798 }
1799 if (mWindowType != eWindowType_dialog) {
1800 ::ShowWindow(mWnd, SW_HIDE);
1801 } else {
1802 ::SetWindowPos(mWnd, 0, 0, 0, 0, 0,
1803 SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER |
1804 SWP_NOACTIVATE);
1805 }
1806 }
1807 }
1808
1809 #ifdef MOZ_XUL
1810 if (!wasVisible && bState) {
1811 Invalidate();
1812 if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
1813 ::UpdateWindow(mWnd);
1814 }
1815 }
1816 #endif
1817
1818 if (mOpeningAnimationSuppressed) {
1819 SuppressAnimation(false);
1820 }
1821 }
1822
1823 /**************************************************************
1824 *
1825 * SECTION: nsIWidget::IsVisible
1826 *
1827 * Returns the visibility state.
1828 *
1829 **************************************************************/
1830
1831 // Return true if the whether the component is visible, false otherwise
IsVisible() const1832 bool nsWindow::IsVisible() const { return mIsVisible; }
1833
1834 /**************************************************************
1835 *
1836 * SECTION: Window clipping utilities
1837 *
1838 * Used in Size and Move operations for setting the proper
1839 * window clipping regions for window transparency.
1840 *
1841 **************************************************************/
1842
ShouldHaveRoundedMenuDropShadow(nsWindow * aWindow)1843 static bool ShouldHaveRoundedMenuDropShadow(nsWindow* aWindow) {
1844 nsView* view = nsView::GetViewFor(aWindow);
1845 return view && view->GetFrame() &&
1846 view->GetFrame()->StyleUIReset()->mWindowShadow ==
1847 StyleWindowShadow::Cliprounded;
1848 }
1849
1850 // XP and Vista visual styles sometimes require window clipping regions to be
1851 // applied for proper transparency. These routines are called on size and move
1852 // operations.
1853 // XXX this is apparently still needed in Windows 7 and later
ClearThemeRegion()1854 void nsWindow::ClearThemeRegion() {
1855 if (mWindowType == eWindowType_popup &&
1856 (mPopupType == ePopupTypeMenu || mPopupType == ePopupTypePanel) &&
1857 ShouldHaveRoundedMenuDropShadow(this)) {
1858 SetWindowRgn(mWnd, nullptr, false);
1859 } else if (!HasGlass() &&
1860 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1861 (mPopupType == ePopupTypeTooltip ||
1862 mPopupType == ePopupTypePanel))) {
1863 SetWindowRgn(mWnd, nullptr, false);
1864 }
1865 }
1866
SetThemeRegion()1867 void nsWindow::SetThemeRegion() {
1868 // Clip the window to the rounded rect area of the popup if needed.
1869 if (mWindowType == eWindowType_popup &&
1870 (mPopupType == ePopupTypeMenu || mPopupType == ePopupTypePanel)) {
1871 nsView* view = nsView::GetViewFor(this);
1872 if (view) {
1873 LayoutDeviceIntSize size =
1874 nsLayoutUtils::GetBorderRadiusForMenuDropShadow(view->GetFrame());
1875 if (size.width || size.height) {
1876 int32_t width =
1877 NSToIntRound(size.width * GetDesktopToDeviceScale().scale);
1878 int32_t height =
1879 NSToIntRound(size.height * GetDesktopToDeviceScale().scale);
1880 HRGN region = CreateRoundRectRgn(0, 0, mBounds.Width() + 1,
1881 mBounds.Height() + 1, width, height);
1882 if (!SetWindowRgn(mWnd, region, false)) {
1883 DeleteObject(region); // region setting failed so delete the region.
1884 }
1885 }
1886 }
1887 }
1888
1889 // Popup types that have a visual styles region applied (bug 376408). This can
1890 // be expanded for other window types as needed. The regions are applied
1891 // generically to the base window so default constants are used for part and
1892 // state. At some point we might need part and state values from
1893 // nsNativeThemeWin's GetThemePartAndState, but currently windows that change
1894 // shape based on state haven't come up.
1895 else if (!HasGlass() &&
1896 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1897 (mPopupType == ePopupTypeTooltip ||
1898 mPopupType == ePopupTypePanel))) {
1899 HRGN hRgn = nullptr;
1900 RECT rect = {0, 0, mBounds.Width(), mBounds.Height()};
1901
1902 HDC dc = ::GetDC(mWnd);
1903 GetThemeBackgroundRegion(nsUXThemeData::GetTheme(eUXTooltip), dc,
1904 TTP_STANDARD, TS_NORMAL, &rect, &hRgn);
1905 if (hRgn) {
1906 if (!SetWindowRgn(mWnd, hRgn,
1907 false)) // do not delete or alter hRgn if accepted.
1908 DeleteObject(hRgn);
1909 }
1910 ::ReleaseDC(mWnd, dc);
1911 }
1912 }
1913
1914 /**************************************************************
1915 *
1916 * SECTION: Touch and APZ-related functions
1917 *
1918 **************************************************************/
1919
RegisterTouchWindow()1920 void nsWindow::RegisterTouchWindow() {
1921 mTouchWindow = true;
1922 ::RegisterTouchWindow(mWnd, TWF_WANTPALM);
1923 ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
1924 }
1925
RegisterTouchForDescendants(HWND aWnd,LPARAM aMsg)1926 BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
1927 nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
1928 if (win) {
1929 ::RegisterTouchWindow(aWnd, TWF_WANTPALM);
1930 }
1931 return TRUE;
1932 }
1933
LockAspectRatio(bool aShouldLock)1934 void nsWindow::LockAspectRatio(bool aShouldLock) {
1935 if (aShouldLock) {
1936 mAspectRatio = (float)mBounds.Height() / (float)mBounds.Width();
1937 } else {
1938 mAspectRatio = 0.0;
1939 }
1940 }
1941
1942 /**************************************************************
1943 *
1944 * SECTION: nsIWidget::SetWindowMouseTransparent
1945 *
1946 * Sets whether the window should ignore mouse events.
1947 *
1948 **************************************************************/
SetWindowMouseTransparent(bool aIsTransparent)1949 void nsWindow::SetWindowMouseTransparent(bool aIsTransparent) {
1950 if (!mWnd) {
1951 return;
1952 }
1953
1954 LONG_PTR oldStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
1955 LONG_PTR newStyle = aIsTransparent ? (oldStyle | WS_EX_TRANSPARENT)
1956 : (oldStyle & ~WS_EX_TRANSPARENT);
1957 ::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, newStyle);
1958 mMouseTransparent = aIsTransparent;
1959 }
1960
1961 /**************************************************************
1962 *
1963 * SECTION: nsIWidget::Move, nsIWidget::Resize,
1964 * nsIWidget::Size, nsIWidget::BeginResizeDrag
1965 *
1966 * Repositioning and sizing a window.
1967 *
1968 **************************************************************/
1969
SetSizeConstraints(const SizeConstraints & aConstraints)1970 void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
1971 SizeConstraints c = aConstraints;
1972
1973 if (mWindowType != eWindowType_popup && mResizable) {
1974 c.mMinSize.width =
1975 std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
1976 c.mMinSize.height =
1977 std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
1978 }
1979
1980 if (mMaxTextureSize > 0) {
1981 // We can't make ThebesLayers bigger than this anyway.. no point it letting
1982 // a window grow bigger as we won't be able to draw content there in
1983 // general.
1984 c.mMaxSize.width = std::min(c.mMaxSize.width, mMaxTextureSize);
1985 c.mMaxSize.height = std::min(c.mMaxSize.height, mMaxTextureSize);
1986 }
1987
1988 mSizeConstraintsScale = GetDefaultScale().scale;
1989
1990 nsBaseWidget::SetSizeConstraints(c);
1991 }
1992
GetSizeConstraints()1993 const SizeConstraints nsWindow::GetSizeConstraints() {
1994 double scale = GetDefaultScale().scale;
1995 if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
1996 return mSizeConstraints;
1997 }
1998 scale /= mSizeConstraintsScale;
1999 SizeConstraints c = mSizeConstraints;
2000 if (c.mMinSize.width != NS_MAXSIZE) {
2001 c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
2002 }
2003 if (c.mMinSize.height != NS_MAXSIZE) {
2004 c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
2005 }
2006 if (c.mMaxSize.width != NS_MAXSIZE) {
2007 c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
2008 }
2009 if (c.mMaxSize.height != NS_MAXSIZE) {
2010 c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
2011 }
2012 return c;
2013 }
2014
2015 // Move this component
Move(double aX,double aY)2016 void nsWindow::Move(double aX, double aY) {
2017 if (mWindowType == eWindowType_toplevel ||
2018 mWindowType == eWindowType_dialog) {
2019 SetSizeMode(nsSizeMode_Normal);
2020 }
2021
2022 // for top-level windows only, convert coordinates from desktop pixels
2023 // (the "parent" coordinate space) to the window's device pixel space
2024 double scale =
2025 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
2026 int32_t x = NSToIntRound(aX * scale);
2027 int32_t y = NSToIntRound(aY * scale);
2028
2029 // Check to see if window needs to be moved first
2030 // to avoid a costly call to SetWindowPos. This check
2031 // can not be moved to the calling code in nsView, because
2032 // some platforms do not position child windows correctly
2033
2034 // Only perform this check for non-popup windows, since the positioning can
2035 // in fact change even when the x/y do not. We always need to perform the
2036 // check. See bug #97805 for details.
2037 if (mWindowType != eWindowType_popup && mBounds.IsEqualXY(x, y)) {
2038 // Nothing to do, since it is already positioned correctly.
2039 return;
2040 }
2041
2042 mBounds.MoveTo(x, y);
2043
2044 if (mWnd) {
2045 #ifdef DEBUG
2046 // complain if a window is moved offscreen (legal, but potentially
2047 // worrisome)
2048 if (mIsTopWidgetWindow) { // only a problem for top-level windows
2049 // Make sure this window is actually on the screen before we move it
2050 // XXX: Needs multiple monitor support
2051 HDC dc = ::GetDC(mWnd);
2052 if (dc) {
2053 if (::GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
2054 RECT workArea;
2055 ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
2056 // no annoying assertions. just mention the issue.
2057 if (x < 0 || x >= workArea.right || y < 0 || y >= workArea.bottom) {
2058 MOZ_LOG(gWindowsLog, LogLevel::Info,
2059 ("window moved to offscreen position\n"));
2060 }
2061 }
2062 ::ReleaseDC(mWnd, dc);
2063 }
2064 }
2065 #endif
2066
2067 // Normally, when the skeleton UI is disabled, we resize+move the window
2068 // before showing it in order to ensure that it restores to the correct
2069 // position when the user un-maximizes it. However, when we are using the
2070 // skeleton UI, this results in the skeleton UI window being moved around
2071 // undesirably before being locked back into the maximized position. To
2072 // avoid this, we simply set the placement to restore to via
2073 // SetWindowPlacement. It's a little bit more of a dance, though, since we
2074 // need to convert the workspace coords that SetWindowPlacement uses to the
2075 // screen space coordinates we normally use with SetWindowPos.
2076 if (mIsShowingPreXULSkeletonUI && WasPreXULSkeletonUIMaximized()) {
2077 WINDOWPLACEMENT pl = {sizeof(WINDOWPLACEMENT)};
2078 VERIFY(::GetWindowPlacement(mWnd, &pl));
2079
2080 HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
2081 if (NS_WARN_IF(!monitor)) {
2082 return;
2083 }
2084 MONITORINFO mi = {sizeof(MONITORINFO)};
2085 VERIFY(::GetMonitorInfo(monitor, &mi));
2086
2087 int32_t deltaX =
2088 x + mi.rcWork.left - mi.rcMonitor.left - pl.rcNormalPosition.left;
2089 int32_t deltaY =
2090 y + mi.rcWork.top - mi.rcMonitor.top - pl.rcNormalPosition.top;
2091 pl.rcNormalPosition.left += deltaX;
2092 pl.rcNormalPosition.right += deltaX;
2093 pl.rcNormalPosition.top += deltaY;
2094 pl.rcNormalPosition.bottom += deltaY;
2095 VERIFY(::SetWindowPlacement(mWnd, &pl));
2096 } else {
2097 ClearThemeRegion();
2098
2099 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE;
2100 // Workaround SetWindowPos bug with D3D9. If our window has a clip
2101 // region, some drivers or OSes may incorrectly copy into the clipped-out
2102 // area.
2103 if (IsPlugin() && !mLayerManager && mClipRects &&
2104 (mClipRectCount != 1 ||
2105 !mClipRects[0].IsEqualInterior(
2106 LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height())))) {
2107 flags |= SWP_NOCOPYBITS;
2108 }
2109 double oldScale = mDefaultScale;
2110 mResizeState = IN_SIZEMOVE;
2111 VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags));
2112 mResizeState = NOT_RESIZING;
2113 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
2114 ChangedDPI();
2115 }
2116
2117 SetThemeRegion();
2118 }
2119
2120 ResizeDirectManipulationViewport();
2121 }
2122 NotifyRollupGeometryChange();
2123 }
2124
2125 // Resize this component
Resize(double aWidth,double aHeight,bool aRepaint)2126 void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) {
2127 // for top-level windows only, convert coordinates from desktop pixels
2128 // (the "parent" coordinate space) to the window's device pixel space
2129 double scale =
2130 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
2131 int32_t width = NSToIntRound(aWidth * scale);
2132 int32_t height = NSToIntRound(aHeight * scale);
2133
2134 NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
2135 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
2136 if (width < 0 || height < 0) {
2137 gfxCriticalNoteOnce << "Negative passed to Resize(" << width << ", "
2138 << height << ") repaint: " << aRepaint;
2139 }
2140
2141 ConstrainSize(&width, &height);
2142
2143 // Avoid unnecessary resizing calls
2144 if (mBounds.IsEqualSize(width, height)) {
2145 if (aRepaint) {
2146 Invalidate();
2147 }
2148 return;
2149 }
2150
2151 // Set cached value for lightweight and printing
2152 bool wasLocking = mAspectRatio != 0.0;
2153 mBounds.SizeTo(width, height);
2154 if (wasLocking) {
2155 LockAspectRatio(true); // This causes us to refresh the mAspectRatio value
2156 }
2157
2158 if (mWnd) {
2159 // Refer to the comment above a similar check in nsWindow::Move
2160 if (mIsShowingPreXULSkeletonUI && WasPreXULSkeletonUIMaximized()) {
2161 WINDOWPLACEMENT pl = {sizeof(WINDOWPLACEMENT)};
2162 VERIFY(::GetWindowPlacement(mWnd, &pl));
2163 pl.rcNormalPosition.right = pl.rcNormalPosition.left + width;
2164 pl.rcNormalPosition.bottom = pl.rcNormalPosition.top + GetHeight(height);
2165 mResizeState = RESIZING;
2166 VERIFY(::SetWindowPlacement(mWnd, &pl));
2167 mResizeState = NOT_RESIZING;
2168 } else {
2169 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE;
2170
2171 if (!aRepaint) {
2172 flags |= SWP_NOREDRAW;
2173 }
2174
2175 ClearThemeRegion();
2176 double oldScale = mDefaultScale;
2177 mResizeState = RESIZING;
2178 VERIFY(
2179 ::SetWindowPos(mWnd, nullptr, 0, 0, width, GetHeight(height), flags));
2180
2181 mResizeState = NOT_RESIZING;
2182 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
2183 ChangedDPI();
2184 }
2185 SetThemeRegion();
2186 }
2187
2188 ResizeDirectManipulationViewport();
2189 }
2190
2191 if (aRepaint) Invalidate();
2192
2193 NotifyRollupGeometryChange();
2194 }
2195
2196 // Resize this component
Resize(double aX,double aY,double aWidth,double aHeight,bool aRepaint)2197 void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight,
2198 bool aRepaint) {
2199 // for top-level windows only, convert coordinates from desktop pixels
2200 // (the "parent" coordinate space) to the window's device pixel space
2201 double scale =
2202 BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
2203 int32_t x = NSToIntRound(aX * scale);
2204 int32_t y = NSToIntRound(aY * scale);
2205 int32_t width = NSToIntRound(aWidth * scale);
2206 int32_t height = NSToIntRound(aHeight * scale);
2207
2208 NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
2209 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
2210 if (width < 0 || height < 0) {
2211 gfxCriticalNoteOnce << "Negative passed to Resize(" << x << " ," << y
2212 << ", " << width << ", " << height
2213 << ") repaint: " << aRepaint;
2214 }
2215
2216 ConstrainSize(&width, &height);
2217
2218 // Avoid unnecessary resizing calls
2219 if (mBounds.IsEqualRect(x, y, width, height)) {
2220 if (aRepaint) {
2221 Invalidate();
2222 }
2223 return;
2224 }
2225
2226 // Set cached value for lightweight and printing
2227 mBounds.SetRect(x, y, width, height);
2228
2229 if (mWnd) {
2230 // Refer to the comment above a similar check in nsWindow::Move
2231 if (mIsShowingPreXULSkeletonUI && WasPreXULSkeletonUIMaximized()) {
2232 WINDOWPLACEMENT pl = {sizeof(WINDOWPLACEMENT)};
2233 VERIFY(::GetWindowPlacement(mWnd, &pl));
2234
2235 HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
2236 if (NS_WARN_IF(!monitor)) {
2237 return;
2238 }
2239 MONITORINFO mi = {sizeof(MONITORINFO)};
2240 VERIFY(::GetMonitorInfo(monitor, &mi));
2241
2242 int32_t deltaX =
2243 x + mi.rcWork.left - mi.rcMonitor.left - pl.rcNormalPosition.left;
2244 int32_t deltaY =
2245 y + mi.rcWork.top - mi.rcMonitor.top - pl.rcNormalPosition.top;
2246 pl.rcNormalPosition.left += deltaX;
2247 pl.rcNormalPosition.right = pl.rcNormalPosition.left + width;
2248 pl.rcNormalPosition.top += deltaY;
2249 pl.rcNormalPosition.bottom = pl.rcNormalPosition.top + GetHeight(height);
2250 VERIFY(::SetWindowPlacement(mWnd, &pl));
2251 } else {
2252 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
2253 if (!aRepaint) {
2254 flags |= SWP_NOREDRAW;
2255 }
2256
2257 ClearThemeRegion();
2258
2259 double oldScale = mDefaultScale;
2260 mResizeState = RESIZING;
2261 VERIFY(
2262 ::SetWindowPos(mWnd, nullptr, x, y, width, GetHeight(height), flags));
2263 mResizeState = NOT_RESIZING;
2264 if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
2265 ChangedDPI();
2266 }
2267
2268 if (mTransitionWnd) {
2269 // If we have a fullscreen transition window, we need to make
2270 // it topmost again, otherwise the taskbar may be raised by
2271 // the system unexpectedly when we leave fullscreen state.
2272 ::SetWindowPos(mTransitionWnd, HWND_TOPMOST, 0, 0, 0, 0,
2273 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
2274 }
2275 SetThemeRegion();
2276 }
2277
2278 ResizeDirectManipulationViewport();
2279 }
2280
2281 if (aRepaint) Invalidate();
2282
2283 NotifyRollupGeometryChange();
2284 }
2285
IsResizingNativeWidget()2286 mozilla::Maybe<bool> nsWindow::IsResizingNativeWidget() {
2287 if (mResizeState == RESIZING) {
2288 return Some(true);
2289 }
2290 return Some(false);
2291 }
2292
BeginResizeDrag(WidgetGUIEvent * aEvent,int32_t aHorizontal,int32_t aVertical)2293 nsresult nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent, int32_t aHorizontal,
2294 int32_t aVertical) {
2295 NS_ENSURE_ARG_POINTER(aEvent);
2296
2297 if (aEvent->mClass != eMouseEventClass) {
2298 // you can only begin a resize drag with a mouse event
2299 return NS_ERROR_INVALID_ARG;
2300 }
2301
2302 if (aEvent->AsMouseEvent()->mButton != MouseButton::ePrimary) {
2303 // you can only begin a resize drag with the left mouse button
2304 return NS_ERROR_INVALID_ARG;
2305 }
2306
2307 // work out what sizemode we're talking about
2308 WPARAM syscommand;
2309 if (aVertical < 0) {
2310 if (aHorizontal < 0) {
2311 syscommand = SC_SIZE | WMSZ_TOPLEFT;
2312 } else if (aHorizontal == 0) {
2313 syscommand = SC_SIZE | WMSZ_TOP;
2314 } else {
2315 syscommand = SC_SIZE | WMSZ_TOPRIGHT;
2316 }
2317 } else if (aVertical == 0) {
2318 if (aHorizontal < 0) {
2319 syscommand = SC_SIZE | WMSZ_LEFT;
2320 } else if (aHorizontal == 0) {
2321 return NS_ERROR_INVALID_ARG;
2322 } else {
2323 syscommand = SC_SIZE | WMSZ_RIGHT;
2324 }
2325 } else {
2326 if (aHorizontal < 0) {
2327 syscommand = SC_SIZE | WMSZ_BOTTOMLEFT;
2328 } else if (aHorizontal == 0) {
2329 syscommand = SC_SIZE | WMSZ_BOTTOM;
2330 } else {
2331 syscommand = SC_SIZE | WMSZ_BOTTOMRIGHT;
2332 }
2333 }
2334
2335 // resizing doesn't work if the mouse is already captured
2336 CaptureMouse(false);
2337
2338 // find the top-level window
2339 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd, true);
2340
2341 // tell Windows to start the resize
2342 ::PostMessage(toplevelWnd, WM_SYSCOMMAND, syscommand,
2343 POINTTOPOINTS(aEvent->mRefPoint));
2344
2345 return NS_OK;
2346 }
2347
2348 /**************************************************************
2349 *
2350 * SECTION: Window Z-order and state.
2351 *
2352 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
2353 * nsIWidget::ConstrainPosition
2354 *
2355 * Z-order, positioning, restore, minimize, and maximize.
2356 *
2357 **************************************************************/
2358
2359 // Position the window behind the given window
PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,nsIWidget * aWidget,bool aActivate)2360 void nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
2361 nsIWidget* aWidget, bool aActivate) {
2362 HWND behind = HWND_TOP;
2363 if (aPlacement == eZPlacementBottom)
2364 behind = HWND_BOTTOM;
2365 else if (aPlacement == eZPlacementBelow && aWidget)
2366 behind = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
2367 UINT flags = SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE;
2368 if (!aActivate) flags |= SWP_NOACTIVATE;
2369
2370 if (!CanTakeFocus() && behind == HWND_TOP) {
2371 // Can't place the window to top so place it behind the foreground window
2372 // (as long as it is not topmost)
2373 HWND wndAfter = ::GetForegroundWindow();
2374 if (!wndAfter)
2375 behind = HWND_BOTTOM;
2376 else if (!(GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST))
2377 behind = wndAfter;
2378 flags |= SWP_NOACTIVATE;
2379 }
2380
2381 ::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
2382 }
2383
GetCurrentShowCmd(HWND aWnd)2384 static UINT GetCurrentShowCmd(HWND aWnd) {
2385 WINDOWPLACEMENT pl;
2386 pl.length = sizeof(pl);
2387 ::GetWindowPlacement(aWnd, &pl);
2388 return pl.showCmd;
2389 }
2390
SetSizeModeInternal(nsSizeMode aMode)2391 void nsWindow::SetSizeModeInternal(nsSizeMode aMode) {
2392 // save the requested state
2393 mLastSizeMode = mSizeMode;
2394 nsBaseWidget::SetSizeMode(aMode);
2395 if (mIsVisible) {
2396 switch (aMode) {
2397 case nsSizeMode_Fullscreen:
2398 ::ShowWindow(mWnd, SW_SHOW);
2399 break;
2400
2401 case nsSizeMode_Maximized:
2402 ::ShowWindow(mWnd, SW_MAXIMIZE);
2403 break;
2404
2405 case nsSizeMode_Minimized:
2406 ::ShowWindow(mWnd, SW_MINIMIZE);
2407 break;
2408
2409 default:
2410 // Don't call ::ShowWindow if we're trying to "restore" a window that is
2411 // already in a normal state. Prevents a bug where snapping to one side
2412 // of the screen and then minimizing would cause Windows to forget our
2413 // window's correct restored position/size.
2414 if (GetCurrentShowCmd(mWnd) != SW_SHOWNORMAL) {
2415 ::ShowWindow(mWnd, SW_RESTORE);
2416 }
2417 }
2418 }
2419
2420 // we activate here to ensure that the right child window is focused
2421 if (mIsVisible && (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen)) {
2422 DispatchFocusToTopLevelWindow(true);
2423 }
2424 }
2425
2426 // Maximize, minimize or restore the window.
SetSizeMode(nsSizeMode aMode)2427 void nsWindow::SetSizeMode(nsSizeMode aMode) {
2428 // If we are still displaying a maximized pre-XUL skeleton UI, ignore the
2429 // noise of sizemode changes. Once we have "shown" the window for the first
2430 // time (called nsWindow::Show(true), even though the window is already
2431 // technically displayed), we will again accept sizemode changes.
2432 if (mIsShowingPreXULSkeletonUI && WasPreXULSkeletonUIMaximized()) {
2433 return;
2434 }
2435
2436 // Let's not try and do anything if we're already in that state.
2437 // (This is needed to prevent problems when calling window.minimize(), which
2438 // calls us directly, and then the OS triggers another call to us.)
2439 if (aMode == mSizeMode) return;
2440
2441 if (aMode == nsSizeMode_Fullscreen) {
2442 MakeFullScreen(true, nullptr);
2443 } else if ((mSizeMode == nsSizeMode_Fullscreen) && (aMode == nsSizeMode_Normal)) {
2444 // If we are in fullscreen mode, minimize should work like normal and
2445 // return us to fullscreen mode when unminimized. Maximize isn't really
2446 // available and won't do anything. "Restore" should do the same thing as
2447 // requesting to end fullscreen.
2448 MakeFullScreen(false, nullptr);
2449 } else {
2450 SetSizeModeInternal(aMode);
2451 }
2452 }
2453
GetWorkspaceID(nsAString & workspaceID)2454 void nsWindow::GetWorkspaceID(nsAString& workspaceID) {
2455 RefPtr<IVirtualDesktopManager> desktopManager = gVirtualDesktopManager;
2456 if (!desktopManager) {
2457 return;
2458 }
2459
2460 GUID desktop;
2461 HRESULT hr = desktopManager->GetWindowDesktopId(mWnd, &desktop);
2462 if (FAILED(hr)) {
2463 return;
2464 }
2465
2466 RPC_WSTR workspaceIDStr = nullptr;
2467 if (UuidToStringW(&desktop, &workspaceIDStr) == RPC_S_OK) {
2468 workspaceID.Assign((wchar_t*)workspaceIDStr);
2469 RpcStringFreeW(&workspaceIDStr);
2470 }
2471 }
2472
MoveToWorkspace(const nsAString & workspaceID)2473 void nsWindow::MoveToWorkspace(const nsAString& workspaceID) {
2474 RefPtr<IVirtualDesktopManager> desktopManager = gVirtualDesktopManager;
2475 if (!desktopManager) {
2476 return;
2477 }
2478
2479 GUID desktop;
2480 const nsString& flat = PromiseFlatString(workspaceID);
2481 RPC_WSTR workspaceIDStr = reinterpret_cast<RPC_WSTR>((wchar_t*)flat.get());
2482 if (UuidFromStringW(workspaceIDStr, &desktop) == RPC_S_OK) {
2483 desktopManager->MoveWindowToDesktop(mWnd, desktop);
2484 }
2485 }
2486
SuppressAnimation(bool aSuppress)2487 void nsWindow::SuppressAnimation(bool aSuppress) {
2488 DWORD dwAttribute = aSuppress ? TRUE : FALSE;
2489 DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED, &dwAttribute,
2490 sizeof dwAttribute);
2491 }
2492
2493 // Constrain a potential move to fit onscreen
2494 // Position (aX, aY) is specified in Windows screen (logical) pixels,
2495 // except when using per-monitor DPI, in which case it's device pixels.
ConstrainPosition(bool aAllowSlop,int32_t * aX,int32_t * aY)2496 void nsWindow::ConstrainPosition(bool aAllowSlop, int32_t* aX, int32_t* aY) {
2497 if (!mIsTopWidgetWindow) // only a problem for top-level windows
2498 return;
2499
2500 double dpiScale = GetDesktopToDeviceScale().scale;
2501
2502 // We need to use the window size in the kind of pixels used for window-
2503 // manipulation APIs.
2504 int32_t logWidth =
2505 std::max<int32_t>(NSToIntRound(mBounds.Width() / dpiScale), 1);
2506 int32_t logHeight =
2507 std::max<int32_t>(NSToIntRound(mBounds.Height() / dpiScale), 1);
2508
2509 /* get our playing field. use the current screen, or failing that
2510 for any reason, use device caps for the default screen. */
2511 RECT screenRect;
2512
2513 nsCOMPtr<nsIScreenManager> screenmgr =
2514 do_GetService(sScreenManagerContractID);
2515 if (!screenmgr) {
2516 return;
2517 }
2518 nsCOMPtr<nsIScreen> screen;
2519 int32_t left, top, width, height;
2520
2521 screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
2522 getter_AddRefs(screen));
2523 if (mSizeMode != nsSizeMode_Fullscreen) {
2524 // For normalized windows, use the desktop work area.
2525 nsresult rv = screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
2526 if (NS_FAILED(rv)) {
2527 return;
2528 }
2529 } else {
2530 // For full screen windows, use the desktop.
2531 nsresult rv = screen->GetRectDisplayPix(&left, &top, &width, &height);
2532 if (NS_FAILED(rv)) {
2533 return;
2534 }
2535 }
2536 screenRect.left = left;
2537 screenRect.right = left + width;
2538 screenRect.top = top;
2539 screenRect.bottom = top + height;
2540
2541 if (aAllowSlop) {
2542 if (*aX < screenRect.left - logWidth + kWindowPositionSlop)
2543 *aX = screenRect.left - logWidth + kWindowPositionSlop;
2544 else if (*aX >= screenRect.right - kWindowPositionSlop)
2545 *aX = screenRect.right - kWindowPositionSlop;
2546
2547 if (*aY < screenRect.top - logHeight + kWindowPositionSlop)
2548 *aY = screenRect.top - logHeight + kWindowPositionSlop;
2549 else if (*aY >= screenRect.bottom - kWindowPositionSlop)
2550 *aY = screenRect.bottom - kWindowPositionSlop;
2551
2552 } else {
2553 if (*aX < screenRect.left)
2554 *aX = screenRect.left;
2555 else if (*aX >= screenRect.right - logWidth)
2556 *aX = screenRect.right - logWidth;
2557
2558 if (*aY < screenRect.top)
2559 *aY = screenRect.top;
2560 else if (*aY >= screenRect.bottom - logHeight)
2561 *aY = screenRect.bottom - logHeight;
2562 }
2563 }
2564
2565 /**************************************************************
2566 *
2567 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
2568 *
2569 * Enabling and disabling the widget.
2570 *
2571 **************************************************************/
2572
2573 // Enable/disable this component
Enable(bool bState)2574 void nsWindow::Enable(bool bState) {
2575 if (mWnd) {
2576 ::EnableWindow(mWnd, bState);
2577 }
2578 }
2579
2580 // Return the current enable state
IsEnabled() const2581 bool nsWindow::IsEnabled() const {
2582 return !mWnd || (::IsWindowEnabled(mWnd) &&
2583 ::IsWindowEnabled(::GetAncestor(mWnd, GA_ROOT)));
2584 }
2585
2586 /**************************************************************
2587 *
2588 * SECTION: nsIWidget::SetFocus
2589 *
2590 * Give the focus to this widget.
2591 *
2592 **************************************************************/
2593
SetFocus(Raise aRaise,mozilla::dom::CallerType aCallerType)2594 void nsWindow::SetFocus(Raise aRaise, mozilla::dom::CallerType aCallerType) {
2595 if (mWnd) {
2596 #ifdef WINSTATE_DEBUG_OUTPUT
2597 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
2598 MOZ_LOG(gWindowsLog, LogLevel::Info,
2599 ("*** SetFocus: [ top] raise=%d\n", aRaise == Raise::Yes));
2600 } else {
2601 MOZ_LOG(gWindowsLog, LogLevel::Info,
2602 ("*** SetFocus: [child] raise=%d\n", aRaise == Raise::Yes));
2603 }
2604 #endif
2605 // Uniconify, if necessary
2606 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd);
2607 if (aRaise == Raise::Yes && ::IsIconic(toplevelWnd)) {
2608 ::ShowWindow(toplevelWnd, SW_RESTORE);
2609 }
2610 ::SetFocus(mWnd);
2611 }
2612 }
2613
2614 /**************************************************************
2615 *
2616 * SECTION: Bounds
2617 *
2618 * GetBounds, GetClientBounds, GetScreenBounds,
2619 * GetRestoredBounds, GetClientOffset
2620 * SetDrawsInTitlebar, SetNonClientMargins
2621 *
2622 * Bound calculations.
2623 *
2624 **************************************************************/
2625
2626 // Return the window's full dimensions in screen coordinates.
2627 // If the window has a parent, converts the origin to an offset
2628 // of the parent's screen origin.
GetBounds()2629 LayoutDeviceIntRect nsWindow::GetBounds() {
2630 if (!mWnd) {
2631 return mBounds;
2632 }
2633
2634 RECT r;
2635 VERIFY(::GetWindowRect(mWnd, &r));
2636
2637 LayoutDeviceIntRect rect;
2638
2639 // assign size
2640 rect.SizeTo(r.right - r.left, r.bottom - r.top);
2641
2642 // popup window bounds' are in screen coordinates, not relative to parent
2643 // window
2644 if (mWindowType == eWindowType_popup) {
2645 rect.MoveTo(r.left, r.top);
2646 return rect;
2647 }
2648
2649 // chrome on parent:
2650 // ___ 5,5 (chrome start)
2651 // | ____ 10,10 (client start)
2652 // | | ____ 20,20 (child start)
2653 // | | |
2654 // 20,20 - 5,5 = 15,15 (??)
2655 // minus GetClientOffset:
2656 // 15,15 - 5,5 = 10,10
2657 //
2658 // no chrome on parent:
2659 // ______ 10,10 (win start)
2660 // | ____ 20,20 (child start)
2661 // | |
2662 // 20,20 - 10,10 = 10,10
2663 //
2664 // walking the chain:
2665 // ___ 5,5 (chrome start)
2666 // | ___ 10,10 (client start)
2667 // | | ___ 20,20 (child start)
2668 // | | | __ 30,30 (child start)
2669 // | | | |
2670 // 30,30 - 20,20 = 10,10 (offset from second child to first)
2671 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
2672 // minus GetClientOffset:
2673 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
2674
2675 // convert coordinates if parent exists
2676 HWND parent = ::GetParent(mWnd);
2677 if (parent) {
2678 RECT pr;
2679 VERIFY(::GetWindowRect(parent, &pr));
2680 r.left -= pr.left;
2681 r.top -= pr.top;
2682 // adjust for chrome
2683 nsWindow* pWidget = static_cast<nsWindow*>(GetParent());
2684 if (pWidget && pWidget->IsTopLevelWidget()) {
2685 LayoutDeviceIntPoint clientOffset = pWidget->GetClientOffset();
2686 r.left -= clientOffset.x;
2687 r.top -= clientOffset.y;
2688 }
2689 }
2690 rect.MoveTo(r.left, r.top);
2691 return rect;
2692 }
2693
2694 // Get this component dimension
GetClientBounds()2695 LayoutDeviceIntRect nsWindow::GetClientBounds() {
2696 if (!mWnd) {
2697 return LayoutDeviceIntRect(0, 0, 0, 0);
2698 }
2699
2700 RECT r;
2701 VERIFY(::GetClientRect(mWnd, &r));
2702
2703 LayoutDeviceIntRect bounds = GetBounds();
2704 LayoutDeviceIntRect rect;
2705 rect.MoveTo(bounds.TopLeft() + GetClientOffset());
2706 rect.SizeTo(r.right - r.left, r.bottom - r.top);
2707 return rect;
2708 }
2709
2710 // Like GetBounds, but don't offset by the parent
GetScreenBounds()2711 LayoutDeviceIntRect nsWindow::GetScreenBounds() {
2712 if (!mWnd) {
2713 return mBounds;
2714 }
2715
2716 RECT r;
2717 VERIFY(::GetWindowRect(mWnd, &r));
2718
2719 return LayoutDeviceIntRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
2720 }
2721
GetRestoredBounds(LayoutDeviceIntRect & aRect)2722 nsresult nsWindow::GetRestoredBounds(LayoutDeviceIntRect& aRect) {
2723 if (SizeMode() == nsSizeMode_Normal) {
2724 aRect = GetScreenBounds();
2725 return NS_OK;
2726 }
2727 if (!mWnd) {
2728 return NS_ERROR_FAILURE;
2729 }
2730
2731 WINDOWPLACEMENT pl = {sizeof(WINDOWPLACEMENT)};
2732 VERIFY(::GetWindowPlacement(mWnd, &pl));
2733 const RECT& r = pl.rcNormalPosition;
2734
2735 HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
2736 if (!monitor) {
2737 return NS_ERROR_FAILURE;
2738 }
2739 MONITORINFO mi = {sizeof(MONITORINFO)};
2740 VERIFY(::GetMonitorInfo(monitor, &mi));
2741
2742 aRect.SetRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
2743 aRect.MoveBy(mi.rcWork.left - mi.rcMonitor.left,
2744 mi.rcWork.top - mi.rcMonitor.top);
2745 return NS_OK;
2746 }
2747
2748 // Return the x,y offset of the client area from the origin of the window. If
2749 // the window is borderless returns (0,0).
GetClientOffset()2750 LayoutDeviceIntPoint nsWindow::GetClientOffset() {
2751 if (!mWnd) {
2752 return LayoutDeviceIntPoint(0, 0);
2753 }
2754
2755 RECT r1;
2756 GetWindowRect(mWnd, &r1);
2757 LayoutDeviceIntPoint pt = WidgetToScreenOffset();
2758 return LayoutDeviceIntPoint(pt.x - r1.left, pt.y - r1.top);
2759 }
2760
SetDrawsInTitlebar(bool aState)2761 void nsWindow::SetDrawsInTitlebar(bool aState) {
2762 nsWindow* window = GetTopLevelWindow(true);
2763 if (window && window != this) {
2764 return window->SetDrawsInTitlebar(aState);
2765 }
2766
2767 if (aState) {
2768 // top, right, bottom, left for nsIntMargin
2769 LayoutDeviceIntMargin margins(0, -1, -1, -1);
2770 SetNonClientMargins(margins);
2771 } else {
2772 LayoutDeviceIntMargin margins(-1, -1, -1, -1);
2773 SetNonClientMargins(margins);
2774 }
2775 }
2776
ResetLayout()2777 void nsWindow::ResetLayout() {
2778 // This will trigger a frame changed event, triggering
2779 // nc calc size and a sizemode gecko event.
2780 SetWindowPos(mWnd, 0, 0, 0, 0, 0,
2781 SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE |
2782 SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER);
2783
2784 // If hidden, just send the frame changed event for now.
2785 if (!mIsVisible) return;
2786
2787 // Send a gecko size event to trigger reflow.
2788 RECT clientRc = {0};
2789 GetClientRect(mWnd, &clientRc);
2790 OnResize(WinUtils::ToIntRect(clientRc).Size());
2791
2792 // Invalidate and update
2793 Invalidate();
2794 }
2795
2796 // Internally track the caption status via a window property. Required
2797 // due to our internal handling of WM_NCACTIVATE when custom client
2798 // margins are set.
2799 static const wchar_t kManageWindowInfoProperty[] = L"ManageWindowInfoProperty";
2800 typedef BOOL(WINAPI* GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
2801 static WindowsDllInterceptor::FuncHookType<GetWindowInfoPtr>
2802 sGetWindowInfoPtrStub;
2803
GetWindowInfoHook(HWND hWnd,PWINDOWINFO pwi)2804 BOOL WINAPI GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi) {
2805 if (!sGetWindowInfoPtrStub) {
2806 NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!");
2807 return FALSE;
2808 }
2809 int windowStatus =
2810 reinterpret_cast<LONG_PTR>(GetPropW(hWnd, kManageWindowInfoProperty));
2811 // No property set, return the default data.
2812 if (!windowStatus) return sGetWindowInfoPtrStub(hWnd, pwi);
2813 // Call GetWindowInfo and update dwWindowStatus with our
2814 // internally tracked value.
2815 BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
2816 if (result && pwi)
2817 pwi->dwWindowStatus = (windowStatus == 1 ? 0 : WS_ACTIVECAPTION);
2818 return result;
2819 }
2820
UpdateGetWindowInfoCaptionStatus(bool aActiveCaption)2821 void nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption) {
2822 if (!mWnd) return;
2823
2824 sUser32Intercept.Init("user32.dll");
2825 sGetWindowInfoPtrStub.Set(sUser32Intercept, "GetWindowInfo",
2826 &GetWindowInfoHook);
2827 if (!sGetWindowInfoPtrStub) {
2828 return;
2829 }
2830
2831 // Update our internally tracked caption status
2832 SetPropW(mWnd, kManageWindowInfoProperty,
2833 reinterpret_cast<HANDLE>(static_cast<INT_PTR>(aActiveCaption) + 1));
2834 }
2835
2836 /**
2837 * Called when the window layout changes: full screen mode transitions,
2838 * theme changes, and composition changes. Calculates the new non-client
2839 * margins and fires off a frame changed event, which triggers an nc calc
2840 * size windows event, kicking the changes in.
2841 *
2842 * The offsets calculated here are based on the value of `mNonClientMargins`
2843 * which is specified in the "chromemargins" attribute of the window. For
2844 * each margin, the value specified has the following meaning:
2845 * -1 - leave the default frame in place
2846 * 0 - remove the frame
2847 * >0 - frame size equals min(0, (default frame size - margin value))
2848 *
2849 * This function calculates and populates `mNonClientOffset`.
2850 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2851 * as (default frame size - offset). For example, if the left frame should
2852 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2853 * will equal 1.
2854 *
2855 * For maximized, fullscreen, and minimized windows, the values stored in
2856 * `mNonClientMargins` are ignored, and special processing takes place.
2857 *
2858 * For non-glass windows, we only allow frames to be their default size
2859 * or removed entirely.
2860 */
UpdateNonClientMargins(int32_t aSizeMode,bool aReflowWindow)2861 bool nsWindow::UpdateNonClientMargins(int32_t aSizeMode, bool aReflowWindow) {
2862 if (!mCustomNonClient) return false;
2863
2864 if (aSizeMode == -1) {
2865 aSizeMode = mSizeMode;
2866 }
2867
2868 bool hasCaption = (mBorderStyle & (eBorderStyle_all | eBorderStyle_title |
2869 eBorderStyle_menu | eBorderStyle_default));
2870
2871 float dpi = GetDPI();
2872
2873 // mCaptionHeight is the default size of the NC area at
2874 // the top of the window. If the window has a caption,
2875 // the size is calculated as the sum of:
2876 // SM_CYFRAME - The thickness of the sizing border
2877 // around a resizable window
2878 // SM_CXPADDEDBORDER - The amount of border padding
2879 // for captioned windows
2880 // SM_CYCAPTION - The height of the caption area
2881 //
2882 // If the window does not have a caption, mCaptionHeight will be equal to
2883 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2884 mCaptionHeight =
2885 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi) +
2886 (hasCaption ? WinUtils::GetSystemMetricsForDpi(SM_CYCAPTION, dpi) +
2887 WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)
2888 : 0);
2889
2890 // mHorResizeMargin is the size of the default NC areas on the
2891 // left and right sides of our window. It is calculated as
2892 // the sum of:
2893 // SM_CXFRAME - The thickness of the sizing border
2894 // SM_CXPADDEDBORDER - The amount of border padding
2895 // for captioned windows
2896 //
2897 // If the window does not have a caption, mHorResizeMargin will be equal to
2898 // `WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi)`
2899 mHorResizeMargin =
2900 WinUtils::GetSystemMetricsForDpi(SM_CXFRAME, dpi) +
2901 (hasCaption ? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)
2902 : 0);
2903
2904 // mVertResizeMargin is the size of the default NC area at the
2905 // bottom of the window. It is calculated as the sum of:
2906 // SM_CYFRAME - The thickness of the sizing border
2907 // SM_CXPADDEDBORDER - The amount of border padding
2908 // for captioned windows.
2909 //
2910 // If the window does not have a caption, mVertResizeMargin will be equal to
2911 // `WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi)`
2912 mVertResizeMargin =
2913 WinUtils::GetSystemMetricsForDpi(SM_CYFRAME, dpi) +
2914 (hasCaption ? WinUtils::GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi)
2915 : 0);
2916
2917 if (aSizeMode == nsSizeMode_Minimized) {
2918 // Use default frame size for minimized windows
2919 mNonClientOffset.top = 0;
2920 mNonClientOffset.left = 0;
2921 mNonClientOffset.right = 0;
2922 mNonClientOffset.bottom = 0;
2923 } else if (aSizeMode == nsSizeMode_Fullscreen) {
2924 // Remove the default frame from the top of our fullscreen window. This
2925 // makes the whole caption part of our client area, allowing us to draw
2926 // in the whole caption area. Additionally remove the default frame from
2927 // the left, right, and bottom.
2928 mNonClientOffset.top = mCaptionHeight;
2929 mNonClientOffset.bottom = mVertResizeMargin;
2930 mNonClientOffset.left = mHorResizeMargin;
2931 mNonClientOffset.right = mHorResizeMargin;
2932 } else if (aSizeMode == nsSizeMode_Maximized) {
2933 // Remove the default frame from the top of our maximized window. This
2934 // makes the whole caption part of our client area, allowing us to draw
2935 // in the whole caption area. Use default frame size on left, right, and
2936 // bottom. The reason this works is that, for maximized windows,
2937 // Windows positions them so that their frames fall off the screen.
2938 // This gives the illusion of windows having no frames when they are
2939 // maximized. If we try to mess with the frame sizes by setting these
2940 // offsets to positive values, our client area will fall off the screen.
2941 mNonClientOffset.top = mCaptionHeight;
2942 mNonClientOffset.bottom = 0;
2943 mNonClientOffset.left = 0;
2944 mNonClientOffset.right = 0;
2945
2946 APPBARDATA appBarData;
2947 appBarData.cbSize = sizeof(appBarData);
2948 UINT taskbarState = SHAppBarMessage(ABM_GETSTATE, &appBarData);
2949 if (ABS_AUTOHIDE & taskbarState) {
2950 UINT edge = -1;
2951 appBarData.hWnd = FindWindow(L"Shell_TrayWnd", nullptr);
2952 if (appBarData.hWnd) {
2953 HMONITOR taskbarMonitor =
2954 ::MonitorFromWindow(appBarData.hWnd, MONITOR_DEFAULTTOPRIMARY);
2955 HMONITOR windowMonitor =
2956 ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONEAREST);
2957 if (taskbarMonitor == windowMonitor) {
2958 SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData);
2959 edge = appBarData.uEdge;
2960 }
2961 }
2962
2963 if (ABE_LEFT == edge) {
2964 mNonClientOffset.left -= 1;
2965 } else if (ABE_RIGHT == edge) {
2966 mNonClientOffset.right -= 1;
2967 } else if (ABE_BOTTOM == edge || ABE_TOP == edge) {
2968 mNonClientOffset.bottom -= 1;
2969 }
2970 }
2971 } else {
2972 bool glass = gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
2973
2974 // We're dealing with a "normal" window (not maximized, minimized, or
2975 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2976 // accordingly.
2977 //
2978 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2979 // frame intact. Setting it to a value greater than 0 reduces the frame
2980 // size by that amount.
2981
2982 if (mNonClientMargins.top > 0 && glass) {
2983 mNonClientOffset.top = std::min(mCaptionHeight, mNonClientMargins.top);
2984 } else if (mNonClientMargins.top == 0) {
2985 mNonClientOffset.top = mCaptionHeight;
2986 } else {
2987 mNonClientOffset.top = 0;
2988 }
2989
2990 if (mNonClientMargins.bottom > 0 && glass) {
2991 mNonClientOffset.bottom =
2992 std::min(mVertResizeMargin, mNonClientMargins.bottom);
2993 } else if (mNonClientMargins.bottom == 0) {
2994 mNonClientOffset.bottom = mVertResizeMargin;
2995 } else {
2996 mNonClientOffset.bottom = 0;
2997 }
2998
2999 if (mNonClientMargins.left > 0 && glass) {
3000 mNonClientOffset.left =
3001 std::min(mHorResizeMargin, mNonClientMargins.left);
3002 } else if (mNonClientMargins.left == 0) {
3003 mNonClientOffset.left = mHorResizeMargin;
3004 } else {
3005 mNonClientOffset.left = 0;
3006 }
3007
3008 if (mNonClientMargins.right > 0 && glass) {
3009 mNonClientOffset.right =
3010 std::min(mHorResizeMargin, mNonClientMargins.right);
3011 } else if (mNonClientMargins.right == 0) {
3012 mNonClientOffset.right = mHorResizeMargin;
3013 } else {
3014 mNonClientOffset.right = 0;
3015 }
3016 }
3017
3018 if (aReflowWindow) {
3019 // Force a reflow of content based on the new client
3020 // dimensions.
3021 ResetLayout();
3022 }
3023
3024 return true;
3025 }
3026
SetNonClientMargins(LayoutDeviceIntMargin & margins)3027 nsresult nsWindow::SetNonClientMargins(LayoutDeviceIntMargin& margins) {
3028 if (!mIsTopWidgetWindow || mBorderStyle == eBorderStyle_none)
3029 return NS_ERROR_INVALID_ARG;
3030
3031 if (mHideChrome) {
3032 mFutureMarginsOnceChromeShows = margins;
3033 mFutureMarginsToUse = true;
3034 return NS_OK;
3035 }
3036 mFutureMarginsToUse = false;
3037
3038 // Request for a reset
3039 if (margins.top == -1 && margins.left == -1 && margins.right == -1 &&
3040 margins.bottom == -1) {
3041 mCustomNonClient = false;
3042 mNonClientMargins = margins;
3043 // Force a reflow of content based on the new client
3044 // dimensions.
3045 ResetLayout();
3046
3047 int windowStatus =
3048 reinterpret_cast<LONG_PTR>(GetPropW(mWnd, kManageWindowInfoProperty));
3049 if (windowStatus) {
3050 ::SendMessageW(mWnd, WM_NCACTIVATE, 1 != windowStatus, 0);
3051 }
3052
3053 return NS_OK;
3054 }
3055
3056 if (margins.top < -1 || margins.bottom < -1 || margins.left < -1 ||
3057 margins.right < -1)
3058 return NS_ERROR_INVALID_ARG;
3059
3060 mNonClientMargins = margins;
3061 mCustomNonClient = true;
3062 if (!UpdateNonClientMargins()) {
3063 NS_WARNING("UpdateNonClientMargins failed!");
3064 return NS_OK;
3065 }
3066
3067 return NS_OK;
3068 }
3069
InvalidateNonClientRegion()3070 void nsWindow::InvalidateNonClientRegion() {
3071 // +-+-----------------------+-+
3072 // | | app non-client chrome | |
3073 // | +-----------------------+ |
3074 // | | app client chrome | | }
3075 // | +-----------------------+ | }
3076 // | | app content | | } area we don't want to invalidate
3077 // | +-----------------------+ | }
3078 // | | app client chrome | | }
3079 // | +-----------------------+ |
3080 // +---------------------------+ <
3081 // ^ ^ windows non-client chrome
3082 // client area = app *
3083 RECT rect;
3084 GetWindowRect(mWnd, &rect);
3085 MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
3086 HRGN winRgn = CreateRectRgnIndirect(&rect);
3087
3088 // Subtract app client chrome and app content leaving
3089 // windows non-client chrome and app non-client chrome
3090 // in winRgn.
3091 GetWindowRect(mWnd, &rect);
3092 rect.top += mCaptionHeight;
3093 rect.right -= mHorResizeMargin;
3094 rect.bottom -= mHorResizeMargin;
3095 rect.left += mVertResizeMargin;
3096 MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
3097 HRGN clientRgn = CreateRectRgnIndirect(&rect);
3098 CombineRgn(winRgn, winRgn, clientRgn, RGN_DIFF);
3099 DeleteObject(clientRgn);
3100
3101 // triggers ncpaint and paint events for the two areas
3102 RedrawWindow(mWnd, nullptr, winRgn, RDW_FRAME | RDW_INVALIDATE);
3103 DeleteObject(winRgn);
3104 }
3105
ExcludeNonClientFromPaintRegion(HRGN aRegion)3106 HRGN nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion) {
3107 RECT rect;
3108 HRGN rgn = nullptr;
3109 if (aRegion == (HRGN)1) { // undocumented value indicating a full refresh
3110 GetWindowRect(mWnd, &rect);
3111 rgn = CreateRectRgnIndirect(&rect);
3112 } else {
3113 rgn = aRegion;
3114 }
3115 GetClientRect(mWnd, &rect);
3116 MapWindowPoints(mWnd, nullptr, (LPPOINT)&rect, 2);
3117 HRGN nonClientRgn = CreateRectRgnIndirect(&rect);
3118 CombineRgn(rgn, rgn, nonClientRgn, RGN_DIFF);
3119 DeleteObject(nonClientRgn);
3120 return rgn;
3121 }
3122
3123 /**************************************************************
3124 *
3125 * SECTION: nsIWidget::SetBackgroundColor
3126 *
3127 * Sets the window background paint color.
3128 *
3129 **************************************************************/
3130
SetBackgroundColor(const nscolor & aColor)3131 void nsWindow::SetBackgroundColor(const nscolor& aColor) {
3132 if (mBrush) ::DeleteObject(mBrush);
3133
3134 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(aColor));
3135 if (mWnd != nullptr) {
3136 ::SetClassLongPtrW(mWnd, GCLP_HBRBACKGROUND, (LONG_PTR)mBrush);
3137 }
3138 }
3139
3140 /**************************************************************
3141 *
3142 * SECTION: nsIWidget::SetCursor
3143 *
3144 * SetCursor and related utilities for manging cursor state.
3145 *
3146 **************************************************************/
3147
3148 // Set this component cursor
CursorFor(nsCursor aCursor)3149 static HCURSOR CursorFor(nsCursor aCursor) {
3150 switch (aCursor) {
3151 case eCursor_select:
3152 return ::LoadCursor(nullptr, IDC_IBEAM);
3153 case eCursor_wait:
3154 return ::LoadCursor(nullptr, IDC_WAIT);
3155 case eCursor_hyperlink:
3156 return ::LoadCursor(nullptr, IDC_HAND);
3157 case eCursor_standard:
3158 case eCursor_context_menu: // XXX See bug 258960.
3159 return ::LoadCursor(nullptr, IDC_ARROW);
3160
3161 case eCursor_n_resize:
3162 case eCursor_s_resize:
3163 return ::LoadCursor(nullptr, IDC_SIZENS);
3164
3165 case eCursor_w_resize:
3166 case eCursor_e_resize:
3167 return ::LoadCursor(nullptr, IDC_SIZEWE);
3168
3169 case eCursor_nw_resize:
3170 case eCursor_se_resize:
3171 return ::LoadCursor(nullptr, IDC_SIZENWSE);
3172
3173 case eCursor_ne_resize:
3174 case eCursor_sw_resize:
3175 return ::LoadCursor(nullptr, IDC_SIZENESW);
3176
3177 case eCursor_crosshair:
3178 return ::LoadCursor(nullptr, IDC_CROSS);
3179
3180 case eCursor_move:
3181 return ::LoadCursor(nullptr, IDC_SIZEALL);
3182
3183 case eCursor_help:
3184 return ::LoadCursor(nullptr, IDC_HELP);
3185
3186 case eCursor_copy: // CSS3
3187 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
3188
3189 case eCursor_alias:
3190 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
3191
3192 case eCursor_cell:
3193 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
3194 case eCursor_grab:
3195 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
3196
3197 case eCursor_grabbing:
3198 return ::LoadCursor(nsToolkit::mDllInstance,
3199 MAKEINTRESOURCE(IDC_GRABBING));
3200
3201 case eCursor_spinning:
3202 return ::LoadCursor(nullptr, IDC_APPSTARTING);
3203
3204 case eCursor_zoom_in:
3205 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
3206
3207 case eCursor_zoom_out:
3208 return ::LoadCursor(nsToolkit::mDllInstance,
3209 MAKEINTRESOURCE(IDC_ZOOMOUT));
3210
3211 case eCursor_not_allowed:
3212 case eCursor_no_drop:
3213 return ::LoadCursor(nullptr, IDC_NO);
3214
3215 case eCursor_col_resize:
3216 return ::LoadCursor(nsToolkit::mDllInstance,
3217 MAKEINTRESOURCE(IDC_COLRESIZE));
3218
3219 case eCursor_row_resize:
3220 return ::LoadCursor(nsToolkit::mDllInstance,
3221 MAKEINTRESOURCE(IDC_ROWRESIZE));
3222
3223 case eCursor_vertical_text:
3224 return ::LoadCursor(nsToolkit::mDllInstance,
3225 MAKEINTRESOURCE(IDC_VERTICALTEXT));
3226
3227 case eCursor_all_scroll:
3228 // XXX not 100% appropriate perhaps
3229 return ::LoadCursor(nullptr, IDC_SIZEALL);
3230
3231 case eCursor_nesw_resize:
3232 return ::LoadCursor(nullptr, IDC_SIZENESW);
3233
3234 case eCursor_nwse_resize:
3235 return ::LoadCursor(nullptr, IDC_SIZENWSE);
3236
3237 case eCursor_ns_resize:
3238 return ::LoadCursor(nullptr, IDC_SIZENS);
3239
3240 case eCursor_ew_resize:
3241 return ::LoadCursor(nullptr, IDC_SIZEWE);
3242
3243 case eCursor_none:
3244 return ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
3245
3246 default:
3247 NS_ERROR("Invalid cursor type");
3248 return nullptr;
3249 }
3250 }
3251
CursorForImage(const nsIWidget::Cursor & aCursor,CSSToLayoutDeviceScale aScale)3252 static HCURSOR CursorForImage(const nsIWidget::Cursor& aCursor,
3253 CSSToLayoutDeviceScale aScale) {
3254 if (!aCursor.IsCustom()) {
3255 return nullptr;
3256 }
3257
3258 nsIntSize size = nsIWidget::CustomCursorSize(aCursor);
3259
3260 // Reject cursors greater than 128 pixels in either direction, to prevent
3261 // spoofing.
3262 // XXX ideally we should rescale. Also, we could modify the API to
3263 // allow trusted content to set larger cursors.
3264 if (size.width > 128 || size.height > 128) {
3265 return nullptr;
3266 }
3267
3268 LayoutDeviceIntSize layoutSize =
3269 RoundedToInt(CSSIntSize(size.width, size.height) * aScale);
3270 LayoutDeviceIntPoint hotspot =
3271 RoundedToInt(CSSIntPoint(aCursor.mHotspotX, aCursor.mHotspotY) * aScale);
3272 HCURSOR cursor;
3273 nsresult rv = nsWindowGfx::CreateIcon(aCursor.mContainer, true, hotspot,
3274 layoutSize, &cursor);
3275 if (NS_FAILED(rv)) {
3276 return nullptr;
3277 }
3278
3279 return cursor;
3280 }
3281
3282 // Setting the actual cursor
SetCursor(const Cursor & aCursor)3283 void nsWindow::SetCursor(const Cursor& aCursor) {
3284 mCursor = aCursor;
3285
3286 if (sCurrentCursor == aCursor && sCustomHCursor) {
3287 ::SetCursor(sCustomHCursor);
3288 return;
3289 }
3290
3291 if (sCustomHCursor) {
3292 ::DestroyIcon(sCustomHCursor);
3293 sCustomHCursor = nullptr;
3294 }
3295
3296 sCurrentCursor = aCursor;
3297 HCURSOR cursor = CursorForImage(aCursor, GetDefaultScale());
3298 if (cursor) {
3299 sCustomHCursor = cursor;
3300 } else {
3301 cursor = CursorFor(aCursor.mDefaultCursor);
3302 }
3303
3304 if (!cursor) {
3305 return;
3306 }
3307
3308 ::SetCursor(cursor);
3309 }
3310
3311 /**************************************************************
3312 *
3313 * SECTION: nsIWidget::Get/SetTransparencyMode
3314 *
3315 * Manage the transparency mode of the window containing this
3316 * widget. Only works for popup and dialog windows when the
3317 * Desktop Window Manager compositor is not enabled.
3318 *
3319 **************************************************************/
3320
3321 #ifdef MOZ_XUL
GetTransparencyMode()3322 nsTransparencyMode nsWindow::GetTransparencyMode() {
3323 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
3324 }
3325
SetTransparencyMode(nsTransparencyMode aMode)3326 void nsWindow::SetTransparencyMode(nsTransparencyMode aMode) {
3327 nsWindow* window = GetTopLevelWindow(true);
3328 MOZ_ASSERT(window);
3329
3330 if (!window || window->DestroyCalled()) {
3331 return;
3332 }
3333
3334 if (nsWindowType::eWindowType_toplevel == window->mWindowType &&
3335 mTransparencyMode != aMode &&
3336 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3337 NS_WARNING("Cannot set transparency mode on top-level windows.");
3338 return;
3339 }
3340
3341 window->SetWindowTranslucencyInner(aMode);
3342 }
3343
UpdateOpaqueRegion(const LayoutDeviceIntRegion & aOpaqueRegion)3344 void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion) {
3345 if (!HasGlass() || GetParent()) return;
3346
3347 // If there is no opaque region or hidechrome=true, set margins
3348 // to support a full sheet of glass. Comments in MSDN indicate
3349 // all values must be set to -1 to get a full sheet of glass.
3350 MARGINS margins = {-1, -1, -1, -1};
3351 if (!aOpaqueRegion.IsEmpty()) {
3352 LayoutDeviceIntRect pluginBounds;
3353 for (nsIWidget* child = GetFirstChild(); child;
3354 child = child->GetNextSibling()) {
3355 if (child->IsPlugin()) {
3356 // Collect the bounds of all plugins for GetLargestRectangle.
3357 LayoutDeviceIntRect childBounds = child->GetBounds();
3358 pluginBounds.UnionRect(pluginBounds, childBounds);
3359 }
3360 }
3361
3362 LayoutDeviceIntRect clientBounds = GetClientBounds();
3363
3364 // Find the largest rectangle and use that to calculate the inset. Our top
3365 // priority is to include the bounds of all plugins.
3366 LayoutDeviceIntRect largest =
3367 aOpaqueRegion.GetLargestRectangle(pluginBounds);
3368 margins.cxLeftWidth = largest.X();
3369 margins.cxRightWidth = clientBounds.Width() - largest.XMost();
3370 margins.cyBottomHeight = clientBounds.Height() - largest.YMost();
3371 if (mCustomNonClient) {
3372 // The minimum glass height must be the caption buttons height,
3373 // otherwise the buttons are drawn incorrectly.
3374 largest.MoveToY(std::max<uint32_t>(
3375 largest.Y(), nsUXThemeData::GetCommandButtonBoxMetrics().cy));
3376 }
3377 margins.cyTopHeight = largest.Y();
3378 }
3379
3380 // Only update glass area if there are changes
3381 if (memcmp(&mGlassMargins, &margins, sizeof mGlassMargins)) {
3382 mGlassMargins = margins;
3383 UpdateGlass();
3384 }
3385 }
3386
3387 /**************************************************************
3388 *
3389 * SECTION: nsIWidget::UpdateWindowDraggingRegion
3390 *
3391 * For setting the draggable titlebar region from CSS
3392 * with -moz-window-dragging: drag.
3393 *
3394 **************************************************************/
3395
UpdateWindowDraggingRegion(const LayoutDeviceIntRegion & aRegion)3396 void nsWindow::UpdateWindowDraggingRegion(
3397 const LayoutDeviceIntRegion& aRegion) {
3398 if (mDraggableRegion != aRegion) {
3399 mDraggableRegion = aRegion;
3400 }
3401 }
3402
UpdateGlass()3403 void nsWindow::UpdateGlass() {
3404 MARGINS margins = mGlassMargins;
3405
3406 // DWMNCRP_USEWINDOWSTYLE - The non-client rendering area is
3407 // rendered based on the window style.
3408 // DWMNCRP_ENABLED - The non-client area rendering is
3409 // enabled; the window style is ignored.
3410 DWMNCRENDERINGPOLICY policy = DWMNCRP_USEWINDOWSTYLE;
3411 switch (mTransparencyMode) {
3412 case eTransparencyBorderlessGlass:
3413 // Only adjust if there is some opaque rectangle
3414 if (margins.cxLeftWidth >= 0) {
3415 margins.cxLeftWidth += kGlassMarginAdjustment;
3416 margins.cyTopHeight += kGlassMarginAdjustment;
3417 margins.cxRightWidth += kGlassMarginAdjustment;
3418 margins.cyBottomHeight += kGlassMarginAdjustment;
3419 }
3420 // Fall through
3421 case eTransparencyGlass:
3422 policy = DWMNCRP_ENABLED;
3423 break;
3424 default:
3425 break;
3426 }
3427
3428 MOZ_LOG(gWindowsLog, LogLevel::Info,
3429 ("glass margins: left:%d top:%d right:%d bottom:%d\n",
3430 margins.cxLeftWidth, margins.cyTopHeight, margins.cxRightWidth,
3431 margins.cyBottomHeight));
3432
3433 // Extends the window frame behind the client area
3434 if (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3435 DwmExtendFrameIntoClientArea(mWnd, &margins);
3436 DwmSetWindowAttribute(mWnd, DWMWA_NCRENDERING_POLICY, &policy,
3437 sizeof policy);
3438 }
3439 }
3440 #endif
3441
3442 /**************************************************************
3443 *
3444 * SECTION: nsIWidget::HideWindowChrome
3445 *
3446 * Show or hide window chrome.
3447 *
3448 **************************************************************/
3449
HideWindowChrome(bool aShouldHide)3450 void nsWindow::HideWindowChrome(bool aShouldHide) {
3451 HWND hwnd = WinUtils::GetTopLevelHWND(mWnd, true);
3452 if (!WinUtils::GetNSWindowPtr(hwnd)) {
3453 NS_WARNING("Trying to hide window decorations in an embedded context");
3454 return;
3455 }
3456
3457 if (mHideChrome == aShouldHide) return;
3458
3459 DWORD_PTR style, exStyle;
3460 mHideChrome = aShouldHide;
3461 if (aShouldHide) {
3462 DWORD_PTR tempStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
3463 DWORD_PTR tempExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
3464
3465 style = tempStyle & ~(WS_CAPTION | WS_THICKFRAME);
3466 exStyle = tempExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
3467 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
3468
3469 mOldStyle = tempStyle;
3470 mOldExStyle = tempExStyle;
3471 } else {
3472 if (!mOldStyle || !mOldExStyle) {
3473 mOldStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
3474 mOldExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
3475 }
3476
3477 style = mOldStyle;
3478 exStyle = mOldExStyle;
3479 if (mFutureMarginsToUse) {
3480 SetNonClientMargins(mFutureMarginsOnceChromeShows);
3481 }
3482 }
3483
3484 VERIFY_WINDOW_STYLE(style);
3485 ::SetWindowLongPtrW(hwnd, GWL_STYLE, style);
3486 ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle);
3487 }
3488
3489 /**************************************************************
3490 *
3491 * SECTION: nsWindow::Invalidate
3492 *
3493 * Invalidate an area of the client for painting.
3494 *
3495 **************************************************************/
3496
3497 // Invalidate this component visible area
Invalidate(bool aEraseBackground,bool aUpdateNCArea,bool aIncludeChildren)3498 void nsWindow::Invalidate(bool aEraseBackground, bool aUpdateNCArea,
3499 bool aIncludeChildren) {
3500 if (!mWnd) {
3501 return;
3502 }
3503
3504 #ifdef WIDGET_DEBUG_OUTPUT
3505 debug_DumpInvalidate(stdout, this, nullptr, "noname", (int32_t)mWnd);
3506 #endif // WIDGET_DEBUG_OUTPUT
3507
3508 DWORD flags = RDW_INVALIDATE;
3509 if (aEraseBackground) {
3510 flags |= RDW_ERASE;
3511 }
3512 if (aUpdateNCArea) {
3513 flags |= RDW_FRAME;
3514 }
3515 if (aIncludeChildren) {
3516 flags |= RDW_ALLCHILDREN;
3517 }
3518
3519 VERIFY(::RedrawWindow(mWnd, nullptr, nullptr, flags));
3520 }
3521
3522 // Invalidate this component visible area
Invalidate(const LayoutDeviceIntRect & aRect)3523 void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) {
3524 if (mWnd) {
3525 #ifdef WIDGET_DEBUG_OUTPUT
3526 debug_DumpInvalidate(stdout, this, &aRect, "noname", (int32_t)mWnd);
3527 #endif // WIDGET_DEBUG_OUTPUT
3528
3529 RECT rect;
3530
3531 rect.left = aRect.X();
3532 rect.top = aRect.Y();
3533 rect.right = aRect.XMost();
3534 rect.bottom = aRect.YMost();
3535
3536 VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
3537 }
3538 }
3539
FullscreenTransitionWindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)3540 static LRESULT CALLBACK FullscreenTransitionWindowProc(HWND hWnd, UINT uMsg,
3541 WPARAM wParam,
3542 LPARAM lParam) {
3543 switch (uMsg) {
3544 case WM_FULLSCREEN_TRANSITION_BEFORE:
3545 case WM_FULLSCREEN_TRANSITION_AFTER: {
3546 DWORD duration = (DWORD)lParam;
3547 DWORD flags = AW_BLEND;
3548 if (uMsg == WM_FULLSCREEN_TRANSITION_AFTER) {
3549 flags |= AW_HIDE;
3550 }
3551 ::AnimateWindow(hWnd, duration, flags);
3552 // The message sender should have added ref for us.
3553 NS_DispatchToMainThread(
3554 already_AddRefed<nsIRunnable>((nsIRunnable*)wParam));
3555 break;
3556 }
3557 case WM_DESTROY:
3558 ::PostQuitMessage(0);
3559 break;
3560 default:
3561 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
3562 }
3563 return 0;
3564 }
3565
3566 struct FullscreenTransitionInitData {
3567 nsIntRect mBounds;
3568 HANDLE mSemaphore;
3569 HANDLE mThread;
3570 HWND mWnd;
3571
FullscreenTransitionInitDataFullscreenTransitionInitData3572 FullscreenTransitionInitData()
3573 : mSemaphore(nullptr), mThread(nullptr), mWnd(nullptr) {}
3574
~FullscreenTransitionInitDataFullscreenTransitionInitData3575 ~FullscreenTransitionInitData() {
3576 if (mSemaphore) {
3577 ::CloseHandle(mSemaphore);
3578 }
3579 if (mThread) {
3580 ::CloseHandle(mThread);
3581 }
3582 }
3583 };
3584
FullscreenTransitionThreadProc(LPVOID lpParam)3585 static DWORD WINAPI FullscreenTransitionThreadProc(LPVOID lpParam) {
3586 // Initialize window class
3587 static bool sInitialized = false;
3588 if (!sInitialized) {
3589 WNDCLASSW wc = {};
3590 wc.lpfnWndProc = ::FullscreenTransitionWindowProc;
3591 wc.hInstance = nsToolkit::mDllInstance;
3592 wc.hbrBackground = ::CreateSolidBrush(RGB(0, 0, 0));
3593 wc.lpszClassName = kClassNameTransition;
3594 ::RegisterClassW(&wc);
3595 sInitialized = true;
3596 }
3597
3598 auto data = static_cast<FullscreenTransitionInitData*>(lpParam);
3599 HWND wnd = ::CreateWindowW(kClassNameTransition, L"", 0, 0, 0, 0, 0, nullptr,
3600 nullptr, nsToolkit::mDllInstance, nullptr);
3601 if (!wnd) {
3602 ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
3603 return 0;
3604 }
3605
3606 // Since AnimateWindow blocks the thread of the transition window,
3607 // we need to hide the cursor for that window, otherwise the system
3608 // would show the busy pointer to the user.
3609 ::ShowCursor(false);
3610 ::SetWindowLongW(wnd, GWL_STYLE, 0);
3611 ::SetWindowLongW(
3612 wnd, GWL_EXSTYLE,
3613 WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE);
3614 ::SetWindowPos(wnd, HWND_TOPMOST, data->mBounds.X(), data->mBounds.Y(),
3615 data->mBounds.Width(), data->mBounds.Height(), 0);
3616 data->mWnd = wnd;
3617 ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
3618 // The initialization data may no longer be valid
3619 // after we release the semaphore.
3620 data = nullptr;
3621
3622 MSG msg;
3623 while (::GetMessageW(&msg, nullptr, 0, 0)) {
3624 ::TranslateMessage(&msg);
3625 ::DispatchMessage(&msg);
3626 }
3627 ::ShowCursor(true);
3628 ::DestroyWindow(wnd);
3629 return 0;
3630 }
3631
3632 class FullscreenTransitionData final : public nsISupports {
3633 public:
3634 NS_DECL_ISUPPORTS
3635
FullscreenTransitionData(HWND aWnd)3636 explicit FullscreenTransitionData(HWND aWnd) : mWnd(aWnd) {
3637 MOZ_ASSERT(NS_IsMainThread(),
3638 "FullscreenTransitionData "
3639 "should be constructed in the main thread");
3640 }
3641
3642 const HWND mWnd;
3643
3644 private:
~FullscreenTransitionData()3645 ~FullscreenTransitionData() {
3646 MOZ_ASSERT(NS_IsMainThread(),
3647 "FullscreenTransitionData "
3648 "should be deconstructed in the main thread");
3649 ::PostMessageW(mWnd, WM_DESTROY, 0, 0);
3650 }
3651 };
3652
NS_IMPL_ISUPPORTS0(FullscreenTransitionData)3653 NS_IMPL_ISUPPORTS0(FullscreenTransitionData)
3654
3655 /* virtual */
3656 bool nsWindow::PrepareForFullscreenTransition(nsISupports** aData) {
3657 // We don't support fullscreen transition when composition is not
3658 // enabled, which could make the transition broken and annoying.
3659 // See bug 1184201.
3660 if (!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
3661 return false;
3662 }
3663
3664 FullscreenTransitionInitData initData;
3665 nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
3666 int32_t x, y, width, height;
3667 screen->GetRectDisplayPix(&x, &y, &width, &height);
3668 MOZ_ASSERT(BoundsUseDesktopPixels(),
3669 "Should only be called on top-level window");
3670 double scale = GetDesktopToDeviceScale().scale; // XXX or GetDefaultScale() ?
3671 initData.mBounds.SetRect(NSToIntRound(x * scale), NSToIntRound(y * scale),
3672 NSToIntRound(width * scale),
3673 NSToIntRound(height * scale));
3674
3675 // Create a semaphore for synchronizing the window handle which will
3676 // be created by the transition thread and used by the main thread for
3677 // posting the transition messages.
3678 initData.mSemaphore = ::CreateSemaphore(nullptr, 0, 1, nullptr);
3679 if (initData.mSemaphore) {
3680 initData.mThread = ::CreateThread(
3681 nullptr, 0, FullscreenTransitionThreadProc, &initData, 0, nullptr);
3682 if (initData.mThread) {
3683 ::WaitForSingleObject(initData.mSemaphore, INFINITE);
3684 }
3685 }
3686 if (!initData.mWnd) {
3687 return false;
3688 }
3689
3690 mTransitionWnd = initData.mWnd;
3691
3692 auto data = new FullscreenTransitionData(initData.mWnd);
3693 *aData = data;
3694 NS_ADDREF(data);
3695 return true;
3696 }
3697
3698 /* virtual */
PerformFullscreenTransition(FullscreenTransitionStage aStage,uint16_t aDuration,nsISupports * aData,nsIRunnable * aCallback)3699 void nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage,
3700 uint16_t aDuration,
3701 nsISupports* aData,
3702 nsIRunnable* aCallback) {
3703 auto data = static_cast<FullscreenTransitionData*>(aData);
3704 nsCOMPtr<nsIRunnable> callback = aCallback;
3705 UINT msg = aStage == eBeforeFullscreenToggle ? WM_FULLSCREEN_TRANSITION_BEFORE
3706 : WM_FULLSCREEN_TRANSITION_AFTER;
3707 WPARAM wparam = (WPARAM)callback.forget().take();
3708 ::PostMessage(data->mWnd, msg, wparam, (LPARAM)aDuration);
3709 }
3710
3711 /* virtual */
CleanupFullscreenTransition()3712 void nsWindow::CleanupFullscreenTransition() {
3713 MOZ_ASSERT(NS_IsMainThread(),
3714 "CleanupFullscreenTransition "
3715 "should only run on the main thread");
3716
3717 mTransitionWnd = nullptr;
3718 }
3719
MakeFullScreen(bool aFullScreen,nsIScreen * aTargetScreen)3720 nsresult nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen) {
3721 if (mFullscreenMode == aFullScreen) {
3722 return NS_OK;
3723 }
3724
3725 if (mWidgetListener) {
3726 mWidgetListener->FullscreenWillChange(aFullScreen);
3727 }
3728
3729 mFullscreenMode = aFullScreen;
3730 if (aFullScreen) {
3731 mOldSizeMode = mSizeMode;
3732 SetSizeModeInternal(nsSizeMode_Fullscreen);
3733 } else {
3734 if (mSizeMode != mOldSizeMode) {
3735 SetSizeModeInternal(mOldSizeMode);
3736 }
3737
3738 MOZ_ASSERT(mSizeMode == mOldSizeMode);
3739 }
3740
3741 // taskbarInfo will be nullptr pre Windows 7 until Bug 680227 is resolved.
3742 nsCOMPtr<nsIWinTaskbar> taskbarInfo = do_GetService(NS_TASKBAR_CONTRACTID);
3743
3744 // Notify the taskbar that we will be entering full screen mode.
3745 if (aFullScreen && taskbarInfo) {
3746 taskbarInfo->PrepareFullScreenHWND(mWnd, TRUE);
3747 }
3748
3749 // If we are going fullscreen, the window size continues to change
3750 // and the window will be reflow again then.
3751 UpdateNonClientMargins(mSizeMode, /* Reflow */ !aFullScreen);
3752
3753 // Will call hide chrome, reposition window. Note this will
3754 // also cache dimensions for restoration, so it should only
3755 // be called once per fullscreen request.
3756 nsBaseWidget::InfallibleMakeFullScreen(aFullScreen, aTargetScreen);
3757
3758 if (mIsVisible && !aFullScreen && mSizeMode == nsSizeMode_Normal) {
3759 MOZ_ASSERT(mSizeMode == mOldSizeMode);
3760
3761 // Ensure the window exiting fullscreen get activated. Window
3762 // activation might be bypassed in SetSizeMode.
3763 DispatchFocusToTopLevelWindow(true);
3764 }
3765
3766 // Notify the taskbar that we have exited full screen mode.
3767 if (!aFullScreen && taskbarInfo) {
3768 taskbarInfo->PrepareFullScreenHWND(mWnd, FALSE);
3769 }
3770
3771 OnSizeModeChange(mSizeMode);
3772
3773 if (mWidgetListener) {
3774 mWidgetListener->FullscreenChanged(aFullScreen);
3775 }
3776
3777 return NS_OK;
3778 }
3779
3780 /**************************************************************
3781 *
3782 * SECTION: Native data storage
3783 *
3784 * nsIWidget::GetNativeData
3785 * nsIWidget::FreeNativeData
3786 *
3787 * Set or clear native data based on a constant.
3788 *
3789 **************************************************************/
3790
3791 // Return some native data according to aDataType
GetNativeData(uint32_t aDataType)3792 void* nsWindow::GetNativeData(uint32_t aDataType) {
3793 switch (aDataType) {
3794 case NS_NATIVE_TMP_WINDOW:
3795 return (void*)::CreateWindowExW(
3796 mIsRTL ? WS_EX_LAYOUTRTL : 0, GetWindowClass(), L"", WS_CHILD,
3797 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, mWnd,
3798 nullptr, nsToolkit::mDllInstance, nullptr);
3799 case NS_NATIVE_PLUGIN_ID:
3800 case NS_NATIVE_PLUGIN_PORT:
3801 case NS_NATIVE_WIDGET:
3802 case NS_NATIVE_WINDOW:
3803 case NS_NATIVE_WINDOW_WEBRTC_DEVICE_ID:
3804 return (void*)mWnd;
3805 case NS_NATIVE_GRAPHIC:
3806 MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
3807 return nullptr;
3808 case NS_RAW_NATIVE_IME_CONTEXT: {
3809 void* pseudoIMEContext = GetPseudoIMEContext();
3810 if (pseudoIMEContext) {
3811 return pseudoIMEContext;
3812 }
3813 [[fallthrough]];
3814 }
3815 case NS_NATIVE_TSF_THREAD_MGR:
3816 case NS_NATIVE_TSF_CATEGORY_MGR:
3817 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
3818 return IMEHandler::GetNativeData(this, aDataType);
3819
3820 default:
3821 break;
3822 }
3823
3824 return nullptr;
3825 }
3826
SetNativeData(uint32_t aDataType,uintptr_t aVal)3827 void nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal) {
3828 NS_ERROR("SetNativeData called with unsupported data type.");
3829 }
3830
3831 // Free some native data according to aDataType
FreeNativeData(void * data,uint32_t aDataType)3832 void nsWindow::FreeNativeData(void* data, uint32_t aDataType) {
3833 switch (aDataType) {
3834 case NS_NATIVE_GRAPHIC:
3835 case NS_NATIVE_WIDGET:
3836 case NS_NATIVE_WINDOW:
3837 case NS_NATIVE_PLUGIN_PORT:
3838 break;
3839 default:
3840 break;
3841 }
3842 }
3843
3844 /**************************************************************
3845 *
3846 * SECTION: nsIWidget::SetTitle
3847 *
3848 * Set the main windows title text.
3849 *
3850 **************************************************************/
3851
SetTitle(const nsAString & aTitle)3852 nsresult nsWindow::SetTitle(const nsAString& aTitle) {
3853 const nsString& strTitle = PromiseFlatString(aTitle);
3854 AutoRestore<bool> sendingText(mSendingSetText);
3855 mSendingSetText = true;
3856 ::SendMessageW(mWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)(LPCWSTR)strTitle.get());
3857 return NS_OK;
3858 }
3859
3860 /**************************************************************
3861 *
3862 * SECTION: nsIWidget::SetIcon
3863 *
3864 * Set the main windows icon.
3865 *
3866 **************************************************************/
3867
SetBigIcon(HICON aIcon)3868 void nsWindow::SetBigIcon(HICON aIcon) {
3869 HICON icon =
3870 (HICON)::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)aIcon);
3871 if (icon) {
3872 ::DestroyIcon(icon);
3873 }
3874
3875 mIconBig = aIcon;
3876 }
3877
SetSmallIcon(HICON aIcon)3878 void nsWindow::SetSmallIcon(HICON aIcon) {
3879 HICON icon = (HICON)::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_SMALL,
3880 (LPARAM)aIcon);
3881 if (icon) {
3882 ::DestroyIcon(icon);
3883 }
3884
3885 mIconSmall = aIcon;
3886 }
3887
SetIcon(const nsAString & aIconSpec)3888 void nsWindow::SetIcon(const nsAString& aIconSpec) {
3889 // Assume the given string is a local identifier for an icon file.
3890
3891 nsCOMPtr<nsIFile> iconFile;
3892 ResolveIconName(aIconSpec, u".ico"_ns, getter_AddRefs(iconFile));
3893 if (!iconFile) return;
3894
3895 nsAutoString iconPath;
3896 iconFile->GetPath(iconPath);
3897
3898 // XXX this should use MZLU (see bug 239279)
3899
3900 ::SetLastError(0);
3901
3902 HICON bigIcon =
3903 (HICON)::LoadImageW(nullptr, (LPCWSTR)iconPath.get(), IMAGE_ICON,
3904 ::GetSystemMetrics(SM_CXICON),
3905 ::GetSystemMetrics(SM_CYICON), LR_LOADFROMFILE);
3906 HICON smallIcon =
3907 (HICON)::LoadImageW(nullptr, (LPCWSTR)iconPath.get(), IMAGE_ICON,
3908 ::GetSystemMetrics(SM_CXSMICON),
3909 ::GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE);
3910
3911 if (bigIcon) {
3912 SetBigIcon(bigIcon);
3913 }
3914 #ifdef DEBUG_SetIcon
3915 else {
3916 NS_LossyConvertUTF16toASCII cPath(iconPath);
3917 MOZ_LOG(gWindowsLog, LogLevel::Info,
3918 ("\nIcon load error; icon=%s, rc=0x%08X\n\n", cPath.get(),
3919 ::GetLastError()));
3920 }
3921 #endif
3922 if (smallIcon) {
3923 SetSmallIcon(smallIcon);
3924 }
3925 #ifdef DEBUG_SetIcon
3926 else {
3927 NS_LossyConvertUTF16toASCII cPath(iconPath);
3928 MOZ_LOG(gWindowsLog, LogLevel::Info,
3929 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", cPath.get(),
3930 ::GetLastError()));
3931 }
3932 #endif
3933 }
3934
3935 /**************************************************************
3936 *
3937 * SECTION: nsIWidget::WidgetToScreenOffset
3938 *
3939 * Return this widget's origin in screen coordinates.
3940 *
3941 **************************************************************/
3942
WidgetToScreenOffset()3943 LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() {
3944 POINT point;
3945 point.x = 0;
3946 point.y = 0;
3947 ::ClientToScreen(mWnd, &point);
3948 return LayoutDeviceIntPoint(point.x, point.y);
3949 }
3950
ClientToWindowSize(const LayoutDeviceIntSize & aClientSize)3951 LayoutDeviceIntSize nsWindow::ClientToWindowSize(
3952 const LayoutDeviceIntSize& aClientSize) {
3953 if (mWindowType == eWindowType_popup && !IsPopupWithTitleBar())
3954 return aClientSize;
3955
3956 // just use (200, 200) as the position
3957 RECT r;
3958 r.left = 200;
3959 r.top = 200;
3960 r.right = 200 + aClientSize.width;
3961 r.bottom = 200 + aClientSize.height;
3962 ::AdjustWindowRectEx(&r, WindowStyle(), false, WindowExStyle());
3963
3964 return LayoutDeviceIntSize(r.right - r.left, r.bottom - r.top);
3965 }
3966
3967 /**************************************************************
3968 *
3969 * SECTION: nsIWidget::EnableDragDrop
3970 *
3971 * Enables/Disables drag and drop of files on this widget.
3972 *
3973 **************************************************************/
3974
EnableDragDrop(bool aEnable)3975 void nsWindow::EnableDragDrop(bool aEnable) {
3976 if (!mWnd) {
3977 // Return early if the window already closed
3978 return;
3979 }
3980
3981 if (aEnable) {
3982 if (!mNativeDragTarget) {
3983 mNativeDragTarget = new nsNativeDragTarget(this);
3984 mNativeDragTarget->AddRef();
3985 if (SUCCEEDED(::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, TRUE,
3986 FALSE))) {
3987 ::RegisterDragDrop(mWnd, (LPDROPTARGET)mNativeDragTarget);
3988 }
3989 }
3990 } else {
3991 if (mWnd && mNativeDragTarget) {
3992 ::RevokeDragDrop(mWnd);
3993 ::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, FALSE, TRUE);
3994 mNativeDragTarget->DragCancel();
3995 NS_RELEASE(mNativeDragTarget);
3996 }
3997 }
3998 }
3999
4000 /**************************************************************
4001 *
4002 * SECTION: nsIWidget::CaptureMouse
4003 *
4004 * Enables/Disables system mouse capture.
4005 *
4006 **************************************************************/
4007
CaptureMouse(bool aCapture)4008 void nsWindow::CaptureMouse(bool aCapture) {
4009 TRACKMOUSEEVENT mTrack;
4010 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
4011 mTrack.dwHoverTime = 0;
4012 mTrack.hwndTrack = mWnd;
4013 if (aCapture) {
4014 mTrack.dwFlags = TME_CANCEL | TME_LEAVE;
4015 ::SetCapture(mWnd);
4016 } else {
4017 mTrack.dwFlags = TME_LEAVE;
4018 ::ReleaseCapture();
4019 }
4020 sIsInMouseCapture = aCapture;
4021 TrackMouseEvent(&mTrack);
4022 }
4023
4024 /**************************************************************
4025 *
4026 * SECTION: nsIWidget::CaptureRollupEvents
4027 *
4028 * Dealing with event rollup on destroy for popups. Enables &
4029 * Disables system capture of any and all events that would
4030 * cause a dropdown to be rolled up.
4031 *
4032 **************************************************************/
4033
CaptureRollupEvents(nsIRollupListener * aListener,bool aDoCapture)4034 void nsWindow::CaptureRollupEvents(nsIRollupListener* aListener,
4035 bool aDoCapture) {
4036 if (aDoCapture) {
4037 gRollupListener = aListener;
4038 if (!sMsgFilterHook && !sCallProcHook && !sCallMouseHook) {
4039 RegisterSpecialDropdownHooks();
4040 }
4041 sProcessHook = true;
4042 } else {
4043 gRollupListener = nullptr;
4044 sProcessHook = false;
4045 UnregisterSpecialDropdownHooks();
4046 }
4047 }
4048
4049 /**************************************************************
4050 *
4051 * SECTION: nsIWidget::GetAttention
4052 *
4053 * Bring this window to the user's attention.
4054 *
4055 **************************************************************/
4056
4057 // Draw user's attention to this window until it comes to foreground.
GetAttention(int32_t aCycleCount)4058 nsresult nsWindow::GetAttention(int32_t aCycleCount) {
4059 // Got window?
4060 if (!mWnd) return NS_ERROR_NOT_INITIALIZED;
4061
4062 HWND flashWnd = WinUtils::GetTopLevelHWND(mWnd, false, false);
4063 HWND fgWnd = ::GetForegroundWindow();
4064 // Don't flash if the flash count is 0 or if the foreground window is our
4065 // window handle or that of our owned-most window.
4066 if (aCycleCount == 0 || flashWnd == fgWnd ||
4067 flashWnd == WinUtils::GetTopLevelHWND(fgWnd, false, false)) {
4068 return NS_OK;
4069 }
4070
4071 DWORD defaultCycleCount = 0;
4072 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &defaultCycleCount, 0);
4073
4074 FLASHWINFO flashInfo = {sizeof(FLASHWINFO), flashWnd, FLASHW_ALL,
4075 aCycleCount > 0 ? aCycleCount : defaultCycleCount, 0};
4076 ::FlashWindowEx(&flashInfo);
4077
4078 return NS_OK;
4079 }
4080
StopFlashing()4081 void nsWindow::StopFlashing() {
4082 HWND flashWnd = mWnd;
4083 while (HWND ownerWnd = ::GetWindow(flashWnd, GW_OWNER)) {
4084 flashWnd = ownerWnd;
4085 }
4086
4087 FLASHWINFO flashInfo = {sizeof(FLASHWINFO), flashWnd, FLASHW_STOP, 0, 0};
4088 ::FlashWindowEx(&flashInfo);
4089 }
4090
4091 /**************************************************************
4092 *
4093 * SECTION: nsIWidget::HasPendingInputEvent
4094 *
4095 * Ask whether there user input events pending. All input events are
4096 * included, including those not targeted at this nsIwidget instance.
4097 *
4098 **************************************************************/
4099
HasPendingInputEvent()4100 bool nsWindow::HasPendingInputEvent() {
4101 // If there is pending input or the user is currently
4102 // moving the window then return true.
4103 // Note: When the user is moving the window WIN32 spins
4104 // a separate event loop and input events are not
4105 // reported to the application.
4106 if (HIWORD(GetQueueStatus(QS_INPUT))) return true;
4107 GUITHREADINFO guiInfo;
4108 guiInfo.cbSize = sizeof(GUITHREADINFO);
4109 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo)) return false;
4110 return GUI_INMOVESIZE == (guiInfo.flags & GUI_INMOVESIZE);
4111 }
4112
4113 /**************************************************************
4114 *
4115 * SECTION: nsIWidget::GetLayerManager
4116 *
4117 * Get the layer manager associated with this widget.
4118 *
4119 **************************************************************/
4120
GetLayerManager(PLayerTransactionChild * aShadowManager,LayersBackend aBackendHint,LayerManagerPersistence aPersistence)4121 LayerManager* nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
4122 LayersBackend aBackendHint,
4123 LayerManagerPersistence aPersistence) {
4124 if (mLayerManager) {
4125 return mLayerManager;
4126 }
4127
4128 if (!mLocalesChangedObserver) {
4129 mLocalesChangedObserver = new LocalesChangedObserver(this);
4130 }
4131
4132 RECT windowRect;
4133 ::GetClientRect(mWnd, &windowRect);
4134
4135 // Try OMTC first.
4136 if (!mLayerManager && ShouldUseOffMainThreadCompositing()) {
4137 gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
4138
4139 // e10s uses the parameter to pass in the shadow manager from the
4140 // BrowserChild so we don't expect to see it there since this doesn't
4141 // support e10s.
4142 NS_ASSERTION(aShadowManager == nullptr,
4143 "Async Compositor not supported with e10s");
4144 CreateCompositor();
4145 }
4146
4147 if (!mLayerManager) {
4148 MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild);
4149 MOZ_ASSERT(!mCompositorWidgetDelegate);
4150
4151 // Ensure we have a widget proxy even if we're not using the compositor,
4152 // since all our transparent window handling lives there.
4153 WinCompositorWidgetInitData initData(
4154 reinterpret_cast<uintptr_t>(mWnd),
4155 reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
4156 mTransparencyMode, mSizeMode);
4157 // If we're not using the compositor, the options don't actually matter.
4158 CompositorOptions options(false, false, false);
4159 mBasicLayersSurface =
4160 new InProcessWinCompositorWidget(initData, options, this);
4161 mCompositorWidgetDelegate = mBasicLayersSurface;
4162 mLayerManager = CreateBasicLayerManager();
4163 }
4164
4165 NS_ASSERTION(mLayerManager, "Couldn't provide a valid layer manager.");
4166
4167 if (mLayerManager) {
4168 // Update the size constraints now that the layer manager has been
4169 // created.
4170 KnowsCompositor* knowsCompositor = mLayerManager->AsKnowsCompositor();
4171 if (knowsCompositor) {
4172 SizeConstraints c = mSizeConstraints;
4173 mMaxTextureSize = knowsCompositor->GetMaxTextureSize();
4174 c.mMaxSize.width = std::min(c.mMaxSize.width, mMaxTextureSize);
4175 c.mMaxSize.height = std::min(c.mMaxSize.height, mMaxTextureSize);
4176 nsBaseWidget::SetSizeConstraints(c);
4177 }
4178 }
4179
4180 return mLayerManager;
4181 }
4182
4183 /**************************************************************
4184 *
4185 * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
4186 *
4187 * Called to connect the nsWindow to the delegate providing
4188 * platform compositing API access.
4189 *
4190 **************************************************************/
4191
SetCompositorWidgetDelegate(CompositorWidgetDelegate * delegate)4192 void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) {
4193 if (delegate) {
4194 mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
4195 MOZ_ASSERT(mCompositorWidgetDelegate,
4196 "nsWindow::SetCompositorWidgetDelegate called with a "
4197 "non-PlatformCompositorWidgetDelegate");
4198 } else {
4199 mCompositorWidgetDelegate = nullptr;
4200 }
4201 }
4202
4203 /**************************************************************
4204 *
4205 * SECTION: nsIWidget::OnDefaultButtonLoaded
4206 *
4207 * Called after the dialog is loaded and it has a default button.
4208 *
4209 **************************************************************/
4210
OnDefaultButtonLoaded(const LayoutDeviceIntRect & aButtonRect)4211 nsresult nsWindow::OnDefaultButtonLoaded(
4212 const LayoutDeviceIntRect& aButtonRect) {
4213 if (aButtonRect.IsEmpty()) return NS_OK;
4214
4215 // Don't snap when we are not active.
4216 HWND activeWnd = ::GetActiveWindow();
4217 if (activeWnd != ::GetForegroundWindow() ||
4218 WinUtils::GetTopLevelHWND(mWnd, true) !=
4219 WinUtils::GetTopLevelHWND(activeWnd, true)) {
4220 return NS_OK;
4221 }
4222
4223 bool isAlwaysSnapCursor =
4224 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
4225
4226 if (!isAlwaysSnapCursor) {
4227 BOOL snapDefaultButton;
4228 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapDefaultButton,
4229 0) ||
4230 !snapDefaultButton)
4231 return NS_OK;
4232 }
4233
4234 LayoutDeviceIntRect widgetRect = GetScreenBounds();
4235 LayoutDeviceIntRect buttonRect(aButtonRect + widgetRect.TopLeft());
4236
4237 LayoutDeviceIntPoint centerOfButton(buttonRect.X() + buttonRect.Width() / 2,
4238 buttonRect.Y() + buttonRect.Height() / 2);
4239 // The center of the button can be outside of the widget.
4240 // E.g., it could be hidden by scrolling.
4241 if (!widgetRect.Contains(centerOfButton)) {
4242 return NS_OK;
4243 }
4244
4245 if (!::SetCursorPos(centerOfButton.x, centerOfButton.y)) {
4246 NS_ERROR("SetCursorPos failed");
4247 return NS_ERROR_FAILURE;
4248 }
4249 return NS_OK;
4250 }
4251
UpdateThemeGeometries(const nsTArray<ThemeGeometry> & aThemeGeometries)4252 void nsWindow::UpdateThemeGeometries(
4253 const nsTArray<ThemeGeometry>& aThemeGeometries) {
4254 RefPtr<LayerManager> layerManager = GetLayerManager();
4255 if (!layerManager) {
4256 return;
4257 }
4258
4259 nsIntRegion clearRegion;
4260 if (!HasGlass() ||
4261 !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
4262 // Make sure and clear old regions we've set previously. Note HasGlass can
4263 // be false for glass desktops if the window we are rendering to doesn't
4264 // make use of glass (e.g. fullscreen browsing).
4265 layerManager->SetRegionToClear(clearRegion);
4266 return;
4267 }
4268
4269 // On Win10, force show the top border:
4270 if (IsWin10OrLater() && mCustomNonClient && mSizeMode == nsSizeMode_Normal) {
4271 RECT rect;
4272 ::GetWindowRect(mWnd, &rect);
4273 // We want 1 pixel of border for every whole 100% of scaling
4274 double borderSize = std::min(1, RoundDown(GetDesktopToDeviceScale().scale));
4275 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
4276 0, 0, rect.right - rect.left, borderSize));
4277 }
4278
4279 mWindowButtonsRect = Nothing();
4280
4281 if (!IsWin10OrLater()) {
4282 for (size_t i = 0; i < aThemeGeometries.Length(); i++) {
4283 if (aThemeGeometries[i].mType ==
4284 nsNativeThemeWin::eThemeGeometryTypeWindowButtons) {
4285 LayoutDeviceIntRect bounds = aThemeGeometries[i].mRect;
4286 // Extend the bounds by one pixel to the right, because that's how much
4287 // the actual window button shape extends past the client area of the
4288 // window (and overlaps the right window frame).
4289 bounds.SetWidth(bounds.Width() + 1);
4290 if (!mWindowButtonsRect) {
4291 mWindowButtonsRect = Some(bounds);
4292 }
4293 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
4294 bounds.X(), bounds.Y(), bounds.Width(),
4295 bounds.Height() - 2.0));
4296 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
4297 bounds.X() + 1.0, bounds.YMost() - 2.0,
4298 bounds.Width() - 2.0, 1.0));
4299 clearRegion.Or(clearRegion, gfx::IntRect::Truncate(
4300 bounds.X() + 2.0, bounds.YMost() - 1.0,
4301 bounds.Width() - 4.0, 1.0));
4302 }
4303 }
4304 }
4305
4306 layerManager->SetRegionToClear(clearRegion);
4307 }
4308
AddWindowOverlayWebRenderCommands(layers::WebRenderBridgeChild * aWrBridge,wr::DisplayListBuilder & aBuilder,wr::IpcResourceUpdateQueue & aResources)4309 void nsWindow::AddWindowOverlayWebRenderCommands(
4310 layers::WebRenderBridgeChild* aWrBridge, wr::DisplayListBuilder& aBuilder,
4311 wr::IpcResourceUpdateQueue& aResources) {
4312 if (mWindowButtonsRect) {
4313 wr::LayoutRect rect = wr::ToLayoutRect(*mWindowButtonsRect);
4314 aBuilder.PushClearRect(rect);
4315 }
4316 }
4317
GetMaxTouchPoints() const4318 uint32_t nsWindow::GetMaxTouchPoints() const {
4319 return WinUtils::GetMaxTouchPoints();
4320 }
4321
SetWindowClass(const nsAString & xulWinType)4322 void nsWindow::SetWindowClass(const nsAString& xulWinType) {
4323 mIsEarlyBlankWindow = xulWinType.EqualsLiteral("navigator:blank");
4324 }
4325
4326 /**************************************************************
4327 **************************************************************
4328 **
4329 ** BLOCK: Moz Events
4330 **
4331 ** Moz GUI event management.
4332 **
4333 **************************************************************
4334 **************************************************************/
4335
4336 /**************************************************************
4337 *
4338 * SECTION: Mozilla event initialization
4339 *
4340 * Helpers for initializing moz events.
4341 *
4342 **************************************************************/
4343
4344 // Event initialization
InitEvent(WidgetGUIEvent & event,LayoutDeviceIntPoint * aPoint)4345 void nsWindow::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint) {
4346 if (nullptr == aPoint) { // use the point from the event
4347 // get the message position in client coordinates
4348 if (mWnd != nullptr) {
4349 DWORD pos = ::GetMessagePos();
4350 POINT cpos;
4351
4352 cpos.x = GET_X_LPARAM(pos);
4353 cpos.y = GET_Y_LPARAM(pos);
4354
4355 ::ScreenToClient(mWnd, &cpos);
4356 event.mRefPoint = LayoutDeviceIntPoint(cpos.x, cpos.y);
4357 } else {
4358 event.mRefPoint = LayoutDeviceIntPoint(0, 0);
4359 }
4360 } else {
4361 // use the point override if provided
4362 event.mRefPoint = *aPoint;
4363 }
4364
4365 event.AssignEventTime(CurrentMessageWidgetEventTime());
4366 }
4367
CurrentMessageWidgetEventTime() const4368 WidgetEventTime nsWindow::CurrentMessageWidgetEventTime() const {
4369 LONG messageTime = ::GetMessageTime();
4370 return WidgetEventTime(messageTime, GetMessageTimeStamp(messageTime));
4371 }
4372
4373 /**************************************************************
4374 *
4375 * SECTION: Moz event dispatch helpers
4376 *
4377 * Helpers for dispatching different types of moz events.
4378 *
4379 **************************************************************/
4380
4381 // Main event dispatch. Invokes callback and ProcessEvent method on
4382 // Event Listener object. Part of nsIWidget.
DispatchEvent(WidgetGUIEvent * event,nsEventStatus & aStatus)4383 nsresult nsWindow::DispatchEvent(WidgetGUIEvent* event,
4384 nsEventStatus& aStatus) {
4385 #ifdef WIDGET_DEBUG_OUTPUT
4386 debug_DumpEvent(stdout, event->mWidget, event, "something", (int32_t)mWnd);
4387 #endif // WIDGET_DEBUG_OUTPUT
4388
4389 aStatus = nsEventStatus_eIgnore;
4390
4391 // Top level windows can have a view attached which requires events be sent
4392 // to the underlying base window and the view. Added when we combined the
4393 // base chrome window with the main content child for nc client area (title
4394 // bar) rendering.
4395 if (mAttachedWidgetListener) {
4396 aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
4397 } else if (mWidgetListener) {
4398 aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
4399 }
4400
4401 // the window can be destroyed during processing of seemingly innocuous events
4402 // like, say, mousedowns due to the magic of scripting. mousedowns will return
4403 // nsEventStatus_eIgnore, which causes problems with the deleted window.
4404 // therefore:
4405 if (mOnDestroyCalled) aStatus = nsEventStatus_eConsumeNoDefault;
4406 return NS_OK;
4407 }
4408
DispatchStandardEvent(EventMessage aMsg)4409 bool nsWindow::DispatchStandardEvent(EventMessage aMsg) {
4410 WidgetGUIEvent event(true, aMsg, this);
4411 InitEvent(event);
4412
4413 bool result = DispatchWindowEvent(&event);
4414 return result;
4415 }
4416
DispatchKeyboardEvent(WidgetKeyboardEvent * event)4417 bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent* event) {
4418 nsEventStatus status = DispatchInputEvent(event).mContentStatus;
4419 return ConvertStatus(status);
4420 }
4421
DispatchContentCommandEvent(WidgetContentCommandEvent * aEvent)4422 bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent* aEvent) {
4423 nsEventStatus status;
4424 DispatchEvent(aEvent, status);
4425 return ConvertStatus(status);
4426 }
4427
DispatchWheelEvent(WidgetWheelEvent * aEvent)4428 bool nsWindow::DispatchWheelEvent(WidgetWheelEvent* aEvent) {
4429 nsEventStatus status =
4430 DispatchInputEvent(aEvent->AsInputEvent()).mContentStatus;
4431 return ConvertStatus(status);
4432 }
4433
DispatchWindowEvent(WidgetGUIEvent * event)4434 bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event) {
4435 nsEventStatus status;
4436 DispatchEvent(event, status);
4437 return ConvertStatus(status);
4438 }
4439
DispatchWindowEvent(WidgetGUIEvent * event,nsEventStatus & aStatus)4440 bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event,
4441 nsEventStatus& aStatus) {
4442 DispatchEvent(event, aStatus);
4443 return ConvertStatus(aStatus);
4444 }
4445
4446 // Recursively dispatch synchronous paints for nsIWidget
4447 // descendants with invalidated rectangles.
DispatchStarvedPaints(HWND aWnd,LPARAM aMsg)4448 BOOL CALLBACK nsWindow::DispatchStarvedPaints(HWND aWnd, LPARAM aMsg) {
4449 LONG_PTR proc = ::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
4450 if (proc == (LONG_PTR)&nsWindow::WindowProc) {
4451 // its one of our windows so check to see if it has a
4452 // invalidated rect. If it does. Dispatch a synchronous
4453 // paint.
4454 if (GetUpdateRect(aWnd, nullptr, FALSE)) VERIFY(::UpdateWindow(aWnd));
4455 }
4456 return TRUE;
4457 }
4458
4459 // Check for pending paints and dispatch any pending paint
4460 // messages for any nsIWidget which is a descendant of the
4461 // top-level window that *this* window is embedded within.
4462 //
4463 // Note: We do not dispatch pending paint messages for non
4464 // nsIWidget managed windows.
DispatchPendingEvents()4465 void nsWindow::DispatchPendingEvents() {
4466 if (mPainting) {
4467 NS_WARNING(
4468 "We were asked to dispatch pending events during painting, "
4469 "denying since that's unsafe.");
4470 return;
4471 }
4472
4473 // We need to ensure that reflow events do not get starved.
4474 // At the same time, we don't want to recurse through here
4475 // as that would prevent us from dispatching starved paints.
4476 static int recursionBlocker = 0;
4477 if (recursionBlocker++ == 0) {
4478 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
4479 --recursionBlocker;
4480 }
4481
4482 // Quickly check to see if there are any paint events pending,
4483 // but only dispatch them if it has been long enough since the
4484 // last paint completed.
4485 if (::GetQueueStatus(QS_PAINT) &&
4486 ((TimeStamp::Now() - mLastPaintEndTime).ToMilliseconds() >= 50)) {
4487 // Find the top level window.
4488 HWND topWnd = WinUtils::GetTopLevelHWND(mWnd);
4489
4490 // Dispatch pending paints for topWnd and all its descendant windows.
4491 // Note: EnumChildWindows enumerates all descendant windows not just
4492 // the children (but not the window itself).
4493 nsWindow::DispatchStarvedPaints(topWnd, 0);
4494 ::EnumChildWindows(topWnd, nsWindow::DispatchStarvedPaints, 0);
4495 }
4496 }
4497
DispatchCustomEvent(const nsString & eventName)4498 void nsWindow::DispatchCustomEvent(const nsString& eventName) {
4499 if (Document* doc = GetDocument()) {
4500 if (nsPIDOMWindowOuter* win = doc->GetWindow()) {
4501 win->DispatchCustomEvent(eventName, ChromeOnlyDispatch::eYes);
4502 }
4503 }
4504 }
4505
TouchEventShouldStartDrag(EventMessage aEventMessage,LayoutDeviceIntPoint aEventPoint)4506 bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage,
4507 LayoutDeviceIntPoint aEventPoint) {
4508 // Allow users to start dragging by double-tapping.
4509 if (aEventMessage == eMouseDoubleClick) {
4510 return true;
4511 }
4512
4513 // In chrome UI, allow touchdownstartsdrag attributes
4514 // to cause any touchdown event to trigger a drag.
4515 if (aEventMessage == eMouseDown) {
4516 WidgetMouseEvent hittest(true, eMouseHitTest, this,
4517 WidgetMouseEvent::eReal);
4518 hittest.mRefPoint = aEventPoint;
4519 hittest.mIgnoreRootScrollFrame = true;
4520 hittest.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
4521 DispatchInputEvent(&hittest);
4522
4523 if (EventTarget* target = hittest.GetDOMEventTarget()) {
4524 if (nsCOMPtr<nsIContent> content = do_QueryInterface(target)) {
4525 // Check if the element or any parent element has the
4526 // attribute we're looking for.
4527 for (Element* element = content->GetAsElementOrParentElement(); element;
4528 element = element->GetParentElement()) {
4529 nsAutoString startDrag;
4530 element->GetAttribute(u"touchdownstartsdrag"_ns, startDrag);
4531 if (!startDrag.IsEmpty()) {
4532 return true;
4533 }
4534 }
4535 }
4536 }
4537 }
4538
4539 return false;
4540 }
4541
4542 // Deal with all sort of mouse event
DispatchMouseEvent(EventMessage aEventMessage,WPARAM wParam,LPARAM lParam,bool aIsContextMenuKey,int16_t aButton,uint16_t aInputSource,WinPointerInfo * aPointerInfo)4543 bool nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
4544 LPARAM lParam, bool aIsContextMenuKey,
4545 int16_t aButton, uint16_t aInputSource,
4546 WinPointerInfo* aPointerInfo) {
4547 bool result = false;
4548
4549 UserActivity();
4550
4551 if (!mWidgetListener) {
4552 return result;
4553 }
4554
4555 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
4556 LayoutDeviceIntPoint mpScreen = eventPoint + WidgetToScreenOffset();
4557
4558 // Suppress mouse moves caused by widget creation. Make sure to do this early
4559 // so that we update sLastMouseMovePoint even for touch-induced mousemove
4560 // events.
4561 if (aEventMessage == eMouseMove) {
4562 if ((sLastMouseMovePoint.x == mpScreen.x) &&
4563 (sLastMouseMovePoint.y == mpScreen.y)) {
4564 return result;
4565 }
4566 sLastMouseMovePoint.x = mpScreen.x;
4567 sLastMouseMovePoint.y = mpScreen.y;
4568 }
4569
4570 if (WinUtils::GetIsMouseFromTouch(aEventMessage)) {
4571 if (mTouchWindow) {
4572 // If mTouchWindow is true, then we must have APZ enabled and be
4573 // feeding it raw touch events. In that case we only want to
4574 // send touch-generated mouse events to content if they should
4575 // start a touch-based drag-and-drop gesture, such as on
4576 // double-tapping or when tapping elements marked with the
4577 // touchdownstartsdrag attribute in chrome UI.
4578 MOZ_ASSERT(mAPZC);
4579 if (TouchEventShouldStartDrag(aEventMessage, eventPoint)) {
4580 aEventMessage = eMouseTouchDrag;
4581 } else {
4582 return result;
4583 }
4584 }
4585 }
4586
4587 uint32_t pointerId =
4588 aPointerInfo ? aPointerInfo->pointerId : MOUSE_POINTERID();
4589
4590 // Since it is unclear whether a user will use the digitizer,
4591 // Postpone initialization until first PEN message will be found.
4592 if (MouseEvent_Binding::MOZ_SOURCE_PEN == aInputSource
4593 // Messages should be only at topLevel window.
4594 && nsWindowType::eWindowType_toplevel == mWindowType
4595 // Currently this scheme is used only when pointer events is enabled.
4596 && InkCollector::sInkCollector) {
4597 InkCollector::sInkCollector->SetTarget(mWnd);
4598 InkCollector::sInkCollector->SetPointerId(pointerId);
4599 }
4600
4601 switch (aEventMessage) {
4602 case eMouseDown:
4603 CaptureMouse(true);
4604 break;
4605
4606 // eMouseMove and eMouseExitFromWidget are here because we need to make
4607 // sure capture flag isn't left on after a drag where we wouldn't see a
4608 // button up message (see bug 324131).
4609 case eMouseUp:
4610 case eMouseMove:
4611 case eMouseExitFromWidget:
4612 if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) &&
4613 sIsInMouseCapture)
4614 CaptureMouse(false);
4615 break;
4616
4617 default:
4618 break;
4619
4620 } // switch
4621
4622 WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal,
4623 aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey
4624 : WidgetMouseEvent::eNormal);
4625 if (aEventMessage == eContextMenu && aIsContextMenuKey) {
4626 LayoutDeviceIntPoint zero(0, 0);
4627 InitEvent(event, &zero);
4628 } else {
4629 InitEvent(event, &eventPoint);
4630 }
4631
4632 ModifierKeyState modifierKeyState;
4633 modifierKeyState.InitInputEvent(event);
4634
4635 // eContextMenu with Shift state is special. It won't fire "contextmenu"
4636 // event in the web content for blocking web content to prevent its default.
4637 // However, Shift+F10 is a standard shortcut key on Windows. Therefore,
4638 // this should not block web page to prevent its default. I.e., it should
4639 // behave same as ContextMenu key without Shift key.
4640 // XXX Should we allow to block web page to prevent its default with
4641 // Ctrl+Shift+F10 or Alt+Shift+F10 instead?
4642 if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
4643 NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
4644 NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
4645 event.mModifiers &= ~MODIFIER_SHIFT;
4646 }
4647
4648 event.mButton = aButton;
4649 event.mInputSource = aInputSource;
4650 if (aPointerInfo) {
4651 // Mouse events from Windows WM_POINTER*. Fill more information in
4652 // WidgetMouseEvent.
4653 event.AssignPointerHelperData(*aPointerInfo);
4654 event.mPressure = aPointerInfo->mPressure;
4655 event.mButtons = aPointerInfo->mButtons;
4656 } else {
4657 // If we get here the mouse events must be from non-touch sources, so
4658 // convert it to pointer events as well
4659 event.convertToPointer = true;
4660 event.pointerId = pointerId;
4661 }
4662
4663 bool insideMovementThreshold =
4664 (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) <
4665 (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
4666 (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) <
4667 (short)::GetSystemMetrics(SM_CYDOUBLECLK));
4668
4669 BYTE eventButton;
4670 switch (aButton) {
4671 case MouseButton::ePrimary:
4672 eventButton = VK_LBUTTON;
4673 break;
4674 case MouseButton::eMiddle:
4675 eventButton = VK_MBUTTON;
4676 break;
4677 case MouseButton::eSecondary:
4678 eventButton = VK_RBUTTON;
4679 break;
4680 default:
4681 eventButton = 0;
4682 break;
4683 }
4684
4685 // Doubleclicks are used to set the click count, then changed to mousedowns
4686 // We're going to time double-clicks from mouse *up* to next mouse *down*
4687 LONG curMsgTime = ::GetMessageTime();
4688
4689 switch (aEventMessage) {
4690 case eMouseDoubleClick:
4691 event.mMessage = eMouseDown;
4692 event.mButton = aButton;
4693 sLastClickCount = 2;
4694 sLastMouseDownTime = curMsgTime;
4695 break;
4696 case eMouseUp:
4697 // remember when this happened for the next mouse down
4698 sLastMousePoint.x = eventPoint.x;
4699 sLastMousePoint.y = eventPoint.y;
4700 sLastMouseButton = eventButton;
4701 break;
4702 case eMouseDown:
4703 // now look to see if we want to convert this to a double- or triple-click
4704 if (((curMsgTime - sLastMouseDownTime) < (LONG)::GetDoubleClickTime()) &&
4705 insideMovementThreshold && eventButton == sLastMouseButton) {
4706 sLastClickCount++;
4707 } else {
4708 // reset the click count, to count *this* click
4709 sLastClickCount = 1;
4710 }
4711 // Set last Click time on MouseDown only
4712 sLastMouseDownTime = curMsgTime;
4713 break;
4714 case eMouseMove:
4715 if (!insideMovementThreshold) {
4716 sLastClickCount = 0;
4717 }
4718 break;
4719 case eMouseExitFromWidget:
4720 event.mExitFrom =
4721 Some(IsTopLevelMouseExit(mWnd) ? WidgetMouseEvent::ePlatformTopLevel
4722 : WidgetMouseEvent::ePlatformChild);
4723 break;
4724 default:
4725 break;
4726 }
4727 event.mClickCount = sLastClickCount;
4728
4729 #ifdef NS_DEBUG_XX
4730 MOZ_LOG(gWindowsLog, LogLevel::Info,
4731 ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
4732 #endif
4733
4734 // call the event callback
4735 if (mWidgetListener) {
4736 if (aEventMessage == eMouseMove) {
4737 LayoutDeviceIntRect rect = GetBounds();
4738 rect.MoveTo(0, 0);
4739
4740 if (rect.Contains(event.mRefPoint)) {
4741 if (sCurrentWindow == nullptr || sCurrentWindow != this) {
4742 if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
4743 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4744 sCurrentWindow->DispatchMouseEvent(
4745 eMouseExitFromWidget, wParam, pos, false, MouseButton::ePrimary,
4746 aInputSource, aPointerInfo);
4747 }
4748 sCurrentWindow = this;
4749 if (!mInDtor) {
4750 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4751 sCurrentWindow->DispatchMouseEvent(
4752 eMouseEnterIntoWidget, wParam, pos, false,
4753 MouseButton::ePrimary, aInputSource, aPointerInfo);
4754 }
4755 }
4756 }
4757 } else if (aEventMessage == eMouseExitFromWidget) {
4758 if (sCurrentWindow == this) {
4759 sCurrentWindow = nullptr;
4760 }
4761 }
4762
4763 result = ConvertStatus(DispatchInputEvent(&event).mContentStatus);
4764
4765 // Release the widget with NS_IF_RELEASE() just in case
4766 // the context menu key code in EventListenerManager::HandleEvent()
4767 // released it already.
4768 return result;
4769 }
4770
4771 return result;
4772 }
4773
GetTopLevelForFocus(HWND aCurWnd)4774 HWND nsWindow::GetTopLevelForFocus(HWND aCurWnd) {
4775 // retrieve the toplevel window or dialogue
4776 HWND toplevelWnd = nullptr;
4777 while (aCurWnd) {
4778 toplevelWnd = aCurWnd;
4779 nsWindow* win = WinUtils::GetNSWindowPtr(aCurWnd);
4780 if (win) {
4781 if (win->mWindowType == eWindowType_toplevel ||
4782 win->mWindowType == eWindowType_dialog) {
4783 break;
4784 }
4785 }
4786
4787 aCurWnd = ::GetParent(aCurWnd); // Parent or owner (if has no parent)
4788 }
4789 return toplevelWnd;
4790 }
4791
DispatchFocusToTopLevelWindow(bool aIsActivate)4792 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate) {
4793 if (aIsActivate) {
4794 sJustGotActivate = false;
4795 }
4796 sJustGotDeactivate = false;
4797 mLastKillFocusWindow = nullptr;
4798
4799 HWND toplevelWnd = GetTopLevelForFocus(mWnd);
4800
4801 if (toplevelWnd) {
4802 nsWindow* win = WinUtils::GetNSWindowPtr(toplevelWnd);
4803 if (win && win->mWidgetListener) {
4804 if (aIsActivate) {
4805 win->mWidgetListener->WindowActivated();
4806 } else {
4807 win->mWidgetListener->WindowDeactivated();
4808 }
4809 }
4810 }
4811 }
4812
WindowAtMouse()4813 HWND nsWindow::WindowAtMouse() {
4814 DWORD pos = ::GetMessagePos();
4815 POINT mp;
4816 mp.x = GET_X_LPARAM(pos);
4817 mp.y = GET_Y_LPARAM(pos);
4818 return ::WindowFromPoint(mp);
4819 }
4820
IsTopLevelMouseExit(HWND aWnd)4821 bool nsWindow::IsTopLevelMouseExit(HWND aWnd) {
4822 HWND mouseWnd = WindowAtMouse();
4823
4824 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4825 // (which includes the non-client area). If the mouse has moved into
4826 // the non-client area, we should treat it as a top-level exit.
4827 HWND mouseTopLevel = WinUtils::GetTopLevelHWND(mouseWnd);
4828 if (mouseWnd == mouseTopLevel) return true;
4829
4830 return WinUtils::GetTopLevelHWND(aWnd) != mouseTopLevel;
4831 }
4832
ConvertStatus(nsEventStatus aStatus)4833 bool nsWindow::ConvertStatus(nsEventStatus aStatus) {
4834 return aStatus == nsEventStatus_eConsumeNoDefault;
4835 }
4836
4837 /**************************************************************
4838 *
4839 * SECTION: IPC
4840 *
4841 * IPC related helpers.
4842 *
4843 **************************************************************/
4844
4845 // static
IsAsyncResponseEvent(UINT aMsg,LRESULT & aResult)4846 bool nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult) {
4847 switch (aMsg) {
4848 case WM_SETFOCUS:
4849 case WM_KILLFOCUS:
4850 case WM_ENABLE:
4851 case WM_WINDOWPOSCHANGING:
4852 case WM_WINDOWPOSCHANGED:
4853 case WM_PARENTNOTIFY:
4854 case WM_ACTIVATEAPP:
4855 case WM_NCACTIVATE:
4856 case WM_ACTIVATE:
4857 case WM_CHILDACTIVATE:
4858 case WM_IME_SETCONTEXT:
4859 case WM_IME_NOTIFY:
4860 case WM_SHOWWINDOW:
4861 case WM_CANCELMODE:
4862 case WM_MOUSEACTIVATE:
4863 case WM_CONTEXTMENU:
4864 aResult = 0;
4865 return true;
4866
4867 case WM_SETTINGCHANGE:
4868 case WM_SETCURSOR:
4869 return false;
4870 }
4871
4872 #ifdef DEBUG
4873 char szBuf[200];
4874 sprintf(szBuf,
4875 "An unhandled ISMEX_SEND message was received during spin loop! (%X)",
4876 aMsg);
4877 NS_WARNING(szBuf);
4878 #endif
4879
4880 return false;
4881 }
4882
IPCWindowProcHandler(UINT & msg,WPARAM & wParam,LPARAM & lParam)4883 void nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam) {
4884 MOZ_ASSERT_IF(
4885 msg != WM_GETOBJECT,
4886 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
4887 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
4888
4889 // Modal UI being displayed in windowless plugins.
4890 if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
4891 (InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) == ISMEX_SEND) {
4892 LRESULT res;
4893 if (IsAsyncResponseEvent(msg, res)) {
4894 ReplyMessage(res);
4895 }
4896 return;
4897 }
4898
4899 // Handle certain sync plugin events sent to the parent which
4900 // trigger ipc calls that result in deadlocks.
4901
4902 DWORD dwResult = 0;
4903 bool handled = false;
4904
4905 switch (msg) {
4906 // Windowless flash sending WM_ACTIVATE events to the main window
4907 // via calls to ShowWindow.
4908 case WM_ACTIVATE:
4909 if (lParam != 0 && LOWORD(wParam) == WA_ACTIVE &&
4910 IsWindow((HWND)lParam)) {
4911 // Check for Adobe Reader X sync activate message from their
4912 // helper window and ignore. Fixes an annoying focus problem.
4913 if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) ==
4914 ISMEX_SEND) {
4915 wchar_t szClass[10];
4916 HWND focusWnd = (HWND)lParam;
4917 if (IsWindowVisible(focusWnd) &&
4918 GetClassNameW(focusWnd, szClass,
4919 sizeof(szClass) / sizeof(char16_t)) &&
4920 !wcscmp(szClass, L"Edit") &&
4921 !WinUtils::IsOurProcessWindow(focusWnd)) {
4922 break;
4923 }
4924 }
4925 handled = true;
4926 }
4927 break;
4928 // Plugins taking or losing focus triggering focus app messages.
4929 case WM_SETFOCUS:
4930 case WM_KILLFOCUS:
4931 // Windowed plugins that pass sys key events to defwndproc generate
4932 // WM_SYSCOMMAND events to the main window.
4933 case WM_SYSCOMMAND:
4934 // Windowed plugins that fire context menu selection events to parent
4935 // windows.
4936 case WM_CONTEXTMENU:
4937 // IME events fired as a result of synchronous focus changes
4938 case WM_IME_SETCONTEXT:
4939 handled = true;
4940 break;
4941 }
4942
4943 if (handled &&
4944 (InSendMessageEx(nullptr) & (ISMEX_REPLIED | ISMEX_SEND)) == ISMEX_SEND) {
4945 ReplyMessage(dwResult);
4946 }
4947 }
4948
4949 /**************************************************************
4950 **************************************************************
4951 **
4952 ** BLOCK: Native events
4953 **
4954 ** Main Windows message handlers and OnXXX handlers for
4955 ** Windows event handling.
4956 **
4957 **************************************************************
4958 **************************************************************/
4959
4960 /**************************************************************
4961 *
4962 * SECTION: Wind proc.
4963 *
4964 * The main Windows event procedures and associated
4965 * message processing methods.
4966 *
4967 **************************************************************/
4968
DisplaySystemMenu(HWND hWnd,nsSizeMode sizeMode,bool isRtl,int32_t x,int32_t y)4969 static bool DisplaySystemMenu(HWND hWnd, nsSizeMode sizeMode, bool isRtl,
4970 int32_t x, int32_t y) {
4971 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
4972 if (hMenu) {
4973 MENUITEMINFO mii;
4974 mii.cbSize = sizeof(MENUITEMINFO);
4975 mii.fMask = MIIM_STATE;
4976 mii.fType = 0;
4977
4978 // update the options
4979 mii.fState = MF_ENABLED;
4980 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4981 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4982 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4983 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4984 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4985
4986 mii.fState = MF_GRAYED;
4987 switch (sizeMode) {
4988 case nsSizeMode_Fullscreen:
4989 // intentional fall through
4990 case nsSizeMode_Maximized:
4991 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4992 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4993 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4994 break;
4995 case nsSizeMode_Minimized:
4996 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4997 break;
4998 case nsSizeMode_Normal:
4999 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
5000 break;
5001 case nsSizeMode_Invalid:
5002 NS_ASSERTION(false, "Did the argument come from invalid IPC?");
5003 break;
5004 default:
5005 MOZ_ASSERT_UNREACHABLE("Unhnalded nsSizeMode value detected");
5006 break;
5007 }
5008 LPARAM cmd = TrackPopupMenu(
5009 hMenu,
5010 (TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_TOPALIGN |
5011 (isRtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
5012 x, y, 0, hWnd, nullptr);
5013 if (cmd) {
5014 PostMessage(hWnd, WM_SYSCOMMAND, cmd, 0);
5015 return true;
5016 }
5017 }
5018 return false;
5019 }
5020
5021 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
5022 // exceptions and passes the real work to WindowProcInternal. See bug 587406
5023 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
WindowProc(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5024 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
5025 LPARAM lParam) {
5026 mozilla::ipc::CancelCPOWs();
5027
5028 BackgroundHangMonitor().NotifyActivity();
5029
5030 return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg,
5031 wParam, lParam);
5032 }
5033
WindowProcInternal(HWND hWnd,UINT msg,WPARAM wParam,LPARAM lParam)5034 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg,
5035 WPARAM wParam, LPARAM lParam) {
5036 if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
5037 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
5038 if (msg == WM_HSCROLL) {
5039 // Route WM_HSCROLL messages to the main window.
5040 hWnd = ::GetParent(::GetParent(hWnd));
5041 } else {
5042 // Handle all other messages with its original window procedure.
5043 WNDPROC prevWindowProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
5044 return ::CallWindowProcW(prevWindowProc, hWnd, msg, wParam, lParam);
5045 }
5046 }
5047
5048 if (msg == MOZ_WM_TRACE) {
5049 // This is a tracer event for measuring event loop latency.
5050 // See WidgetTraceEvent.cpp for more details.
5051 mozilla::SignalTracerThread();
5052 return 0;
5053 }
5054
5055 // Get the window which caused the event and ask it to process the message
5056 nsWindow* targetWindow = WinUtils::GetNSWindowPtr(hWnd);
5057 NS_ASSERTION(targetWindow, "nsWindow* is null!");
5058 if (!targetWindow) return ::DefWindowProcW(hWnd, msg, wParam, lParam);
5059
5060 // Hold the window for the life of this method, in case it gets
5061 // destroyed during processing, unless we're in the dtor already.
5062 nsCOMPtr<nsIWidget> kungFuDeathGrip;
5063 if (!targetWindow->mInDtor) kungFuDeathGrip = targetWindow;
5064
5065 targetWindow->IPCWindowProcHandler(msg, wParam, lParam);
5066
5067 // Create this here so that we store the last rolled up popup until after
5068 // the event has been processed.
5069 nsAutoRollup autoRollup;
5070
5071 LRESULT popupHandlingResult;
5072 if (DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult))
5073 return popupHandlingResult;
5074
5075 // Call ProcessMessage
5076 LRESULT retValue;
5077 if (targetWindow->ProcessMessage(msg, wParam, lParam, &retValue)) {
5078 return retValue;
5079 }
5080
5081 LRESULT res = ::CallWindowProcW(targetWindow->GetPrevWindowProc(), hWnd, msg,
5082 wParam, lParam);
5083
5084 return res;
5085 }
5086
GetQuitType()5087 const char16_t* GetQuitType() {
5088 if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, false)) {
5089 DWORD cchCmdLine = 0;
5090 HRESULT rc = ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr,
5091 &cchCmdLine, nullptr);
5092 if (rc == S_OK) {
5093 return u"os-restart";
5094 }
5095 }
5096 return nullptr;
5097 }
5098
ForceFontUpdate()5099 static void ForceFontUpdate() {
5100 // update device context font cache
5101 // Dirty but easiest way:
5102 // Changing nsIPrefBranch entry which triggers callbacks
5103 // and flows into calling mDeviceContext->FlushFontCache()
5104 // to update the font cache in all the instance of Browsers
5105 static const char kPrefName[] = "font.internaluseonly.changed";
5106 bool fontInternalChange = Preferences::GetBool(kPrefName, false);
5107 Preferences::SetBool(kPrefName, !fontInternalChange);
5108 }
5109
ExternalHandlerProcessMessage(UINT aMessage,WPARAM & aWParam,LPARAM & aLParam,MSGResult & aResult)5110 bool nsWindow::ExternalHandlerProcessMessage(UINT aMessage, WPARAM& aWParam,
5111 LPARAM& aLParam,
5112 MSGResult& aResult) {
5113 if (mWindowHook.Notify(mWnd, aMessage, aWParam, aLParam, aResult)) {
5114 return true;
5115 }
5116
5117 if (IMEHandler::ProcessMessage(this, aMessage, aWParam, aLParam, aResult)) {
5118 return true;
5119 }
5120
5121 if (MouseScrollHandler::ProcessMessage(this, aMessage, aWParam, aLParam,
5122 aResult)) {
5123 return true;
5124 }
5125
5126 return false;
5127 }
5128
5129 // The main windows message processing method.
ProcessMessage(UINT msg,WPARAM & wParam,LPARAM & lParam,LRESULT * aRetValue)5130 bool nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
5131 LRESULT* aRetValue) {
5132 #if defined(EVENT_DEBUG_OUTPUT)
5133 // First param shows all events, second param indicates whether
5134 // to show mouse move events. See nsWindowDbg for details.
5135 PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS);
5136 #endif
5137
5138 MSGResult msgResult(aRetValue);
5139 if (ExternalHandlerProcessMessage(msg, wParam, lParam, msgResult)) {
5140 return (msgResult.mConsumed || !mWnd);
5141 }
5142
5143 bool result = false; // call the default nsWindow proc
5144 *aRetValue = 0;
5145
5146 // Glass hit testing w/custom transparent margins
5147 LRESULT dwmHitResult;
5148 if (mCustomNonClient &&
5149 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled() &&
5150 /* We don't do this for win10 glass with a custom titlebar,
5151 * in order to avoid the caption buttons breaking. */
5152 !(IsWin10OrLater() && HasGlass()) &&
5153 DwmDefWindowProc(mWnd, msg, wParam, lParam, &dwmHitResult)) {
5154 *aRetValue = dwmHitResult;
5155 return true;
5156 }
5157
5158 // (Large blocks of code should be broken out into OnEvent handlers.)
5159 switch (msg) {
5160 // WM_QUERYENDSESSION must be handled by all windows.
5161 // Otherwise Windows thinks the window can just be killed at will.
5162 case WM_QUERYENDSESSION:
5163 if (sCanQuit == TRI_UNKNOWN) {
5164 // Ask if it's ok to quit, and store the answer until we
5165 // get WM_ENDSESSION signaling the round is complete.
5166 nsCOMPtr<nsIObserverService> obsServ =
5167 mozilla::services::GetObserverService();
5168 nsCOMPtr<nsISupportsPRBool> cancelQuit =
5169 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
5170 cancelQuit->SetData(false);
5171
5172 const char16_t* quitType = GetQuitType();
5173 obsServ->NotifyObservers(cancelQuit, "quit-application-requested",
5174 quitType);
5175
5176 bool abortQuit;
5177 cancelQuit->GetData(&abortQuit);
5178 sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
5179 }
5180 *aRetValue = sCanQuit ? TRUE : FALSE;
5181 result = true;
5182 break;
5183
5184 case MOZ_WM_STARTA11Y:
5185 #if defined(ACCESSIBILITY)
5186 Unused << GetAccessible();
5187 result = true;
5188 #else
5189 result = false;
5190 #endif
5191 break;
5192
5193 case WM_ENDSESSION:
5194 case MOZ_WM_APP_QUIT:
5195 if (msg == MOZ_WM_APP_QUIT || (wParam == TRUE && sCanQuit == TRI_TRUE)) {
5196 // Let's fake a shutdown sequence without actually closing windows etc.
5197 // to avoid Windows killing us in the middle. A proper shutdown would
5198 // require having a chance to pump some messages. Unfortunately
5199 // Windows won't let us do that. Bug 212316.
5200 nsCOMPtr<nsIObserverService> obsServ =
5201 mozilla::services::GetObserverService();
5202 const char16_t* syncShutdown = u"syncShutdown";
5203 const char16_t* quitType = GetQuitType();
5204
5205 AppShutdown::Init(AppShutdownMode::Normal, 0);
5206
5207 obsServ->NotifyObservers(nullptr, "quit-application-granted",
5208 syncShutdown);
5209 obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
5210
5211 AppShutdown::OnShutdownConfirmed();
5212
5213 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownConfirmed,
5214 quitType);
5215 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownNetTeardown,
5216 nullptr);
5217 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTeardown,
5218 nullptr);
5219 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdown, nullptr);
5220 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownQM,
5221 nullptr);
5222 AppShutdown::AdvanceShutdownPhase(ShutdownPhase::AppShutdownTelemetry,
5223 nullptr);
5224
5225 AppShutdown::DoImmediateExit();
5226 }
5227 sCanQuit = TRI_UNKNOWN;
5228 result = true;
5229 break;
5230
5231 case WM_SYSCOLORCHANGE:
5232 // No need to invalidate layout for system color changes, but we need to
5233 // invalidate style.
5234 NotifyThemeChanged(widget::ThemeChangeKind::Style);
5235 break;
5236
5237 case WM_THEMECHANGED: {
5238 // Before anything else, push updates to child processes
5239 WinContentSystemParameters::GetSingleton()->OnThemeChanged();
5240
5241 // Update non-client margin offsets
5242 UpdateNonClientMargins();
5243 nsUXThemeData::UpdateNativeThemeInfo();
5244
5245 // We assume pretty much everything could've changed here.
5246 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout);
5247
5248 // Invalidate the window so that the repaint will
5249 // pick up the new theme.
5250 Invalidate(true, true, true);
5251 } break;
5252
5253 case WM_WTSSESSION_CHANGE: {
5254 switch (wParam) {
5255 case WTS_CONSOLE_CONNECT:
5256 case WTS_REMOTE_CONNECT:
5257 case WTS_SESSION_UNLOCK:
5258 // When a session becomes visible, we should invalidate.
5259 Invalidate(true, true, true);
5260 break;
5261 default:
5262 break;
5263 }
5264 } break;
5265
5266 case WM_FONTCHANGE: {
5267 // We only handle this message for the hidden window,
5268 // as we only need to update the (global) font list once
5269 // for any given change, not once per window!
5270 if (mWindowType != eWindowType_invisible) {
5271 break;
5272 }
5273
5274 nsresult rv;
5275 bool didChange = false;
5276
5277 // update the global font list
5278 nsCOMPtr<nsIFontEnumerator> fontEnum =
5279 do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv);
5280 if (NS_SUCCEEDED(rv)) {
5281 fontEnum->UpdateFontList(&didChange);
5282 ForceFontUpdate();
5283 } // if (NS_SUCCEEDED(rv))
5284 } break;
5285
5286 case WM_SETTINGCHANGE: {
5287 if (wParam == SPI_SETCLIENTAREAANIMATION ||
5288 // CaretBlinkTime is cached in nsLookAndFeel
5289 wParam == SPI_SETKEYBOARDDELAY) {
5290 // This only affects reduced motion settings and and carent blink time,
5291 // so no need to invalidate style / layout.
5292 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly);
5293 break;
5294 }
5295 if (wParam == SPI_SETFONTSMOOTHING ||
5296 wParam == SPI_SETFONTSMOOTHINGTYPE) {
5297 gfxDWriteFont::UpdateSystemTextQuality();
5298 break;
5299 }
5300 if (lParam) {
5301 auto lParamString = reinterpret_cast<const wchar_t*>(lParam);
5302 if (!wcscmp(lParamString, L"ImmersiveColorSet")) {
5303 // This affects system colors (-moz-win-accentcolor), so gotta pass
5304 // the style flag.
5305 NotifyThemeChanged(widget::ThemeChangeKind::Style);
5306 break;
5307 }
5308 if (IsWin10OrLater() && mWindowType == eWindowType_invisible) {
5309 if (!wcscmp(lParamString, L"UserInteractionMode")) {
5310 nsCOMPtr<nsIWindowsUIUtils> uiUtils(
5311 do_GetService("@mozilla.org/windows-ui-utils;1"));
5312 if (uiUtils) {
5313 uiUtils->UpdateTabletModeState();
5314 }
5315 }
5316 }
5317
5318 // UserInteractionMode, ConvertibleSlateMode, SystemDockMode may cause
5319 // @media(pointer) queries to change, which layout needs to know about
5320 //
5321 // (WM_SETTINGCHANGE will be sent to all top-level windows, so we
5322 // only respond to the hidden top-level window to avoid hammering
5323 // layout with a bunch of NotifyThemeChanged() calls)
5324 //
5325 if (mWindowType == eWindowType_invisible) {
5326 if (!wcscmp(lParamString, L"UserInteractionMode") ||
5327 !wcscmp(lParamString, L"ConvertibleSlateMode") ||
5328 !wcscmp(lParamString, L"SystemDockMode")) {
5329 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly);
5330 }
5331 }
5332 }
5333 } break;
5334
5335 case WM_DEVICECHANGE: {
5336 if (wParam == DBT_DEVICEARRIVAL || wParam == DBT_DEVICEREMOVECOMPLETE) {
5337 DEV_BROADCAST_HDR* hdr = reinterpret_cast<DEV_BROADCAST_HDR*>(lParam);
5338 // Check dbch_devicetype explicitly since we will get other device types
5339 // (e.g. DBT_DEVTYP_VOLUME) for some reasons even if we specify
5340 // DBT_DEVTYP_DEVICEINTERFACE in the filter for
5341 // RegisterDeviceNotification.
5342 if (hdr->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
5343 // This can only change media queries (any-hover/any-pointer).
5344 NotifyThemeChanged(widget::ThemeChangeKind::MediaQueriesOnly);
5345 }
5346 }
5347 } break;
5348
5349 case WM_NCCALCSIZE: {
5350 // NOTE: the following block is mirrored in PreXULSkeletonUI.cpp, and
5351 // will need to be kept in sync.
5352 if (mCustomNonClient) {
5353 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
5354 // the proposed window rectangle for our window. During our
5355 // processing of the `WM_NCCALCSIZE` message, we are expected to
5356 // modify the `RECT` that `lParam` points to, so that its value upon
5357 // our return is the new client area. We must return 0 if `wParam`
5358 // is `FALSE`.
5359 //
5360 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
5361 // struct. This struct contains an array of 3 `RECT`s, the first of
5362 // which has the exact same meaning as the `RECT` that is pointed to
5363 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
5364 // conjunction with our return value, can
5365 // be used to specify portions of the source and destination window
5366 // rectangles that are valid and should be preserved. We opt not to
5367 // implement an elaborate client-area preservation technique, and
5368 // simply return 0, which means "preserve the entire old client area
5369 // and align it with the upper-left corner of our new client area".
5370 RECT* clientRect =
5371 wParam ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam))->rgrc[0]
5372 : (reinterpret_cast<RECT*>(lParam));
5373 clientRect->top += mCaptionHeight - mNonClientOffset.top;
5374 clientRect->left += mHorResizeMargin - mNonClientOffset.left;
5375 clientRect->right -= mHorResizeMargin - mNonClientOffset.right;
5376 clientRect->bottom -= mVertResizeMargin - mNonClientOffset.bottom;
5377 // Make client rect's width and height more than 0 to
5378 // avoid problems of webrender and angle.
5379 clientRect->right = std::max(clientRect->right, clientRect->left + 1);
5380 clientRect->bottom = std::max(clientRect->bottom, clientRect->top + 1);
5381
5382 result = true;
5383 *aRetValue = 0;
5384 }
5385 break;
5386 }
5387
5388 case WM_NCHITTEST: {
5389 if (mMouseTransparent) {
5390 // Treat this window as transparent.
5391 *aRetValue = HTTRANSPARENT;
5392 result = true;
5393 break;
5394 }
5395
5396 /*
5397 * If an nc client area margin has been moved, we are responsible
5398 * for calculating where the resize margins are and returning the
5399 * appropriate set of hit test constants. DwmDefWindowProc (above)
5400 * will handle hit testing on it's command buttons if we are on a
5401 * composited desktop.
5402 */
5403
5404 if (!mCustomNonClient) break;
5405
5406 *aRetValue =
5407 ClientMarginHitTestPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
5408 result = true;
5409 break;
5410 }
5411
5412 case WM_SETTEXT:
5413 /*
5414 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
5415 * custom titlebar we paint ourselves, or if we're the ones
5416 * sending the message with an updated title
5417 */
5418
5419 if ((mSendingSetText &&
5420 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) ||
5421 !mCustomNonClient || mNonClientMargins.top == -1)
5422 break;
5423
5424 {
5425 // From msdn, the way around this is to disable the visible state
5426 // temporarily. We need the text to be set but we don't want the
5427 // redraw to occur. However, we need to make sure that we don't
5428 // do this at the same time that a Present is happening.
5429 //
5430 // To do this we take mPresentLock in nsWindow::PreRender and
5431 // if that lock is taken we wait before doing WM_SETTEXT
5432 if (mCompositorWidgetDelegate) {
5433 mCompositorWidgetDelegate->EnterPresentLock();
5434 }
5435 DWORD style = GetWindowLong(mWnd, GWL_STYLE);
5436 SetWindowLong(mWnd, GWL_STYLE, style & ~WS_VISIBLE);
5437 *aRetValue =
5438 CallWindowProcW(GetPrevWindowProc(), mWnd, msg, wParam, lParam);
5439 SetWindowLong(mWnd, GWL_STYLE, style);
5440 if (mCompositorWidgetDelegate) {
5441 mCompositorWidgetDelegate->LeavePresentLock();
5442 }
5443
5444 return true;
5445 }
5446
5447 case WM_NCACTIVATE: {
5448 /*
5449 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
5450 * through WM_NCPAINT via InvalidateNonClientRegion.
5451 */
5452 UpdateGetWindowInfoCaptionStatus(FALSE != wParam);
5453
5454 if (!mCustomNonClient) break;
5455
5456 // There is a case that rendered result is not kept. Bug 1237617
5457 if (wParam == TRUE && !gfxEnv::DisableForcePresent() &&
5458 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
5459 NS_DispatchToMainThread(NewRunnableMethod(
5460 "nsWindow::ForcePresent", this, &nsWindow::ForcePresent));
5461 }
5462
5463 // let the dwm handle nc painting on glass
5464 // Never allow native painting if we are on fullscreen
5465 if (mSizeMode != nsSizeMode_Fullscreen &&
5466 gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled())
5467 break;
5468
5469 if (wParam == TRUE) {
5470 // going active
5471 *aRetValue = FALSE; // ignored
5472 result = true;
5473 // invalidate to trigger a paint
5474 InvalidateNonClientRegion();
5475 break;
5476 } else {
5477 // going inactive
5478 *aRetValue = TRUE; // go ahead and deactive
5479 result = true;
5480 // invalidate to trigger a paint
5481 InvalidateNonClientRegion();
5482 break;
5483 }
5484 }
5485
5486 case WM_NCPAINT: {
5487 /*
5488 * ClearType changes often don't send a WM_SETTINGCHANGE message. But they
5489 * do seem to always send a WM_NCPAINT message, so let's update on that.
5490 */
5491 gfxDWriteFont::UpdateSystemTextQuality();
5492
5493 /*
5494 * Reset the non-client paint region so that it excludes the
5495 * non-client areas we paint manually. Then call defwndproc
5496 * to do the actual painting.
5497 */
5498
5499 if (!mCustomNonClient) break;
5500
5501 // let the dwm handle nc painting on glass
5502 if (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) break;
5503
5504 HRGN paintRgn = ExcludeNonClientFromPaintRegion((HRGN)wParam);
5505 LRESULT res = CallWindowProcW(GetPrevWindowProc(), mWnd, msg,
5506 (WPARAM)paintRgn, lParam);
5507 if (paintRgn != (HRGN)wParam) DeleteObject(paintRgn);
5508 *aRetValue = res;
5509 result = true;
5510 } break;
5511
5512 case WM_POWERBROADCAST:
5513 switch (wParam) {
5514 case PBT_APMSUSPEND:
5515 PostSleepWakeNotification(true);
5516 break;
5517 case PBT_APMRESUMEAUTOMATIC:
5518 case PBT_APMRESUMECRITICAL:
5519 case PBT_APMRESUMESUSPEND:
5520 PostSleepWakeNotification(false);
5521 break;
5522 }
5523 break;
5524
5525 case WM_CLOSE: // close request
5526 if (mWidgetListener) mWidgetListener->RequestWindowClose(this);
5527 result = true; // abort window closure
5528 break;
5529
5530 case WM_DESTROY:
5531 // clean up.
5532 DestroyLayerManager();
5533 OnDestroy();
5534 result = true;
5535 break;
5536
5537 case WM_PAINT:
5538 *aRetValue = (int)OnPaint(nullptr, 0);
5539 result = true;
5540 break;
5541
5542 case WM_PRINTCLIENT:
5543 result = OnPaint((HDC)wParam, 0);
5544 break;
5545
5546 case WM_HOTKEY:
5547 result = OnHotKey(wParam, lParam);
5548 break;
5549
5550 case WM_SYSCHAR:
5551 case WM_CHAR: {
5552 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5553 result = ProcessCharMessage(nativeMsg, nullptr);
5554 DispatchPendingEvents();
5555 } break;
5556
5557 case WM_SYSKEYUP:
5558 case WM_KEYUP: {
5559 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5560 nativeMsg.time = ::GetMessageTime();
5561 result = ProcessKeyUpMessage(nativeMsg, nullptr);
5562 DispatchPendingEvents();
5563 } break;
5564
5565 case WM_SYSKEYDOWN:
5566 case WM_KEYDOWN: {
5567 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
5568 result = ProcessKeyDownMessage(nativeMsg, nullptr);
5569 DispatchPendingEvents();
5570 } break;
5571
5572 // say we've dealt with erase background if widget does
5573 // not need auto-erasing
5574 case WM_ERASEBKGND:
5575 if (!AutoErase((HDC)wParam)) {
5576 *aRetValue = 1;
5577 result = true;
5578 }
5579 break;
5580
5581 case WM_MOUSEMOVE: {
5582 LPARAM lParamScreen = lParamToScreen(lParam);
5583 mMouseInDraggableArea = WithinDraggableRegion(GET_X_LPARAM(lParamScreen),
5584 GET_Y_LPARAM(lParamScreen));
5585
5586 if (!mMousePresent && !sIsInMouseCapture) {
5587 // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
5588 TRACKMOUSEEVENT mTrack;
5589 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
5590 mTrack.dwFlags = TME_LEAVE;
5591 mTrack.dwHoverTime = 0;
5592 mTrack.hwndTrack = mWnd;
5593 TrackMouseEvent(&mTrack);
5594 }
5595 mMousePresent = true;
5596
5597 // Suppress dispatch of pending events
5598 // when mouse moves are generated by widget
5599 // creation instead of user input.
5600 POINT mp;
5601 mp.x = GET_X_LPARAM(lParamScreen);
5602 mp.y = GET_Y_LPARAM(lParamScreen);
5603 bool userMovedMouse = false;
5604 if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
5605 userMovedMouse = true;
5606 }
5607
5608 result =
5609 DispatchMouseEvent(eMouseMove, wParam, lParam, false,
5610 MouseButton::ePrimary, MOUSE_INPUT_SOURCE(),
5611 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5612 if (userMovedMouse) {
5613 DispatchPendingEvents();
5614 }
5615 } break;
5616
5617 case WM_NCMOUSEMOVE: {
5618 LPARAM lParamClient = lParamToClient(lParam);
5619 if (WithinDraggableRegion(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) {
5620 if (!sIsInMouseCapture) {
5621 TRACKMOUSEEVENT mTrack;
5622 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
5623 mTrack.dwFlags = TME_LEAVE | TME_NONCLIENT;
5624 mTrack.dwHoverTime = 0;
5625 mTrack.hwndTrack = mWnd;
5626 TrackMouseEvent(&mTrack);
5627 }
5628 // If we noticed the mouse moving in our draggable region, forward the
5629 // message as a normal WM_MOUSEMOVE.
5630 SendMessage(mWnd, WM_MOUSEMOVE, 0, lParamClient);
5631 } else {
5632 // We've transitioned from a draggable area to somewhere else within
5633 // the non-client area - perhaps one of the edges of the window for
5634 // resizing.
5635 mMouseInDraggableArea = false;
5636 }
5637
5638 if (mMousePresent && !sIsInMouseCapture && !mMouseInDraggableArea) {
5639 SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
5640 }
5641 } break;
5642
5643 case WM_LBUTTONDOWN: {
5644 result =
5645 DispatchMouseEvent(eMouseDown, wParam, lParam, false,
5646 MouseButton::ePrimary, MOUSE_INPUT_SOURCE(),
5647 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5648 DispatchPendingEvents();
5649 } break;
5650
5651 case WM_LBUTTONUP: {
5652 result =
5653 DispatchMouseEvent(eMouseUp, wParam, lParam, false,
5654 MouseButton::ePrimary, MOUSE_INPUT_SOURCE(),
5655 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5656 DispatchPendingEvents();
5657 } break;
5658
5659 case WM_NCMOUSELEAVE: {
5660 mMouseInDraggableArea = false;
5661
5662 if (EventIsInsideWindow(this)) {
5663 // If we're handling WM_NCMOUSELEAVE and the mouse is still over the
5664 // window, then by process of elimination, the mouse has moved from the
5665 // non-client to client area, so no need to fall-through to the
5666 // WM_MOUSELEAVE handler. We also need to re-register for the
5667 // WM_MOUSELEAVE message, since according to the documentation at [1],
5668 // all tracking requested via TrackMouseEvent is cleared once
5669 // WM_NCMOUSELEAVE or WM_MOUSELEAVE fires.
5670 // [1]:
5671 // https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-trackmouseevent
5672 TRACKMOUSEEVENT mTrack;
5673 mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
5674 mTrack.dwFlags = TME_LEAVE;
5675 mTrack.dwHoverTime = 0;
5676 mTrack.hwndTrack = mWnd;
5677 TrackMouseEvent(&mTrack);
5678 break;
5679 }
5680 // We've transitioned from non-client to outside of the window, so
5681 // fall-through to the WM_MOUSELEAVE handler.
5682 }
5683 case WM_MOUSELEAVE: {
5684 if (!mMousePresent) break;
5685 if (mMouseInDraggableArea) break;
5686 mMousePresent = false;
5687
5688 // Check if the mouse is over the fullscreen transition window, if so
5689 // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
5690 // transition window disappears will not be ignored, even if the mouse
5691 // hasn't moved.
5692 if (mTransitionWnd && WindowAtMouse() == mTransitionWnd) {
5693 sLastMouseMovePoint = {0};
5694 }
5695
5696 // We need to check mouse button states and put them in for
5697 // wParam.
5698 WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0) |
5699 (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0) |
5700 (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
5701 // Synthesize an event position because we don't get one from
5702 // WM_MOUSELEAVE.
5703 LPARAM pos = lParamToClient(::GetMessagePos());
5704 DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false,
5705 MouseButton::ePrimary, MOUSE_INPUT_SOURCE());
5706 } break;
5707
5708 case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER: {
5709 LPARAM pos = lParamToClient(::GetMessagePos());
5710 MOZ_ASSERT(InkCollector::sInkCollector);
5711 uint16_t pointerId = InkCollector::sInkCollector->GetPointerId();
5712 if (pointerId != 0) {
5713 WinPointerInfo pointerInfo;
5714 pointerInfo.pointerId = pointerId;
5715 DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false,
5716 MouseButton::ePrimary,
5717 MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
5718 InkCollector::sInkCollector->ClearTarget();
5719 InkCollector::sInkCollector->ClearPointerId();
5720 }
5721 } break;
5722
5723 case WM_CONTEXTMENU: {
5724 // If the context menu is brought up by a touch long-press, then
5725 // the APZ code is responsible for dealing with this, so we don't
5726 // need to do anything.
5727 if (mTouchWindow &&
5728 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
5729 MOZ_ASSERT(mAPZC); // since mTouchWindow is true, APZ must be enabled
5730 result = true;
5731 break;
5732 }
5733
5734 // if the context menu is brought up from the keyboard, |lParam|
5735 // will be -1.
5736 LPARAM pos;
5737 bool contextMenukey = false;
5738 if (lParam == -1) {
5739 contextMenukey = true;
5740 pos = lParamToClient(GetMessagePos());
5741 } else {
5742 pos = lParamToClient(lParam);
5743 }
5744
5745 result = DispatchMouseEvent(
5746 eContextMenu, wParam, pos, contextMenukey,
5747 contextMenukey ? MouseButton::ePrimary : MouseButton::eSecondary,
5748 MOUSE_INPUT_SOURCE());
5749 if (lParam != -1 && !result && mCustomNonClient &&
5750 mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) {
5751 // Blank area hit, throw up the system menu.
5752 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam),
5753 GET_Y_LPARAM(lParam));
5754 result = true;
5755 }
5756 } break;
5757
5758 case WM_POINTERLEAVE:
5759 case WM_POINTERDOWN:
5760 case WM_POINTERUP:
5761 case WM_POINTERUPDATE:
5762 result = OnPointerEvents(msg, wParam, lParam);
5763 if (result) {
5764 DispatchPendingEvents();
5765 }
5766 break;
5767
5768 case DM_POINTERHITTEST:
5769 if (mDmOwner) {
5770 UINT contactId = GET_POINTERID_WPARAM(wParam);
5771 POINTER_INPUT_TYPE pointerType;
5772 if (mPointerEvents.GetPointerType(contactId, &pointerType) &&
5773 pointerType == PT_TOUCHPAD) {
5774 mDmOwner->SetContact(contactId);
5775 }
5776 }
5777 break;
5778
5779 case WM_LBUTTONDBLCLK:
5780 result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5781 MouseButton::ePrimary, MOUSE_INPUT_SOURCE());
5782 DispatchPendingEvents();
5783 break;
5784
5785 case WM_MBUTTONDOWN:
5786 result = DispatchMouseEvent(eMouseDown, wParam, lParam, false,
5787 MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5788 DispatchPendingEvents();
5789 break;
5790
5791 case WM_MBUTTONUP:
5792 result = DispatchMouseEvent(eMouseUp, wParam, lParam, false,
5793 MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5794 DispatchPendingEvents();
5795 break;
5796
5797 case WM_MBUTTONDBLCLK:
5798 result = DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5799 MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5800 DispatchPendingEvents();
5801 break;
5802
5803 case WM_NCMBUTTONDOWN:
5804 result = DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
5805 MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5806 DispatchPendingEvents();
5807 break;
5808
5809 case WM_NCMBUTTONUP:
5810 result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5811 MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5812 DispatchPendingEvents();
5813 break;
5814
5815 case WM_NCMBUTTONDBLCLK:
5816 result =
5817 DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
5818 false, MouseButton::eMiddle, MOUSE_INPUT_SOURCE());
5819 DispatchPendingEvents();
5820 break;
5821
5822 case WM_RBUTTONDOWN:
5823 result =
5824 DispatchMouseEvent(eMouseDown, wParam, lParam, false,
5825 MouseButton::eSecondary, MOUSE_INPUT_SOURCE(),
5826 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5827 DispatchPendingEvents();
5828 break;
5829
5830 case WM_RBUTTONUP:
5831 result =
5832 DispatchMouseEvent(eMouseUp, wParam, lParam, false,
5833 MouseButton::eSecondary, MOUSE_INPUT_SOURCE(),
5834 mPointerEvents.GetCachedPointerInfo(msg, wParam));
5835 DispatchPendingEvents();
5836 break;
5837
5838 case WM_RBUTTONDBLCLK:
5839 result =
5840 DispatchMouseEvent(eMouseDoubleClick, wParam, lParam, false,
5841 MouseButton::eSecondary, MOUSE_INPUT_SOURCE());
5842 DispatchPendingEvents();
5843 break;
5844
5845 case WM_NCRBUTTONDOWN:
5846 result =
5847 DispatchMouseEvent(eMouseDown, 0, lParamToClient(lParam), false,
5848 MouseButton::eSecondary, MOUSE_INPUT_SOURCE());
5849 DispatchPendingEvents();
5850 break;
5851
5852 case WM_NCRBUTTONUP:
5853 result =
5854 DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5855 MouseButton::eSecondary, MOUSE_INPUT_SOURCE());
5856 DispatchPendingEvents();
5857 break;
5858
5859 case WM_NCRBUTTONDBLCLK:
5860 result = DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
5861 false, MouseButton::eSecondary,
5862 MOUSE_INPUT_SOURCE());
5863 DispatchPendingEvents();
5864 break;
5865
5866 // Windows doesn't provide to customize the behavior of 4th nor 5th button
5867 // of mouse. If 5-button mouse works with standard mouse deriver of
5868 // Windows, users cannot disable 4th button (browser back) nor 5th button
5869 // (browser forward). We should allow to do it with our prefs since we can
5870 // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
5871 // messages are not sent to DefWindowProc.
5872 case WM_XBUTTONDOWN:
5873 case WM_XBUTTONUP:
5874 case WM_NCXBUTTONDOWN:
5875 case WM_NCXBUTTONUP:
5876 *aRetValue = TRUE;
5877 switch (GET_XBUTTON_WPARAM(wParam)) {
5878 case XBUTTON1:
5879 result = !Preferences::GetBool("mousebutton.4th.enabled", true);
5880 break;
5881 case XBUTTON2:
5882 result = !Preferences::GetBool("mousebutton.5th.enabled", true);
5883 break;
5884 default:
5885 break;
5886 }
5887 break;
5888
5889 case WM_SIZING: {
5890 if (mAspectRatio > 0) {
5891 LPRECT rect = (LPRECT)lParam;
5892 int32_t newWidth, newHeight;
5893
5894 // The following conditions and switch statement borrow heavily from the
5895 // Chromium source code from
5896 // https://chromium.googlesource.com/chromium/src/+/456d6e533cfb4531995e0ef52c279d4b5aa8a352/ui/views/window/window_resize_utils.cc#45
5897 if (wParam == WMSZ_LEFT || wParam == WMSZ_RIGHT ||
5898 wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT) {
5899 newWidth = rect->right - rect->left;
5900 newHeight = newWidth * mAspectRatio;
5901 } else {
5902 newHeight = rect->bottom - rect->top;
5903 newWidth = newHeight / mAspectRatio;
5904 }
5905
5906 switch (wParam) {
5907 case WMSZ_RIGHT:
5908 case WMSZ_BOTTOM:
5909 rect->right = newWidth + rect->left;
5910 rect->bottom = rect->top + newHeight;
5911 break;
5912 case WMSZ_TOP:
5913 rect->right = newWidth + rect->left;
5914 rect->top = rect->bottom - newHeight;
5915 break;
5916 case WMSZ_LEFT:
5917 case WMSZ_TOPLEFT:
5918 rect->left = rect->right - newWidth;
5919 rect->top = rect->bottom - newHeight;
5920 break;
5921 case WMSZ_TOPRIGHT:
5922 rect->right = rect->left + newWidth;
5923 rect->top = rect->bottom - newHeight;
5924 break;
5925 case WMSZ_BOTTOMLEFT:
5926 rect->left = rect->right - newWidth;
5927 rect->bottom = rect->top + newHeight;
5928 break;
5929 case WMSZ_BOTTOMRIGHT:
5930 rect->right = rect->left + newWidth;
5931 rect->bottom = rect->top + newHeight;
5932 break;
5933 }
5934 }
5935
5936 // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
5937 // resize or move event. Instead we wait for first VM_SIZING message
5938 // within a ENTERSIZEMOVE to consider this a live resize event.
5939 if (mResizeState == IN_SIZEMOVE) {
5940 mResizeState = RESIZING;
5941 NotifyLiveResizeStarted();
5942 }
5943 break;
5944 }
5945
5946 case WM_MOVING:
5947 FinishLiveResizing(MOVING);
5948 if (WinUtils::IsPerMonitorDPIAware()) {
5949 // Sometimes, we appear to miss a WM_DPICHANGED message while moving
5950 // a window around. Therefore, call ChangedDPI and ResetLayout here
5951 // if it appears that the window's scaling is not what we expect.
5952 // This causes the prescontext and appshell window management code to
5953 // check the appUnitsPerDevPixel value and current widget size, and
5954 // refresh them if necessary. If nothing has changed, these calls will
5955 // return without actually triggering any extra reflow or painting.
5956 if (WinUtils::LogToPhysFactor(mWnd) != mDefaultScale) {
5957 ChangedDPI();
5958 ResetLayout();
5959 if (mWidgetListener) {
5960 mWidgetListener->UIResolutionChanged();
5961 }
5962 }
5963 }
5964 break;
5965
5966 case WM_ENTERSIZEMOVE: {
5967 if (mResizeState == NOT_RESIZING) {
5968 mResizeState = IN_SIZEMOVE;
5969 }
5970 break;
5971 }
5972
5973 case WM_EXITSIZEMOVE: {
5974 FinishLiveResizing(NOT_RESIZING);
5975
5976 if (!sIsInMouseCapture) {
5977 NotifySizeMoveDone();
5978 }
5979
5980 break;
5981 }
5982
5983 case WM_DISPLAYCHANGE: {
5984 ScreenHelperWin::RefreshScreens();
5985 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
5986 if (gfxInfo) {
5987 gfxInfo->RefreshMonitors();
5988 }
5989 if (mWidgetListener) {
5990 mWidgetListener->UIResolutionChanged();
5991 }
5992 break;
5993 }
5994
5995 case WM_NCLBUTTONDBLCLK:
5996 DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam), false,
5997 MouseButton::ePrimary, MOUSE_INPUT_SOURCE());
5998 result = DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam), false,
5999 MouseButton::ePrimary, MOUSE_INPUT_SOURCE());
6000 DispatchPendingEvents();
6001 break;
6002
6003 case WM_NCLBUTTONDOWN: {
6004 // Dispatch a custom event when this happens in the draggable region, so
6005 // that non-popup-based panels can react to it. This doesn't send an
6006 // actual mousedown event because that would break dragging or interfere
6007 // with other mousedown handling in the caption area.
6008 if (WithinDraggableRegion(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))) {
6009 DispatchCustomEvent(u"draggableregionleftmousedown"_ns);
6010 }
6011 break;
6012 }
6013
6014 case WM_APPCOMMAND: {
6015 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
6016 result = HandleAppCommandMsg(nativeMsg, aRetValue);
6017 break;
6018 }
6019
6020 // The WM_ACTIVATE event is fired when a window is raised or lowered,
6021 // and the loword of wParam specifies which. But we don't want to tell
6022 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
6023 // events are fired. Instead, set either the sJustGotActivate or
6024 // gJustGotDeactivate flags and activate/deactivate once the focus
6025 // events arrive.
6026 case WM_ACTIVATE:
6027 if (mWidgetListener) {
6028 int32_t fActive = LOWORD(wParam);
6029
6030 if (WA_INACTIVE == fActive) {
6031 // when minimizing a window, the deactivation and focus events will
6032 // be fired in the reverse order. Instead, just deactivate right away.
6033 // This can also happen when a modal system dialog is opened, so check
6034 // if the last window to receive the WM_KILLFOCUS message was this one
6035 // or a child of this one.
6036 if (HIWORD(wParam) ||
6037 (mLastKillFocusWindow &&
6038 (GetTopLevelForFocus(mLastKillFocusWindow) == mWnd))) {
6039 DispatchFocusToTopLevelWindow(false);
6040 } else {
6041 sJustGotDeactivate = true;
6042 }
6043 if (mIsTopWidgetWindow) {
6044 mLastKeyboardLayout = KeyboardLayout::GetInstance()->GetLayout();
6045 }
6046 } else {
6047 StopFlashing();
6048
6049 sJustGotActivate = true;
6050 WidgetMouseEvent event(true, eMouseActivate, this,
6051 WidgetMouseEvent::eReal);
6052 InitEvent(event);
6053 ModifierKeyState modifierKeyState;
6054 modifierKeyState.InitInputEvent(event);
6055 DispatchInputEvent(&event);
6056 if (sSwitchKeyboardLayout && mLastKeyboardLayout)
6057 ActivateKeyboardLayout(mLastKeyboardLayout, 0);
6058 }
6059 }
6060 break;
6061
6062 case WM_MOUSEACTIVATE:
6063 // A popup with a parent owner should not be activated when clicked but
6064 // should still allow the mouse event to be fired, so the return value
6065 // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
6066 // just use default processing so that the window is activated.
6067 if (IsPopup() && IsOwnerForegroundWindow()) {
6068 *aRetValue = MA_NOACTIVATE;
6069 result = true;
6070 }
6071 break;
6072
6073 case WM_WINDOWPOSCHANGING: {
6074 LPWINDOWPOS info = (LPWINDOWPOS)lParam;
6075 OnWindowPosChanging(info);
6076 result = true;
6077 } break;
6078
6079 case WM_GETMINMAXINFO: {
6080 MINMAXINFO* mmi = (MINMAXINFO*)lParam;
6081 // Set the constraints. The minimum size should also be constrained to the
6082 // default window maximum size so that it fits on screen.
6083 mmi->ptMinTrackSize.x =
6084 std::min((int32_t)mmi->ptMaxTrackSize.x,
6085 std::max((int32_t)mmi->ptMinTrackSize.x,
6086 mSizeConstraints.mMinSize.width));
6087 mmi->ptMinTrackSize.y =
6088 std::min((int32_t)mmi->ptMaxTrackSize.y,
6089 std::max((int32_t)mmi->ptMinTrackSize.y,
6090 mSizeConstraints.mMinSize.height));
6091 mmi->ptMaxTrackSize.x = std::min((int32_t)mmi->ptMaxTrackSize.x,
6092 mSizeConstraints.mMaxSize.width);
6093 mmi->ptMaxTrackSize.y = std::min((int32_t)mmi->ptMaxTrackSize.y,
6094 mSizeConstraints.mMaxSize.height);
6095 } break;
6096
6097 case WM_SETFOCUS:
6098 // If previous focused window isn't ours, it must have received the
6099 // redirected message. So, we should forget it.
6100 if (!WinUtils::IsOurProcessWindow(HWND(wParam))) {
6101 RedirectedKeyDownMessageManager::Forget();
6102 }
6103 if (sJustGotActivate) {
6104 DispatchFocusToTopLevelWindow(true);
6105 }
6106 break;
6107
6108 case WM_KILLFOCUS:
6109 if (sJustGotDeactivate) {
6110 DispatchFocusToTopLevelWindow(false);
6111 } else {
6112 mLastKillFocusWindow = mWnd;
6113 }
6114 break;
6115
6116 case WM_WINDOWPOSCHANGED: {
6117 WINDOWPOS* wp = (LPWINDOWPOS)lParam;
6118 OnWindowPosChanged(wp);
6119 result = true;
6120 } break;
6121
6122 case WM_INPUTLANGCHANGEREQUEST:
6123 *aRetValue = TRUE;
6124 result = false;
6125 break;
6126
6127 case WM_INPUTLANGCHANGE:
6128 KeyboardLayout::GetInstance()->OnLayoutChange(
6129 reinterpret_cast<HKL>(lParam));
6130 nsBidiKeyboard::OnLayoutChange();
6131 result = false; // always pass to child window
6132 break;
6133
6134 case WM_DESTROYCLIPBOARD: {
6135 nsIClipboard* clipboard;
6136 nsresult rv = CallGetService(kCClipboardCID, &clipboard);
6137 if (NS_SUCCEEDED(rv)) {
6138 clipboard->EmptyClipboard(nsIClipboard::kGlobalClipboard);
6139 NS_RELEASE(clipboard);
6140 }
6141 } break;
6142
6143 #ifdef ACCESSIBILITY
6144 case WM_GETOBJECT: {
6145 *aRetValue = 0;
6146 // Do explicit casting to make it working on 64bit systems (see bug 649236
6147 // for details).
6148 int32_t objId = static_cast<DWORD>(lParam);
6149 if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
6150 RefPtr<IAccessible> root(
6151 a11y::LazyInstantiator::GetRootAccessible(mWnd));
6152 if (root) {
6153 *aRetValue = LresultFromObject(IID_IAccessible, wParam, root);
6154 a11y::LazyInstantiator::EnableBlindAggregation(mWnd);
6155 result = true;
6156 }
6157 }
6158 } break;
6159 #endif
6160
6161 case WM_SYSCOMMAND: {
6162 WPARAM filteredWParam = (wParam & 0xFFF0);
6163 if (mSizeMode == nsSizeMode_Fullscreen && filteredWParam == SC_RESTORE &&
6164 GetCurrentShowCmd(mWnd) != SW_SHOWMINIMIZED) {
6165 MakeFullScreen(false);
6166 result = true;
6167 }
6168
6169 // Handle the system menu manually when we're in full screen mode
6170 // so we can set the appropriate options.
6171 if (filteredWParam == SC_KEYMENU && lParam == VK_SPACE &&
6172 mSizeMode == nsSizeMode_Fullscreen) {
6173 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, MOZ_SYSCONTEXT_X_POS,
6174 MOZ_SYSCONTEXT_Y_POS);
6175 result = true;
6176 }
6177 } break;
6178
6179 case WM_DWMCOMPOSITIONCHANGED:
6180 // Every window will get this message, but gfxVars only broadcasts
6181 // updates when the value actually changes
6182 if (XRE_IsParentProcess()) {
6183 BOOL dwmEnabled = FALSE;
6184 if (FAILED(::DwmIsCompositionEnabled(&dwmEnabled)) || !dwmEnabled) {
6185 gfxVars::SetDwmCompositionEnabled(false);
6186 } else {
6187 gfxVars::SetDwmCompositionEnabled(true);
6188 }
6189 }
6190
6191 UpdateNonClientMargins();
6192 BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
6193 // TODO: Why is NotifyThemeChanged needed, what does it affect? And can we
6194 // make it more granular by tweaking the ChangeKind we pass?
6195 NotifyThemeChanged(widget::ThemeChangeKind::StyleAndLayout);
6196 UpdateGlass();
6197 Invalidate(true, true, true);
6198 break;
6199
6200 case WM_DPICHANGED: {
6201 LPRECT rect = (LPRECT)lParam;
6202 OnDPIChanged(rect->left, rect->top, rect->right - rect->left,
6203 rect->bottom - rect->top);
6204 break;
6205 }
6206
6207 case WM_UPDATEUISTATE: {
6208 // If the UI state has changed, fire an event so the UI updates the
6209 // keyboard cues based on the system setting and how the window was
6210 // opened. For example, a dialog opened via a keyboard press on a button
6211 // should enable cues, whereas the same dialog opened via a mouse click of
6212 // the button should not.
6213 if (mWindowType == eWindowType_toplevel ||
6214 mWindowType == eWindowType_dialog) {
6215 int32_t action = LOWORD(wParam);
6216 if (action == UIS_SET || action == UIS_CLEAR) {
6217 int32_t flags = HIWORD(wParam);
6218 UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
6219 if (flags & UISF_HIDEFOCUS) {
6220 showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear
6221 : UIStateChangeType_Set;
6222 }
6223 NotifyUIStateChanged(showFocusRings);
6224 }
6225 }
6226
6227 break;
6228 }
6229
6230 /* Gesture support events */
6231 case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
6232 // According to MS samples, this must be handled to enable
6233 // rotational support in multi-touch drivers.
6234 result = true;
6235 *aRetValue = TABLET_ROTATE_GESTURE_ENABLE;
6236 break;
6237
6238 case WM_TOUCH:
6239 result = OnTouch(wParam, lParam);
6240 if (result) {
6241 *aRetValue = 0;
6242 }
6243 break;
6244
6245 case WM_GESTURE:
6246 result = OnGesture(wParam, lParam);
6247 break;
6248
6249 case WM_GESTURENOTIFY: {
6250 if (mWindowType != eWindowType_invisible && !IsPlugin()) {
6251 // A GestureNotify event is dispatched to decide which single-finger
6252 // panning direction should be active (including none) and if pan
6253 // feedback should be displayed. Java and plugin windows can make their
6254 // own calls.
6255
6256 GESTURENOTIFYSTRUCT* gestureinfo = (GESTURENOTIFYSTRUCT*)lParam;
6257 nsPointWin touchPoint;
6258 touchPoint = gestureinfo->ptsLocation;
6259 touchPoint.ScreenToClient(mWnd);
6260 WidgetGestureNotifyEvent gestureNotifyEvent(true, eGestureNotify, this);
6261 gestureNotifyEvent.mRefPoint =
6262 LayoutDeviceIntPoint::FromUnknownPoint(touchPoint);
6263 nsEventStatus status;
6264 DispatchEvent(&gestureNotifyEvent, status);
6265 mDisplayPanFeedback = gestureNotifyEvent.mDisplayPanFeedback;
6266 if (!mTouchWindow)
6267 mGesture.SetWinGestureSupport(mWnd, gestureNotifyEvent.mPanDirection);
6268 }
6269 result = false; // should always bubble to DefWindowProc
6270 } break;
6271
6272 case WM_CLEAR: {
6273 WidgetContentCommandEvent command(true, eContentCommandDelete, this);
6274 DispatchWindowEvent(&command);
6275 result = true;
6276 } break;
6277
6278 case WM_CUT: {
6279 WidgetContentCommandEvent command(true, eContentCommandCut, this);
6280 DispatchWindowEvent(&command);
6281 result = true;
6282 } break;
6283
6284 case WM_COPY: {
6285 WidgetContentCommandEvent command(true, eContentCommandCopy, this);
6286 DispatchWindowEvent(&command);
6287 result = true;
6288 } break;
6289
6290 case WM_PASTE: {
6291 WidgetContentCommandEvent command(true, eContentCommandPaste, this);
6292 DispatchWindowEvent(&command);
6293 result = true;
6294 } break;
6295
6296 case EM_UNDO: {
6297 WidgetContentCommandEvent command(true, eContentCommandUndo, this);
6298 DispatchWindowEvent(&command);
6299 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
6300 result = true;
6301 } break;
6302
6303 case EM_REDO: {
6304 WidgetContentCommandEvent command(true, eContentCommandRedo, this);
6305 DispatchWindowEvent(&command);
6306 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
6307 result = true;
6308 } break;
6309
6310 case EM_CANPASTE: {
6311 // Support EM_CANPASTE message only when wParam isn't specified or
6312 // is plain text format.
6313 if (wParam == 0 || wParam == CF_TEXT || wParam == CF_UNICODETEXT) {
6314 WidgetContentCommandEvent command(true, eContentCommandPaste, this,
6315 true);
6316 DispatchWindowEvent(&command);
6317 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
6318 result = true;
6319 }
6320 } break;
6321
6322 case EM_CANUNDO: {
6323 WidgetContentCommandEvent command(true, eContentCommandUndo, this, true);
6324 DispatchWindowEvent(&command);
6325 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
6326 result = true;
6327 } break;
6328
6329 case EM_CANREDO: {
6330 WidgetContentCommandEvent command(true, eContentCommandRedo, this, true);
6331 DispatchWindowEvent(&command);
6332 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
6333 result = true;
6334 } break;
6335
6336 case MOZ_WM_SKEWFIX: {
6337 TimeStamp skewStamp;
6338 if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam,
6339 &skewStamp)) {
6340 TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(),
6341 skewStamp);
6342 }
6343 } break;
6344
6345 default: {
6346 if (msg == nsAppShell::GetTaskbarButtonCreatedMessage()) {
6347 SetHasTaskbarIconBeenCreated();
6348 }
6349 } break;
6350 }
6351
6352 //*aRetValue = result;
6353 if (mWnd) {
6354 return result;
6355 } else {
6356 // Events which caused mWnd destruction and aren't consumed
6357 // will crash during the Windows default processing.
6358 return true;
6359 }
6360 }
6361
FinishLiveResizing(ResizeState aNewState)6362 void nsWindow::FinishLiveResizing(ResizeState aNewState) {
6363 if (mResizeState == RESIZING) {
6364 NotifyLiveResizeStopped();
6365 }
6366 mResizeState = aNewState;
6367 ForcePresent();
6368 }
6369
6370 /**************************************************************
6371 *
6372 * SECTION: Broadcast messaging
6373 *
6374 * Broadcast messages to all windows.
6375 *
6376 **************************************************************/
6377
6378 // Enumerate all child windows sending aMsg to each of them
BroadcastMsgToChildren(HWND aWnd,LPARAM aMsg)6379 BOOL CALLBACK nsWindow::BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg) {
6380 WNDPROC winProc = (WNDPROC)::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
6381 if (winProc == &nsWindow::WindowProc) {
6382 // it's one of our windows so go ahead and send a message to it
6383 ::CallWindowProcW(winProc, aWnd, aMsg, 0, 0);
6384 }
6385 return TRUE;
6386 }
6387
6388 // Enumerate all top level windows specifying that the children of each
6389 // top level window should be enumerated. Do *not* send the message to
6390 // each top level window since it is assumed that the toolkit will send
6391 // aMsg to them directly.
BroadcastMsg(HWND aTopWindow,LPARAM aMsg)6392 BOOL CALLBACK nsWindow::BroadcastMsg(HWND aTopWindow, LPARAM aMsg) {
6393 // Iterate each of aTopWindows child windows sending the aMsg
6394 // to each of them.
6395 ::EnumChildWindows(aTopWindow, nsWindow::BroadcastMsgToChildren, aMsg);
6396 return TRUE;
6397 }
6398
6399 /**************************************************************
6400 *
6401 * SECTION: Event processing helpers
6402 *
6403 * Special processing for certain event types and
6404 * synthesized events.
6405 *
6406 **************************************************************/
6407
ClientMarginHitTestPoint(int32_t mx,int32_t my)6408 int32_t nsWindow::ClientMarginHitTestPoint(int32_t mx, int32_t my) {
6409 if (mSizeMode == nsSizeMode_Minimized || mSizeMode == nsSizeMode_Fullscreen) {
6410 return HTCLIENT;
6411 }
6412
6413 // Calculations are done in screen coords
6414 RECT winRect;
6415 GetWindowRect(mWnd, &winRect);
6416
6417 // hit return constants:
6418 // HTBORDER - non-resizable border
6419 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
6420 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
6421 // HTTOPLEFT, HTTOPRIGHT - resizable corner
6422 // HTCAPTION - general title bar area
6423 // HTCLIENT - area considered the client
6424 // HTCLOSE - hovering over the close button
6425 // HTMAXBUTTON - maximize button
6426 // HTMINBUTTON - minimize button
6427
6428 int32_t testResult = HTCLIENT;
6429
6430 bool isResizable = (mBorderStyle & (eBorderStyle_all | eBorderStyle_resizeh |
6431 eBorderStyle_default)) > 0
6432 ? true
6433 : false;
6434 if (mSizeMode == nsSizeMode_Maximized) isResizable = false;
6435
6436 // Ensure being accessible to borders of window. Even if contents are in
6437 // this area, the area must behave as border.
6438 nsIntMargin nonClientSize(
6439 std::max(mCaptionHeight - mNonClientOffset.top, kResizableBorderMinSize),
6440 std::max(mHorResizeMargin - mNonClientOffset.right,
6441 kResizableBorderMinSize),
6442 std::max(mVertResizeMargin - mNonClientOffset.bottom,
6443 kResizableBorderMinSize),
6444 std::max(mHorResizeMargin - mNonClientOffset.left,
6445 kResizableBorderMinSize));
6446
6447 bool allowContentOverride = mSizeMode == nsSizeMode_Maximized ||
6448 (mx >= winRect.left + nonClientSize.left &&
6449 mx <= winRect.right - nonClientSize.right &&
6450 my >= winRect.top + nonClientSize.top &&
6451 my <= winRect.bottom - nonClientSize.bottom);
6452
6453 // The border size. If there is no content under mouse cursor, the border
6454 // size should be larger than the values in system settings. Otherwise,
6455 // contents under the mouse cursor should be able to override the behavior.
6456 // E.g., user must expect that Firefox button always opens the popup menu
6457 // even when the user clicks on the above edge of it.
6458 nsIntMargin borderSize(std::max(nonClientSize.top, mVertResizeMargin),
6459 std::max(nonClientSize.right, mHorResizeMargin),
6460 std::max(nonClientSize.bottom, mVertResizeMargin),
6461 std::max(nonClientSize.left, mHorResizeMargin));
6462
6463 bool top = false;
6464 bool bottom = false;
6465 bool left = false;
6466 bool right = false;
6467
6468 if (my >= winRect.top && my < winRect.top + borderSize.top) {
6469 top = true;
6470 } else if (my <= winRect.bottom && my > winRect.bottom - borderSize.bottom) {
6471 bottom = true;
6472 }
6473
6474 // (the 2x case here doubles the resize area for corners)
6475 int multiplier = (top || bottom) ? 2 : 1;
6476 if (mx >= winRect.left &&
6477 mx < winRect.left + (multiplier * borderSize.left)) {
6478 left = true;
6479 } else if (mx <= winRect.right &&
6480 mx > winRect.right - (multiplier * borderSize.right)) {
6481 right = true;
6482 }
6483
6484 if (isResizable) {
6485 if (top) {
6486 testResult = HTTOP;
6487 if (left)
6488 testResult = HTTOPLEFT;
6489 else if (right)
6490 testResult = HTTOPRIGHT;
6491 } else if (bottom) {
6492 testResult = HTBOTTOM;
6493 if (left)
6494 testResult = HTBOTTOMLEFT;
6495 else if (right)
6496 testResult = HTBOTTOMRIGHT;
6497 } else {
6498 if (left) testResult = HTLEFT;
6499 if (right) testResult = HTRIGHT;
6500 }
6501 } else {
6502 if (top)
6503 testResult = HTCAPTION;
6504 else if (bottom || left || right)
6505 testResult = HTBORDER;
6506 }
6507
6508 if (!sIsInMouseCapture && allowContentOverride) {
6509 POINT pt = {mx, my};
6510 ::ScreenToClient(mWnd, &pt);
6511
6512 if (pt.x == mCachedHitTestPoint.x && pt.y == mCachedHitTestPoint.y &&
6513 TimeStamp::Now() - mCachedHitTestTime <
6514 TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS)) {
6515 return mCachedHitTestResult;
6516 }
6517
6518 mCachedHitTestPoint = {pt.x, pt.y};
6519 mCachedHitTestTime = TimeStamp::Now();
6520
6521 if (mDraggableRegion.Contains(pt.x, pt.y)) {
6522 testResult = HTCAPTION;
6523 } else {
6524 testResult = HTCLIENT;
6525 }
6526 mCachedHitTestResult = testResult;
6527 }
6528
6529 return testResult;
6530 }
6531
WithinDraggableRegion(int32_t screenX,int32_t screenY)6532 bool nsWindow::WithinDraggableRegion(int32_t screenX, int32_t screenY) {
6533 return ClientMarginHitTestPoint(screenX, screenY) == HTCAPTION;
6534 }
6535
GetMessageTimeStamp(LONG aEventTime) const6536 TimeStamp nsWindow::GetMessageTimeStamp(LONG aEventTime) const {
6537 CurrentWindowsTimeGetter getCurrentTime(mWnd);
6538 return TimeConverter().GetTimeStampFromSystemTime(aEventTime, getCurrentTime);
6539 }
6540
PostSleepWakeNotification(const bool aIsSleepMode)6541 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode) {
6542 if (aIsSleepMode == gIsSleepMode) return;
6543
6544 gIsSleepMode = aIsSleepMode;
6545
6546 nsCOMPtr<nsIObserverService> observerService =
6547 mozilla::services::GetObserverService();
6548 if (observerService)
6549 observerService->NotifyObservers(nullptr,
6550 aIsSleepMode
6551 ? NS_WIDGET_SLEEP_OBSERVER_TOPIC
6552 : NS_WIDGET_WAKE_OBSERVER_TOPIC,
6553 nullptr);
6554 }
6555
ProcessCharMessage(const MSG & aMsg,bool * aEventDispatched)6556 LRESULT nsWindow::ProcessCharMessage(const MSG& aMsg, bool* aEventDispatched) {
6557 if (IMEHandler::IsComposingOn(this)) {
6558 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION);
6559 }
6560 // These must be checked here too as a lone WM_CHAR could be received
6561 // if a child window didn't handle it (for example Alt+Space in a content
6562 // window)
6563 ModifierKeyState modKeyState;
6564 NativeKey nativeKey(this, aMsg, modKeyState);
6565 return static_cast<LRESULT>(nativeKey.HandleCharMessage(aEventDispatched));
6566 }
6567
ProcessKeyUpMessage(const MSG & aMsg,bool * aEventDispatched)6568 LRESULT nsWindow::ProcessKeyUpMessage(const MSG& aMsg, bool* aEventDispatched) {
6569 ModifierKeyState modKeyState;
6570 NativeKey nativeKey(this, aMsg, modKeyState);
6571 bool result = nativeKey.HandleKeyUpMessage(aEventDispatched);
6572 if (aMsg.wParam == VK_F10) {
6573 // Bug 1382199: Windows default behavior will trigger the System menu bar
6574 // when F10 is released. Among other things, this causes the System menu bar
6575 // to appear when a web page overrides the contextmenu event. We *never*
6576 // want this default behavior, so eat this key (never pass it to Windows).
6577 return true;
6578 }
6579 return result;
6580 }
6581
ProcessKeyDownMessage(const MSG & aMsg,bool * aEventDispatched)6582 LRESULT nsWindow::ProcessKeyDownMessage(const MSG& aMsg,
6583 bool* aEventDispatched) {
6584 // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
6585 // must clean up the redirected message information itself. For more
6586 // information, see above comment of
6587 // RedirectedKeyDownMessageManager::AutoFlusher class definition in
6588 // KeyboardLayout.h.
6589 RedirectedKeyDownMessageManager::AutoFlusher redirectedMsgFlusher(this, aMsg);
6590
6591 ModifierKeyState modKeyState;
6592
6593 NativeKey nativeKey(this, aMsg, modKeyState);
6594 LRESULT result =
6595 static_cast<LRESULT>(nativeKey.HandleKeyDownMessage(aEventDispatched));
6596 // HandleKeyDownMessage cleaned up the redirected message information
6597 // itself, so, we should do nothing.
6598 redirectedMsgFlusher.Cancel();
6599
6600 if (aMsg.wParam == VK_MENU ||
6601 (aMsg.wParam == VK_F10 && !modKeyState.IsShift())) {
6602 // We need to let Windows handle this keypress,
6603 // by returning false, if there's a native menu
6604 // bar somewhere in our containing window hierarchy.
6605 // Otherwise we handle the keypress and don't pass
6606 // it on to Windows, by returning true.
6607 bool hasNativeMenu = false;
6608 HWND hWnd = mWnd;
6609 while (hWnd) {
6610 if (::GetMenu(hWnd)) {
6611 hasNativeMenu = true;
6612 break;
6613 }
6614 hWnd = ::GetParent(hWnd);
6615 }
6616 result = !hasNativeMenu;
6617 }
6618
6619 return result;
6620 }
6621
SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,int32_t aNativeKeyCode,uint32_t aModifierFlags,const nsAString & aCharacters,const nsAString & aUnmodifiedCharacters,nsIObserver * aObserver)6622 nsresult nsWindow::SynthesizeNativeKeyEvent(
6623 int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode,
6624 uint32_t aModifierFlags, const nsAString& aCharacters,
6625 const nsAString& aUnmodifiedCharacters, nsIObserver* aObserver) {
6626 AutoObserverNotifier notifier(aObserver, "keyevent");
6627
6628 KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
6629 return keyboardLayout->SynthesizeNativeKeyEvent(
6630 this, aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
6631 aUnmodifiedCharacters);
6632 }
6633
SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,NativeMouseMessage aNativeMessage,MouseButton aButton,nsIWidget::Modifiers aModifierFlags,nsIObserver * aObserver)6634 nsresult nsWindow::SynthesizeNativeMouseEvent(
6635 LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage,
6636 MouseButton aButton, nsIWidget::Modifiers aModifierFlags,
6637 nsIObserver* aObserver) {
6638 AutoObserverNotifier notifier(aObserver, "mouseevent");
6639
6640 INPUT input;
6641 memset(&input, 0, sizeof(input));
6642
6643 // TODO (bug 1693240):
6644 // Now, we synthesize native mouse events asynchronously since we want to
6645 // synthesize the event on the front window at the point. However, Windows
6646 // does not provide a way to set modifier only while a mouse message is
6647 // being handled, and MOUSEEVENTF_MOVE may be coalesced by Windows. So, we
6648 // need a trick for handling it.
6649
6650 switch (aNativeMessage) {
6651 case NativeMouseMessage::Move:
6652 input.mi.dwFlags = MOUSEEVENTF_MOVE;
6653 // Reset sLastMouseMovePoint so that even if we're moving the mouse
6654 // to the position it's already at, we still dispatch a mousemove
6655 // event, because the callers of this function expect that.
6656 sLastMouseMovePoint = {0};
6657 break;
6658 case NativeMouseMessage::ButtonDown:
6659 case NativeMouseMessage::ButtonUp: {
6660 const bool isDown = aNativeMessage == NativeMouseMessage::ButtonDown;
6661 switch (aButton) {
6662 case MouseButton::ePrimary:
6663 input.mi.dwFlags = isDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
6664 break;
6665 case MouseButton::eMiddle:
6666 input.mi.dwFlags =
6667 isDown ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
6668 break;
6669 case MouseButton::eSecondary:
6670 input.mi.dwFlags =
6671 isDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
6672 break;
6673 case MouseButton::eX1:
6674 input.mi.dwFlags = isDown ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
6675 input.mi.mouseData = XBUTTON1;
6676 break;
6677 case MouseButton::eX2:
6678 input.mi.dwFlags = isDown ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
6679 input.mi.mouseData = XBUTTON2;
6680 break;
6681 default:
6682 return NS_ERROR_INVALID_ARG;
6683 }
6684 break;
6685 }
6686 case NativeMouseMessage::EnterWindow:
6687 case NativeMouseMessage::LeaveWindow:
6688 MOZ_ASSERT_UNREACHABLE("Non supported mouse event on Windows");
6689 return NS_ERROR_INVALID_ARG;
6690 }
6691
6692 input.type = INPUT_MOUSE;
6693 ::SetCursorPos(aPoint.x, aPoint.y);
6694 ::SendInput(1, &input, sizeof(INPUT));
6695
6696 return NS_OK;
6697 }
6698
SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,uint32_t aNativeMessage,double aDeltaX,double aDeltaY,double aDeltaZ,uint32_t aModifierFlags,uint32_t aAdditionalFlags,nsIObserver * aObserver)6699 nsresult nsWindow::SynthesizeNativeMouseScrollEvent(
6700 LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX,
6701 double aDeltaY, double aDeltaZ, uint32_t aModifierFlags,
6702 uint32_t aAdditionalFlags, nsIObserver* aObserver) {
6703 AutoObserverNotifier notifier(aObserver, "mousescrollevent");
6704 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
6705 this, aPoint, aNativeMessage,
6706 (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL)
6707 ? static_cast<int32_t>(aDeltaY)
6708 : static_cast<int32_t>(aDeltaX),
6709 aModifierFlags, aAdditionalFlags);
6710 }
6711
6712 /**************************************************************
6713 *
6714 * SECTION: OnXXX message handlers
6715 *
6716 * For message handlers that need to be broken out or
6717 * implemented in specific platform code.
6718 *
6719 **************************************************************/
6720
OnWindowPosChanged(WINDOWPOS * wp)6721 void nsWindow::OnWindowPosChanged(WINDOWPOS* wp) {
6722 if (wp == nullptr) return;
6723
6724 #ifdef WINSTATE_DEBUG_OUTPUT
6725 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
6726 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [ top] "));
6727 } else {
6728 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [child] "));
6729 }
6730 MOZ_LOG(gWindowsLog, LogLevel::Info, ("WINDOWPOS flags:"));
6731 if (wp->flags & SWP_FRAMECHANGED) {
6732 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_FRAMECHANGED "));
6733 }
6734 if (wp->flags & SWP_SHOWWINDOW) {
6735 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_SHOWWINDOW "));
6736 }
6737 if (wp->flags & SWP_NOSIZE) {
6738 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOSIZE "));
6739 }
6740 if (wp->flags & SWP_HIDEWINDOW) {
6741 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_HIDEWINDOW "));
6742 }
6743 if (wp->flags & SWP_NOZORDER) {
6744 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOZORDER "));
6745 }
6746 if (wp->flags & SWP_NOACTIVATE) {
6747 MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOACTIVATE "));
6748 }
6749 MOZ_LOG(gWindowsLog, LogLevel::Info, ("\n"));
6750 #endif
6751
6752 // Handle window size mode changes
6753 if (wp->flags & SWP_FRAMECHANGED && mSizeMode != nsSizeMode_Fullscreen) {
6754 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6755 // windows when fullscreen games disable desktop composition. If we're
6756 // minimized and not being activated, ignore the event and let windows
6757 // handle it.
6758 if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
6759 return;
6760
6761 WINDOWPLACEMENT pl;
6762 pl.length = sizeof(pl);
6763 ::GetWindowPlacement(mWnd, &pl);
6764
6765 nsSizeMode previousSizeMode = mSizeMode;
6766
6767 // Windows has just changed the size mode of this window. The call to
6768 // SizeModeChanged will trigger a call into SetSizeMode where we will
6769 // set the min/max window state again or for nsSizeMode_Normal, call
6770 // SetWindow with a parameter of SW_RESTORE. There's no need however as
6771 // this window's mode has already changed. Updating mSizeMode here
6772 // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
6773 // to window docking. (bug 489258)
6774 if (pl.showCmd == SW_SHOWMAXIMIZED)
6775 mSizeMode =
6776 (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6777 else if (pl.showCmd == SW_SHOWMINIMIZED)
6778 mSizeMode = nsSizeMode_Minimized;
6779 else if (mFullscreenMode)
6780 mSizeMode = nsSizeMode_Fullscreen;
6781 else
6782 mSizeMode = nsSizeMode_Normal;
6783
6784 #ifdef WINSTATE_DEBUG_OUTPUT
6785 switch (mSizeMode) {
6786 case nsSizeMode_Normal:
6787 MOZ_LOG(gWindowsLog, LogLevel::Info,
6788 ("*** mSizeMode: nsSizeMode_Normal\n"));
6789 break;
6790 case nsSizeMode_Minimized:
6791 MOZ_LOG(gWindowsLog, LogLevel::Info,
6792 ("*** mSizeMode: nsSizeMode_Minimized\n"));
6793 break;
6794 case nsSizeMode_Maximized:
6795 MOZ_LOG(gWindowsLog, LogLevel::Info,
6796 ("*** mSizeMode: nsSizeMode_Maximized\n"));
6797 break;
6798 default:
6799 MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** mSizeMode: ??????\n"));
6800 break;
6801 }
6802 #endif
6803
6804 if (mSizeMode != previousSizeMode) OnSizeModeChange(mSizeMode);
6805
6806 // If window was restored, window activation was bypassed during the
6807 // SetSizeMode call originating from OnWindowPosChanging to avoid saving
6808 // pre-restore attributes. Force activation now to get correct attributes.
6809 if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
6810 DispatchFocusToTopLevelWindow(true);
6811
6812 mLastSizeMode = mSizeMode;
6813
6814 // Skip window size change events below on minimization.
6815 if (mSizeMode == nsSizeMode_Minimized) return;
6816 }
6817
6818 // Handle window position changes
6819 if (!(wp->flags & SWP_NOMOVE)) {
6820 mBounds.MoveTo(wp->x, wp->y);
6821 NotifyWindowMoved(wp->x, wp->y);
6822 }
6823
6824 // Handle window size changes
6825 if (!(wp->flags & SWP_NOSIZE)) {
6826 RECT r;
6827 int32_t newWidth, newHeight;
6828
6829 ::GetWindowRect(mWnd, &r);
6830
6831 newWidth = r.right - r.left;
6832 newHeight = r.bottom - r.top;
6833
6834 if (newWidth > mLastSize.width) {
6835 RECT drect;
6836
6837 // getting wider
6838 drect.left = wp->x + mLastSize.width;
6839 drect.top = wp->y;
6840 drect.right = drect.left + (newWidth - mLastSize.width);
6841 drect.bottom = drect.top + newHeight;
6842
6843 ::RedrawWindow(mWnd, &drect, nullptr,
6844 RDW_INVALIDATE | RDW_NOERASE | RDW_NOINTERNALPAINT |
6845 RDW_ERASENOW | RDW_ALLCHILDREN);
6846 }
6847 if (newHeight > mLastSize.height) {
6848 RECT drect;
6849
6850 // getting taller
6851 drect.left = wp->x;
6852 drect.top = wp->y + mLastSize.height;
6853 drect.right = drect.left + newWidth;
6854 drect.bottom = drect.top + (newHeight - mLastSize.height);
6855
6856 ::RedrawWindow(mWnd, &drect, nullptr,
6857 RDW_INVALIDATE | RDW_NOERASE | RDW_NOINTERNALPAINT |
6858 RDW_ERASENOW | RDW_ALLCHILDREN);
6859 }
6860
6861 mBounds.SizeTo(newWidth, newHeight);
6862 mLastSize.width = newWidth;
6863 mLastSize.height = newHeight;
6864
6865 #ifdef WINSTATE_DEBUG_OUTPUT
6866 MOZ_LOG(gWindowsLog, LogLevel::Info,
6867 ("*** Resize window: %d x %d x %d x %d\n", wp->x, wp->y, newWidth,
6868 newHeight));
6869 #endif
6870
6871 if (mAspectRatio > 0) {
6872 // It's possible (via Windows Aero Snap) that the size of the window
6873 // has changed such that it violates the aspect ratio constraint. If so,
6874 // queue up an event to enforce the aspect ratio constraint and repaint.
6875 // When resized with Windows Aero Snap, we are in the NOT_RESIZING state.
6876 float newAspectRatio = (float)newHeight / newWidth;
6877 if (mResizeState == NOT_RESIZING && mAspectRatio != newAspectRatio) {
6878 // Hold a reference to self alive and pass it into the lambda to make
6879 // sure this nsIWidget stays alive long enough to run this function.
6880 nsCOMPtr<nsIWidget> self(this);
6881 NS_DispatchToMainThread(NS_NewRunnableFunction(
6882 "EnforceAspectRatio", [self, this, newWidth]() -> void {
6883 if (mWnd) {
6884 Resize(newWidth, newWidth * mAspectRatio, true);
6885 }
6886 }));
6887 }
6888 }
6889
6890 // If a maximized window is resized, recalculate the non-client margins.
6891 if (mSizeMode == nsSizeMode_Maximized) {
6892 if (UpdateNonClientMargins(nsSizeMode_Maximized, true)) {
6893 // gecko resize event already sent by UpdateNonClientMargins.
6894 return;
6895 }
6896 }
6897 }
6898
6899 // Notify the widget listener for size change of client area for gecko
6900 // events. This needs to be done when either window size is changed,
6901 // or window frame is changed. They may not happen together.
6902 // However, we don't invoke that for popup when window frame changes,
6903 // because popups may trigger frame change before size change via
6904 // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
6905 // code below call OnResize with a wrong client size first, which can
6906 // lead to flickerling for some popups.
6907 if (!(wp->flags & SWP_NOSIZE) ||
6908 ((wp->flags & SWP_FRAMECHANGED) && !IsPopup())) {
6909 RECT r;
6910 LayoutDeviceIntSize clientSize;
6911 if (::GetClientRect(mWnd, &r)) {
6912 clientSize = WinUtils::ToIntRect(r).Size();
6913 } else {
6914 clientSize = mBounds.Size();
6915 }
6916 // Send a gecko resize event
6917 OnResize(clientSize);
6918 }
6919 }
6920
OnWindowPosChanging(LPWINDOWPOS & info)6921 void nsWindow::OnWindowPosChanging(LPWINDOWPOS& info) {
6922 // Update non-client margins if the frame size is changing, and let the
6923 // browser know we are changing size modes, so alternative css can kick in.
6924 // If we're going into fullscreen mode, ignore this, since it'll reset
6925 // margins to normal mode.
6926 if ((info->flags & SWP_FRAMECHANGED && !(info->flags & SWP_NOSIZE)) &&
6927 mSizeMode != nsSizeMode_Fullscreen) {
6928 WINDOWPLACEMENT pl;
6929 pl.length = sizeof(pl);
6930 ::GetWindowPlacement(mWnd, &pl);
6931 nsSizeMode sizeMode;
6932 if (pl.showCmd == SW_SHOWMAXIMIZED)
6933 sizeMode =
6934 (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6935 else if (pl.showCmd == SW_SHOWMINIMIZED)
6936 sizeMode = nsSizeMode_Minimized;
6937 else if (mFullscreenMode)
6938 sizeMode = nsSizeMode_Fullscreen;
6939 else
6940 sizeMode = nsSizeMode_Normal;
6941
6942 OnSizeModeChange(sizeMode);
6943
6944 UpdateNonClientMargins(sizeMode, false);
6945 }
6946
6947 // Force fullscreen. This works around a bug in Windows 10 1809 where
6948 // using fullscreen when a window is "snapped" causes a spurious resize
6949 // smaller than the full screen, see bug 1482920.
6950 if (mSizeMode == nsSizeMode_Fullscreen && !(info->flags & SWP_NOMOVE) &&
6951 !(info->flags & SWP_NOSIZE)) {
6952 nsCOMPtr<nsIScreenManager> screenmgr =
6953 do_GetService(sScreenManagerContractID);
6954 if (screenmgr) {
6955 LayoutDeviceIntRect bounds(info->x, info->y, info->cx, info->cy);
6956 DesktopIntRect deskBounds =
6957 RoundedToInt(bounds / GetDesktopToDeviceScale());
6958 nsCOMPtr<nsIScreen> screen;
6959 screenmgr->ScreenForRect(deskBounds.X(), deskBounds.Y(),
6960 deskBounds.Width(), deskBounds.Height(),
6961 getter_AddRefs(screen));
6962
6963 if (screen) {
6964 int32_t x, y, width, height;
6965 screen->GetRect(&x, &y, &width, &height);
6966
6967 info->x = x;
6968 info->y = y;
6969 info->cx = width;
6970 info->cy = height;
6971 }
6972 }
6973 }
6974
6975 // enforce local z-order rules
6976 if (!(info->flags & SWP_NOZORDER)) {
6977 HWND hwndAfter = info->hwndInsertAfter;
6978
6979 nsWindow* aboveWindow = 0;
6980 nsWindowZ placement;
6981
6982 if (hwndAfter == HWND_BOTTOM)
6983 placement = nsWindowZBottom;
6984 else if (hwndAfter == HWND_TOP || hwndAfter == HWND_TOPMOST ||
6985 hwndAfter == HWND_NOTOPMOST)
6986 placement = nsWindowZTop;
6987 else {
6988 placement = nsWindowZRelative;
6989 aboveWindow = WinUtils::GetNSWindowPtr(hwndAfter);
6990 }
6991
6992 if (mWidgetListener) {
6993 nsCOMPtr<nsIWidget> actualBelow = nullptr;
6994 if (mWidgetListener->ZLevelChanged(false, &placement, aboveWindow,
6995 getter_AddRefs(actualBelow))) {
6996 if (placement == nsWindowZBottom)
6997 info->hwndInsertAfter = HWND_BOTTOM;
6998 else if (placement == nsWindowZTop)
6999 info->hwndInsertAfter = HWND_TOP;
7000 else {
7001 info->hwndInsertAfter =
7002 (HWND)actualBelow->GetNativeData(NS_NATIVE_WINDOW);
7003 }
7004 }
7005 }
7006 }
7007 // prevent rude external programs from making hidden window visible
7008 if (mWindowType == eWindowType_invisible) info->flags &= ~SWP_SHOWWINDOW;
7009
7010 // When waking from sleep or switching out of tablet mode, Windows 10
7011 // Version 1809 will reopen popup windows that should be hidden. Detect
7012 // this case and refuse to show the window.
7013 static bool sDWMUnhidesPopups = IsWin10Sep2018UpdateOrLater();
7014 if (sDWMUnhidesPopups && (info->flags & SWP_SHOWWINDOW) &&
7015 mWindowType == eWindowType_popup && mWidgetListener &&
7016 mWidgetListener->ShouldNotBeVisible()) {
7017 info->flags &= ~SWP_SHOWWINDOW;
7018 }
7019 }
7020
UserActivity()7021 void nsWindow::UserActivity() {
7022 // Check if we have the idle service, if not we try to get it.
7023 if (!mIdleService) {
7024 mIdleService = do_GetService("@mozilla.org/widget/useridleservice;1");
7025 }
7026
7027 // Check that we now have the idle service.
7028 if (mIdleService) {
7029 mIdleService->ResetIdleTimeOut(0);
7030 }
7031 }
7032
GetTouchCoordinates(WPARAM wParam,LPARAM lParam)7033 LayoutDeviceIntPoint nsWindow::GetTouchCoordinates(WPARAM wParam,
7034 LPARAM lParam) {
7035 LayoutDeviceIntPoint ret;
7036 uint32_t cInputs = LOWORD(wParam);
7037 if (cInputs != 1) {
7038 // Just return 0,0 if there isn't exactly one touch point active
7039 return ret;
7040 }
7041 PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
7042 if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
7043 sizeof(TOUCHINPUT))) {
7044 ret.x = TOUCH_COORD_TO_PIXEL(pInputs[0].x);
7045 ret.y = TOUCH_COORD_TO_PIXEL(pInputs[0].y);
7046 }
7047 delete[] pInputs;
7048 // Note that we don't call CloseTouchInputHandle here because we need
7049 // to read the touch input info again in OnTouch later.
7050 return ret;
7051 }
7052
7053 // Helper function for TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT,
7054 // uint32_t).
TouchDeviceNeedsPanGestureConversion(HANDLE aSource)7055 static bool TouchDeviceNeedsPanGestureConversion(HANDLE aSource) {
7056 std::string deviceName;
7057 UINT dataSize = 0;
7058 // The first call just queries how long the name string will be.
7059 GetRawInputDeviceInfoA(aSource, RIDI_DEVICENAME, nullptr, &dataSize);
7060 if (!dataSize || dataSize > 0x10000) {
7061 return false;
7062 }
7063 deviceName.resize(dataSize);
7064 // The second call actually populates the string.
7065 UINT result = GetRawInputDeviceInfoA(aSource, RIDI_DEVICENAME, &deviceName[0],
7066 &dataSize);
7067 if (result == UINT_MAX) {
7068 return false;
7069 }
7070 // The affected device name is "\\?\VIRTUAL_DIGITIZER", but each backslash
7071 // needs to be escaped with another one.
7072 std::string expectedDeviceName = "\\\\?\\VIRTUAL_DIGITIZER";
7073 // For some reason, the dataSize returned by the first call is double the
7074 // actual length of the device name (as if it were returning the size of a
7075 // wide-character string in bytes) even though we are using the narrow
7076 // version of the API. For the comparison against the expected device name
7077 // to pass, we truncate the buffer to be no longer tha the expected device
7078 // name.
7079 if (deviceName.substr(0, expectedDeviceName.length()) != expectedDeviceName) {
7080 return false;
7081 }
7082
7083 RID_DEVICE_INFO deviceInfo;
7084 deviceInfo.cbSize = sizeof(deviceInfo);
7085 dataSize = sizeof(deviceInfo);
7086 result =
7087 GetRawInputDeviceInfoA(aSource, RIDI_DEVICEINFO, &deviceInfo, &dataSize);
7088 if (result == UINT_MAX) {
7089 return false;
7090 }
7091 // The device identifiers that we check for here come from bug 1355162
7092 // comment 1 (see also bug 1511901 comment 35).
7093 return deviceInfo.dwType == RIM_TYPEHID && deviceInfo.hid.dwVendorId == 0 &&
7094 deviceInfo.hid.dwProductId == 0 &&
7095 deviceInfo.hid.dwVersionNumber == 1 &&
7096 deviceInfo.hid.usUsagePage == 13 && deviceInfo.hid.usUsage == 4;
7097 }
7098
7099 // Determine if the touch device that originated |aOSEvent| needs to have
7100 // touch events representing a two-finger gesture converted to pan
7101 // gesture events.
7102 // We only do this for touch devices with a specific name and identifiers.
TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent,uint32_t aTouchCount)7103 static bool TouchDeviceNeedsPanGestureConversion(PTOUCHINPUT aOSEvent,
7104 uint32_t aTouchCount) {
7105 if (!StaticPrefs::apz_windows_check_for_pan_gesture_conversion()) {
7106 return false;
7107 }
7108 if (aTouchCount == 0) {
7109 return false;
7110 }
7111 HANDLE source = aOSEvent[0].hSource;
7112
7113 // Cache the result of this computation for each touch device.
7114 // Touch devices are identified by the HANDLE stored in the hSource
7115 // field of TOUCHINPUT.
7116 static std::map<HANDLE, bool> sResultCache;
7117 auto [iter, inserted] = sResultCache.emplace(source, false);
7118 if (inserted) {
7119 iter->second = TouchDeviceNeedsPanGestureConversion(source);
7120 }
7121 return iter->second;
7122 }
7123
ConvertTouchToPanGesture(const MultiTouchInput & aTouchInput,PTOUCHINPUT aOSEvent)7124 Maybe<PanGestureInput> nsWindow::ConvertTouchToPanGesture(
7125 const MultiTouchInput& aTouchInput, PTOUCHINPUT aOSEvent) {
7126 // Checks if the touch device that originated the touch event is one
7127 // for which we want to convert the touch events to pang gesture events.
7128 bool shouldConvert = TouchDeviceNeedsPanGestureConversion(
7129 aOSEvent, aTouchInput.mTouches.Length());
7130 if (!shouldConvert) {
7131 return Nothing();
7132 }
7133
7134 // Only two-finger gestures need conversion.
7135 if (aTouchInput.mTouches.Length() != 2) {
7136 return Nothing();
7137 }
7138
7139 PanGestureInput::PanGestureType eventType = PanGestureInput::PANGESTURE_PAN;
7140 if (aTouchInput.mType == MultiTouchInput::MULTITOUCH_START) {
7141 eventType = PanGestureInput::PANGESTURE_START;
7142 } else if (aTouchInput.mType == MultiTouchInput::MULTITOUCH_END) {
7143 eventType = PanGestureInput::PANGESTURE_END;
7144 } else if (aTouchInput.mType == MultiTouchInput::MULTITOUCH_CANCEL) {
7145 eventType = PanGestureInput::PANGESTURE_CANCELLED;
7146 }
7147
7148 // Use the midpoint of the two touches as the start point of the pan gesture.
7149 ScreenPoint focusPoint = (aTouchInput.mTouches[0].mScreenPoint +
7150 aTouchInput.mTouches[1].mScreenPoint) /
7151 2;
7152 // To compute the displacement of the pan gesture, we keep track of the
7153 // location of the previous event.
7154 ScreenPoint displacement = (eventType == PanGestureInput::PANGESTURE_START)
7155 ? ScreenPoint(0, 0)
7156 : (focusPoint - mLastPanGestureFocus);
7157 mLastPanGestureFocus = focusPoint;
7158
7159 // We need to negate the displacement because for a touch event, moving the
7160 // fingers down results in scrolling up, but for a touchpad gesture, we want
7161 // moving the fingers down to result in scrolling down.
7162 PanGestureInput result(eventType, aTouchInput.mTime, aTouchInput.mTimeStamp,
7163 focusPoint, -displacement, aTouchInput.modifiers);
7164 result.mSimulateMomentum = true;
7165
7166 return Some(result);
7167 }
7168
7169 // Dispatch an event that originated as an OS touch event.
7170 // Usually, we want to dispatch it as a touch event, but some touchpads
7171 // produce touch events for two-finger scrolling, which need to be converted
7172 // to pan gesture events for correct behaviour.
DispatchTouchOrPanGestureInput(MultiTouchInput & aTouchInput,PTOUCHINPUT aOSEvent)7173 void nsWindow::DispatchTouchOrPanGestureInput(MultiTouchInput& aTouchInput,
7174 PTOUCHINPUT aOSEvent) {
7175 if (Maybe<PanGestureInput> panInput =
7176 ConvertTouchToPanGesture(aTouchInput, aOSEvent)) {
7177 DispatchPanGestureInput(*panInput);
7178 return;
7179 }
7180
7181 DispatchTouchInput(aTouchInput);
7182 }
7183
OnTouch(WPARAM wParam,LPARAM lParam)7184 bool nsWindow::OnTouch(WPARAM wParam, LPARAM lParam) {
7185 uint32_t cInputs = LOWORD(wParam);
7186 PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
7187
7188 if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
7189 sizeof(TOUCHINPUT))) {
7190 MultiTouchInput touchInput, touchEndInput;
7191
7192 // Walk across the touch point array processing each contact point.
7193 for (uint32_t i = 0; i < cInputs; i++) {
7194 bool addToEvent = false, addToEndEvent = false;
7195
7196 // N.B.: According with MS documentation
7197 // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
7198 // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or
7199 // TOUCHEVENTF_UP. Possibly, it means that TOUCHEVENTF_MOVE and
7200 // TOUCHEVENTF_UP can be combined together.
7201
7202 if (pInputs[i].dwFlags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE)) {
7203 if (touchInput.mTimeStamp.IsNull()) {
7204 // Initialize a touch event to send.
7205 touchInput.mType = MultiTouchInput::MULTITOUCH_MOVE;
7206 touchInput.mTime = ::GetMessageTime();
7207 touchInput.mTimeStamp = GetMessageTimeStamp(touchInput.mTime);
7208 ModifierKeyState modifierKeyState;
7209 touchInput.modifiers = modifierKeyState.GetModifiers();
7210 }
7211 // Pres shell expects this event to be a eTouchStart
7212 // if any new contact points have been added since the last event sent.
7213 if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) {
7214 touchInput.mType = MultiTouchInput::MULTITOUCH_START;
7215 }
7216 addToEvent = true;
7217 }
7218 if (pInputs[i].dwFlags & TOUCHEVENTF_UP) {
7219 // Pres shell expects removed contacts points to be delivered in a
7220 // separate eTouchEnd event containing only the contact points that were
7221 // removed.
7222 if (touchEndInput.mTimeStamp.IsNull()) {
7223 // Initialize a touch event to send.
7224 touchEndInput.mType = MultiTouchInput::MULTITOUCH_END;
7225 touchEndInput.mTime = ::GetMessageTime();
7226 touchEndInput.mTimeStamp = GetMessageTimeStamp(touchEndInput.mTime);
7227 ModifierKeyState modifierKeyState;
7228 touchEndInput.modifiers = modifierKeyState.GetModifiers();
7229 }
7230 addToEndEvent = true;
7231 }
7232 if (!addToEvent && !addToEndEvent) {
7233 // Filter out spurious Windows events we don't understand, like palm
7234 // contact.
7235 continue;
7236 }
7237
7238 // Setup the touch point we'll append to the touch event array.
7239 nsPointWin touchPoint;
7240 touchPoint.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
7241 touchPoint.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
7242 touchPoint.ScreenToClient(mWnd);
7243
7244 // Initialize the touch data.
7245 SingleTouchData touchData(
7246 pInputs[i].dwID, // aIdentifier
7247 ScreenIntPoint::FromUnknownPoint(touchPoint), // aScreenPoint
7248 // The contact area info cannot be trusted even when
7249 // TOUCHINPUTMASKF_CONTACTAREA is set when the input source is pen,
7250 // which somehow violates the API docs. (bug 1710509) Ultimately the
7251 // dwFlags check will become redundant since we want to migrate to
7252 // WM_POINTER for pens. (bug 1707075)
7253 (pInputs[i].dwMask & TOUCHINPUTMASKF_CONTACTAREA) &&
7254 !(pInputs[i].dwFlags & TOUCHEVENTF_PEN)
7255 ? ScreenSize(TOUCH_COORD_TO_PIXEL(pInputs[i].cxContact) / 2,
7256 TOUCH_COORD_TO_PIXEL(pInputs[i].cyContact) / 2)
7257 : ScreenSize(1, 1), // aRadius
7258 0.0f, // aRotationAngle
7259 0.0f); // aForce
7260
7261 // Append touch data to the appropriate event.
7262 if (addToEvent) {
7263 touchInput.mTouches.AppendElement(touchData);
7264 }
7265 if (addToEndEvent) {
7266 touchEndInput.mTouches.AppendElement(touchData);
7267 }
7268 }
7269
7270 // Dispatch touch start and touch move event if we have one.
7271 if (!touchInput.mTimeStamp.IsNull()) {
7272 DispatchTouchOrPanGestureInput(touchInput, pInputs);
7273 }
7274 // Dispatch touch end event if we have one.
7275 if (!touchEndInput.mTimeStamp.IsNull()) {
7276 DispatchTouchOrPanGestureInput(touchEndInput, pInputs);
7277 }
7278 }
7279
7280 delete[] pInputs;
7281 CloseTouchInputHandle((HTOUCHINPUT)lParam);
7282 return true;
7283 }
7284
7285 // Gesture event processing. Handles WM_GESTURE events.
OnGesture(WPARAM wParam,LPARAM lParam)7286 bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) {
7287 // Treatment for pan events which translate into scroll events:
7288 if (mGesture.IsPanEvent(lParam)) {
7289 if (!mGesture.ProcessPanMessage(mWnd, wParam, lParam))
7290 return false; // ignore
7291
7292 nsEventStatus status;
7293
7294 WidgetWheelEvent wheelEvent(true, eWheel, this);
7295
7296 ModifierKeyState modifierKeyState;
7297 modifierKeyState.InitInputEvent(wheelEvent);
7298
7299 wheelEvent.mButton = 0;
7300 wheelEvent.mTime = ::GetMessageTime();
7301 wheelEvent.mTimeStamp = GetMessageTimeStamp(wheelEvent.mTime);
7302 wheelEvent.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
7303
7304 bool endFeedback = true;
7305
7306 if (mGesture.PanDeltaToPixelScroll(wheelEvent)) {
7307 DispatchEvent(&wheelEvent, status);
7308 }
7309
7310 if (mDisplayPanFeedback) {
7311 mGesture.UpdatePanFeedbackX(
7312 mWnd, DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaX)),
7313 endFeedback);
7314 mGesture.UpdatePanFeedbackY(
7315 mWnd, DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaY)),
7316 endFeedback);
7317 mGesture.PanFeedbackFinalize(mWnd, endFeedback);
7318 }
7319
7320 CloseGestureInfoHandle((HGESTUREINFO)lParam);
7321
7322 return true;
7323 }
7324
7325 // Other gestures translate into simple gesture events:
7326 WidgetSimpleGestureEvent event(true, eVoidEvent, this);
7327 if (!mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event)) {
7328 return false; // fall through to DefWndProc
7329 }
7330
7331 // Polish up and send off the new event
7332 ModifierKeyState modifierKeyState;
7333 modifierKeyState.InitInputEvent(event);
7334 event.mButton = 0;
7335 event.mTime = ::GetMessageTime();
7336 event.mTimeStamp = GetMessageTimeStamp(event.mTime);
7337 event.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
7338
7339 nsEventStatus status;
7340 DispatchEvent(&event, status);
7341 if (status == nsEventStatus_eIgnore) {
7342 return false; // Ignored, fall through
7343 }
7344
7345 // Only close this if we process and return true.
7346 CloseGestureInfoHandle((HGESTUREINFO)lParam);
7347
7348 return true; // Handled
7349 }
7350
ConfigureChildren(const nsTArray<Configuration> & aConfigurations)7351 nsresult nsWindow::ConfigureChildren(
7352 const nsTArray<Configuration>& aConfigurations) {
7353 // If this is a remotely updated widget we receive clipping, position, and
7354 // size information from a source other than our owner. Don't let our parent
7355 // update this information.
7356 if (mWindowType == eWindowType_plugin_ipc_chrome) {
7357 return NS_OK;
7358 }
7359
7360 // XXXroc we could use BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
7361 // here, if that helps in some situations. So far I haven't seen a
7362 // need.
7363 for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
7364 const Configuration& configuration = aConfigurations[i];
7365 nsWindow* w = static_cast<nsWindow*>(configuration.mChild.get());
7366 NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child");
7367 nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true);
7368 NS_ENSURE_SUCCESS(rv, rv);
7369 LayoutDeviceIntRect bounds = w->GetBounds();
7370 if (bounds.Size() != configuration.mBounds.Size()) {
7371 w->Resize(configuration.mBounds.X(), configuration.mBounds.Y(),
7372 configuration.mBounds.Width(), configuration.mBounds.Height(),
7373 true);
7374 } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
7375 w->Move(configuration.mBounds.X(), configuration.mBounds.Y());
7376
7377 if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ||
7378 GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_BASIC) {
7379 // XXX - Workaround for Bug 587508. This will invalidate the part of the
7380 // plugin window that might be touched by moving content somehow. The
7381 // underlying problem should be found and fixed!
7382 LayoutDeviceIntRegion r;
7383 r.Sub(bounds, configuration.mBounds);
7384 r.MoveBy(-bounds.X(), -bounds.Y());
7385 LayoutDeviceIntRect toInvalidate = r.GetBounds();
7386
7387 WinUtils::InvalidatePluginAsWorkaround(w, toInvalidate);
7388 }
7389 }
7390 rv = w->SetWindowClipRegion(configuration.mClipRegion, false);
7391 NS_ENSURE_SUCCESS(rv, rv);
7392 }
7393 return NS_OK;
7394 }
7395
CreateHRGNFromArray(const nsTArray<LayoutDeviceIntRect> & aRects)7396 static HRGN CreateHRGNFromArray(const nsTArray<LayoutDeviceIntRect>& aRects) {
7397 int32_t size = sizeof(RGNDATAHEADER) + sizeof(RECT) * aRects.Length();
7398 AutoTArray<uint8_t, 100> buf;
7399 buf.SetLength(size);
7400 RGNDATA* data = reinterpret_cast<RGNDATA*>(buf.Elements());
7401 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
7402 data->rdh.dwSize = sizeof(data->rdh);
7403 data->rdh.iType = RDH_RECTANGLES;
7404 data->rdh.nCount = aRects.Length();
7405 LayoutDeviceIntRect bounds;
7406 for (uint32_t i = 0; i < aRects.Length(); ++i) {
7407 const LayoutDeviceIntRect& r = aRects[i];
7408 bounds.UnionRect(bounds, r);
7409 ::SetRect(&rects[i], r.X(), r.Y(), r.XMost(), r.YMost());
7410 }
7411 ::SetRect(&data->rdh.rcBound, bounds.X(), bounds.Y(), bounds.XMost(),
7412 bounds.YMost());
7413 return ::ExtCreateRegion(nullptr, buf.Length(), data);
7414 }
7415
SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect> & aRects,bool aIntersectWithExisting)7416 nsresult nsWindow::SetWindowClipRegion(
7417 const nsTArray<LayoutDeviceIntRect>& aRects, bool aIntersectWithExisting) {
7418 if (IsWindowClipRegionEqual(aRects)) {
7419 return NS_OK;
7420 }
7421
7422 nsBaseWidget::SetWindowClipRegion(aRects, aIntersectWithExisting);
7423
7424 HRGN dest = CreateHRGNFromArray(aRects);
7425 if (!dest) return NS_ERROR_OUT_OF_MEMORY;
7426
7427 if (aIntersectWithExisting) {
7428 HRGN current = ::CreateRectRgn(0, 0, 0, 0);
7429 if (current) {
7430 if (::GetWindowRgn(mWnd, current) != 0 /*ERROR*/) {
7431 ::CombineRgn(dest, dest, current, RGN_AND);
7432 }
7433 ::DeleteObject(current);
7434 }
7435 }
7436
7437 // If a plugin is not visible, especially if it is in a background tab,
7438 // it should not be able to steal keyboard focus. This code checks whether
7439 // the region that the plugin is being clipped to is NULLREGION. If it is,
7440 // the plugin window gets disabled.
7441 if (IsPlugin()) {
7442 if (NULLREGION == ::CombineRgn(dest, dest, dest, RGN_OR)) {
7443 ::ShowWindow(mWnd, SW_HIDE);
7444 ::EnableWindow(mWnd, FALSE);
7445 } else {
7446 ::EnableWindow(mWnd, TRUE);
7447 ::ShowWindow(mWnd, SW_SHOW);
7448 }
7449 }
7450 if (!::SetWindowRgn(mWnd, dest, TRUE)) {
7451 ::DeleteObject(dest);
7452 return NS_ERROR_FAILURE;
7453 }
7454 return NS_OK;
7455 }
7456
7457 // WM_DESTROY event handler
OnDestroy()7458 void nsWindow::OnDestroy() {
7459 mOnDestroyCalled = true;
7460
7461 // Make sure we don't get destroyed in the process of tearing down.
7462 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
7463
7464 // Dispatch the destroy notification.
7465 if (!mInDtor) NotifyWindowDestroyed();
7466
7467 // Prevent the widget from sending additional events.
7468 mWidgetListener = nullptr;
7469 mAttachedWidgetListener = nullptr;
7470
7471 DestroyDirectManipulation();
7472
7473 if (mWnd == mLastKillFocusWindow) {
7474 mLastKillFocusWindow = nullptr;
7475 }
7476 // Unregister notifications from terminal services
7477 ::WTSUnRegisterSessionNotification(mWnd);
7478
7479 // Free our subclass and clear |this| stored in the window props. We will no
7480 // longer receive events from Windows after this point.
7481 SubclassWindow(FALSE);
7482
7483 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow
7484 // can be cleared. (It's used in tracking windows for mouse events.)
7485 if (sCurrentWindow == this) sCurrentWindow = nullptr;
7486
7487 // Disconnects us from our parent, will call our GetParent().
7488 nsBaseWidget::Destroy();
7489
7490 // Release references to children, device context, toolkit, and app shell.
7491 nsBaseWidget::OnDestroy();
7492
7493 // Clear our native parent handle.
7494 // XXX Windows will take care of this in the proper order, and
7495 // SetParent(nullptr)'s remove child on the parent already took place in
7496 // nsBaseWidget's Destroy call above.
7497 // SetParent(nullptr);
7498 mParent = nullptr;
7499
7500 // We have to destroy the native drag target before we null out our window
7501 // pointer.
7502 EnableDragDrop(false);
7503
7504 // If we're going away and for some reason we're still the rollup widget,
7505 // rollup and turn off capture.
7506 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
7507 nsCOMPtr<nsIWidget> rollupWidget;
7508 if (rollupListener) {
7509 rollupWidget = rollupListener->GetRollupWidget();
7510 }
7511 if (this == rollupWidget) {
7512 if (rollupListener) rollupListener->Rollup(0, false, nullptr, nullptr);
7513 CaptureRollupEvents(nullptr, false);
7514 }
7515
7516 IMEHandler::OnDestroyWindow(this);
7517
7518 // Free GDI window class objects
7519 if (mBrush) {
7520 VERIFY(::DeleteObject(mBrush));
7521 mBrush = nullptr;
7522 }
7523
7524 // Destroy any custom cursor resources.
7525 if (mCursor.IsCustom()) {
7526 SetCursor(Cursor{eCursor_standard});
7527 }
7528
7529 if (mCompositorWidgetDelegate) {
7530 mCompositorWidgetDelegate->OnDestroyWindow();
7531 }
7532 mBasicLayersSurface = nullptr;
7533
7534 // Finalize panning feedback to possibly restore window displacement
7535 mGesture.PanFeedbackFinalize(mWnd, true);
7536
7537 // Clear the main HWND.
7538 mWnd = nullptr;
7539 }
7540
7541 // Send a resize message to the listener
OnResize(const LayoutDeviceIntSize & aSize)7542 bool nsWindow::OnResize(const LayoutDeviceIntSize& aSize) {
7543 if (mCompositorWidgetDelegate &&
7544 !mCompositorWidgetDelegate->OnWindowResize(aSize)) {
7545 return false;
7546 }
7547
7548 bool result = false;
7549 if (mWidgetListener) {
7550 result = mWidgetListener->WindowResized(this, aSize.width, aSize.height);
7551 }
7552
7553 // If there is an attached view, inform it as well as the normal widget
7554 // listener.
7555 if (mAttachedWidgetListener) {
7556 return mAttachedWidgetListener->WindowResized(this, aSize.width,
7557 aSize.height);
7558 }
7559
7560 return result;
7561 }
7562
OnSizeModeChange(nsSizeMode aSizeMode)7563 void nsWindow::OnSizeModeChange(nsSizeMode aSizeMode) {
7564 if (mCompositorWidgetDelegate) {
7565 mCompositorWidgetDelegate->OnWindowModeChange(aSizeMode);
7566 }
7567
7568 if (mWidgetListener) {
7569 mWidgetListener->SizeModeChanged(aSizeMode);
7570 }
7571 }
7572
OnHotKey(WPARAM wParam,LPARAM lParam)7573 bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam) { return true; }
7574
7575 // Can be overriden. Controls auto-erase of background.
AutoErase(HDC dc)7576 bool nsWindow::AutoErase(HDC dc) { return false; }
7577
IsPopup()7578 bool nsWindow::IsPopup() { return mWindowType == eWindowType_popup; }
7579
ShouldUseOffMainThreadCompositing()7580 bool nsWindow::ShouldUseOffMainThreadCompositing() {
7581 if (IsSmallPopup()) {
7582 return false;
7583 }
7584
7585 return nsBaseWidget::ShouldUseOffMainThreadCompositing();
7586 }
7587
WindowUsesOMTC()7588 void nsWindow::WindowUsesOMTC() {
7589 ULONG_PTR style = ::GetClassLongPtr(mWnd, GCL_STYLE);
7590 if (!style) {
7591 NS_WARNING("Could not get window class style");
7592 return;
7593 }
7594 style |= CS_HREDRAW | CS_VREDRAW;
7595 DebugOnly<ULONG_PTR> result = ::SetClassLongPtr(mWnd, GCL_STYLE, style);
7596 NS_WARNING_ASSERTION(result, "Could not reset window class style");
7597 }
7598
HasBogusPopupsDropShadowOnMultiMonitor()7599 bool nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
7600 if (sHasBogusPopupsDropShadowOnMultiMonitor == TRI_UNKNOWN) {
7601 // Since any change in the preferences requires a restart, this can be
7602 // done just once.
7603 // Check for Direct2D first.
7604 sHasBogusPopupsDropShadowOnMultiMonitor =
7605 gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ? TRI_TRUE
7606 : TRI_FALSE;
7607 if (!sHasBogusPopupsDropShadowOnMultiMonitor) {
7608 // Otherwise check if Direct3D 9 may be used.
7609 if (gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
7610 !gfxConfig::IsEnabled(gfx::Feature::OPENGL_COMPOSITING)) {
7611 nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
7612 if (gfxInfo) {
7613 int32_t status;
7614 nsCString discardFailureId;
7615 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(
7616 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, discardFailureId,
7617 &status))) {
7618 if (status == nsIGfxInfo::FEATURE_STATUS_OK ||
7619 gfxConfig::IsForcedOnByUser(gfx::Feature::HW_COMPOSITING)) {
7620 sHasBogusPopupsDropShadowOnMultiMonitor = TRI_TRUE;
7621 }
7622 }
7623 }
7624 }
7625 }
7626 }
7627 return !!sHasBogusPopupsDropShadowOnMultiMonitor;
7628 }
7629
OnDPIChanged(int32_t x,int32_t y,int32_t width,int32_t height)7630 void nsWindow::OnDPIChanged(int32_t x, int32_t y, int32_t width,
7631 int32_t height) {
7632 // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
7633 // they remain tied to their original parent's resolution.
7634 if (mWindowType == eWindowType_popup) {
7635 return;
7636 }
7637 if (StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
7638 return;
7639 }
7640 mDefaultScale = -1.0; // force recomputation of scale factor
7641
7642 if (mResizeState != RESIZING && mSizeMode == nsSizeMode_Normal) {
7643 // Limit the position (if not in the middle of a drag-move) & size,
7644 // if it would overflow the destination screen
7645 nsCOMPtr<nsIScreenManager> sm = do_GetService(sScreenManagerContractID);
7646 if (sm) {
7647 nsCOMPtr<nsIScreen> screen;
7648 sm->ScreenForRect(x, y, width, height, getter_AddRefs(screen));
7649 if (screen) {
7650 int32_t availLeft, availTop, availWidth, availHeight;
7651 screen->GetAvailRect(&availLeft, &availTop, &availWidth, &availHeight);
7652 if (mResizeState != MOVING) {
7653 x = std::max(x, availLeft);
7654 y = std::max(y, availTop);
7655 }
7656 width = std::min(width, availWidth);
7657 height = std::min(height, availHeight);
7658 }
7659 }
7660
7661 Resize(x, y, width, height, true);
7662 }
7663 UpdateNonClientMargins();
7664 ChangedDPI();
7665 ResetLayout();
7666 }
7667
7668 /**************************************************************
7669 **************************************************************
7670 **
7671 ** BLOCK: IME management and accessibility
7672 **
7673 ** Handles managing IME input and accessibility.
7674 **
7675 **************************************************************
7676 **************************************************************/
7677
SetInputContext(const InputContext & aContext,const InputContextAction & aAction)7678 void nsWindow::SetInputContext(const InputContext& aContext,
7679 const InputContextAction& aAction) {
7680 InputContext newInputContext = aContext;
7681 IMEHandler::SetInputContext(this, newInputContext, aAction);
7682 mInputContext = newInputContext;
7683 }
7684
GetInputContext()7685 InputContext nsWindow::GetInputContext() {
7686 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7687 if (WinUtils::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
7688 mInputContext.mIMEState.mOpen = IMEState::OPEN;
7689 } else {
7690 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7691 }
7692 return mInputContext;
7693 }
7694
GetNativeTextEventDispatcherListener()7695 TextEventDispatcherListener* nsWindow::GetNativeTextEventDispatcherListener() {
7696 return IMEHandler::GetNativeTextEventDispatcherListener();
7697 }
7698
7699 #ifdef ACCESSIBILITY
7700 # ifdef DEBUG
7701 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc) \
7702 if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) { \
7703 printf( \
7704 "Get the window:\n {\n HWND: %p, parent HWND: %p, wndobj: " \
7705 "%p,\n", \
7706 aHwnd, ::GetParent(aHwnd), aWnd); \
7707 printf(" acc: %p", aAcc); \
7708 if (aAcc) { \
7709 nsAutoString name; \
7710 aAcc->Name(name); \
7711 printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get()); \
7712 } \
7713 printf("\n }\n"); \
7714 }
7715
7716 # else
7717 # define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
7718 # endif
7719
GetAccessible()7720 a11y::LocalAccessible* nsWindow::GetAccessible() {
7721 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7722 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled)
7723 return nullptr;
7724
7725 if (mInDtor || mOnDestroyCalled || mWindowType == eWindowType_invisible) {
7726 return nullptr;
7727 }
7728
7729 // In case of popup window return a popup accessible.
7730 nsView* view = nsView::GetViewFor(this);
7731 if (view) {
7732 nsIFrame* frame = view->GetFrame();
7733 if (frame && nsLayoutUtils::IsPopup(frame)) {
7734 nsAccessibilityService* accService = GetOrCreateAccService();
7735 if (accService) {
7736 a11y::DocAccessible* docAcc =
7737 GetAccService()->GetDocAccessible(frame->PresShell());
7738 if (docAcc) {
7739 NS_LOG_WMGETOBJECT(
7740 this, mWnd,
7741 docAcc->GetAccessibleOrDescendant(frame->GetContent()));
7742 return docAcc->GetAccessibleOrDescendant(frame->GetContent());
7743 }
7744 }
7745 }
7746 }
7747
7748 // otherwise root document accessible.
7749 NS_LOG_WMGETOBJECT(this, mWnd, GetRootAccessible());
7750 return GetRootAccessible();
7751 }
7752 #endif
7753
7754 /**************************************************************
7755 **************************************************************
7756 **
7757 ** BLOCK: Transparency
7758 **
7759 ** Window transparency helpers.
7760 **
7761 **************************************************************
7762 **************************************************************/
7763
7764 #ifdef MOZ_XUL
7765
SetWindowTranslucencyInner(nsTransparencyMode aMode)7766 void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode) {
7767 if (aMode == mTransparencyMode) return;
7768
7769 // stop on dialogs and popups!
7770 HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
7771 nsWindow* parent = WinUtils::GetNSWindowPtr(hWnd);
7772
7773 if (!parent) {
7774 NS_WARNING("Trying to use transparent chrome in an embedded context");
7775 return;
7776 }
7777
7778 if (parent != this) {
7779 NS_WARNING(
7780 "Setting SetWindowTranslucencyInner on a parent this is not us!");
7781 }
7782
7783 if (aMode == eTransparencyTransparent) {
7784 // If we're switching to the use of a transparent window, hide the chrome
7785 // on our parent.
7786 HideWindowChrome(true);
7787 } else if (mHideChrome && mTransparencyMode == eTransparencyTransparent) {
7788 // if we're switching out of transparent, re-enable our parent's chrome.
7789 HideWindowChrome(false);
7790 }
7791
7792 LONG_PTR style = ::GetWindowLongPtrW(hWnd, GWL_STYLE),
7793 exStyle = ::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
7794
7795 if (parent->mIsVisible) {
7796 style |= WS_VISIBLE;
7797 if (parent->mSizeMode == nsSizeMode_Maximized) {
7798 style |= WS_MAXIMIZE;
7799 } else if (parent->mSizeMode == nsSizeMode_Minimized) {
7800 style |= WS_MINIMIZE;
7801 }
7802 }
7803
7804 if (aMode == eTransparencyTransparent)
7805 exStyle |= WS_EX_LAYERED;
7806 else
7807 exStyle &= ~WS_EX_LAYERED;
7808
7809 VERIFY_WINDOW_STYLE(style);
7810 ::SetWindowLongPtrW(hWnd, GWL_STYLE, style);
7811 ::SetWindowLongPtrW(hWnd, GWL_EXSTYLE, exStyle);
7812
7813 if (HasGlass()) memset(&mGlassMargins, 0, sizeof mGlassMargins);
7814 mTransparencyMode = aMode;
7815
7816 if (mCompositorWidgetDelegate) {
7817 mCompositorWidgetDelegate->UpdateTransparency(aMode);
7818 }
7819 UpdateGlass();
7820
7821 // Clear window by transparent black when compositor window is used in GPU
7822 // process and non-client area rendering by DWM is enabled.
7823 // It is for showing non-client area rendering. See nsWindow::UpdateGlass().
7824 if (HasGlass() && GetLayerManager()->AsKnowsCompositor() &&
7825 GetLayerManager()->AsKnowsCompositor()->GetUseCompositorWnd()) {
7826 HDC hdc;
7827 RECT rect;
7828 hdc = ::GetWindowDC(mWnd);
7829 ::GetWindowRect(mWnd, &rect);
7830 ::MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
7831 ::FillRect(hdc, &rect,
7832 reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
7833 ReleaseDC(mWnd, hdc);
7834 }
7835
7836 // Disable double buffering with D3D compositor for disabling compositor
7837 // window usage.
7838 if (HasGlass() && !gfxVars::UseWebRender() &&
7839 gfxVars::UseDoubleBufferingWithCompositor()) {
7840 gfxVars::SetUseDoubleBufferingWithCompositor(false);
7841 GPUProcessManager::Get()->ResetCompositors();
7842 }
7843 }
7844
7845 #endif // MOZ_XUL
7846
7847 /**************************************************************
7848 **************************************************************
7849 **
7850 ** BLOCK: Popup rollup hooks
7851 **
7852 ** Deals with CaptureRollup on popup windows.
7853 **
7854 **************************************************************
7855 **************************************************************/
7856
7857 // Schedules a timer for a window, so we can rollup after processing the hook
7858 // event
ScheduleHookTimer(HWND aWnd,UINT aMsgId)7859 void nsWindow::ScheduleHookTimer(HWND aWnd, UINT aMsgId) {
7860 // In some cases multiple hooks may be scheduled
7861 // so ignore any other requests once one timer is scheduled
7862 if (sHookTimerId == 0) {
7863 // Remember the window handle and the message ID to be used later
7864 sRollupMsgId = aMsgId;
7865 sRollupMsgWnd = aWnd;
7866 // Schedule native timer for doing the rollup after
7867 // this event is done being processed
7868 sHookTimerId = ::SetTimer(nullptr, 0, 0, (TIMERPROC)HookTimerForPopups);
7869 NS_ASSERTION(sHookTimerId, "Timer couldn't be created.");
7870 }
7871 }
7872
7873 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7874 int gLastMsgCode = 0;
7875 extern MSGFEventMsgInfo gMSGFEvents[];
7876 #endif
7877
7878 // Process Menu messages, rollup when popup is clicked.
MozSpecialMsgFilter(int code,WPARAM wParam,LPARAM lParam)7879 LRESULT CALLBACK nsWindow::MozSpecialMsgFilter(int code, WPARAM wParam,
7880 LPARAM lParam) {
7881 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7882 if (sProcessHook) {
7883 MSG* pMsg = (MSG*)lParam;
7884
7885 int inx = 0;
7886 while (gMSGFEvents[inx].mId != code && gMSGFEvents[inx].mStr != nullptr) {
7887 inx++;
7888 }
7889 if (code != gLastMsgCode) {
7890 if (gMSGFEvents[inx].mId == code) {
7891 # ifdef DEBUG
7892 MOZ_LOG(gWindowsLog, LogLevel::Info,
7893 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n", code,
7894 gMSGFEvents[inx].mStr, pMsg->hwnd));
7895 # endif
7896 } else {
7897 # ifdef DEBUG
7898 MOZ_LOG(gWindowsLog, LogLevel::Info,
7899 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n", code,
7900 gMSGFEvents[inx].mId, pMsg->hwnd));
7901 # endif
7902 }
7903 gLastMsgCode = code;
7904 }
7905 PrintEvent(pMsg->message, FALSE, FALSE);
7906 }
7907 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7908
7909 if (sProcessHook && code == MSGF_MENU) {
7910 MSG* pMsg = (MSG*)lParam;
7911 ScheduleHookTimer(pMsg->hwnd, pMsg->message);
7912 }
7913
7914 return ::CallNextHookEx(sMsgFilterHook, code, wParam, lParam);
7915 }
7916
7917 // Process all mouse messages. Roll up when a click is in a native window
7918 // that doesn't have an nsIWidget.
MozSpecialMouseProc(int code,WPARAM wParam,LPARAM lParam)7919 LRESULT CALLBACK nsWindow::MozSpecialMouseProc(int code, WPARAM wParam,
7920 LPARAM lParam) {
7921 if (sProcessHook) {
7922 switch (WinUtils::GetNativeMessage(wParam)) {
7923 case WM_LBUTTONDOWN:
7924 case WM_RBUTTONDOWN:
7925 case WM_MBUTTONDOWN:
7926 case WM_MOUSEWHEEL:
7927 case WM_MOUSEHWHEEL: {
7928 MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
7929 nsIWidget* mozWin = WinUtils::GetNSWindowPtr(ms->hwnd);
7930 if (mozWin) {
7931 // If this window is windowed plugin window, the mouse events are not
7932 // sent to us.
7933 if (static_cast<nsWindow*>(mozWin)->IsPlugin())
7934 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7935 } else {
7936 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7937 }
7938 break;
7939 }
7940 }
7941 }
7942 return ::CallNextHookEx(sCallMouseHook, code, wParam, lParam);
7943 }
7944
7945 // Process all messages. Roll up when the window is moving, or
7946 // is resizing or when maximized or mininized.
MozSpecialWndProc(int code,WPARAM wParam,LPARAM lParam)7947 LRESULT CALLBACK nsWindow::MozSpecialWndProc(int code, WPARAM wParam,
7948 LPARAM lParam) {
7949 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7950 if (sProcessHook) {
7951 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7952 PrintEvent(cwpt->message, FALSE, FALSE);
7953 }
7954 #endif
7955
7956 if (sProcessHook) {
7957 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7958 if (cwpt->message == WM_MOVING || cwpt->message == WM_SIZING ||
7959 cwpt->message == WM_GETMINMAXINFO) {
7960 ScheduleHookTimer(cwpt->hwnd, (UINT)cwpt->message);
7961 }
7962 }
7963
7964 return ::CallNextHookEx(sCallProcHook, code, wParam, lParam);
7965 }
7966
7967 // Register the special "hooks" for dropdown processing.
RegisterSpecialDropdownHooks()7968 void nsWindow::RegisterSpecialDropdownHooks() {
7969 NS_ASSERTION(!sMsgFilterHook, "sMsgFilterHook must be NULL!");
7970 NS_ASSERTION(!sCallProcHook, "sCallProcHook must be NULL!");
7971
7972 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7973
7974 // Install msg hook for moving the window and resizing
7975 if (!sMsgFilterHook) {
7976 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7977 sMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, MozSpecialMsgFilter,
7978 nullptr, GetCurrentThreadId());
7979 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7980 if (!sMsgFilterHook) {
7981 MOZ_LOG(gWindowsLog, LogLevel::Info,
7982 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7983 }
7984 #endif
7985 }
7986
7987 // Install msg hook for menus
7988 if (!sCallProcHook) {
7989 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7990 sCallProcHook = SetWindowsHookEx(WH_CALLWNDPROC, MozSpecialWndProc, nullptr,
7991 GetCurrentThreadId());
7992 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7993 if (!sCallProcHook) {
7994 MOZ_LOG(
7995 gWindowsLog, LogLevel::Info,
7996 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7997 }
7998 #endif
7999 }
8000
8001 // Install msg hook for the mouse
8002 if (!sCallMouseHook) {
8003 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
8004 sCallMouseHook = SetWindowsHookEx(WH_MOUSE, MozSpecialMouseProc, nullptr,
8005 GetCurrentThreadId());
8006 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
8007 if (!sCallMouseHook) {
8008 MOZ_LOG(gWindowsLog, LogLevel::Info,
8009 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
8010 }
8011 #endif
8012 }
8013 }
8014
8015 // Unhook special message hooks for dropdowns.
UnregisterSpecialDropdownHooks()8016 void nsWindow::UnregisterSpecialDropdownHooks() {
8017 DISPLAY_NMM_PRT(
8018 "***************** De-installing Msg Hooks ***************\n");
8019
8020 if (sCallProcHook) {
8021 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
8022 if (!::UnhookWindowsHookEx(sCallProcHook)) {
8023 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
8024 }
8025 sCallProcHook = nullptr;
8026 }
8027
8028 if (sMsgFilterHook) {
8029 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
8030 if (!::UnhookWindowsHookEx(sMsgFilterHook)) {
8031 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
8032 }
8033 sMsgFilterHook = nullptr;
8034 }
8035
8036 if (sCallMouseHook) {
8037 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
8038 if (!::UnhookWindowsHookEx(sCallMouseHook)) {
8039 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
8040 }
8041 sCallMouseHook = nullptr;
8042 }
8043 }
8044
8045 // This timer is designed to only fire one time at most each time a "hook"
8046 // function is used to rollup the dropdown. In some cases, the timer may be
8047 // scheduled from the hook, but that hook event or a subsequent event may roll
8048 // up the dropdown before this timer function is executed.
8049 //
8050 // For example, if an MFC control takes focus, the combobox will lose focus and
8051 // rollup before this function fires.
HookTimerForPopups(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)8052 VOID CALLBACK nsWindow::HookTimerForPopups(HWND hwnd, UINT uMsg, UINT idEvent,
8053 DWORD dwTime) {
8054 if (sHookTimerId != 0) {
8055 // if the window is nullptr then we need to use the ID to kill the timer
8056 DebugOnly<BOOL> status = ::KillTimer(nullptr, sHookTimerId);
8057 NS_ASSERTION(status, "Hook Timer was not killed.");
8058 sHookTimerId = 0;
8059 }
8060
8061 if (sRollupMsgId != 0) {
8062 // Note: DealWithPopups does the check to make sure that the rollup widget
8063 // is set.
8064 LRESULT popupHandlingResult;
8065 nsAutoRollup autoRollup;
8066 DealWithPopups(sRollupMsgWnd, sRollupMsgId, 0, 0, &popupHandlingResult);
8067 sRollupMsgId = 0;
8068 sRollupMsgWnd = nullptr;
8069 }
8070 }
8071
ClearResourcesCallback(HWND aWnd,LPARAM aMsg)8072 BOOL CALLBACK nsWindow::ClearResourcesCallback(HWND aWnd, LPARAM aMsg) {
8073 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
8074 if (window) {
8075 window->ClearCachedResources();
8076 }
8077 return TRUE;
8078 }
8079
ClearCachedResources()8080 void nsWindow::ClearCachedResources() {
8081 if (mLayerManager &&
8082 mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
8083 mLayerManager->ClearCachedResources();
8084 }
8085 ::EnumChildWindows(mWnd, nsWindow::ClearResourcesCallback, 0);
8086 }
8087
IsDifferentThreadWindow(HWND aWnd)8088 static bool IsDifferentThreadWindow(HWND aWnd) {
8089 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd, nullptr);
8090 }
8091
8092 // static
EventIsInsideWindow(nsWindow * aWindow,Maybe<POINT> aEventPoint)8093 bool nsWindow::EventIsInsideWindow(nsWindow* aWindow,
8094 Maybe<POINT> aEventPoint) {
8095 RECT r;
8096 ::GetWindowRect(aWindow->mWnd, &r);
8097 POINT mp;
8098 if (aEventPoint) {
8099 mp = *aEventPoint;
8100 } else {
8101 DWORD pos = ::GetMessagePos();
8102 mp.x = GET_X_LPARAM(pos);
8103 mp.y = GET_Y_LPARAM(pos);
8104 }
8105
8106 // was the event inside this window?
8107 return static_cast<bool>(::PtInRect(&r, mp));
8108 }
8109
8110 // static
GetPopupsToRollup(nsIRollupListener * aRollupListener,uint32_t * aPopupsToRollup,Maybe<POINT> aEventPoint)8111 bool nsWindow::GetPopupsToRollup(nsIRollupListener* aRollupListener,
8112 uint32_t* aPopupsToRollup,
8113 Maybe<POINT> aEventPoint) {
8114 // If we're dealing with menus, we probably have submenus and we don't want
8115 // to rollup some of them if the click is in a parent menu of the current
8116 // submenu.
8117 *aPopupsToRollup = UINT32_MAX;
8118 AutoTArray<nsIWidget*, 5> widgetChain;
8119 uint32_t sameTypeCount = aRollupListener->GetSubmenuWidgetChain(&widgetChain);
8120 for (uint32_t i = 0; i < widgetChain.Length(); ++i) {
8121 nsIWidget* widget = widgetChain[i];
8122 if (EventIsInsideWindow(static_cast<nsWindow*>(widget), aEventPoint)) {
8123 // Don't roll up if the mouse event occurred within a menu of the
8124 // same type. If the mouse event occurred in a menu higher than that,
8125 // roll up, but pass the number of popups to Rollup so that only those
8126 // of the same type close up.
8127 if (i < sameTypeCount) {
8128 return false;
8129 }
8130
8131 *aPopupsToRollup = sameTypeCount;
8132 break;
8133 }
8134 }
8135 return true;
8136 }
8137
8138 // static
NeedsToHandleNCActivateDelayed(HWND aWnd)8139 bool nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd) {
8140 // While popup is open, popup window might be activated by other application.
8141 // At this time, we need to take back focus to the previous window but it
8142 // causes flickering its nonclient area because WM_NCACTIVATE comes before
8143 // WM_ACTIVATE and we cannot know which window will take focus at receiving
8144 // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
8145 //
8146 // If non-popup window receives WM_NCACTIVATE at deactivating, default
8147 // wndproc shouldn't handle it as deactivating. Instead, at receiving
8148 // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
8149 // This returns true if the window needs to handle WM_NCACTIVATE later.
8150
8151 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
8152 return window && !window->IsPopup();
8153 }
8154
IsTouchSupportEnabled(HWND aWnd)8155 static bool IsTouchSupportEnabled(HWND aWnd) {
8156 nsWindow* topWindow =
8157 WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd, true));
8158 return topWindow ? topWindow->IsTouchWindow() : false;
8159 }
8160
8161 // static
DealWithPopups(HWND aWnd,UINT aMessage,WPARAM aWParam,LPARAM aLParam,LRESULT * aResult)8162 bool nsWindow::DealWithPopups(HWND aWnd, UINT aMessage, WPARAM aWParam,
8163 LPARAM aLParam, LRESULT* aResult) {
8164 NS_ASSERTION(aResult, "Bad outResult");
8165
8166 // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
8167 *aResult = MA_NOACTIVATE;
8168
8169 if (!::IsWindowVisible(aWnd)) {
8170 return false;
8171 }
8172
8173 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
8174 NS_ENSURE_TRUE(rollupListener, false);
8175
8176 nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget();
8177 if (!popup) {
8178 return false;
8179 }
8180
8181 static bool sSendingNCACTIVATE = false;
8182 static bool sPendingNCACTIVATE = false;
8183 uint32_t popupsToRollup = UINT32_MAX;
8184
8185 bool consumeRollupEvent = false;
8186
8187 nsWindow* popupWindow = static_cast<nsWindow*>(popup.get());
8188 UINT nativeMessage = WinUtils::GetNativeMessage(aMessage);
8189 switch (nativeMessage) {
8190 case WM_TOUCH:
8191 if (!IsTouchSupportEnabled(aWnd)) {
8192 // If APZ is disabled, don't allow touch inputs to dismiss popups. The
8193 // compatibility mouse events will do it instead.
8194 return false;
8195 }
8196 [[fallthrough]];
8197 case WM_LBUTTONDOWN:
8198 case WM_RBUTTONDOWN:
8199 case WM_MBUTTONDOWN:
8200 case WM_NCLBUTTONDOWN:
8201 case WM_NCRBUTTONDOWN:
8202 case WM_NCMBUTTONDOWN:
8203 if (nativeMessage != WM_TOUCH && IsTouchSupportEnabled(aWnd) &&
8204 MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
8205 // If any of these mouse events are really compatibility events that
8206 // Windows is sending for touch inputs, then don't allow them to dismiss
8207 // popups when APZ is enabled (instead we do the dismissing as part of
8208 // WM_TOUCH handling which is more correct).
8209 // If we don't do this, then when the user lifts their finger after a
8210 // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
8211 // us will dismiss the contextmenu popup that we displayed as part of
8212 // handling the long-tap-up.
8213 return false;
8214 }
8215 if (!EventIsInsideWindow(popupWindow) &&
8216 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
8217 break;
8218 }
8219 return false;
8220 case WM_POINTERDOWN: {
8221 WinPointerEvents pointerEvents;
8222 if (!pointerEvents.ShouldRollupOnPointerEvent(nativeMessage, aWParam)) {
8223 return false;
8224 }
8225 POINT pt;
8226 pt.x = GET_X_LPARAM(aLParam);
8227 pt.y = GET_Y_LPARAM(aLParam);
8228 if (!GetPopupsToRollup(rollupListener, &popupsToRollup, Some(pt))) {
8229 return false;
8230 }
8231 if (EventIsInsideWindow(popupWindow, Some(pt))) {
8232 // Don't roll up if the event is inside the popup window.
8233 return false;
8234 }
8235 } break;
8236 case MOZ_WM_DMANIP: {
8237 POINT pt;
8238 ::GetCursorPos(&pt);
8239 if (!GetPopupsToRollup(rollupListener, &popupsToRollup, Some(pt))) {
8240 return false;
8241 }
8242 if (EventIsInsideWindow(popupWindow, Some(pt))) {
8243 // Don't roll up if the event is inside the popup window
8244 return false;
8245 }
8246 } break;
8247 case WM_MOUSEWHEEL:
8248 case WM_MOUSEHWHEEL:
8249 // We need to check if the popup thinks that it should cause closing
8250 // itself when mouse wheel events are fired outside the rollup widget.
8251 if (!EventIsInsideWindow(popupWindow)) {
8252 // Check if we should consume this event even if we don't roll-up:
8253 consumeRollupEvent = rollupListener->ShouldConsumeOnMouseWheelEvent();
8254 *aResult = MA_ACTIVATE;
8255 if (rollupListener->ShouldRollupOnMouseWheelEvent() &&
8256 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
8257 break;
8258 }
8259 }
8260 return consumeRollupEvent;
8261
8262 case WM_ACTIVATEAPP:
8263 break;
8264
8265 case WM_ACTIVATE:
8266 // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
8267 // because we cannot distinguish it's caused by mouse or not.
8268 if (LOWORD(aWParam) == WA_ACTIVE && aLParam) {
8269 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
8270 if (window && window->IsPopup()) {
8271 // Cancel notifying widget listeners of deactivating the previous
8272 // active window (see WM_KILLFOCUS case in ProcessMessage()).
8273 sJustGotDeactivate = false;
8274 // Reactivate the window later.
8275 ::PostMessageW(aWnd, MOZ_WM_REACTIVATE, aWParam, aLParam);
8276 return true;
8277 }
8278 // Don't rollup the popup when focus moves back to the parent window
8279 // from a popup because such case is caused by strange mouse drivers.
8280 nsWindow* prevWindow =
8281 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
8282 if (prevWindow && prevWindow->IsPopup()) {
8283 // Consume this message here since previous window must not have
8284 // been inactivated since we've already stopped accepting the
8285 // inactivation below.
8286 return true;
8287 }
8288 } else if (LOWORD(aWParam) == WA_INACTIVE) {
8289 nsWindow* activeWindow =
8290 WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
8291 if (sPendingNCACTIVATE && NeedsToHandleNCActivateDelayed(aWnd)) {
8292 // If focus moves to non-popup widget or focusable popup, the window
8293 // needs to update its nonclient area.
8294 if (!activeWindow || !activeWindow->IsPopup()) {
8295 sSendingNCACTIVATE = true;
8296 ::SendMessageW(aWnd, WM_NCACTIVATE, false, 0);
8297 sSendingNCACTIVATE = false;
8298 }
8299 sPendingNCACTIVATE = false;
8300 }
8301 // If focus moves from/to popup, we don't need to rollup the popup
8302 // because such case is caused by strange mouse drivers. And in
8303 // such case, we should consume the message here since we need to
8304 // hide this odd focus move from our content. (If we didn't consume
8305 // the message here, ProcessMessage() will notify widget listener of
8306 // inactivation and that causes unnecessary reflow for supporting
8307 // -moz-window-inactive pseudo class.
8308 if (activeWindow) {
8309 if (activeWindow->IsPopup()) {
8310 return true;
8311 }
8312 nsWindow* deactiveWindow = WinUtils::GetNSWindowPtr(aWnd);
8313 if (deactiveWindow && deactiveWindow->IsPopup()) {
8314 return true;
8315 }
8316 }
8317 } else if (LOWORD(aWParam) == WA_CLICKACTIVE) {
8318 // If the WM_ACTIVATE message is caused by a click in a popup,
8319 // we should not rollup any popups.
8320 nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
8321 if ((window && window->IsPopup()) ||
8322 !GetPopupsToRollup(rollupListener, &popupsToRollup)) {
8323 return false;
8324 }
8325 }
8326 break;
8327
8328 case MOZ_WM_REACTIVATE:
8329 // The previous active window should take back focus.
8330 if (::IsWindow(reinterpret_cast<HWND>(aLParam))) {
8331 // FYI: Even without this API call, you see expected result (e.g., the
8332 // owner window of the popup keeps active without flickering
8333 // the non-client area). And also this causes initializing
8334 // TSF and it causes using CPU time a lot. However, even if we
8335 // consume WM_ACTIVE messages, native focus change has already
8336 // been occurred. I.e., a popup window is active now. Therefore,
8337 // you'll see some odd behavior if we don't reactivate the owner
8338 // window here. For example, if you do:
8339 // 1. Turn wheel on a bookmark panel.
8340 // 2. Turn wheel on another window.
8341 // then, you'll see that the another window becomes active but the
8342 // owner window of the bookmark panel looks still active and the
8343 // bookmark panel keeps open. The reason is that the first wheel
8344 // operation gives focus to the bookmark panel. Therefore, when
8345 // the next operation gives focus to the another window, previous
8346 // focus window is the bookmark panel (i.e., a popup window).
8347 // So, in this case, our hack around here prevents to inactivate
8348 // the owner window and roll up the bookmark panel.
8349 ::SetForegroundWindow(reinterpret_cast<HWND>(aLParam));
8350 }
8351 return true;
8352
8353 case WM_NCACTIVATE:
8354 if (!aWParam && !sSendingNCACTIVATE &&
8355 NeedsToHandleNCActivateDelayed(aWnd)) {
8356 // Don't just consume WM_NCACTIVATE. It doesn't handle only the
8357 // nonclient area state change.
8358 ::DefWindowProcW(aWnd, aMessage, TRUE, aLParam);
8359 // Accept the deactivating because it's necessary to receive following
8360 // WM_ACTIVATE.
8361 *aResult = TRUE;
8362 sPendingNCACTIVATE = true;
8363 return true;
8364 }
8365 return false;
8366
8367 case WM_MOUSEACTIVATE:
8368 if (!EventIsInsideWindow(popupWindow) &&
8369 GetPopupsToRollup(rollupListener, &popupsToRollup)) {
8370 // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
8371 // of TweakUI is enabled. Then, check if the popup should be rolled up
8372 // with rollup listener. If not, just consume the message.
8373 if (HIWORD(aLParam) == WM_MOUSEMOVE &&
8374 !rollupListener->ShouldRollupOnMouseActivate()) {
8375 return true;
8376 }
8377 // Otherwise, it should be handled by wndproc.
8378 return false;
8379 }
8380
8381 // Prevent the click inside the popup from causing a change in window
8382 // activation. Since the popup is shown non-activated, we need to eat any
8383 // requests to activate the window while it is displayed. Windows will
8384 // automatically activate the popup on the mousedown otherwise.
8385 return true;
8386
8387 case WM_SHOWWINDOW:
8388 // If the window is being minimized, close popups.
8389 if (aLParam == SW_PARENTCLOSING) {
8390 break;
8391 }
8392 return false;
8393
8394 case WM_KILLFOCUS:
8395 // If focus moves to other window created in different process/thread,
8396 // e.g., a plugin window, popups should be rolled up.
8397 if (IsDifferentThreadWindow(reinterpret_cast<HWND>(aWParam))) {
8398 break;
8399 }
8400 return false;
8401
8402 case WM_MOVING:
8403 case WM_MENUSELECT:
8404 break;
8405
8406 default:
8407 return false;
8408 }
8409
8410 // Only need to deal with the last rollup for left mouse down events.
8411 NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
8412
8413 if (nativeMessage == WM_TOUCH || nativeMessage == WM_LBUTTONDOWN ||
8414 nativeMessage == WM_POINTERDOWN) {
8415 LayoutDeviceIntPoint pos;
8416 if (nativeMessage == WM_TOUCH) {
8417 if (nsWindow* win = WinUtils::GetNSWindowPtr(aWnd)) {
8418 pos = win->GetTouchCoordinates(aWParam, aLParam);
8419 }
8420 } else {
8421 POINT pt;
8422 pt.x = GET_X_LPARAM(aLParam);
8423 pt.y = GET_Y_LPARAM(aLParam);
8424 ::ClientToScreen(aWnd, &pt);
8425 pos = LayoutDeviceIntPoint(pt.x, pt.y);
8426 }
8427
8428 nsIContent* lastRollup;
8429 consumeRollupEvent =
8430 rollupListener->Rollup(popupsToRollup, true, &pos, &lastRollup);
8431 nsAutoRollup::SetLastRollup(lastRollup);
8432 } else {
8433 consumeRollupEvent =
8434 rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
8435 }
8436
8437 // Tell hook to stop processing messages
8438 sProcessHook = false;
8439 sRollupMsgId = 0;
8440 sRollupMsgWnd = nullptr;
8441
8442 // If we are NOT supposed to be consuming events, let it go through
8443 if (consumeRollupEvent && nativeMessage != WM_RBUTTONDOWN) {
8444 *aResult = MA_ACTIVATE;
8445 return true;
8446 }
8447
8448 return false;
8449 }
8450
8451 /**************************************************************
8452 **************************************************************
8453 **
8454 ** BLOCK: Misc. utility methods and functions.
8455 **
8456 ** General use.
8457 **
8458 **************************************************************
8459 **************************************************************/
8460
8461 // Note that the result of GetTopLevelWindow method can be different from the
8462 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8463 // window. Because our top level window may be contained in another window
8464 // which is not managed by us.
GetTopLevelWindow(bool aStopOnDialogOrPopup)8465 nsWindow* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup) {
8466 nsWindow* curWindow = this;
8467
8468 while (true) {
8469 if (aStopOnDialogOrPopup) {
8470 switch (curWindow->mWindowType) {
8471 case eWindowType_dialog:
8472 case eWindowType_popup:
8473 return curWindow;
8474 default:
8475 break;
8476 }
8477 }
8478
8479 // Retrieve the top level parent or owner window
8480 nsWindow* parentWindow = curWindow->GetParentWindow(true);
8481
8482 if (!parentWindow) return curWindow;
8483
8484 curWindow = parentWindow;
8485 }
8486 }
8487
8488 // Set a flag if hwnd is a (non-popup) visible window from this process,
8489 // and bail out of the enumeration. Otherwise leave the flag unmodified
8490 // and continue the enumeration.
8491 // lParam must be a bool* pointing at the flag to be set.
EnumVisibleWindowsProc(HWND hwnd,LPARAM lParam)8492 static BOOL CALLBACK EnumVisibleWindowsProc(HWND hwnd, LPARAM lParam) {
8493 DWORD pid;
8494 ::GetWindowThreadProcessId(hwnd, &pid);
8495 if (pid == ::GetCurrentProcessId() && ::IsWindowVisible(hwnd)) {
8496 // Don't count popups as visible windows, since they don't take focus,
8497 // in case we only have a popup visible (see bug 1554490 where the gfx
8498 // test window is an offscreen popup).
8499 nsWindow* window = WinUtils::GetNSWindowPtr(hwnd);
8500 if (!window || !window->IsPopup()) {
8501 bool* windowsVisible = reinterpret_cast<bool*>(lParam);
8502 *windowsVisible = true;
8503 return FALSE;
8504 }
8505 }
8506 return TRUE;
8507 }
8508
8509 // Determine if it would be ok to activate a window, taking focus.
8510 // We want to avoid stealing focus from another app (bug 225305).
CanTakeFocus()8511 bool nsWindow::CanTakeFocus() {
8512 HWND fgWnd = ::GetForegroundWindow();
8513 if (!fgWnd) {
8514 // There is no foreground window, so don't worry about stealing focus.
8515 return true;
8516 }
8517 // We can take focus if the current foreground window is already from
8518 // this process.
8519 DWORD pid;
8520 ::GetWindowThreadProcessId(fgWnd, &pid);
8521 if (pid == ::GetCurrentProcessId()) {
8522 return true;
8523 }
8524
8525 bool windowsVisible = false;
8526 ::EnumWindows(EnumVisibleWindowsProc,
8527 reinterpret_cast<LPARAM>(&windowsVisible));
8528
8529 if (!windowsVisible) {
8530 // We're probably creating our first visible window, allow that to
8531 // take focus.
8532 return true;
8533 }
8534 return false;
8535 }
8536
GetMainWindowClass()8537 /* static */ const wchar_t* nsWindow::GetMainWindowClass() {
8538 static const wchar_t* sMainWindowClass = nullptr;
8539 if (!sMainWindowClass) {
8540 nsAutoString className;
8541 Preferences::GetString("ui.window_class_override", className);
8542 if (!className.IsEmpty()) {
8543 sMainWindowClass = wcsdup(className.get());
8544 } else {
8545 sMainWindowClass = kClassNameGeneral;
8546 }
8547 }
8548 return sMainWindowClass;
8549 }
8550
lParamToScreen(LPARAM lParam)8551 LPARAM nsWindow::lParamToScreen(LPARAM lParam) {
8552 POINT pt;
8553 pt.x = GET_X_LPARAM(lParam);
8554 pt.y = GET_Y_LPARAM(lParam);
8555 ::ClientToScreen(mWnd, &pt);
8556 return MAKELPARAM(pt.x, pt.y);
8557 }
8558
lParamToClient(LPARAM lParam)8559 LPARAM nsWindow::lParamToClient(LPARAM lParam) {
8560 POINT pt;
8561 pt.x = GET_X_LPARAM(lParam);
8562 pt.y = GET_Y_LPARAM(lParam);
8563 ::ScreenToClient(mWnd, &pt);
8564 return MAKELPARAM(pt.x, pt.y);
8565 }
8566
PickerOpen()8567 void nsWindow::PickerOpen() { mPickerDisplayCount++; }
8568
PickerClosed()8569 void nsWindow::PickerClosed() {
8570 NS_ASSERTION(mPickerDisplayCount > 0, "mPickerDisplayCount out of sync!");
8571 if (!mPickerDisplayCount) return;
8572 mPickerDisplayCount--;
8573 if (!mPickerDisplayCount && mDestroyCalled) {
8574 Destroy();
8575 }
8576 }
8577
WidgetTypeSupportsAcceleration()8578 bool nsWindow::WidgetTypeSupportsAcceleration() {
8579 // We don't currently support using an accelerated layer manager with
8580 // transparent windows so don't even try. I'm also not sure if we even
8581 // want to support this case. See bug 593471.
8582 //
8583 // Windows' support for transparent accelerated surfaces isn't great.
8584 // Some possible approaches:
8585 // - Readback the data and update it using
8586 // UpdateLayeredWindow/UpdateLayeredWindowIndirect
8587 // This is what WPF does. See
8588 // CD3DDeviceLevel1::PresentWithGDI/CD3DSwapChainWithSwDC in WpfGfx. The
8589 // rationale for not using IDirect3DSurface9::GetDC is explained here:
8590 // https://web.archive.org/web/20160521191104/https://blogs.msdn.microsoft.com/dwayneneed/2008/09/08/transparent-windows-in-wpf/
8591 // - Use D3D11_RESOURCE_MISC_GDI_COMPATIBLE, IDXGISurface1::GetDC(),
8592 // and UpdateLayeredWindowIndirect.
8593 // This is suggested here:
8594 // https://docs.microsoft.com/en-us/archive/msdn-magazine/2009/december/windows-with-c-layered-windows-with-direct2d
8595 // but might have the same problem that IDirect3DSurface9::GetDC has.
8596 // - Creating the window with the WS_EX_NOREDIRECTIONBITMAP flag and use
8597 // DirectComposition.
8598 // Not supported on Win7.
8599 // - Using DwmExtendFrameIntoClientArea with negative margins and something
8600 // to turn off the glass effect.
8601 // This doesn't work when the DWM is not running (Win7)
8602 //
8603 // Also see bug 1150376, D3D11 composition can cause issues on some devices
8604 // on Windows 7 where presentation fails randomly for windows with drop
8605 // shadows.
8606 return mTransparencyMode != eTransparencyTransparent &&
8607 !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
8608 }
8609
DispatchTouchEventFromWMPointer(UINT msg,LPARAM aLParam,const WinPointerInfo & aPointerInfo,mozilla::MouseButton aButton)8610 bool nsWindow::DispatchTouchEventFromWMPointer(
8611 UINT msg, LPARAM aLParam, const WinPointerInfo& aPointerInfo,
8612 mozilla::MouseButton aButton) {
8613 MultiTouchInput::MultiTouchType touchType;
8614 switch (msg) {
8615 case WM_POINTERDOWN:
8616 touchType = MultiTouchInput::MULTITOUCH_START;
8617 break;
8618 case WM_POINTERUPDATE:
8619 if (aPointerInfo.mPressure == 0) {
8620 return false; // hover
8621 }
8622 touchType = MultiTouchInput::MULTITOUCH_MOVE;
8623 break;
8624 case WM_POINTERUP:
8625 touchType = MultiTouchInput::MULTITOUCH_END;
8626 break;
8627 default:
8628 return false;
8629 }
8630
8631 nsPointWin touchPoint;
8632 touchPoint.x = GET_X_LPARAM(aLParam);
8633 touchPoint.y = GET_Y_LPARAM(aLParam);
8634 touchPoint.ScreenToClient(mWnd);
8635
8636 SingleTouchData touchData(static_cast<int32_t>(aPointerInfo.pointerId),
8637 ScreenIntPoint::FromUnknownPoint(touchPoint),
8638 ScreenSize(1, 1), // pixel size radius for pen
8639 0.0f, // no radius rotation
8640 aPointerInfo.mPressure);
8641 touchData.mTiltX = aPointerInfo.tiltX;
8642 touchData.mTiltY = aPointerInfo.tiltY;
8643 touchData.mTwist = aPointerInfo.twist;
8644
8645 MultiTouchInput touchInput;
8646 touchInput.mType = touchType;
8647 touchInput.mTime = ::GetMessageTime();
8648 touchInput.mTimeStamp =
8649 GetMessageTimeStamp(static_cast<long>(touchInput.mTime));
8650 touchInput.mTouches.AppendElement(touchData);
8651 touchInput.mButton = aButton;
8652 touchInput.mButtons = aPointerInfo.mButtons;
8653
8654 // POINTER_INFO.dwKeyStates can't be used as it only supports Shift and Ctrl
8655 ModifierKeyState modifierKeyState;
8656 touchInput.modifiers = modifierKeyState.GetModifiers();
8657
8658 DispatchTouchInput(touchInput, MouseEvent_Binding::MOZ_SOURCE_PEN);
8659 return true;
8660 }
8661
PenFlagsToMouseButton(PEN_FLAGS aPenFlags)8662 static MouseButton PenFlagsToMouseButton(PEN_FLAGS aPenFlags) {
8663 // Theoretically flags can be set together but they do not
8664 if (aPenFlags & PEN_FLAG_BARREL) {
8665 return MouseButton::eSecondary;
8666 }
8667 if (aPenFlags & PEN_FLAG_ERASER) {
8668 return MouseButton::eEraser;
8669 }
8670 return MouseButton::ePrimary;
8671 }
8672
OnPointerEvents(UINT msg,WPARAM aWParam,LPARAM aLParam)8673 bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam) {
8674 if (!mPointerEvents.ShouldHandleWinPointerMessages(msg, aWParam)) {
8675 return false;
8676 }
8677 if (!mPointerEvents.ShouldFirePointerEventByWinPointerMessages()) {
8678 // We have to handle WM_POINTER* to fetch and cache pen related information
8679 // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
8680 // handler. This is because Windows doesn't support ::DoDragDrop in the
8681 // touch or pen message handlers.
8682 mPointerEvents.ConvertAndCachePointerInfo(msg, aWParam);
8683 // Don't consume the Windows WM_POINTER* messages
8684 return false;
8685 }
8686
8687 uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
8688 POINTER_PEN_INFO penInfo{};
8689 if (!mPointerEvents.GetPointerPenInfo(pointerId, &penInfo)) {
8690 return false;
8691 }
8692
8693 // When dispatching mouse events with pen, there may be some
8694 // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
8695 // small movements. Those events will reset sLastMousePoint and reset
8696 // sLastClickCount. To prevent that, we keep the last pen down position
8697 // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
8698 // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
8699 // eMouseMove for WM_POINTERUPDATE.
8700 static POINT sLastPointerDownPoint = {0};
8701
8702 // We don't support chorded buttons for pen. Keep the button at
8703 // WM_POINTERDOWN.
8704 static mozilla::MouseButton sLastPenDownButton = MouseButton::ePrimary;
8705 static bool sPointerDown = false;
8706
8707 EventMessage message;
8708 mozilla::MouseButton button = MouseButton::ePrimary;
8709 switch (msg) {
8710 case WM_POINTERDOWN: {
8711 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
8712 GET_Y_LPARAM(aLParam));
8713 sLastPointerDownPoint.x = eventPoint.x;
8714 sLastPointerDownPoint.y = eventPoint.y;
8715 message = eMouseDown;
8716 button = PenFlagsToMouseButton(penInfo.penFlags);
8717 sLastPenDownButton = button;
8718 sPointerDown = true;
8719 } break;
8720 case WM_POINTERUP:
8721 message = eMouseUp;
8722 MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN");
8723 button = sPointerDown ? sLastPenDownButton : MouseButton::ePrimary;
8724 sPointerDown = false;
8725 break;
8726 case WM_POINTERUPDATE:
8727 message = eMouseMove;
8728 if (sPointerDown) {
8729 LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
8730 GET_Y_LPARAM(aLParam));
8731 int32_t movementX = sLastPointerDownPoint.x > eventPoint.x
8732 ? sLastPointerDownPoint.x - eventPoint.x
8733 : eventPoint.x - sLastPointerDownPoint.x;
8734 int32_t movementY = sLastPointerDownPoint.y > eventPoint.y
8735 ? sLastPointerDownPoint.y - eventPoint.y
8736 : eventPoint.y - sLastPointerDownPoint.y;
8737 bool insideMovementThreshold =
8738 movementX < (int32_t)::GetSystemMetrics(SM_CXDRAG) &&
8739 movementY < (int32_t)::GetSystemMetrics(SM_CYDRAG);
8740
8741 if (insideMovementThreshold) {
8742 // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
8743 // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
8744 return false;
8745 }
8746 button = sLastPenDownButton;
8747 }
8748 break;
8749 case WM_POINTERLEAVE:
8750 message = eMouseExitFromWidget;
8751 break;
8752 default:
8753 return false;
8754 }
8755
8756 // Windows defines the pen pressure is normalized to a range between 0 and
8757 // 1024. Convert it to float.
8758 float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
8759 int16_t buttons = sPointerDown
8760 ? nsContentUtils::GetButtonsFlagForButton(button)
8761 : MouseButtonsFlag::eNoButtons;
8762 WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
8763 buttons);
8764 pointerInfo.twist = penInfo.rotation;
8765
8766 if (StaticPrefs::dom_w3c_pointer_events_scroll_by_pen_enabled() &&
8767 DispatchTouchEventFromWMPointer(msg, aLParam, pointerInfo, button)) {
8768 return true;
8769 }
8770
8771 // The aLParam of WM_POINTER* is the screen location. Convert it to client
8772 // location
8773 LPARAM newLParam = lParamToClient(aLParam);
8774 DispatchMouseEvent(message, aWParam, newLParam, false, button,
8775 MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
8776 // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
8777 // WM_MOUSEMOVE.
8778 return true;
8779 }
8780
GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData * aInitData)8781 void nsWindow::GetCompositorWidgetInitData(
8782 mozilla::widget::CompositorWidgetInitData* aInitData) {
8783 *aInitData = WinCompositorWidgetInitData(
8784 reinterpret_cast<uintptr_t>(mWnd),
8785 reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
8786 mTransparencyMode, mSizeMode);
8787 }
8788
SynchronouslyRepaintOnResize()8789 bool nsWindow::SynchronouslyRepaintOnResize() {
8790 return !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
8791 }
8792
MaybeDispatchInitialFocusEvent()8793 void nsWindow::MaybeDispatchInitialFocusEvent() {
8794 if (mIsShowingPreXULSkeletonUI && ::GetActiveWindow() == mWnd) {
8795 DispatchFocusToTopLevelWindow(true);
8796 }
8797 }
8798
CreateTopLevelWindow()8799 already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() {
8800 nsCOMPtr<nsIWidget> window = new nsWindow();
8801 return window.forget();
8802 }
8803
CreateChildWindow()8804 already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() {
8805 nsCOMPtr<nsIWidget> window = new nsWindow(true);
8806 return window.forget();
8807 }
8808