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