Bug 867104 - Add a crashtest. r=ehsan
[gecko.git] / widget / windows / nsWindow.cpp
blobbe795c512c9269b9f3b80692fc4671e15ecb6873
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sts=2 sw=2 et cin: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * nsWindow - Native window management and event handling.
9 *
10 * nsWindow is organized into a set of major blocks and
11 * block subsections. The layout is as follows:
13 * Includes
14 * Variables
15 * nsIWidget impl.
16 * nsIWidget methods and utilities
17 * nsSwitchToUIThread impl.
18 * nsSwitchToUIThread methods and utilities
19 * Moz events
20 * Event initialization
21 * Event dispatching
22 * Native events
23 * Wndproc(s)
24 * Event processing
25 * OnEvent event handlers
26 * IME management and accessibility
27 * Transparency
28 * Popup hook handling
29 * Misc. utilities
30 * Child window impl.
32 * Search for "BLOCK:" to find major blocks.
33 * Search for "SECTION:" to find specific sections.
35 * Blocks should be split out into separate files if they
36 * become unmanageable.
38 * Related source:
40 * nsWindowDefs.h - Definitions, macros, structs, enums
41 * and general setup.
42 * nsWindowDbg.h/.cpp - Debug related code and directives.
43 * nsWindowGfx.h/.cpp - Graphics and painting.
47 /**************************************************************
48 **************************************************************
50 ** BLOCK: Includes
52 ** Include headers.
54 **************************************************************
55 **************************************************************/
57 #include "mozilla/MathAlgorithms.h"
58 #include "mozilla/Util.h"
60 #include "mozilla/ipc/RPCChannel.h"
61 #include <algorithm>
63 #include "nsWindow.h"
65 #include <shellapi.h>
66 #include <windows.h>
67 #include <process.h>
68 #include <commctrl.h>
69 #include <unknwn.h>
70 #include <psapi.h>
72 #include "prlog.h"
73 #include "prtime.h"
74 #include "prprf.h"
75 #include "prmem.h"
77 #include "mozilla/WidgetTraceEvent.h"
78 #include "nsIAppShell.h"
79 #include "nsISupportsPrimitives.h"
80 #include "nsIDOMMouseEvent.h"
81 #include "nsITheme.h"
82 #include "nsIObserverService.h"
83 #include "nsIScreenManager.h"
84 #include "imgIContainer.h"
85 #include "nsIFile.h"
86 #include "nsIRollupListener.h"
87 #include "nsIServiceManager.h"
88 #include "nsIClipboard.h"
89 #include "nsIMM32Handler.h"
90 #include "WinMouseScrollHandler.h"
91 #include "nsFontMetrics.h"
92 #include "nsIFontEnumerator.h"
93 #include "nsGUIEvent.h"
94 #include "nsFont.h"
95 #include "nsRect.h"
96 #include "nsThreadUtils.h"
97 #include "nsNativeCharsetUtils.h"
98 #include "nsGkAtoms.h"
99 #include "nsCRT.h"
100 #include "nsAppDirectoryServiceDefs.h"
101 #include "nsXPIDLString.h"
102 #include "nsWidgetsCID.h"
103 #include "nsTHashtable.h"
104 #include "nsHashKeys.h"
105 #include "nsString.h"
106 #include "mozilla/Services.h"
107 #include "nsNativeThemeWin.h"
108 #include "nsWindowsDllInterceptor.h"
109 #include "nsIWindowMediator.h"
110 #include "nsIServiceManager.h"
111 #include "nsWindowGfx.h"
112 #include "gfxWindowsPlatform.h"
113 #include "Layers.h"
114 #include "nsPrintfCString.h"
115 #include "mozilla/Preferences.h"
116 #include "nsISound.h"
117 #include "WinTaskbar.h"
118 #include "WinUtils.h"
119 #include "WidgetUtils.h"
120 #include "nsIWidgetListener.h"
121 #include "mozilla/dom/Touch.h"
123 #ifdef MOZ_ENABLE_D3D9_LAYER
124 #include "LayerManagerD3D9.h"
125 #endif
127 #ifdef MOZ_ENABLE_D3D10_LAYER
128 #include "LayerManagerD3D10.h"
129 #endif
131 #include "LayerManagerOGL.h"
132 #include "nsIGfxInfo.h"
133 #include "BasicLayers.h"
134 #include "nsUXThemeConstants.h"
135 #include "KeyboardLayout.h"
136 #include "nsNativeDragTarget.h"
137 #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
138 #include <zmouse.h>
139 #include <richedit.h>
141 #if defined(ACCESSIBILITY)
142 #include "oleidl.h"
143 #include <winuser.h>
144 #include "nsAccessibilityService.h"
145 #include "mozilla/a11y/Platform.h"
146 #if !defined(WINABLEAPI)
147 #include <winable.h>
148 #endif // !defined(WINABLEAPI)
149 #endif // defined(ACCESSIBILITY)
151 #include "nsIWinTaskbar.h"
152 #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
154 // Windowless plugin support
155 #include "npapi.h"
157 #include "nsWindowDefs.h"
159 #include "nsCrashOnException.h"
160 #include "nsIXULRuntime.h"
162 #include "nsIContent.h"
164 #include "mozilla/HangMonitor.h"
165 #include "WinIMEHandler.h"
167 using namespace mozilla;
168 using namespace mozilla::dom;
169 using namespace mozilla::layers;
170 using namespace mozilla::widget;
172 /**************************************************************
173 **************************************************************
175 ** BLOCK: Variables
177 ** nsWindow Class static initializations and global variables.
179 **************************************************************
180 **************************************************************/
182 /**************************************************************
184 * SECTION: nsWindow statics
186 **************************************************************/
188 bool nsWindow::sDropShadowEnabled = true;
189 uint32_t nsWindow::sInstanceCount = 0;
190 bool nsWindow::sSwitchKeyboardLayout = false;
191 BOOL nsWindow::sIsOleInitialized = FALSE;
192 HCURSOR nsWindow::sHCursor = NULL;
193 imgIContainer* nsWindow::sCursorImgContainer = nullptr;
194 nsWindow* nsWindow::sCurrentWindow = nullptr;
195 bool nsWindow::sJustGotDeactivate = false;
196 bool nsWindow::sJustGotActivate = false;
197 bool nsWindow::sIsInMouseCapture = false;
199 // imported in nsWidgetFactory.cpp
200 TriStateBool nsWindow::sCanQuit = TRI_UNKNOWN;
202 // Hook Data Memebers for Dropdowns. sProcessHook Tells the
203 // hook methods whether they should be processing the hook
204 // messages.
205 HHOOK nsWindow::sMsgFilterHook = NULL;
206 HHOOK nsWindow::sCallProcHook = NULL;
207 HHOOK nsWindow::sCallMouseHook = NULL;
208 bool nsWindow::sProcessHook = false;
209 UINT nsWindow::sRollupMsgId = 0;
210 HWND nsWindow::sRollupMsgWnd = NULL;
211 UINT nsWindow::sHookTimerId = 0;
213 // Mouse Clicks - static variable definitions for figuring
214 // out 1 - 3 Clicks.
215 POINT nsWindow::sLastMousePoint = {0};
216 POINT nsWindow::sLastMouseMovePoint = {0};
217 LONG nsWindow::sLastMouseDownTime = 0L;
218 LONG nsWindow::sLastClickCount = 0L;
219 BYTE nsWindow::sLastMouseButton = 0;
221 // Trim heap on minimize. (initialized, but still true.)
222 int nsWindow::sTrimOnMinimize = 2;
224 // Default value for general window class (used when the pref is the empty string).
225 const char* nsWindow::sDefaultMainWindowClass = kClassNameGeneral;
227 // If we're using D3D9, this will not be allowed during initial 5 seconds.
228 bool nsWindow::sAllowD3D9 = false;
230 TriStateBool nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor = TRI_UNKNOWN;
232 // Used in OOPP plugin focus processing.
233 const PRUnichar* kOOPPPluginFocusEventId = L"OOPP Plugin Focus Widget Event";
234 uint32_t nsWindow::sOOPPPluginFocusEvent =
235 RegisterWindowMessageW(kOOPPPluginFocusEventId);
237 MSG nsWindow::sRedirectedKeyDown;
239 /**************************************************************
241 * SECTION: globals variables
243 **************************************************************/
245 static const char *sScreenManagerContractID = "@mozilla.org/gfx/screenmanager;1";
247 #ifdef PR_LOGGING
248 PRLogModuleInfo* gWindowsLog = nullptr;
249 #endif
251 // Kbd layout. Used throughout character processing.
252 static KeyboardLayout gKbdLayout;
254 // Global used in Show window enumerations.
255 static bool gWindowsVisible = false;
257 // True if we have sent a notification that we are suspending/sleeping.
258 static bool gIsSleepMode = false;
260 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
262 // General purpose user32.dll hook object
263 static WindowsDllInterceptor sUser32Intercept;
265 // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
266 // the default window border Windows paints. Glass will be extended inward
267 // this distance to remove the border.
268 static const int32_t kGlassMarginAdjustment = 2;
270 // When the client area is extended out into the default window frame area,
271 // this is the minimum amount of space along the edge of resizable windows
272 // we will always display a resize cursor in, regardless of the underlying
273 // content.
274 static const int32_t kResizableBorderMinSize = 3;
276 // We should never really try to accelerate windows bigger than this. In some
277 // cases this might lead to no D3D9 acceleration where we could have had it
278 // but D3D9 does not reliably report when it supports bigger windows. 8192
279 // is as safe as we can get, we know at least D3D10 hardware always supports
280 // this, other hardware we expect to report correctly in D3D9.
281 #define MAX_ACCELERATED_DIMENSION 8192
284 /**************************************************************
285 **************************************************************
287 ** BLOCK: nsIWidget impl.
289 ** nsIWidget interface implementation, broken down into
290 ** sections.
292 **************************************************************
293 **************************************************************/
295 /**************************************************************
297 * SECTION: nsWindow construction and destruction
299 **************************************************************/
301 nsWindow::nsWindow() : nsWindowBase()
303 #ifdef PR_LOGGING
304 if (!gWindowsLog) {
305 gWindowsLog = PR_NewLogModule("nsWindow");
307 #endif
309 mIconSmall = nullptr;
310 mIconBig = nullptr;
311 mWnd = nullptr;
312 mPaintDC = nullptr;
313 mPrevWndProc = nullptr;
314 mNativeDragTarget = nullptr;
315 mInDtor = false;
316 mIsVisible = false;
317 mIsTopWidgetWindow = false;
318 mUnicodeWidget = true;
319 mDisplayPanFeedback = false;
320 mTouchWindow = false;
321 mCustomNonClient = false;
322 mHideChrome = false;
323 mFullscreenMode = false;
324 mMousePresent = false;
325 mDestroyCalled = false;
326 mPickerDisplayCount = 0;
327 mWindowType = eWindowType_child;
328 mBorderStyle = eBorderStyle_default;
329 mOldSizeMode = nsSizeMode_Normal;
330 mLastSizeMode = nsSizeMode_Normal;
331 mLastPoint.x = 0;
332 mLastPoint.y = 0;
333 mLastSize.width = 0;
334 mLastSize.height = 0;
335 mOldStyle = 0;
336 mOldExStyle = 0;
337 mPainting = 0;
338 mLastKeyboardLayout = 0;
339 mBlurSuppressLevel = 0;
340 mLastPaintEndTime = TimeStamp::Now();
341 #ifdef MOZ_XUL
342 mTransparentSurface = nullptr;
343 mMemoryDC = nullptr;
344 mTransparencyMode = eTransparencyOpaque;
345 memset(&mGlassMargins, 0, sizeof mGlassMargins);
346 #endif
347 mBackground = ::GetSysColor(COLOR_BTNFACE);
348 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(mBackground));
349 mForeground = ::GetSysColor(COLOR_WINDOWTEXT);
351 mTaskbarPreview = nullptr;
352 mHasTaskbarIconBeenCreated = false;
354 // Global initialization
355 if (!sInstanceCount) {
356 // Global app registration id for Win7 and up. See
357 // WinTaskbar.cpp for details.
358 mozilla::widget::WinTaskbar::RegisterAppUserModelID();
359 gKbdLayout.LoadLayout(::GetKeyboardLayout(0));
360 IMEHandler::Initialize();
361 if (SUCCEEDED(::OleInitialize(NULL))) {
362 sIsOleInitialized = TRUE;
364 NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
365 MouseScrollHandler::Initialize();
366 // Init titlebar button info for custom frames.
367 nsUXThemeData::InitTitlebarInfo();
368 // Init theme data
369 nsUXThemeData::UpdateNativeThemeInfo();
370 ForgetRedirectedKeyDownMessage();
371 } // !sInstanceCount
373 mIdleService = nullptr;
375 sInstanceCount++;
378 nsWindow::~nsWindow()
380 mInDtor = true;
382 // If the widget was released without calling Destroy() then the native window still
383 // exists, and we need to destroy it. This will also result in a call to OnDestroy.
385 // XXX How could this happen???
386 if (NULL != mWnd)
387 Destroy();
389 // Free app icon resources. This must happen after `OnDestroy` (see bug 708033).
390 if (mIconSmall)
391 ::DestroyIcon(mIconSmall);
393 if (mIconBig)
394 ::DestroyIcon(mIconBig);
396 sInstanceCount--;
398 // Global shutdown
399 if (sInstanceCount == 0) {
400 IMEHandler::Terminate();
401 NS_IF_RELEASE(sCursorImgContainer);
402 if (sIsOleInitialized) {
403 ::OleFlushClipboard();
404 ::OleUninitialize();
405 sIsOleInitialized = FALSE;
409 NS_IF_RELEASE(mNativeDragTarget);
412 NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
414 /**************************************************************
416 * SECTION: nsIWidget::Create, nsIWidget::Destroy
418 * Creating and destroying windows for this widget.
420 **************************************************************/
422 // Allow Derived classes to modify the height that is passed
423 // when the window is created or resized.
424 int32_t nsWindow::GetHeight(int32_t aProposedHeight)
426 return aProposedHeight;
429 // Create the proper widget
430 nsresult
431 nsWindow::Create(nsIWidget *aParent,
432 nsNativeWidget aNativeParent,
433 const nsIntRect &aRect,
434 nsDeviceContext *aContext,
435 nsWidgetInitData *aInitData)
437 nsWidgetInitData defaultInitData;
438 if (!aInitData)
439 aInitData = &defaultInitData;
441 mUnicodeWidget = aInitData->mUnicode;
443 nsIWidget *baseParent = aInitData->mWindowType == eWindowType_dialog ||
444 aInitData->mWindowType == eWindowType_toplevel ||
445 aInitData->mWindowType == eWindowType_invisible ?
446 nullptr : aParent;
448 mIsTopWidgetWindow = (nullptr == baseParent);
449 mBounds = aRect;
451 // Ensure that the toolkit is created.
452 nsToolkit::GetToolkit();
454 BaseCreate(baseParent, aRect, aContext, aInitData);
456 HWND parent;
457 if (aParent) { // has a nsIWidget parent
458 parent = aParent ? (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW) : NULL;
459 mParent = aParent;
460 } else { // has a nsNative parent
461 parent = (HWND)aNativeParent;
462 mParent = aNativeParent ?
463 WinUtils::GetNSWindowPtr((HWND)aNativeParent) : nullptr;
466 mIsRTL = aInitData->mRTL;
468 DWORD style = WindowStyle();
469 DWORD extendedStyle = WindowExStyle();
471 if (mWindowType == eWindowType_popup) {
472 if (!aParent) {
473 parent = NULL;
476 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
477 WinUtils::GetWindowsVersion() <= WinUtils::WIN7_VERSION) {
478 extendedStyle |= WS_EX_COMPOSITED;
481 if (aInitData->mIsDragPopup) {
482 // This flag makes the window transparent to mouse events
483 extendedStyle |= WS_EX_TRANSPARENT;
485 } else if (mWindowType == eWindowType_invisible) {
486 // Make sure CreateWindowEx succeeds at creating a toplevel window
487 style &= ~0x40000000; // WS_CHILDWINDOW
488 } else {
489 // See if the caller wants to explictly set clip children and clip siblings
490 if (aInitData->clipChildren) {
491 style |= WS_CLIPCHILDREN;
492 } else {
493 style &= ~WS_CLIPCHILDREN;
495 if (aInitData->clipSiblings) {
496 style |= WS_CLIPSIBLINGS;
500 nsAutoString className;
501 if (aInitData->mDropShadow) {
502 GetWindowPopupClass(className);
503 } else {
504 GetWindowClass(className);
506 // Plugins are created in the disabled state so that they can't
507 // steal focus away from our main window. This is especially
508 // important if the plugin has loaded in a background tab.
509 if(aInitData->mWindowType == eWindowType_plugin) {
510 style |= WS_DISABLED;
512 mWnd = ::CreateWindowExW(extendedStyle,
513 className.get(),
514 L"",
515 style,
516 aRect.x,
517 aRect.y,
518 aRect.width,
519 GetHeight(aRect.height),
520 parent,
521 NULL,
522 nsToolkit::mDllInstance,
523 NULL);
525 if (!mWnd) {
526 NS_WARNING("nsWindow CreateWindowEx failed.");
527 return NS_ERROR_FAILURE;
530 if (mIsRTL && nsUXThemeData::dwmSetWindowAttributePtr) {
531 DWORD dwAttribute = TRUE;
532 nsUXThemeData::dwmSetWindowAttributePtr(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
535 if (mWindowType != eWindowType_plugin &&
536 mWindowType != eWindowType_invisible &&
537 MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
538 // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
540 // We create two zero-sized windows as descendants of the top-level window,
541 // like so:
543 // Top-level window (MozillaWindowClass)
544 // FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
545 // FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
547 // We need to have the middle window, otherwise the Trackpoint driver
548 // will fail to deliver scroll messages. WM_MOUSEWHEEL messages are
549 // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
550 // window hierarchy until they are handled by nsWindow::WindowProc.
551 // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
552 // but these do not propagate automatically, so we have the window
553 // procedure pretend that they were dispatched to the top-level window
554 // instead.
556 // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
557 // is given below so that it catches the Trackpoint driver's heuristics.
558 HWND scrollContainerWnd = ::CreateWindowW
559 (className.get(), L"FAKETRACKPOINTSCROLLCONTAINER",
560 WS_CHILD | WS_VISIBLE,
561 0, 0, 0, 0, mWnd, NULL, nsToolkit::mDllInstance, NULL);
562 HWND scrollableWnd = ::CreateWindowW
563 (className.get(), L"FAKETRACKPOINTSCROLLABLE",
564 WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | 0x30,
565 0, 0, 0, 0, scrollContainerWnd, NULL, nsToolkit::mDllInstance, NULL);
567 // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
568 // WindowProcInternal can distinguish it from the top-level window
569 // easily.
570 ::SetWindowLongPtrW(scrollableWnd, GWLP_ID, eFakeTrackPointScrollableID);
572 // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
573 // old window procedure in its "user data".
574 WNDPROC oldWndProc;
575 if (mUnicodeWidget)
576 oldWndProc = (WNDPROC)::SetWindowLongPtrW(scrollableWnd, GWLP_WNDPROC,
577 (LONG_PTR)nsWindow::WindowProc);
578 else
579 oldWndProc = (WNDPROC)::SetWindowLongPtrA(scrollableWnd, GWLP_WNDPROC,
580 (LONG_PTR)nsWindow::WindowProc);
581 ::SetWindowLongPtrW(scrollableWnd, GWLP_USERDATA, (LONG_PTR)oldWndProc);
584 SubclassWindow(TRUE);
586 IMEHandler::InitInputContext(this, mInputContext);
588 // If the internal variable set by the config.trim_on_minimize pref has not
589 // been initialized, and if this is the hidden window (conveniently created
590 // before any visible windows, and after the profile has been initialized),
591 // do some initialization work.
592 if (sTrimOnMinimize == 2 && mWindowType == eWindowType_invisible) {
593 // Our internal trim prevention logic is effective on 2K/XP at maintaining
594 // the working set when windows are minimized, but on Vista and up it has
595 // little to no effect. Since this feature has been the source of numerous
596 // bugs over the years, disable it (sTrimOnMinimize=1) on Vista and up.
597 sTrimOnMinimize =
598 Preferences::GetBool("config.trim_on_minimize",
599 (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION)) ? 1 : 0;
600 sSwitchKeyboardLayout =
601 Preferences::GetBool("intl.keyboard.per_window_layout", false);
604 return NS_OK;
607 // Close this nsWindow
608 NS_METHOD nsWindow::Destroy()
610 // WM_DESTROY has already fired, avoid calling it twice
611 if (mOnDestroyCalled)
612 return NS_OK;
614 // Don't destroy windows that have file pickers open, we'll tear these down
615 // later once the picker is closed.
616 mDestroyCalled = true;
617 if (mPickerDisplayCount)
618 return NS_OK;
620 // During the destruction of all of our children, make sure we don't get deleted.
621 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
624 * On windows the LayerManagerOGL destructor wants the widget to be around for
625 * cleanup. It also would like to have the HWND intact, so we NULL it here.
627 if (mLayerManager) {
628 mLayerManager->Destroy();
630 mLayerManager = nullptr;
632 /* We should clear our cached resources now and not wait for the GC to
633 * delete the nsWindow. */
634 ClearCachedResources();
636 // The DestroyWindow function destroys the specified window. The function sends WM_DESTROY
637 // and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus
638 // from it. The function also destroys the window's menu, flushes the thread message queue,
639 // destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if
640 // the window is at the top of the viewer chain).
642 // If the specified window is a parent or owner window, DestroyWindow automatically destroys
643 // the associated child or owned windows when it destroys the parent or owner window. The
644 // function first destroys child or owned windows, and then it destroys the parent or owner
645 // window.
646 VERIFY(::DestroyWindow(mWnd));
648 // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If OnDestroy()
649 // didn't get called, call it now.
650 if (false == mOnDestroyCalled) {
651 LRESULT result;
652 mWindowHook.Notify(mWnd, WM_DESTROY, 0, 0, &result);
653 OnDestroy();
656 return NS_OK;
659 /**************************************************************
661 * SECTION: Window class utilities
663 * Utilities for calculating the proper window class name for
664 * Create window.
666 **************************************************************/
668 void nsWindow::RegisterWindowClass(const nsString& aClassName, UINT aExtraStyle,
669 LPWSTR aIconID)
671 WNDCLASSW wc;
672 if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName.get(), &wc)) {
673 // already registered
674 return;
677 wc.style = CS_DBLCLKS | aExtraStyle;
678 wc.lpfnWndProc = ::DefWindowProcW;
679 wc.cbClsExtra = 0;
680 wc.cbWndExtra = 0;
681 wc.hInstance = nsToolkit::mDllInstance;
682 wc.hIcon = aIconID ? ::LoadIconW(::GetModuleHandleW(NULL), aIconID) : NULL;
683 wc.hCursor = NULL;
684 wc.hbrBackground = mBrush;
685 wc.lpszMenuName = NULL;
686 wc.lpszClassName = aClassName.get();
688 if (!::RegisterClassW(&wc)) {
689 // For older versions of Win32 (i.e., not XP), the registration may
690 // fail with aExtraStyle, so we have to re-register without it.
691 wc.style = CS_DBLCLKS;
692 ::RegisterClassW(&wc);
696 static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);
698 // Return the proper window class for everything except popups.
699 void nsWindow::GetWindowClass(nsString& aWindowClass)
701 switch (mWindowType) {
702 case eWindowType_invisible:
703 aWindowClass.AssignLiteral(kClassNameHidden);
704 RegisterWindowClass(aWindowClass, 0, gStockApplicationIcon);
705 break;
706 case eWindowType_dialog:
707 aWindowClass.AssignLiteral(kClassNameDialog);
708 RegisterWindowClass(aWindowClass, 0, 0);
709 break;
710 default:
711 GetMainWindowClass(aWindowClass);
712 RegisterWindowClass(aWindowClass, 0, gStockApplicationIcon);
713 break;
717 // Return the proper popup window class
718 void nsWindow::GetWindowPopupClass(nsString& aWindowClass)
720 aWindowClass.AssignLiteral(kClassNameDropShadow);
721 RegisterWindowClass(aWindowClass, CS_XP_DROPSHADOW, gStockApplicationIcon);
724 /**************************************************************
726 * SECTION: Window styles utilities
728 * Return the proper windows styles and extended styles.
730 **************************************************************/
732 // Return nsWindow styles
733 DWORD nsWindow::WindowStyle()
735 DWORD style;
737 switch (mWindowType) {
738 case eWindowType_plugin:
739 case eWindowType_child:
740 style = WS_OVERLAPPED;
741 break;
743 case eWindowType_dialog:
744 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK |
745 DS_MODALFRAME | WS_CLIPCHILDREN;
746 if (mBorderStyle != eBorderStyle_default)
747 style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
748 break;
750 case eWindowType_popup:
751 style = WS_POPUP;
752 if (!HasGlass()) {
753 style |= WS_OVERLAPPED;
755 break;
757 default:
758 NS_ERROR("unknown border style");
759 // fall through
761 case eWindowType_toplevel:
762 case eWindowType_invisible:
763 style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU |
764 WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN;
765 break;
768 if (mBorderStyle != eBorderStyle_default && mBorderStyle != eBorderStyle_all) {
769 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_border))
770 style &= ~WS_BORDER;
772 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_title)) {
773 style &= ~WS_DLGFRAME;
774 style |= WS_POPUP;
775 style &= ~WS_CHILD;
778 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_close))
779 style &= ~0;
780 // XXX The close box can only be removed by changing the window class,
781 // as far as I know --- roc+moz@cs.cmu.edu
783 if (mBorderStyle == eBorderStyle_none ||
784 !(mBorderStyle & (eBorderStyle_menu | eBorderStyle_close)))
785 style &= ~WS_SYSMENU;
786 // Looks like getting rid of the system menu also does away with the
787 // close box. So, we only get rid of the system menu if you want neither it
788 // nor the close box. How does the Windows "Dialog" window class get just
789 // closebox and no sysmenu? Who knows.
791 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_resizeh))
792 style &= ~WS_THICKFRAME;
794 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_minimize))
795 style &= ~WS_MINIMIZEBOX;
797 if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_maximize))
798 style &= ~WS_MAXIMIZEBOX;
800 if (IsPopupWithTitleBar()) {
801 style |= WS_CAPTION;
802 if (mBorderStyle & eBorderStyle_close) {
803 style |= WS_SYSMENU;
808 VERIFY_WINDOW_STYLE(style);
809 return style;
812 // Return nsWindow extended styles
813 DWORD nsWindow::WindowExStyle()
815 switch (mWindowType)
817 case eWindowType_plugin:
818 case eWindowType_child:
819 return 0;
821 case eWindowType_dialog:
822 return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
824 case eWindowType_popup:
826 DWORD extendedStyle = WS_EX_TOOLWINDOW;
827 if (mPopupLevel == ePopupLevelTop)
828 extendedStyle |= WS_EX_TOPMOST;
829 return extendedStyle;
831 default:
832 NS_ERROR("unknown border style");
833 // fall through
835 case eWindowType_toplevel:
836 case eWindowType_invisible:
837 return WS_EX_WINDOWEDGE;
841 /**************************************************************
843 * SECTION: Window subclassing utilities
845 * Set or clear window subclasses on native windows. Used in
846 * Create and Destroy.
848 **************************************************************/
850 // Subclass (or remove the subclass from) this component's nsWindow
851 void nsWindow::SubclassWindow(BOOL bState)
853 if (bState) {
854 if (!mWnd || !IsWindow(mWnd)) {
855 NS_ERROR("Invalid window handle");
858 if (mUnicodeWidget) {
859 mPrevWndProc =
860 reinterpret_cast<WNDPROC>(
861 SetWindowLongPtrW(mWnd,
862 GWLP_WNDPROC,
863 reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
864 } else {
865 mPrevWndProc =
866 reinterpret_cast<WNDPROC>(
867 SetWindowLongPtrA(mWnd,
868 GWLP_WNDPROC,
869 reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
871 NS_ASSERTION(mPrevWndProc, "Null standard window procedure");
872 // connect the this pointer to the nsWindow handle
873 WinUtils::SetNSWindowPtr(mWnd, this);
874 } else {
875 if (IsWindow(mWnd)) {
876 if (mUnicodeWidget) {
877 SetWindowLongPtrW(mWnd,
878 GWLP_WNDPROC,
879 reinterpret_cast<LONG_PTR>(mPrevWndProc));
880 } else {
881 SetWindowLongPtrA(mWnd,
882 GWLP_WNDPROC,
883 reinterpret_cast<LONG_PTR>(mPrevWndProc));
886 WinUtils::SetNSWindowPtr(mWnd, NULL);
887 mPrevWndProc = NULL;
891 /**************************************************************
893 * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
895 * Set or clear the parent widgets using window properties, and
896 * handles calculating native parent handles.
898 **************************************************************/
900 // Get and set parent widgets
901 NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent)
903 mParent = aNewParent;
905 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
906 nsIWidget* parent = GetParent();
907 if (parent) {
908 parent->RemoveChild(this);
910 if (aNewParent) {
911 ReparentNativeWidget(aNewParent);
912 aNewParent->AddChild(this);
913 return NS_OK;
915 if (mWnd) {
916 // If we have no parent, SetParent should return the desktop.
917 VERIFY(::SetParent(mWnd, nullptr));
919 return NS_OK;
922 NS_IMETHODIMP
923 nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
925 NS_PRECONDITION(aNewParent, "");
927 mParent = aNewParent;
928 if (mWindowType == eWindowType_popup) {
929 return NS_OK;
931 HWND newParent = (HWND)aNewParent->GetNativeData(NS_NATIVE_WINDOW);
932 NS_ASSERTION(newParent, "Parent widget has a null native window handle");
933 if (newParent && mWnd) {
934 ::SetParent(mWnd, newParent);
936 return NS_OK;
939 nsIWidget* nsWindow::GetParent(void)
941 return GetParentWindow(false);
944 float nsWindow::GetDPI()
946 HDC dc = ::GetDC(mWnd);
947 if (!dc)
948 return 96.0f;
950 double heightInches = ::GetDeviceCaps(dc, VERTSIZE)/MM_PER_INCH_FLOAT;
951 int heightPx = ::GetDeviceCaps(dc, VERTRES);
952 ::ReleaseDC(mWnd, dc);
953 if (heightInches < 0.25) {
954 // Something's broken
955 return 96.0f;
957 return float(heightPx/heightInches);
960 double nsWindow::GetDefaultScaleInternal()
962 return gfxWindowsPlatform::GetPlatform()->GetDPIScale();
965 nsWindow* nsWindow::GetParentWindow(bool aIncludeOwner)
967 if (mIsTopWidgetWindow) {
968 // Must use a flag instead of mWindowType to tell if the window is the
969 // owned by the topmost widget, because a child window can be embedded inside
970 // a HWND which is not associated with a nsIWidget.
971 return nullptr;
974 // If this widget has already been destroyed, pretend we have no parent.
975 // This corresponds to code in Destroy which removes the destroyed
976 // widget from its parent's child list.
977 if (mInDtor || mOnDestroyCalled)
978 return nullptr;
981 // aIncludeOwner set to true implies walking the parent chain to retrieve the
982 // root owner. aIncludeOwner set to false implies the search will stop at the
983 // true parent (default).
984 nsWindow* widget = nullptr;
985 if (mWnd) {
986 HWND parent = nullptr;
987 if (aIncludeOwner)
988 parent = ::GetParent(mWnd);
989 else
990 parent = ::GetAncestor(mWnd, GA_PARENT);
992 if (parent) {
993 widget = WinUtils::GetNSWindowPtr(parent);
994 if (widget) {
995 // If the widget is in the process of being destroyed then
996 // do NOT return it
997 if (widget->mInDtor) {
998 widget = nullptr;
1004 return widget;
1007 BOOL CALLBACK
1008 nsWindow::EnumAllChildWindProc(HWND aWnd, LPARAM aParam)
1010 nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
1011 if (wnd) {
1012 ((nsWindow::WindowEnumCallback*)aParam)(wnd);
1014 return TRUE;
1017 BOOL CALLBACK
1018 nsWindow::EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam)
1020 nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
1021 if (wnd) {
1022 ((nsWindow::WindowEnumCallback*)aParam)(wnd);
1024 EnumChildWindows(aWnd, EnumAllChildWindProc, aParam);
1025 return TRUE;
1028 void
1029 nsWindow::EnumAllWindows(WindowEnumCallback aCallback)
1031 EnumThreadWindows(GetCurrentThreadId(),
1032 EnumAllThreadWindowProc,
1033 (LPARAM)aCallback);
1036 /**************************************************************
1038 * SECTION: nsIWidget::Show
1040 * Hide or show this component.
1042 **************************************************************/
1044 NS_METHOD nsWindow::Show(bool bState)
1046 if (mWindowType == eWindowType_popup) {
1047 // See bug 603793. When we try to draw D3D9/10 windows with a drop shadow
1048 // without the DWM on a secondary monitor, windows fails to composite
1049 // our windows correctly. We therefor switch off the drop shadow for
1050 // pop-up windows when the DWM is disabled and two monitors are
1051 // connected.
1052 if (HasBogusPopupsDropShadowOnMultiMonitor() &&
1053 WinUtils::GetMonitorCount() > 1 &&
1054 !nsUXThemeData::CheckForCompositor())
1056 if (sDropShadowEnabled) {
1057 ::SetClassLongA(mWnd, GCL_STYLE, 0);
1058 sDropShadowEnabled = false;
1060 } else {
1061 if (!sDropShadowEnabled) {
1062 ::SetClassLongA(mWnd, GCL_STYLE, CS_DROPSHADOW);
1063 sDropShadowEnabled = true;
1067 // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
1068 // some popup menus to become invisible.
1069 LONG_PTR exStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
1070 if (exStyle & WS_EX_LAYERED) {
1071 ::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, exStyle & ~WS_EX_COMPOSITED);
1075 bool syncInvalidate = false;
1077 bool wasVisible = mIsVisible;
1078 // Set the status now so that anyone asking during ShowWindow or
1079 // SetWindowPos would get the correct answer.
1080 mIsVisible = bState;
1082 // We may have cached an out of date visible state. This can happen
1083 // when session restore sets the full screen mode.
1084 if (mIsVisible)
1085 mOldStyle |= WS_VISIBLE;
1086 else
1087 mOldStyle &= ~WS_VISIBLE;
1089 if (!mIsVisible && wasVisible) {
1090 ClearCachedResources();
1093 if (mWnd) {
1094 if (bState) {
1095 if (!wasVisible && mWindowType == eWindowType_toplevel) {
1096 // speed up the initial paint after show for
1097 // top level windows:
1098 syncInvalidate = true;
1099 switch (mSizeMode) {
1100 case nsSizeMode_Fullscreen:
1101 ::ShowWindow(mWnd, SW_SHOW);
1102 break;
1103 case nsSizeMode_Maximized :
1104 ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
1105 break;
1106 case nsSizeMode_Minimized :
1107 ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
1108 break;
1109 default:
1110 if (CanTakeFocus()) {
1111 ::ShowWindow(mWnd, SW_SHOWNORMAL);
1112 } else {
1113 // Place the window behind the foreground window
1114 // (as long as it is not topmost)
1115 HWND wndAfter = ::GetForegroundWindow();
1116 if (!wndAfter)
1117 wndAfter = HWND_BOTTOM;
1118 else if (GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST)
1119 wndAfter = HWND_TOP;
1120 ::SetWindowPos(mWnd, wndAfter, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE |
1121 SWP_NOMOVE | SWP_NOACTIVATE);
1122 GetAttention(2);
1124 break;
1126 } else {
1127 DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
1128 if (wasVisible)
1129 flags |= SWP_NOZORDER;
1131 if (mWindowType == eWindowType_popup) {
1132 // ensure popups are the topmost of the TOPMOST
1133 // layer. Remember not to set the SWP_NOZORDER
1134 // flag as that might allow the taskbar to overlap
1135 // the popup.
1136 flags |= SWP_NOACTIVATE;
1137 HWND owner = ::GetWindow(mWnd, GW_OWNER);
1138 ::SetWindowPos(mWnd, owner ? 0 : HWND_TOPMOST, 0, 0, 0, 0, flags);
1139 } else {
1140 if (mWindowType == eWindowType_dialog && !CanTakeFocus())
1141 flags |= SWP_NOACTIVATE;
1143 ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
1147 if (!wasVisible && (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)) {
1148 // when a toplevel window or dialog is shown, initialize the UI state
1149 ::SendMessageW(mWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
1151 } else {
1152 // Clear contents to avoid ghosting of old content if we display
1153 // this window again.
1154 if (wasVisible && mTransparencyMode == eTransparencyTransparent) {
1155 ClearTranslucentWindow();
1157 if (mWindowType != eWindowType_dialog) {
1158 ::ShowWindow(mWnd, SW_HIDE);
1159 } else {
1160 ::SetWindowPos(mWnd, 0, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE |
1161 SWP_NOZORDER | SWP_NOACTIVATE);
1166 #ifdef MOZ_XUL
1167 if (!wasVisible && bState) {
1168 Invalidate();
1169 if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
1170 ::UpdateWindow(mWnd);
1173 #endif
1175 return NS_OK;
1178 /**************************************************************
1180 * SECTION: nsIWidget::IsVisible
1182 * Returns the visibility state.
1184 **************************************************************/
1186 // Return true if the whether the component is visible, false otherwise
1187 bool nsWindow::IsVisible() const
1189 return mIsVisible;
1192 /**************************************************************
1194 * SECTION: Window clipping utilities
1196 * Used in Size and Move operations for setting the proper
1197 * window clipping regions for window transparency.
1199 **************************************************************/
1201 // XP and Vista visual styles sometimes require window clipping regions to be applied for proper
1202 // transparency. These routines are called on size and move operations.
1203 void nsWindow::ClearThemeRegion()
1205 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
1206 !HasGlass() &&
1207 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1208 (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
1209 SetWindowRgn(mWnd, NULL, false);
1213 void nsWindow::SetThemeRegion()
1215 // Popup types that have a visual styles region applied (bug 376408). This can be expanded
1216 // for other window types as needed. The regions are applied generically to the base window
1217 // so default constants are used for part and state. At some point we might need part and
1218 // state values from nsNativeThemeWin's GetThemePartAndState, but currently windows that
1219 // change shape based on state haven't come up.
1220 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION &&
1221 !HasGlass() &&
1222 (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
1223 (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
1224 HRGN hRgn = nullptr;
1225 RECT rect = {0,0,mBounds.width,mBounds.height};
1227 HDC dc = ::GetDC(mWnd);
1228 GetThemeBackgroundRegion(nsUXThemeData::GetTheme(eUXTooltip), dc, TTP_STANDARD, TS_NORMAL, &rect, &hRgn);
1229 if (hRgn) {
1230 if (!SetWindowRgn(mWnd, hRgn, false)) // do not delete or alter hRgn if accepted.
1231 DeleteObject(hRgn);
1233 ::ReleaseDC(mWnd, dc);
1237 /**************************************************************
1239 * SECTION: nsIWidget::RegisterTouchWindow,
1240 * nsIWidget::UnregisterTouchWindow, and helper functions
1242 * Used to register the native window to receive touch events
1244 **************************************************************/
1246 NS_METHOD nsWindow::RegisterTouchWindow() {
1247 mTouchWindow = true;
1248 mGesture.RegisterTouchWindow(mWnd);
1249 ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
1250 return NS_OK;
1253 NS_METHOD nsWindow::UnregisterTouchWindow() {
1254 mTouchWindow = false;
1255 mGesture.UnregisterTouchWindow(mWnd);
1256 ::EnumChildWindows(mWnd, nsWindow::UnregisterTouchForDescendants, 0);
1257 return NS_OK;
1260 BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
1261 nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
1262 if (win)
1263 win->mGesture.RegisterTouchWindow(aWnd);
1264 return TRUE;
1267 BOOL CALLBACK nsWindow::UnregisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
1268 nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
1269 if (win)
1270 win->mGesture.UnregisterTouchWindow(aWnd);
1271 return TRUE;
1274 /**************************************************************
1276 * SECTION: nsIWidget::Move, nsIWidget::Resize,
1277 * nsIWidget::Size, nsIWidget::BeginResizeDrag
1279 * Repositioning and sizing a window.
1281 **************************************************************/
1283 void
1284 nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
1286 SizeConstraints c = aConstraints;
1287 if (mWindowType != eWindowType_popup) {
1288 c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
1289 c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
1292 nsBaseWidget::SetSizeConstraints(c);
1295 // Move this component
1296 NS_METHOD nsWindow::Move(double aX, double aY)
1298 if (mWindowType == eWindowType_toplevel ||
1299 mWindowType == eWindowType_dialog) {
1300 SetSizeMode(nsSizeMode_Normal);
1303 // for top-level windows only, convert coordinates from global display pixels
1304 // (the "parent" coordinate space) to the window's device pixel space
1305 double scale = BoundsUseDisplayPixels() ? GetDefaultScale() : 1.0;
1306 int32_t x = NSToIntRound(aX * scale);
1307 int32_t y = NSToIntRound(aY * scale);
1309 // Check to see if window needs to be moved first
1310 // to avoid a costly call to SetWindowPos. This check
1311 // can not be moved to the calling code in nsView, because
1312 // some platforms do not position child windows correctly
1314 // Only perform this check for non-popup windows, since the positioning can
1315 // in fact change even when the x/y do not. We always need to perform the
1316 // check. See bug #97805 for details.
1317 if (mWindowType != eWindowType_popup && (mBounds.x == x) && (mBounds.y == y))
1319 // Nothing to do, since it is already positioned correctly.
1320 return NS_OK;
1323 mBounds.x = x;
1324 mBounds.y = y;
1326 if (mWnd) {
1327 #ifdef DEBUG
1328 // complain if a window is moved offscreen (legal, but potentially worrisome)
1329 if (mIsTopWidgetWindow) { // only a problem for top-level windows
1330 // Make sure this window is actually on the screen before we move it
1331 // XXX: Needs multiple monitor support
1332 HDC dc = ::GetDC(mWnd);
1333 if (dc) {
1334 if (::GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
1335 RECT workArea;
1336 ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
1337 // no annoying assertions. just mention the issue.
1338 if (x < 0 || x >= workArea.right || y < 0 || y >= workArea.bottom) {
1339 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
1340 ("window moved to offscreen position\n"));
1343 ::ReleaseDC(mWnd, dc);
1346 #endif
1347 ClearThemeRegion();
1349 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE;
1350 // Workaround SetWindowPos bug with D3D9. If our window has a clip
1351 // region, some drivers or OSes may incorrectly copy into the clipped-out
1352 // area.
1353 if (mWindowType == eWindowType_plugin &&
1354 (!mLayerManager || mLayerManager->GetBackendType() == LAYERS_D3D9) &&
1355 mClipRects &&
1356 (mClipRectCount != 1 || !mClipRects[0].IsEqualInterior(nsIntRect(0, 0, mBounds.width, mBounds.height)))) {
1357 flags |= SWP_NOCOPYBITS;
1359 VERIFY(::SetWindowPos(mWnd, NULL, x, y, 0, 0, flags));
1361 SetThemeRegion();
1363 NotifyRollupGeometryChange();
1364 return NS_OK;
1367 // Resize this component
1368 NS_METHOD nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
1370 // for top-level windows only, convert coordinates from global display pixels
1371 // (the "parent" coordinate space) to the window's device pixel space
1372 double scale = BoundsUseDisplayPixels() ? GetDefaultScale() : 1.0;
1373 int32_t width = NSToIntRound(aWidth * scale);
1374 int32_t height = NSToIntRound(aHeight * scale);
1376 NS_ASSERTION((width >= 0) , "Negative width passed to nsWindow::Resize");
1377 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
1379 ConstrainSize(&width, &height);
1381 // Avoid unnecessary resizing calls
1382 if (mBounds.width == width && mBounds.height == height) {
1383 if (aRepaint) {
1384 Invalidate();
1386 return NS_OK;
1389 #ifdef MOZ_XUL
1390 if (eTransparencyTransparent == mTransparencyMode)
1391 ResizeTranslucentWindow(width, height);
1392 #endif
1394 // Set cached value for lightweight and printing
1395 mBounds.width = width;
1396 mBounds.height = height;
1398 if (mWnd) {
1399 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE;
1401 if (!aRepaint) {
1402 flags |= SWP_NOREDRAW;
1405 ClearThemeRegion();
1406 VERIFY(::SetWindowPos(mWnd, NULL, 0, 0, width, GetHeight(height), flags));
1407 SetThemeRegion();
1410 if (aRepaint)
1411 Invalidate();
1413 NotifyRollupGeometryChange();
1414 return NS_OK;
1417 // Resize this component
1418 NS_METHOD nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint)
1420 // for top-level windows only, convert coordinates from global display pixels
1421 // (the "parent" coordinate space) to the window's device pixel space
1422 double scale = BoundsUseDisplayPixels() ? GetDefaultScale() : 1.0;
1423 int32_t x = NSToIntRound(aX * scale);
1424 int32_t y = NSToIntRound(aY * scale);
1425 int32_t width = NSToIntRound(aWidth * scale);
1426 int32_t height = NSToIntRound(aHeight * scale);
1428 NS_ASSERTION((width >= 0), "Negative width passed to nsWindow::Resize");
1429 NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
1431 ConstrainSize(&width, &height);
1433 // Avoid unnecessary resizing calls
1434 if (mBounds.x == x && mBounds.y == y &&
1435 mBounds.width == width && mBounds.height == height) {
1436 if (aRepaint) {
1437 Invalidate();
1439 return NS_OK;
1442 #ifdef MOZ_XUL
1443 if (eTransparencyTransparent == mTransparencyMode)
1444 ResizeTranslucentWindow(width, height);
1445 #endif
1447 // Set cached value for lightweight and printing
1448 mBounds.x = x;
1449 mBounds.y = y;
1450 mBounds.width = width;
1451 mBounds.height = height;
1453 if (mWnd) {
1454 UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
1455 if (!aRepaint) {
1456 flags |= SWP_NOREDRAW;
1459 ClearThemeRegion();
1460 VERIFY(::SetWindowPos(mWnd, NULL, x, y, width, GetHeight(height), flags));
1461 SetThemeRegion();
1464 if (aRepaint)
1465 Invalidate();
1467 NotifyRollupGeometryChange();
1468 return NS_OK;
1471 NS_IMETHODIMP
1472 nsWindow::BeginResizeDrag(nsGUIEvent* aEvent, int32_t aHorizontal, int32_t aVertical)
1474 NS_ENSURE_ARG_POINTER(aEvent);
1476 if (aEvent->eventStructType != NS_MOUSE_EVENT) {
1477 // you can only begin a resize drag with a mouse event
1478 return NS_ERROR_INVALID_ARG;
1481 nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
1482 if (mouseEvent->button != nsMouseEvent::eLeftButton) {
1483 // you can only begin a resize drag with the left mouse button
1484 return NS_ERROR_INVALID_ARG;
1487 // work out what sizemode we're talking about
1488 WPARAM syscommand;
1489 if (aVertical < 0) {
1490 if (aHorizontal < 0) {
1491 syscommand = SC_SIZE | WMSZ_TOPLEFT;
1492 } else if (aHorizontal == 0) {
1493 syscommand = SC_SIZE | WMSZ_TOP;
1494 } else {
1495 syscommand = SC_SIZE | WMSZ_TOPRIGHT;
1497 } else if (aVertical == 0) {
1498 if (aHorizontal < 0) {
1499 syscommand = SC_SIZE | WMSZ_LEFT;
1500 } else if (aHorizontal == 0) {
1501 return NS_ERROR_INVALID_ARG;
1502 } else {
1503 syscommand = SC_SIZE | WMSZ_RIGHT;
1505 } else {
1506 if (aHorizontal < 0) {
1507 syscommand = SC_SIZE | WMSZ_BOTTOMLEFT;
1508 } else if (aHorizontal == 0) {
1509 syscommand = SC_SIZE | WMSZ_BOTTOM;
1510 } else {
1511 syscommand = SC_SIZE | WMSZ_BOTTOMRIGHT;
1515 // resizing doesn't work if the mouse is already captured
1516 CaptureMouse(false);
1518 // find the top-level window
1519 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd, true);
1521 // tell Windows to start the resize
1522 ::PostMessage(toplevelWnd, WM_SYSCOMMAND, syscommand,
1523 POINTTOPOINTS(aEvent->refPoint));
1525 return NS_OK;
1528 /**************************************************************
1530 * SECTION: Window Z-order and state.
1532 * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
1533 * nsIWidget::ConstrainPosition
1535 * Z-order, positioning, restore, minimize, and maximize.
1537 **************************************************************/
1539 // Position the window behind the given window
1540 NS_METHOD nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
1541 nsIWidget *aWidget, bool aActivate)
1543 HWND behind = HWND_TOP;
1544 if (aPlacement == eZPlacementBottom)
1545 behind = HWND_BOTTOM;
1546 else if (aPlacement == eZPlacementBelow && aWidget)
1547 behind = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
1548 UINT flags = SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE;
1549 if (!aActivate)
1550 flags |= SWP_NOACTIVATE;
1552 if (!CanTakeFocus() && behind == HWND_TOP)
1554 // Can't place the window to top so place it behind the foreground window
1555 // (as long as it is not topmost)
1556 HWND wndAfter = ::GetForegroundWindow();
1557 if (!wndAfter)
1558 behind = HWND_BOTTOM;
1559 else if (!(GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST))
1560 behind = wndAfter;
1561 flags |= SWP_NOACTIVATE;
1564 ::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
1565 return NS_OK;
1568 // Maximize, minimize or restore the window.
1569 NS_IMETHODIMP nsWindow::SetSizeMode(int32_t aMode) {
1571 nsresult rv;
1573 // Let's not try and do anything if we're already in that state.
1574 // (This is needed to prevent problems when calling window.minimize(), which
1575 // calls us directly, and then the OS triggers another call to us.)
1576 if (aMode == mSizeMode)
1577 return NS_OK;
1579 // save the requested state
1580 mLastSizeMode = mSizeMode;
1581 rv = nsBaseWidget::SetSizeMode(aMode);
1582 if (NS_SUCCEEDED(rv) && mIsVisible) {
1583 int mode;
1585 switch (aMode) {
1586 case nsSizeMode_Fullscreen :
1587 mode = SW_SHOW;
1588 break;
1590 case nsSizeMode_Maximized :
1591 mode = SW_MAXIMIZE;
1592 break;
1594 case nsSizeMode_Minimized :
1595 // Using SW_SHOWMINIMIZED prevents the working set from being trimmed but
1596 // keeps the window active in the tray. So after the window is minimized,
1597 // windows will fire WM_WINDOWPOSCHANGED (OnWindowPosChanged) at which point
1598 // we will do some additional processing to get the active window set right.
1599 // If sTrimOnMinimize is set, we let windows handle minimization normally
1600 // using SW_MINIMIZE.
1601 mode = sTrimOnMinimize ? SW_MINIMIZE : SW_SHOWMINIMIZED;
1602 break;
1604 default :
1605 mode = SW_RESTORE;
1608 WINDOWPLACEMENT pl;
1609 pl.length = sizeof(pl);
1610 ::GetWindowPlacement(mWnd, &pl);
1611 // Don't call ::ShowWindow if we're trying to "restore" a window that is
1612 // already in a normal state. Prevents a bug where snapping to one side
1613 // of the screen and then minimizing would cause Windows to forget our
1614 // window's correct restored position/size.
1615 if( !(pl.showCmd == SW_SHOWNORMAL && mode == SW_RESTORE) ) {
1616 ::ShowWindow(mWnd, mode);
1618 // we activate here to ensure that the right child window is focused
1619 if (mode == SW_MAXIMIZE || mode == SW_SHOW)
1620 DispatchFocusToTopLevelWindow(true);
1622 return rv;
1625 // Constrain a potential move to fit onscreen
1626 // Position (aX, aY) is specified in Windows screen (logical) pixels
1627 NS_METHOD nsWindow::ConstrainPosition(bool aAllowSlop,
1628 int32_t *aX, int32_t *aY)
1630 if (!mIsTopWidgetWindow) // only a problem for top-level windows
1631 return NS_OK;
1633 double dpiScale = GetDefaultScale();
1635 // we need to use the window size in logical screen pixels
1636 int32_t logWidth = std::max<int32_t>(NSToIntRound(mBounds.width / dpiScale), 1);
1637 int32_t logHeight = std::max<int32_t>(NSToIntRound(mBounds.height / dpiScale), 1);
1639 bool doConstrain = false; // whether we have enough info to do anything
1641 /* get our playing field. use the current screen, or failing that
1642 for any reason, use device caps for the default screen. */
1643 RECT screenRect;
1645 nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
1646 if (screenmgr) {
1647 nsCOMPtr<nsIScreen> screen;
1648 int32_t left, top, width, height;
1650 screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
1651 getter_AddRefs(screen));
1652 if (screen) {
1653 if (mSizeMode != nsSizeMode_Fullscreen) {
1654 // For normalized windows, use the desktop work area.
1655 screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
1656 } else {
1657 // For full screen windows, use the desktop.
1658 screen->GetRectDisplayPix(&left, &top, &width, &height);
1660 screenRect.left = left;
1661 screenRect.right = left + width;
1662 screenRect.top = top;
1663 screenRect.bottom = top + height;
1664 doConstrain = true;
1666 } else {
1667 if (mWnd) {
1668 HDC dc = ::GetDC(mWnd);
1669 if (dc) {
1670 if (::GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
1671 if (mSizeMode != nsSizeMode_Fullscreen) {
1672 ::SystemParametersInfo(SPI_GETWORKAREA, 0, &screenRect, 0);
1673 } else {
1674 screenRect.left = screenRect.top = 0;
1675 screenRect.right = GetSystemMetrics(SM_CXFULLSCREEN);
1676 screenRect.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
1678 doConstrain = true;
1680 ::ReleaseDC(mWnd, dc);
1685 if (aAllowSlop) {
1686 if (*aX < screenRect.left - logWidth + kWindowPositionSlop)
1687 *aX = screenRect.left - logWidth + kWindowPositionSlop;
1688 else if (*aX >= screenRect.right - kWindowPositionSlop)
1689 *aX = screenRect.right - kWindowPositionSlop;
1691 if (*aY < screenRect.top - logHeight + kWindowPositionSlop)
1692 *aY = screenRect.top - logHeight + kWindowPositionSlop;
1693 else if (*aY >= screenRect.bottom - kWindowPositionSlop)
1694 *aY = screenRect.bottom - kWindowPositionSlop;
1696 } else {
1698 if (*aX < screenRect.left)
1699 *aX = screenRect.left;
1700 else if (*aX >= screenRect.right - logWidth)
1701 *aX = screenRect.right - logWidth;
1703 if (*aY < screenRect.top)
1704 *aY = screenRect.top;
1705 else if (*aY >= screenRect.bottom - logHeight)
1706 *aY = screenRect.bottom - logHeight;
1709 return NS_OK;
1712 /**************************************************************
1714 * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
1716 * Enabling and disabling the widget.
1718 **************************************************************/
1720 // Enable/disable this component
1721 NS_METHOD nsWindow::Enable(bool bState)
1723 if (mWnd) {
1724 ::EnableWindow(mWnd, bState);
1726 return NS_OK;
1729 // Return the current enable state
1730 bool nsWindow::IsEnabled() const
1732 return !mWnd ||
1733 (::IsWindowEnabled(mWnd) &&
1734 ::IsWindowEnabled(::GetAncestor(mWnd, GA_ROOT)));
1738 /**************************************************************
1740 * SECTION: nsIWidget::SetFocus
1742 * Give the focus to this widget.
1744 **************************************************************/
1746 NS_METHOD nsWindow::SetFocus(bool aRaise)
1748 if (mWnd) {
1749 #ifdef WINSTATE_DEBUG_OUTPUT
1750 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
1751 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
1752 ("*** SetFocus: [ top] raise=%d\n", aRaise));
1753 } else {
1754 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
1755 ("*** SetFocus: [child] raise=%d\n", aRaise));
1757 #endif
1758 // Uniconify, if necessary
1759 HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd);
1760 if (aRaise && ::IsIconic(toplevelWnd)) {
1761 ::ShowWindow(toplevelWnd, SW_RESTORE);
1763 ::SetFocus(mWnd);
1765 return NS_OK;
1769 /**************************************************************
1771 * SECTION: Bounds
1773 * GetBounds, GetClientBounds, GetScreenBounds, GetClientOffset
1774 * SetDrawsInTitlebar, GetNonClientMargins, SetNonClientMargins
1776 * Bound calculations.
1778 **************************************************************/
1780 // Return the window's full dimensions in screen coordinates.
1781 // If the window has a parent, converts the origin to an offset
1782 // of the parent's screen origin.
1783 NS_METHOD nsWindow::GetBounds(nsIntRect &aRect)
1785 if (mWnd) {
1786 RECT r;
1787 VERIFY(::GetWindowRect(mWnd, &r));
1789 // assign size
1790 aRect.width = r.right - r.left;
1791 aRect.height = r.bottom - r.top;
1793 // popup window bounds' are in screen coordinates, not relative to parent
1794 // window
1795 if (mWindowType == eWindowType_popup) {
1796 aRect.x = r.left;
1797 aRect.y = r.top;
1798 return NS_OK;
1801 // chrome on parent:
1802 // ___ 5,5 (chrome start)
1803 // | ____ 10,10 (client start)
1804 // | | ____ 20,20 (child start)
1805 // | | |
1806 // 20,20 - 5,5 = 15,15 (??)
1807 // minus GetClientOffset:
1808 // 15,15 - 5,5 = 10,10
1810 // no chrome on parent:
1811 // ______ 10,10 (win start)
1812 // | ____ 20,20 (child start)
1813 // | |
1814 // 20,20 - 10,10 = 10,10
1816 // walking the chain:
1817 // ___ 5,5 (chrome start)
1818 // | ___ 10,10 (client start)
1819 // | | ___ 20,20 (child start)
1820 // | | | __ 30,30 (child start)
1821 // | | | |
1822 // 30,30 - 20,20 = 10,10 (offset from second child to first)
1823 // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
1824 // minus GetClientOffset:
1825 // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
1827 // convert coordinates if parent exists
1828 HWND parent = ::GetParent(mWnd);
1829 if (parent) {
1830 RECT pr;
1831 VERIFY(::GetWindowRect(parent, &pr));
1832 r.left -= pr.left;
1833 r.top -= pr.top;
1834 // adjust for chrome
1835 nsWindow* pWidget = static_cast<nsWindow*>(GetParent());
1836 if (pWidget && pWidget->IsTopLevelWidget()) {
1837 nsIntPoint clientOffset = pWidget->GetClientOffset();
1838 r.left -= clientOffset.x;
1839 r.top -= clientOffset.y;
1842 aRect.x = r.left;
1843 aRect.y = r.top;
1844 } else {
1845 aRect = mBounds;
1847 return NS_OK;
1850 // Get this component dimension
1851 NS_METHOD nsWindow::GetClientBounds(nsIntRect &aRect)
1853 if (mWnd) {
1854 RECT r;
1855 VERIFY(::GetClientRect(mWnd, &r));
1857 nsIntRect bounds;
1858 GetBounds(bounds);
1859 aRect.MoveTo(bounds.TopLeft() + GetClientOffset());
1860 aRect.width = r.right - r.left;
1861 aRect.height = r.bottom - r.top;
1863 } else {
1864 aRect.SetRect(0,0,0,0);
1866 return NS_OK;
1869 // Like GetBounds, but don't offset by the parent
1870 NS_METHOD nsWindow::GetScreenBounds(nsIntRect &aRect)
1872 if (mWnd) {
1873 RECT r;
1874 VERIFY(::GetWindowRect(mWnd, &r));
1876 aRect.width = r.right - r.left;
1877 aRect.height = r.bottom - r.top;
1878 aRect.x = r.left;
1879 aRect.y = r.top;
1880 } else
1881 aRect = mBounds;
1883 return NS_OK;
1886 // return the x,y offset of the client area from the origin
1887 // of the window. If the window is borderless returns (0,0).
1888 nsIntPoint nsWindow::GetClientOffset()
1890 if (!mWnd) {
1891 return nsIntPoint(0, 0);
1894 RECT r1;
1895 GetWindowRect(mWnd, &r1);
1896 nsIntPoint pt = WidgetToScreenOffset();
1897 return nsIntPoint(pt.x - r1.left, pt.y - r1.top);
1900 void
1901 nsWindow::SetDrawsInTitlebar(bool aState)
1903 nsWindow * window = GetTopLevelWindow(true);
1904 if (window && window != this) {
1905 return window->SetDrawsInTitlebar(aState);
1908 if (aState) {
1909 // top, right, bottom, left for nsIntMargin
1910 nsIntMargin margins(0, -1, -1, -1);
1911 SetNonClientMargins(margins);
1913 else {
1914 nsIntMargin margins(-1, -1, -1, -1);
1915 SetNonClientMargins(margins);
1919 NS_IMETHODIMP
1920 nsWindow::GetNonClientMargins(nsIntMargin &margins)
1922 nsWindow * window = GetTopLevelWindow(true);
1923 if (window && window != this) {
1924 return window->GetNonClientMargins(margins);
1927 if (mCustomNonClient) {
1928 margins = mNonClientMargins;
1929 return NS_OK;
1932 margins.top = GetSystemMetrics(SM_CYCAPTION);
1933 margins.bottom = GetSystemMetrics(SM_CYFRAME);
1934 margins.top += margins.bottom;
1935 margins.left = margins.right = GetSystemMetrics(SM_CXFRAME);
1937 return NS_OK;
1940 void
1941 nsWindow::ResetLayout()
1943 // This will trigger a frame changed event, triggering
1944 // nc calc size and a sizemode gecko event.
1945 SetWindowPos(mWnd, 0, 0, 0, 0, 0,
1946 SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|
1947 SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
1949 // If hidden, just send the frame changed event for now.
1950 if (!mIsVisible)
1951 return;
1953 // Send a gecko size event to trigger reflow.
1954 RECT clientRc = {0};
1955 GetClientRect(mWnd, &clientRc);
1956 nsIntRect evRect(WinUtils::ToIntRect(clientRc));
1957 OnResize(evRect);
1959 // Invalidate and update
1960 Invalidate();
1963 // Internally track the caption status via a window property. Required
1964 // due to our internal handling of WM_NCACTIVATE when custom client
1965 // margins are set.
1966 static const PRUnichar kManageWindowInfoProperty[] = L"ManageWindowInfoProperty";
1967 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
1968 static GetWindowInfoPtr sGetWindowInfoPtrStub = NULL;
1970 BOOL WINAPI
1971 GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
1973 if (!sGetWindowInfoPtrStub) {
1974 NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!");
1975 return FALSE;
1977 int windowStatus =
1978 reinterpret_cast<LONG_PTR>(GetPropW(hWnd, kManageWindowInfoProperty));
1979 // No property set, return the default data.
1980 if (!windowStatus)
1981 return sGetWindowInfoPtrStub(hWnd, pwi);
1982 // Call GetWindowInfo and update dwWindowStatus with our
1983 // internally tracked value.
1984 BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
1985 if (result && pwi)
1986 pwi->dwWindowStatus = (windowStatus == 1 ? 0 : WS_ACTIVECAPTION);
1987 return result;
1990 void
1991 nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption)
1993 if (!mWnd)
1994 return;
1996 if (!sGetWindowInfoPtrStub) {
1997 sUser32Intercept.Init("user32.dll");
1998 if (!sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(GetWindowInfoHook),
1999 (void**) &sGetWindowInfoPtrStub))
2000 return;
2002 // Update our internally tracked caption status
2003 SetPropW(mWnd, kManageWindowInfoProperty,
2004 reinterpret_cast<HANDLE>(static_cast<int>(aActiveCaption) + 1));
2008 * Called when the window layout changes: full screen mode transitions,
2009 * theme changes, and composition changes. Calculates the new non-client
2010 * margins and fires off a frame changed event, which triggers an nc calc
2011 * size windows event, kicking the changes in.
2013 * The offsets calculated here are based on the value of `mNonClientMargins`
2014 * which is specified in the "chromemargins" attribute of the window. For
2015 * each margin, the value specified has the following meaning:
2016 * -1 - leave the default frame in place
2017 * 0 - remove the frame
2018 * >0 - frame size equals min(0, (default frame size - margin value))
2020 * This function calculates and populates `mNonClientOffset`.
2021 * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
2022 * as (default frame size - offset). For example, if the left frame should
2023 * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
2024 * will equal 1.
2026 * For maximized, fullscreen, and minimized windows, the values stored in
2027 * `mNonClientMargins` are ignored, and special processing takes place.
2029 * For non-glass windows, we only allow frames to be their default size
2030 * or removed entirely.
2032 bool
2033 nsWindow::UpdateNonClientMargins(int32_t aSizeMode, bool aReflowWindow)
2035 if (!mCustomNonClient)
2036 return false;
2038 if (aSizeMode == -1) {
2039 aSizeMode = mSizeMode;
2042 bool hasCaption = (mBorderStyle
2043 & (eBorderStyle_all
2044 | eBorderStyle_title
2045 | eBorderStyle_menu
2046 | eBorderStyle_default));
2048 // mCaptionHeight is the default size of the NC area at
2049 // the top of the window. If the window has a caption,
2050 // the size is calculated as the sum of:
2051 // SM_CYFRAME - The thickness of the sizing border
2052 // around a resizable window
2053 // SM_CXPADDEDBORDER - The amount of border padding
2054 // for captioned windows
2055 // SM_CYCAPTION - The height of the caption area
2057 // If the window does not have a caption, mCaptionHeight will be equal to
2058 // `GetSystemMetrics(SM_CYFRAME)`
2059 mCaptionHeight = GetSystemMetrics(SM_CYFRAME)
2060 + (hasCaption ? GetSystemMetrics(SM_CYCAPTION)
2061 + GetSystemMetrics(SM_CXPADDEDBORDER)
2062 : 0);
2064 // mHorResizeMargin is the size of the default NC areas on the
2065 // left and right sides of our window. It is calculated as
2066 // the sum of:
2067 // SM_CXFRAME - The thickness of the sizing border
2068 // SM_CXPADDEDBORDER - The amount of border padding
2069 // for captioned windows
2071 // If the window does not have a caption, mHorResizeMargin will be equal to
2072 // `GetSystemMetrics(SM_CXFRAME)`
2073 mHorResizeMargin = GetSystemMetrics(SM_CXFRAME)
2074 + (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
2076 // mVertResizeMargin is the size of the default NC area at the
2077 // bottom of the window. It is calculated as the sum of:
2078 // SM_CYFRAME - The thickness of the sizing border
2079 // SM_CXPADDEDBORDER - The amount of border padding
2080 // for captioned windows.
2082 // If the window does not have a caption, mVertResizeMargin will be equal to
2083 // `GetSystemMetrics(SM_CYFRAME)`
2084 mVertResizeMargin = GetSystemMetrics(SM_CYFRAME)
2085 + (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
2087 if (aSizeMode == nsSizeMode_Minimized) {
2088 // Use default frame size for minimized windows
2089 mNonClientOffset.top = 0;
2090 mNonClientOffset.left = 0;
2091 mNonClientOffset.right = 0;
2092 mNonClientOffset.bottom = 0;
2093 } else if (aSizeMode == nsSizeMode_Fullscreen) {
2094 // Remove the default frame from the top of our fullscreen window. This
2095 // makes the whole caption part of our client area, allowing us to draw
2096 // in the whole caption area. Additionally remove the default frame from
2097 // the left, right, and bottom.
2098 mNonClientOffset.top = mCaptionHeight;
2099 mNonClientOffset.bottom = mVertResizeMargin;
2100 mNonClientOffset.left = mHorResizeMargin;
2101 mNonClientOffset.right = mHorResizeMargin;
2102 } else if (aSizeMode == nsSizeMode_Maximized) {
2103 // Remove the default frame from the top of our maximized window. This
2104 // makes the whole caption part of our client area, allowing us to draw
2105 // in the whole caption area. Use default frame size on left, right, and
2106 // bottom. The reason this works is that, for maximized windows,
2107 // Windows positions them so that their frames fall off the screen.
2108 // This gives the illusion of windows having no frames when they are
2109 // maximized. If we try to mess with the frame sizes by setting these
2110 // offsets to positive values, our client area will fall off the screen.
2111 mNonClientOffset.top = mCaptionHeight;
2112 mNonClientOffset.bottom = 0;
2113 mNonClientOffset.left = 0;
2114 mNonClientOffset.right = 0;
2116 APPBARDATA appBarData;
2117 appBarData.cbSize = sizeof(appBarData);
2118 UINT taskbarState = SHAppBarMessage(ABM_GETSTATE, &appBarData);
2119 if (ABS_AUTOHIDE & taskbarState) {
2120 UINT edge = -1;
2121 appBarData.hWnd = FindWindow(L"Shell_TrayWnd", NULL);
2122 if (appBarData.hWnd) {
2123 HMONITOR taskbarMonitor = ::MonitorFromWindow(appBarData.hWnd,
2124 MONITOR_DEFAULTTOPRIMARY);
2125 HMONITOR windowMonitor = ::MonitorFromWindow(mWnd,
2126 MONITOR_DEFAULTTONEAREST);
2127 if (taskbarMonitor == windowMonitor) {
2128 SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData);
2129 edge = appBarData.uEdge;
2133 if (ABE_LEFT == edge) {
2134 mNonClientOffset.left -= 1;
2135 } else if (ABE_RIGHT == edge) {
2136 mNonClientOffset.right -= 1;
2137 } else if (ABE_BOTTOM == edge || ABE_TOP == edge) {
2138 mNonClientOffset.bottom -= 1;
2141 } else {
2142 bool glass = nsUXThemeData::CheckForCompositor();
2144 // We're dealing with a "normal" window (not maximized, minimized, or
2145 // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
2146 // accordingly.
2148 // Setting `mNonClientOffset` to 0 has the effect of leaving the default
2149 // frame intact. Setting it to a value greater than 0 reduces the frame
2150 // size by that amount.
2152 if (mNonClientMargins.top > 0 && glass) {
2153 mNonClientOffset.top = std::min(mCaptionHeight, mNonClientMargins.top);
2154 } else if (mNonClientMargins.top == 0) {
2155 mNonClientOffset.top = mCaptionHeight;
2156 } else {
2157 mNonClientOffset.top = 0;
2160 if (mNonClientMargins.bottom > 0 && glass) {
2161 mNonClientOffset.bottom = std::min(mVertResizeMargin, mNonClientMargins.bottom);
2162 } else if (mNonClientMargins.bottom == 0) {
2163 mNonClientOffset.bottom = mVertResizeMargin;
2164 } else {
2165 mNonClientOffset.bottom = 0;
2168 if (mNonClientMargins.left > 0 && glass) {
2169 mNonClientOffset.left = std::min(mHorResizeMargin, mNonClientMargins.left);
2170 } else if (mNonClientMargins.left == 0) {
2171 mNonClientOffset.left = mHorResizeMargin;
2172 } else {
2173 mNonClientOffset.left = 0;
2176 if (mNonClientMargins.right > 0 && glass) {
2177 mNonClientOffset.right = std::min(mHorResizeMargin, mNonClientMargins.right);
2178 } else if (mNonClientMargins.right == 0) {
2179 mNonClientOffset.right = mHorResizeMargin;
2180 } else {
2181 mNonClientOffset.right = 0;
2185 if (aReflowWindow) {
2186 // Force a reflow of content based on the new client
2187 // dimensions.
2188 ResetLayout();
2191 return true;
2194 NS_IMETHODIMP
2195 nsWindow::SetNonClientMargins(nsIntMargin &margins)
2197 if (!mIsTopWidgetWindow ||
2198 mBorderStyle & eBorderStyle_none ||
2199 mHideChrome)
2200 return NS_ERROR_INVALID_ARG;
2202 // Request for a reset
2203 if (margins.top == -1 && margins.left == -1 &&
2204 margins.right == -1 && margins.bottom == -1) {
2205 mCustomNonClient = false;
2206 mNonClientMargins = margins;
2207 RemovePropW(mWnd, kManageWindowInfoProperty);
2208 // Force a reflow of content based on the new client
2209 // dimensions.
2210 ResetLayout();
2211 return NS_OK;
2214 if (margins.top < -1 || margins.bottom < -1 ||
2215 margins.left < -1 || margins.right < -1)
2216 return NS_ERROR_INVALID_ARG;
2218 mNonClientMargins = margins;
2219 mCustomNonClient = true;
2220 if (!UpdateNonClientMargins()) {
2221 NS_WARNING("UpdateNonClientMargins failed!");
2222 return NS_OK;
2225 return NS_OK;
2228 void
2229 nsWindow::InvalidateNonClientRegion()
2231 // +-+-----------------------+-+
2232 // | | app non-client chrome | |
2233 // | +-----------------------+ |
2234 // | | app client chrome | | }
2235 // | +-----------------------+ | }
2236 // | | app content | | } area we don't want to invalidate
2237 // | +-----------------------+ | }
2238 // | | app client chrome | | }
2239 // | +-----------------------+ |
2240 // +---------------------------+ <
2241 // ^ ^ windows non-client chrome
2242 // client area = app *
2243 RECT rect;
2244 GetWindowRect(mWnd, &rect);
2245 MapWindowPoints(NULL, mWnd, (LPPOINT)&rect, 2);
2246 HRGN winRgn = CreateRectRgnIndirect(&rect);
2248 // Subtract app client chrome and app content leaving
2249 // windows non-client chrome and app non-client chrome
2250 // in winRgn.
2251 GetWindowRect(mWnd, &rect);
2252 rect.top += mCaptionHeight;
2253 rect.right -= mHorResizeMargin;
2254 rect.bottom -= mHorResizeMargin;
2255 rect.left += mVertResizeMargin;
2256 MapWindowPoints(NULL, mWnd, (LPPOINT)&rect, 2);
2257 HRGN clientRgn = CreateRectRgnIndirect(&rect);
2258 CombineRgn(winRgn, winRgn, clientRgn, RGN_DIFF);
2259 DeleteObject(clientRgn);
2261 // triggers ncpaint and paint events for the two areas
2262 RedrawWindow(mWnd, NULL, winRgn, RDW_FRAME|RDW_INVALIDATE);
2263 DeleteObject(winRgn);
2266 HRGN
2267 nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion)
2269 RECT rect;
2270 HRGN rgn = NULL;
2271 if (aRegion == (HRGN)1) { // undocumented value indicating a full refresh
2272 GetWindowRect(mWnd, &rect);
2273 rgn = CreateRectRgnIndirect(&rect);
2274 } else {
2275 rgn = aRegion;
2277 GetClientRect(mWnd, &rect);
2278 MapWindowPoints(mWnd, NULL, (LPPOINT)&rect, 2);
2279 HRGN nonClientRgn = CreateRectRgnIndirect(&rect);
2280 CombineRgn(rgn, rgn, nonClientRgn, RGN_DIFF);
2281 DeleteObject(nonClientRgn);
2282 return rgn;
2285 /**************************************************************
2287 * SECTION: nsIWidget::SetBackgroundColor
2289 * Sets the window background paint color.
2291 **************************************************************/
2293 NS_METHOD nsWindow::SetBackgroundColor(const nscolor &aColor)
2295 nsBaseWidget::SetBackgroundColor(aColor);
2297 if (mBrush)
2298 ::DeleteObject(mBrush);
2300 mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(mBackground));
2301 if (mWnd != NULL) {
2302 ::SetClassLongPtrW(mWnd, GCLP_HBRBACKGROUND, (LONG_PTR)mBrush);
2304 return NS_OK;
2307 /**************************************************************
2309 * SECTION: nsIWidget::SetCursor
2311 * SetCursor and related utilities for manging cursor state.
2313 **************************************************************/
2315 // Set this component cursor
2316 NS_METHOD nsWindow::SetCursor(nsCursor aCursor)
2318 // Only change cursor if it's changing
2320 //XXX mCursor isn't always right. Scrollbars and others change it, too.
2321 //XXX If we want this optimization we need a better way to do it.
2322 //if (aCursor != mCursor) {
2323 HCURSOR newCursor = NULL;
2325 switch (aCursor) {
2326 case eCursor_select:
2327 newCursor = ::LoadCursor(NULL, IDC_IBEAM);
2328 break;
2330 case eCursor_wait:
2331 newCursor = ::LoadCursor(NULL, IDC_WAIT);
2332 break;
2334 case eCursor_hyperlink:
2336 newCursor = ::LoadCursor(NULL, IDC_HAND);
2337 break;
2340 case eCursor_standard:
2341 newCursor = ::LoadCursor(NULL, IDC_ARROW);
2342 break;
2344 case eCursor_n_resize:
2345 case eCursor_s_resize:
2346 newCursor = ::LoadCursor(NULL, IDC_SIZENS);
2347 break;
2349 case eCursor_w_resize:
2350 case eCursor_e_resize:
2351 newCursor = ::LoadCursor(NULL, IDC_SIZEWE);
2352 break;
2354 case eCursor_nw_resize:
2355 case eCursor_se_resize:
2356 newCursor = ::LoadCursor(NULL, IDC_SIZENWSE);
2357 break;
2359 case eCursor_ne_resize:
2360 case eCursor_sw_resize:
2361 newCursor = ::LoadCursor(NULL, IDC_SIZENESW);
2362 break;
2364 case eCursor_crosshair:
2365 newCursor = ::LoadCursor(NULL, IDC_CROSS);
2366 break;
2368 case eCursor_move:
2369 newCursor = ::LoadCursor(NULL, IDC_SIZEALL);
2370 break;
2372 case eCursor_help:
2373 newCursor = ::LoadCursor(NULL, IDC_HELP);
2374 break;
2376 case eCursor_copy: // CSS3
2377 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
2378 break;
2380 case eCursor_alias:
2381 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
2382 break;
2384 case eCursor_cell:
2385 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
2386 break;
2388 case eCursor_grab:
2389 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
2390 break;
2392 case eCursor_grabbing:
2393 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRABBING));
2394 break;
2396 case eCursor_spinning:
2397 newCursor = ::LoadCursor(NULL, IDC_APPSTARTING);
2398 break;
2400 case eCursor_context_menu:
2401 // XXX this CSS3 cursor needs to be implemented
2402 break;
2404 case eCursor_zoom_in:
2405 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
2406 break;
2408 case eCursor_zoom_out:
2409 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMOUT));
2410 break;
2412 case eCursor_not_allowed:
2413 case eCursor_no_drop:
2414 newCursor = ::LoadCursor(NULL, IDC_NO);
2415 break;
2417 case eCursor_col_resize:
2418 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COLRESIZE));
2419 break;
2421 case eCursor_row_resize:
2422 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ROWRESIZE));
2423 break;
2425 case eCursor_vertical_text:
2426 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_VERTICALTEXT));
2427 break;
2429 case eCursor_all_scroll:
2430 // XXX not 100% appropriate perhaps
2431 newCursor = ::LoadCursor(NULL, IDC_SIZEALL);
2432 break;
2434 case eCursor_nesw_resize:
2435 newCursor = ::LoadCursor(NULL, IDC_SIZENESW);
2436 break;
2438 case eCursor_nwse_resize:
2439 newCursor = ::LoadCursor(NULL, IDC_SIZENWSE);
2440 break;
2442 case eCursor_ns_resize:
2443 newCursor = ::LoadCursor(NULL, IDC_SIZENS);
2444 break;
2446 case eCursor_ew_resize:
2447 newCursor = ::LoadCursor(NULL, IDC_SIZEWE);
2448 break;
2450 case eCursor_none:
2451 newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
2452 break;
2454 default:
2455 NS_ERROR("Invalid cursor type");
2456 break;
2459 if (NULL != newCursor) {
2460 mCursor = aCursor;
2461 HCURSOR oldCursor = ::SetCursor(newCursor);
2463 if (sHCursor == oldCursor) {
2464 NS_IF_RELEASE(sCursorImgContainer);
2465 if (sHCursor != NULL)
2466 ::DestroyIcon(sHCursor);
2467 sHCursor = NULL;
2471 return NS_OK;
2474 // Setting the actual cursor
2475 NS_IMETHODIMP nsWindow::SetCursor(imgIContainer* aCursor,
2476 uint32_t aHotspotX, uint32_t aHotspotY)
2478 if (sCursorImgContainer == aCursor && sHCursor) {
2479 ::SetCursor(sHCursor);
2480 return NS_OK;
2483 int32_t width;
2484 int32_t height;
2486 nsresult rv;
2487 rv = aCursor->GetWidth(&width);
2488 NS_ENSURE_SUCCESS(rv, rv);
2489 rv = aCursor->GetHeight(&height);
2490 NS_ENSURE_SUCCESS(rv, rv);
2492 // Reject cursors greater than 128 pixels in either direction, to prevent
2493 // spoofing.
2494 // XXX ideally we should rescale. Also, we could modify the API to
2495 // allow trusted content to set larger cursors.
2496 if (width > 128 || height > 128)
2497 return NS_ERROR_NOT_AVAILABLE;
2499 HCURSOR cursor;
2500 // No scaling
2501 gfxIntSize size(0, 0);
2502 rv = nsWindowGfx::CreateIcon(aCursor, true, aHotspotX, aHotspotY, size, &cursor);
2503 NS_ENSURE_SUCCESS(rv, rv);
2505 mCursor = nsCursor(-1);
2506 ::SetCursor(cursor);
2508 NS_IF_RELEASE(sCursorImgContainer);
2509 sCursorImgContainer = aCursor;
2510 NS_ADDREF(sCursorImgContainer);
2512 if (sHCursor != NULL)
2513 ::DestroyIcon(sHCursor);
2514 sHCursor = cursor;
2516 return NS_OK;
2519 /**************************************************************
2521 * SECTION: nsIWidget::Get/SetTransparencyMode
2523 * Manage the transparency mode of the top-level window
2524 * containing this widget.
2526 **************************************************************/
2528 #ifdef MOZ_XUL
2529 nsTransparencyMode nsWindow::GetTransparencyMode()
2531 return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
2534 void nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
2536 GetTopLevelWindow(true)->SetWindowTranslucencyInner(aMode);
2539 static const nsIntRegion
2540 RegionFromArray(const nsTArray<nsIntRect>& aRects)
2542 nsIntRegion region;
2543 for (uint32_t i = 0; i < aRects.Length(); ++i) {
2544 region.Or(region, aRects[i]);
2546 return region;
2549 void nsWindow::UpdateOpaqueRegion(const nsIntRegion &aOpaqueRegion)
2551 if (!HasGlass() || GetParent())
2552 return;
2554 // If there is no opaque region or hidechrome=true, set margins
2555 // to support a full sheet of glass. Comments in MSDN indicate
2556 // all values must be set to -1 to get a full sheet of glass.
2557 MARGINS margins = { -1, -1, -1, -1 };
2558 if (!aOpaqueRegion.IsEmpty()) {
2559 nsIntRect pluginBounds;
2560 for (nsIWidget* child = GetFirstChild(); child; child = child->GetNextSibling()) {
2561 nsWindowType type;
2562 child->GetWindowType(type);
2563 if (type == eWindowType_plugin) {
2564 // Collect the bounds of all plugins for GetLargestRectangle.
2565 nsIntRect childBounds;
2566 child->GetBounds(childBounds);
2567 pluginBounds.UnionRect(pluginBounds, childBounds);
2571 nsIntRect clientBounds;
2572 GetClientBounds(clientBounds);
2574 // Find the largest rectangle and use that to calculate the inset. Our top
2575 // priority is to include the bounds of all plugins.
2576 nsIntRect largest = aOpaqueRegion.GetLargestRectangle(pluginBounds);
2577 margins.cxLeftWidth = largest.x;
2578 margins.cxRightWidth = clientBounds.width - largest.XMost();
2579 margins.cyBottomHeight = clientBounds.height - largest.YMost();
2580 if (mCustomNonClient) {
2581 // The minimum glass height must be the caption buttons height,
2582 // otherwise the buttons are drawn incorrectly.
2583 largest.y = std::max<uint32_t>(largest.y,
2584 nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy);
2586 margins.cyTopHeight = largest.y;
2589 // Only update glass area if there are changes
2590 if (memcmp(&mGlassMargins, &margins, sizeof mGlassMargins)) {
2591 mGlassMargins = margins;
2592 UpdateGlass();
2596 void nsWindow::UpdateGlass()
2598 MARGINS margins = mGlassMargins;
2600 // DWMNCRP_USEWINDOWSTYLE - The non-client rendering area is
2601 // rendered based on the window style.
2602 // DWMNCRP_ENABLED - The non-client area rendering is
2603 // enabled; the window style is ignored.
2604 DWMNCRENDERINGPOLICY policy = DWMNCRP_USEWINDOWSTYLE;
2605 switch (mTransparencyMode) {
2606 case eTransparencyBorderlessGlass:
2607 // Only adjust if there is some opaque rectangle
2608 if (margins.cxLeftWidth >= 0) {
2609 margins.cxLeftWidth += kGlassMarginAdjustment;
2610 margins.cyTopHeight += kGlassMarginAdjustment;
2611 margins.cxRightWidth += kGlassMarginAdjustment;
2612 margins.cyBottomHeight += kGlassMarginAdjustment;
2614 // Fall through
2615 case eTransparencyGlass:
2616 policy = DWMNCRP_ENABLED;
2617 break;
2620 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
2621 ("glass margins: left:%d top:%d right:%d bottom:%d\n",
2622 margins.cxLeftWidth, margins.cyTopHeight,
2623 margins.cxRightWidth, margins.cyBottomHeight));
2625 // Extends the window frame behind the client area
2626 if(nsUXThemeData::CheckForCompositor()) {
2627 nsUXThemeData::dwmExtendFrameIntoClientAreaPtr(mWnd, &margins);
2628 nsUXThemeData::dwmSetWindowAttributePtr(mWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof policy);
2631 #endif
2633 /**************************************************************
2635 * SECTION: nsIWidget::HideWindowChrome
2637 * Show or hide window chrome.
2639 **************************************************************/
2641 NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide)
2643 HWND hwnd = WinUtils::GetTopLevelHWND(mWnd, true);
2644 if (!WinUtils::GetNSWindowPtr(hwnd))
2646 NS_WARNING("Trying to hide window decorations in an embedded context");
2647 return NS_ERROR_FAILURE;
2650 if (mHideChrome == aShouldHide)
2651 return NS_OK;
2653 DWORD_PTR style, exStyle;
2654 mHideChrome = aShouldHide;
2655 if (aShouldHide) {
2656 DWORD_PTR tempStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
2657 DWORD_PTR tempExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
2659 style = tempStyle & ~(WS_CAPTION | WS_THICKFRAME);
2660 exStyle = tempExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
2661 WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
2663 mOldStyle = tempStyle;
2664 mOldExStyle = tempExStyle;
2666 else {
2667 if (!mOldStyle || !mOldExStyle) {
2668 mOldStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
2669 mOldExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
2672 style = mOldStyle;
2673 exStyle = mOldExStyle;
2676 VERIFY_WINDOW_STYLE(style);
2677 ::SetWindowLongPtrW(hwnd, GWL_STYLE, style);
2678 ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle);
2680 return NS_OK;
2683 /**************************************************************
2685 * SECTION: nsWindow::Invalidate
2687 * Invalidate an area of the client for painting.
2689 **************************************************************/
2691 // Invalidate this component visible area
2692 NS_METHOD nsWindow::Invalidate(bool aEraseBackground,
2693 bool aUpdateNCArea,
2694 bool aIncludeChildren)
2696 if (!mWnd) {
2697 return NS_OK;
2700 #ifdef WIDGET_DEBUG_OUTPUT
2701 debug_DumpInvalidate(stdout,
2702 this,
2703 nullptr,
2704 nsAutoCString("noname"),
2705 (int32_t) mWnd);
2706 #endif // WIDGET_DEBUG_OUTPUT
2708 DWORD flags = RDW_INVALIDATE;
2709 if (aEraseBackground) {
2710 flags |= RDW_ERASE;
2712 if (aUpdateNCArea) {
2713 flags |= RDW_FRAME;
2715 if (aIncludeChildren) {
2716 flags |= RDW_ALLCHILDREN;
2719 VERIFY(::RedrawWindow(mWnd, NULL, NULL, flags));
2720 return NS_OK;
2723 // Invalidate this component visible area
2724 NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect)
2726 if (mWnd)
2728 #ifdef WIDGET_DEBUG_OUTPUT
2729 debug_DumpInvalidate(stdout,
2730 this,
2731 &aRect,
2732 nsAutoCString("noname"),
2733 (int32_t) mWnd);
2734 #endif // WIDGET_DEBUG_OUTPUT
2736 RECT rect;
2738 rect.left = aRect.x;
2739 rect.top = aRect.y;
2740 rect.right = aRect.x + aRect.width;
2741 rect.bottom = aRect.y + aRect.height;
2743 VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
2745 return NS_OK;
2748 NS_IMETHODIMP
2749 nsWindow::MakeFullScreen(bool aFullScreen)
2751 // taskbarInfo will be NULL pre Windows 7 until Bug 680227 is resolved.
2752 nsCOMPtr<nsIWinTaskbar> taskbarInfo =
2753 do_GetService(NS_TASKBAR_CONTRACTID);
2755 mFullscreenMode = aFullScreen;
2756 if (aFullScreen) {
2757 if (mSizeMode == nsSizeMode_Fullscreen)
2758 return NS_OK;
2759 mOldSizeMode = mSizeMode;
2760 SetSizeMode(nsSizeMode_Fullscreen);
2762 // Notify the taskbar that we will be entering full screen mode.
2763 if (taskbarInfo) {
2764 taskbarInfo->PrepareFullScreenHWND(mWnd, TRUE);
2766 } else {
2767 SetSizeMode(mOldSizeMode);
2770 UpdateNonClientMargins();
2772 bool visible = mIsVisible;
2773 if (mOldSizeMode == nsSizeMode_Normal)
2774 Show(false);
2776 // Will call hide chrome, reposition window. Note this will
2777 // also cache dimensions for restoration, so it should only
2778 // be called once per fullscreen request.
2779 nsresult rv = nsBaseWidget::MakeFullScreen(aFullScreen);
2781 if (visible) {
2782 Show(true);
2783 Invalidate();
2786 // Notify the taskbar that we have exited full screen mode.
2787 if (!aFullScreen && taskbarInfo) {
2788 taskbarInfo->PrepareFullScreenHWND(mWnd, FALSE);
2791 if (mWidgetListener)
2792 mWidgetListener->SizeModeChanged(mSizeMode);
2794 return rv;
2797 /**************************************************************
2799 * SECTION: Native data storage
2801 * nsIWidget::GetNativeData
2802 * nsIWidget::FreeNativeData
2804 * Set or clear native data based on a constant.
2806 **************************************************************/
2808 // Return some native data according to aDataType
2809 void* nsWindow::GetNativeData(uint32_t aDataType)
2811 nsAutoString className;
2812 switch (aDataType) {
2813 case NS_NATIVE_TMP_WINDOW:
2814 GetWindowClass(className);
2815 return (void*)::CreateWindowExW(mIsRTL ? WS_EX_LAYOUTRTL : 0,
2816 className.get(),
2817 L"",
2818 WS_CHILD,
2819 CW_USEDEFAULT,
2820 CW_USEDEFAULT,
2821 CW_USEDEFAULT,
2822 CW_USEDEFAULT,
2823 mWnd,
2824 NULL,
2825 nsToolkit::mDllInstance,
2826 NULL);
2827 case NS_NATIVE_PLUGIN_PORT:
2828 case NS_NATIVE_WIDGET:
2829 case NS_NATIVE_WINDOW:
2830 case NS_NATIVE_SHAREABLE_WINDOW:
2831 return (void*)mWnd;
2832 case NS_NATIVE_GRAPHIC:
2833 // XXX: This is sleezy!! Remember to Release the DC after using it!
2834 #ifdef MOZ_XUL
2835 return (void*)(eTransparencyTransparent == mTransparencyMode) ?
2836 mMemoryDC : ::GetDC(mWnd);
2837 #else
2838 return (void*)::GetDC(mWnd);
2839 #endif
2841 case NS_NATIVE_TSF_THREAD_MGR:
2842 case NS_NATIVE_TSF_CATEGORY_MGR:
2843 case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
2844 return IMEHandler::GetNativeData(aDataType);
2846 default:
2847 break;
2850 return NULL;
2853 // Free some native data according to aDataType
2854 void nsWindow::FreeNativeData(void * data, uint32_t aDataType)
2856 switch (aDataType)
2858 case NS_NATIVE_GRAPHIC:
2859 #ifdef MOZ_XUL
2860 if (eTransparencyTransparent != mTransparencyMode)
2861 ::ReleaseDC(mWnd, (HDC)data);
2862 #else
2863 ::ReleaseDC(mWnd, (HDC)data);
2864 #endif
2865 break;
2866 case NS_NATIVE_WIDGET:
2867 case NS_NATIVE_WINDOW:
2868 case NS_NATIVE_PLUGIN_PORT:
2869 break;
2870 default:
2871 break;
2875 /**************************************************************
2877 * SECTION: nsIWidget::SetTitle
2879 * Set the main windows title text.
2881 **************************************************************/
2883 NS_METHOD nsWindow::SetTitle(const nsAString& aTitle)
2885 const nsString& strTitle = PromiseFlatString(aTitle);
2886 ::SendMessageW(mWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)(LPCWSTR)strTitle.get());
2887 return NS_OK;
2890 /**************************************************************
2892 * SECTION: nsIWidget::SetIcon
2894 * Set the main windows icon.
2896 **************************************************************/
2898 NS_METHOD nsWindow::SetIcon(const nsAString& aIconSpec)
2900 // Assume the given string is a local identifier for an icon file.
2902 nsCOMPtr<nsIFile> iconFile;
2903 ResolveIconName(aIconSpec, NS_LITERAL_STRING(".ico"),
2904 getter_AddRefs(iconFile));
2905 if (!iconFile)
2906 return NS_OK; // not an error if icon is not found
2908 nsAutoString iconPath;
2909 iconFile->GetPath(iconPath);
2911 // XXX this should use MZLU (see bug 239279)
2913 ::SetLastError(0);
2915 HICON bigIcon = (HICON)::LoadImageW(NULL,
2916 (LPCWSTR)iconPath.get(),
2917 IMAGE_ICON,
2918 ::GetSystemMetrics(SM_CXICON),
2919 ::GetSystemMetrics(SM_CYICON),
2920 LR_LOADFROMFILE );
2921 HICON smallIcon = (HICON)::LoadImageW(NULL,
2922 (LPCWSTR)iconPath.get(),
2923 IMAGE_ICON,
2924 ::GetSystemMetrics(SM_CXSMICON),
2925 ::GetSystemMetrics(SM_CYSMICON),
2926 LR_LOADFROMFILE );
2928 if (bigIcon) {
2929 HICON icon = (HICON) ::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)bigIcon);
2930 if (icon)
2931 ::DestroyIcon(icon);
2932 mIconBig = bigIcon;
2934 #ifdef DEBUG_SetIcon
2935 else {
2936 NS_LossyConvertUTF16toASCII cPath(iconPath);
2937 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
2938 ("\nIcon load error; icon=%s, rc=0x%08X\n\n",
2939 cPath.get(), ::GetLastError()));
2941 #endif
2942 if (smallIcon) {
2943 HICON icon = (HICON) ::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)smallIcon);
2944 if (icon)
2945 ::DestroyIcon(icon);
2946 mIconSmall = smallIcon;
2948 #ifdef DEBUG_SetIcon
2949 else {
2950 NS_LossyConvertUTF16toASCII cPath(iconPath);
2951 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
2952 ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n",
2953 cPath.get(), ::GetLastError()));
2955 #endif
2956 return NS_OK;
2959 /**************************************************************
2961 * SECTION: nsIWidget::WidgetToScreenOffset
2963 * Return this widget's origin in screen coordinates.
2965 **************************************************************/
2967 nsIntPoint nsWindow::WidgetToScreenOffset()
2969 POINT point;
2970 point.x = 0;
2971 point.y = 0;
2972 ::ClientToScreen(mWnd, &point);
2973 return nsIntPoint(point.x, point.y);
2976 nsIntSize nsWindow::ClientToWindowSize(const nsIntSize& aClientSize)
2978 if (mWindowType == eWindowType_popup && !IsPopupWithTitleBar())
2979 return aClientSize;
2981 // just use (200, 200) as the position
2982 RECT r;
2983 r.left = 200;
2984 r.top = 200;
2985 r.right = 200 + aClientSize.width;
2986 r.bottom = 200 + aClientSize.height;
2987 ::AdjustWindowRectEx(&r, WindowStyle(), false, WindowExStyle());
2989 return nsIntSize(r.right - r.left, r.bottom - r.top);
2992 /**************************************************************
2994 * SECTION: nsIWidget::EnableDragDrop
2996 * Enables/Disables drag and drop of files on this widget.
2998 **************************************************************/
3000 NS_METHOD nsWindow::EnableDragDrop(bool aEnable)
3002 NS_ASSERTION(mWnd, "nsWindow::EnableDragDrop() called after Destroy()");
3004 nsresult rv = NS_ERROR_FAILURE;
3005 if (aEnable) {
3006 if (nullptr == mNativeDragTarget) {
3007 mNativeDragTarget = new nsNativeDragTarget(this);
3008 if (NULL != mNativeDragTarget) {
3009 mNativeDragTarget->AddRef();
3010 if (S_OK == ::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget,TRUE,FALSE)) {
3011 if (S_OK == ::RegisterDragDrop(mWnd, (LPDROPTARGET)mNativeDragTarget)) {
3012 rv = NS_OK;
3017 } else {
3018 if (nullptr != mWnd && NULL != mNativeDragTarget) {
3019 ::RevokeDragDrop(mWnd);
3020 if (S_OK == ::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, FALSE, TRUE)) {
3021 rv = NS_OK;
3023 mNativeDragTarget->DragCancel();
3024 NS_RELEASE(mNativeDragTarget);
3027 return rv;
3030 /**************************************************************
3032 * SECTION: nsIWidget::CaptureMouse
3034 * Enables/Disables system mouse capture.
3036 **************************************************************/
3038 NS_METHOD nsWindow::CaptureMouse(bool aCapture)
3040 if (!nsToolkit::gMouseTrailer) {
3041 NS_ERROR("nsWindow::CaptureMouse called after nsToolkit destroyed");
3042 return NS_OK;
3045 if (aCapture) {
3046 nsToolkit::gMouseTrailer->SetCaptureWindow(mWnd);
3047 ::SetCapture(mWnd);
3048 } else {
3049 nsToolkit::gMouseTrailer->SetCaptureWindow(NULL);
3050 ::ReleaseCapture();
3052 sIsInMouseCapture = aCapture;
3053 return NS_OK;
3056 /**************************************************************
3058 * SECTION: nsIWidget::CaptureRollupEvents
3060 * Dealing with event rollup on destroy for popups. Enables &
3061 * Disables system capture of any and all events that would
3062 * cause a dropdown to be rolled up.
3064 **************************************************************/
3066 NS_IMETHODIMP nsWindow::CaptureRollupEvents(nsIRollupListener * aListener,
3067 bool aDoCapture)
3069 if (aDoCapture) {
3070 gRollupListener = aListener;
3071 if (!sMsgFilterHook && !sCallProcHook && !sCallMouseHook) {
3072 RegisterSpecialDropdownHooks();
3074 sProcessHook = true;
3075 } else {
3076 gRollupListener = nullptr;
3077 sProcessHook = false;
3078 UnregisterSpecialDropdownHooks();
3081 return NS_OK;
3084 /**************************************************************
3086 * SECTION: nsIWidget::GetAttention
3088 * Bring this window to the user's attention.
3090 **************************************************************/
3092 // Draw user's attention to this window until it comes to foreground.
3093 NS_IMETHODIMP
3094 nsWindow::GetAttention(int32_t aCycleCount)
3096 // Got window?
3097 if (!mWnd)
3098 return NS_ERROR_NOT_INITIALIZED;
3100 HWND flashWnd = WinUtils::GetTopLevelHWND(mWnd, false, false);
3101 HWND fgWnd = ::GetForegroundWindow();
3102 // Don't flash if the flash count is 0 or if the foreground window is our
3103 // window handle or that of our owned-most window.
3104 if (aCycleCount == 0 ||
3105 flashWnd == fgWnd ||
3106 flashWnd == WinUtils::GetTopLevelHWND(fgWnd, false, false)) {
3107 return NS_OK;
3110 DWORD defaultCycleCount = 0;
3111 ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &defaultCycleCount, 0);
3113 FLASHWINFO flashInfo = { sizeof(FLASHWINFO), flashWnd,
3114 FLASHW_ALL, aCycleCount > 0 ? aCycleCount : defaultCycleCount, 0 };
3115 ::FlashWindowEx(&flashInfo);
3117 return NS_OK;
3120 void nsWindow::StopFlashing()
3122 HWND flashWnd = mWnd;
3123 while (HWND ownerWnd = ::GetWindow(flashWnd, GW_OWNER)) {
3124 flashWnd = ownerWnd;
3127 FLASHWINFO flashInfo = { sizeof(FLASHWINFO), flashWnd,
3128 FLASHW_STOP, 0, 0 };
3129 ::FlashWindowEx(&flashInfo);
3132 /**************************************************************
3134 * SECTION: nsIWidget::HasPendingInputEvent
3136 * Ask whether there user input events pending. All input events are
3137 * included, including those not targeted at this nsIwidget instance.
3139 **************************************************************/
3141 bool
3142 nsWindow::HasPendingInputEvent()
3144 // If there is pending input or the user is currently
3145 // moving the window then return true.
3146 // Note: When the user is moving the window WIN32 spins
3147 // a separate event loop and input events are not
3148 // reported to the application.
3149 if (HIWORD(GetQueueStatus(QS_INPUT)))
3150 return true;
3151 GUITHREADINFO guiInfo;
3152 guiInfo.cbSize = sizeof(GUITHREADINFO);
3153 if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo))
3154 return false;
3155 return GUI_INMOVESIZE == (guiInfo.flags & GUI_INMOVESIZE);
3158 /**************************************************************
3160 * SECTION: nsIWidget::GetLayerManager
3162 * Get the layer manager associated with this widget.
3164 **************************************************************/
3166 struct LayerManagerPrefs {
3167 LayerManagerPrefs()
3168 : mAccelerateByDefault(true)
3169 , mDisableAcceleration(false)
3170 , mPreferOpenGL(false)
3171 , mPreferD3D9(false)
3173 bool mAccelerateByDefault;
3174 bool mDisableAcceleration;
3175 bool mForceAcceleration;
3176 bool mPreferOpenGL;
3177 bool mPreferD3D9;
3180 static void
3181 GetLayerManagerPrefs(LayerManagerPrefs* aManagerPrefs)
3183 Preferences::GetBool("layers.acceleration.disabled",
3184 &aManagerPrefs->mDisableAcceleration);
3185 Preferences::GetBool("layers.acceleration.force-enabled",
3186 &aManagerPrefs->mForceAcceleration);
3187 Preferences::GetBool("layers.prefer-opengl",
3188 &aManagerPrefs->mPreferOpenGL);
3189 Preferences::GetBool("layers.prefer-d3d9",
3190 &aManagerPrefs->mPreferD3D9);
3192 const char *acceleratedEnv = PR_GetEnv("MOZ_ACCELERATED");
3193 aManagerPrefs->mAccelerateByDefault =
3194 aManagerPrefs->mAccelerateByDefault ||
3195 (acceleratedEnv && (*acceleratedEnv != '0'));
3197 bool safeMode = false;
3198 nsCOMPtr<nsIXULRuntime> xr = do_GetService("@mozilla.org/xre/runtime;1");
3199 if (xr)
3200 xr->GetInSafeMode(&safeMode);
3201 aManagerPrefs->mDisableAcceleration =
3202 aManagerPrefs->mDisableAcceleration || safeMode;
3205 LayerManager*
3206 nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
3207 LayersBackend aBackendHint,
3208 LayerManagerPersistence aPersistence,
3209 bool* aAllowRetaining)
3211 if (aAllowRetaining) {
3212 *aAllowRetaining = true;
3215 #ifdef MOZ_ENABLE_D3D10_LAYER
3216 if (mLayerManager) {
3217 if (mLayerManager->GetBackendType() == LAYERS_D3D10)
3219 LayerManagerD3D10 *layerManagerD3D10 =
3220 static_cast<LayerManagerD3D10*>(mLayerManager.get());
3221 if (layerManagerD3D10->device() !=
3222 gfxWindowsPlatform::GetPlatform()->GetD3D10Device())
3224 MOZ_ASSERT(!mLayerManager->IsInTransaction());
3226 mLayerManager->Destroy();
3227 mLayerManager = nullptr;
3231 #endif
3233 RECT windowRect;
3234 ::GetClientRect(mWnd, &windowRect);
3236 // Try OMTC first.
3237 if (!mLayerManager && ShouldUseOffMainThreadCompositing()) {
3238 // e10s uses the parameter to pass in the shadow manager from the TabChild
3239 // so we don't expect to see it there since this doesn't support e10s.
3240 NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s");
3241 CreateCompositor();
3244 if (!mLayerManager ||
3245 (!sAllowD3D9 && aPersistence == LAYER_MANAGER_PERSISTENT &&
3246 mLayerManager->GetBackendType() == LAYERS_BASIC &&
3247 !ShouldUseOffMainThreadCompositing())) {
3248 // If D3D9 is not currently allowed but the permanent manager is required,
3249 // -and- we're currently using basic layers, run through this check.
3250 LayerManagerPrefs prefs;
3251 GetLayerManagerPrefs(&prefs);
3253 /* We don't currently support using an accelerated layer manager with
3254 * transparent windows so don't even try. I'm also not sure if we even
3255 * want to support this case. See bug #593471 */
3256 if (eTransparencyTransparent == mTransparencyMode ||
3257 prefs.mDisableAcceleration ||
3258 windowRect.right - windowRect.left > MAX_ACCELERATED_DIMENSION ||
3259 windowRect.bottom - windowRect.top > MAX_ACCELERATED_DIMENSION)
3260 mUseLayersAcceleration = false;
3261 else if (prefs.mAccelerateByDefault)
3262 mUseLayersAcceleration = true;
3264 if (mUseLayersAcceleration) {
3265 if (aPersistence == LAYER_MANAGER_PERSISTENT && !sAllowD3D9) {
3266 MOZ_ASSERT(!mLayerManager || !mLayerManager->IsInTransaction());
3268 // This will clear out our existing layer manager if we have one since
3269 // if we hit this with a LayerManager we're always using BasicLayers.
3270 nsToolkit::StartAllowingD3D9();
3273 #ifdef MOZ_ENABLE_D3D10_LAYER
3274 if (!prefs.mPreferD3D9 && !prefs.mPreferOpenGL) {
3275 nsRefPtr<LayerManagerD3D10> layerManager =
3276 new LayerManagerD3D10(this);
3277 if (layerManager->Initialize(prefs.mForceAcceleration)) {
3278 mLayerManager = layerManager;
3281 #endif
3282 #ifdef MOZ_ENABLE_D3D9_LAYER
3283 if (!prefs.mPreferOpenGL && !mLayerManager && sAllowD3D9) {
3284 nsRefPtr<LayerManagerD3D9> layerManager =
3285 new LayerManagerD3D9(this);
3286 if (layerManager->Initialize(prefs.mForceAcceleration)) {
3287 mLayerManager = layerManager;
3290 #endif
3291 if (!mLayerManager && prefs.mPreferOpenGL) {
3292 nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
3293 int32_t status = nsIGfxInfo::FEATURE_NO_INFO;
3295 if (gfxInfo && !prefs.mForceAcceleration) {
3296 gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
3299 if (status == nsIGfxInfo::FEATURE_NO_INFO) {
3300 nsRefPtr<LayerManagerOGL> layerManager =
3301 new LayerManagerOGL(this);
3302 if (layerManager->Initialize()) {
3303 mLayerManager = layerManager;
3306 } else {
3307 NS_WARNING("OpenGL accelerated layers are not supported on this system.");
3312 // Fall back to software if we couldn't use any hardware backends.
3313 if (!mLayerManager) {
3314 // Try to use an async compositor first, if possible
3315 if (ShouldUseOffMainThreadCompositing()) {
3316 // e10s uses the parameter to pass in the shadow manager from the TabChild
3317 // so we don't expect to see it there since this doesn't support e10s.
3318 NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s");
3319 CreateCompositor();
3322 if (!mLayerManager)
3323 mLayerManager = CreateBasicLayerManager();
3327 NS_ASSERTION(mLayerManager, "Couldn't provide a valid layer manager.");
3329 return mLayerManager;
3332 /**************************************************************
3334 * SECTION: nsIWidget::GetThebesSurface
3336 * Get the Thebes surface associated with this widget.
3338 **************************************************************/
3340 gfxASurface *nsWindow::GetThebesSurface()
3342 #ifdef CAIRO_HAS_D2D_SURFACE
3343 if (mD2DWindowSurface) {
3344 return mD2DWindowSurface;
3346 #endif
3347 if (mPaintDC)
3348 return (new gfxWindowsSurface(mPaintDC));
3350 #ifdef CAIRO_HAS_D2D_SURFACE
3351 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
3352 gfxWindowsPlatform::RENDER_DIRECT2D) {
3353 gfxASurface::gfxContentType content = gfxASurface::CONTENT_COLOR;
3354 #if defined(MOZ_XUL)
3355 if (mTransparencyMode != eTransparencyOpaque) {
3356 content = gfxASurface::CONTENT_COLOR_ALPHA;
3358 #endif
3359 return (new gfxD2DSurface(mWnd, content));
3360 } else {
3361 #endif
3362 uint32_t flags = gfxWindowsSurface::FLAG_TAKE_DC;
3363 if (mTransparencyMode != eTransparencyOpaque) {
3364 flags |= gfxWindowsSurface::FLAG_IS_TRANSPARENT;
3366 return (new gfxWindowsSurface(mWnd, flags));
3367 #ifdef CAIRO_HAS_D2D_SURFACE
3369 #endif
3372 /**************************************************************
3374 * SECTION: nsIWidget::OnDefaultButtonLoaded
3376 * Called after the dialog is loaded and it has a default button.
3378 **************************************************************/
3380 NS_IMETHODIMP
3381 nsWindow::OnDefaultButtonLoaded(const nsIntRect &aButtonRect)
3383 if (aButtonRect.IsEmpty())
3384 return NS_OK;
3386 // Don't snap when we are not active.
3387 HWND activeWnd = ::GetActiveWindow();
3388 if (activeWnd != ::GetForegroundWindow() ||
3389 WinUtils::GetTopLevelHWND(mWnd, true) !=
3390 WinUtils::GetTopLevelHWND(activeWnd, true)) {
3391 return NS_OK;
3394 bool isAlwaysSnapCursor =
3395 Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
3397 if (!isAlwaysSnapCursor) {
3398 BOOL snapDefaultButton;
3399 if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0,
3400 &snapDefaultButton, 0) || !snapDefaultButton)
3401 return NS_OK;
3404 nsIntRect widgetRect;
3405 nsresult rv = GetScreenBounds(widgetRect);
3406 NS_ENSURE_SUCCESS(rv, rv);
3407 nsIntRect buttonRect(aButtonRect + widgetRect.TopLeft());
3409 nsIntPoint centerOfButton(buttonRect.x + buttonRect.width / 2,
3410 buttonRect.y + buttonRect.height / 2);
3411 // The center of the button can be outside of the widget.
3412 // E.g., it could be hidden by scrolling.
3413 if (!widgetRect.Contains(centerOfButton)) {
3414 return NS_OK;
3417 if (!::SetCursorPos(centerOfButton.x, centerOfButton.y)) {
3418 NS_ERROR("SetCursorPos failed");
3419 return NS_ERROR_FAILURE;
3421 return NS_OK;
3424 NS_IMETHODIMP
3425 nsWindow::OverrideSystemMouseScrollSpeed(double aOriginalDeltaX,
3426 double aOriginalDeltaY,
3427 double& aOverriddenDeltaX,
3428 double& aOverriddenDeltaY)
3430 // The default vertical and horizontal scrolling speed is 3, this is defined
3431 // on the document of SystemParametersInfo in MSDN.
3432 const uint32_t kSystemDefaultScrollingSpeed = 3;
3434 double absOriginDeltaX = Abs(aOriginalDeltaX);
3435 double absOriginDeltaY = Abs(aOriginalDeltaY);
3437 // Compute the simple overridden speed.
3438 double absComputedOverriddenDeltaX, absComputedOverriddenDeltaY;
3439 nsresult rv =
3440 nsBaseWidget::OverrideSystemMouseScrollSpeed(absOriginDeltaX,
3441 absOriginDeltaY,
3442 absComputedOverriddenDeltaX,
3443 absComputedOverriddenDeltaY);
3444 NS_ENSURE_SUCCESS(rv, rv);
3446 aOverriddenDeltaX = aOriginalDeltaX;
3447 aOverriddenDeltaY = aOriginalDeltaY;
3449 if (absComputedOverriddenDeltaX == absOriginDeltaX &&
3450 absComputedOverriddenDeltaY == absOriginDeltaY) {
3451 // We don't override now.
3452 return NS_OK;
3455 // Otherwise, we should check whether the user customized the system settings
3456 // or not. If the user did it, we should respect the will.
3457 UINT systemSpeed;
3458 if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &systemSpeed, 0)) {
3459 return NS_ERROR_FAILURE;
3461 // The default vertical scrolling speed is 3, this is defined on the document
3462 // of SystemParametersInfo in MSDN.
3463 if (systemSpeed != kSystemDefaultScrollingSpeed) {
3464 return NS_OK;
3467 // Only Vista and later, Windows has the system setting of horizontal
3468 // scrolling by the mouse wheel.
3469 if (WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION) {
3470 if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &systemSpeed, 0)) {
3471 return NS_ERROR_FAILURE;
3473 // The default horizontal scrolling speed is 3, this is defined on the
3474 // document of SystemParametersInfo in MSDN.
3475 if (systemSpeed != kSystemDefaultScrollingSpeed) {
3476 return NS_OK;
3480 // Limit the overridden delta value from the system settings. The mouse
3481 // driver might accelerate the scrolling speed already. If so, we shouldn't
3482 // override the scrolling speed for preventing the unexpected high speed
3483 // scrolling.
3484 double absDeltaLimitX, absDeltaLimitY;
3485 rv =
3486 nsBaseWidget::OverrideSystemMouseScrollSpeed(kSystemDefaultScrollingSpeed,
3487 kSystemDefaultScrollingSpeed,
3488 absDeltaLimitX,
3489 absDeltaLimitY);
3490 NS_ENSURE_SUCCESS(rv, rv);
3492 // If the given delta is larger than our computed limitation value, the delta
3493 // was accelerated by the mouse driver. So, we should do nothing here.
3494 if (absDeltaLimitX <= absOriginDeltaX || absDeltaLimitY <= absOriginDeltaY) {
3495 return NS_OK;
3498 aOverriddenDeltaX = std::min(absComputedOverriddenDeltaX, absDeltaLimitX);
3499 aOverriddenDeltaY = std::min(absComputedOverriddenDeltaY, absDeltaLimitY);
3501 if (aOriginalDeltaX < 0) {
3502 aOverriddenDeltaX *= -1;
3504 if (aOriginalDeltaY < 0) {
3505 aOverriddenDeltaY *= -1;
3507 return NS_OK;
3510 /**************************************************************
3511 **************************************************************
3513 ** BLOCK: Moz Events
3515 ** Moz GUI event management.
3517 **************************************************************
3518 **************************************************************/
3520 /**************************************************************
3522 * SECTION: Mozilla event initialization
3524 * Helpers for initializing moz events.
3526 **************************************************************/
3528 // Event intialization
3529 void nsWindow::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
3531 if (nullptr == aPoint) { // use the point from the event
3532 // get the message position in client coordinates
3533 if (mWnd != NULL) {
3535 DWORD pos = ::GetMessagePos();
3536 POINT cpos;
3538 cpos.x = GET_X_LPARAM(pos);
3539 cpos.y = GET_Y_LPARAM(pos);
3541 ::ScreenToClient(mWnd, &cpos);
3542 event.refPoint.x = cpos.x;
3543 event.refPoint.y = cpos.y;
3544 } else {
3545 event.refPoint.x = 0;
3546 event.refPoint.y = 0;
3549 else {
3550 // use the point override if provided
3551 event.refPoint.x = aPoint->x;
3552 event.refPoint.y = aPoint->y;
3555 event.time = ::GetMessageTime();
3556 mLastPoint = event.refPoint;
3559 /**************************************************************
3561 * SECTION: Moz event dispatch helpers
3563 * Helpers for dispatching different types of moz events.
3565 **************************************************************/
3567 // Main event dispatch. Invokes callback and ProcessEvent method on
3568 // Event Listener object. Part of nsIWidget.
3569 NS_IMETHODIMP nsWindow::DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus)
3571 #ifdef WIDGET_DEBUG_OUTPUT
3572 debug_DumpEvent(stdout,
3573 event->widget,
3574 event,
3575 nsAutoCString("something"),
3576 (int32_t) mWnd);
3577 #endif // WIDGET_DEBUG_OUTPUT
3579 aStatus = nsEventStatus_eIgnore;
3581 // Top level windows can have a view attached which requires events be sent
3582 // to the underlying base window and the view. Added when we combined the
3583 // base chrome window with the main content child for nc client area (title
3584 // bar) rendering.
3585 if (mAttachedWidgetListener) {
3586 aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
3588 else if (mWidgetListener) {
3589 aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
3592 // the window can be destroyed during processing of seemingly innocuous events like, say,
3593 // mousedowns due to the magic of scripting. mousedowns will return nsEventStatus_eIgnore,
3594 // which causes problems with the deleted window. therefore:
3595 if (mOnDestroyCalled)
3596 aStatus = nsEventStatus_eConsumeNoDefault;
3597 return NS_OK;
3600 bool nsWindow::DispatchStandardEvent(uint32_t aMsg)
3602 nsGUIEvent event(true, aMsg, this);
3603 InitEvent(event);
3605 bool result = DispatchWindowEvent(&event);
3606 return result;
3609 bool nsWindow::DispatchWindowEvent(nsGUIEvent* event)
3611 nsEventStatus status;
3612 DispatchEvent(event, status);
3613 return ConvertStatus(status);
3616 bool nsWindow::DispatchWindowEvent(nsGUIEvent* event, nsEventStatus &aStatus) {
3617 DispatchEvent(event, aStatus);
3618 return ConvertStatus(aStatus);
3621 void nsWindow::InitKeyEvent(nsKeyEvent& aKeyEvent,
3622 const NativeKey& aNativeKey,
3623 const ModifierKeyState &aModKeyState)
3625 nsIntPoint point(0, 0);
3626 InitEvent(aKeyEvent, &point);
3627 aKeyEvent.mKeyNameIndex = aNativeKey.GetKeyNameIndex();
3628 aKeyEvent.location = aNativeKey.GetKeyLocation();
3629 aModKeyState.InitInputEvent(aKeyEvent);
3632 bool nsWindow::DispatchKeyEvent(nsKeyEvent& aKeyEvent,
3633 const MSG *aMsgSentToPlugin)
3635 UserActivity();
3637 NPEvent pluginEvent;
3638 if (aMsgSentToPlugin && PluginHasFocus()) {
3639 pluginEvent.event = aMsgSentToPlugin->message;
3640 pluginEvent.wParam = aMsgSentToPlugin->wParam;
3641 pluginEvent.lParam = aMsgSentToPlugin->lParam;
3642 aKeyEvent.pluginEvent = (void *)&pluginEvent;
3645 return DispatchWindowEvent(&aKeyEvent);
3648 bool nsWindow::DispatchCommandEvent(uint32_t aEventCommand)
3650 nsCOMPtr<nsIAtom> command;
3651 switch (aEventCommand) {
3652 case APPCOMMAND_BROWSER_BACKWARD:
3653 command = nsGkAtoms::Back;
3654 break;
3655 case APPCOMMAND_BROWSER_FORWARD:
3656 command = nsGkAtoms::Forward;
3657 break;
3658 case APPCOMMAND_BROWSER_REFRESH:
3659 command = nsGkAtoms::Reload;
3660 break;
3661 case APPCOMMAND_BROWSER_STOP:
3662 command = nsGkAtoms::Stop;
3663 break;
3664 case APPCOMMAND_BROWSER_SEARCH:
3665 command = nsGkAtoms::Search;
3666 break;
3667 case APPCOMMAND_BROWSER_FAVORITES:
3668 command = nsGkAtoms::Bookmarks;
3669 break;
3670 case APPCOMMAND_BROWSER_HOME:
3671 command = nsGkAtoms::Home;
3672 break;
3673 case APPCOMMAND_CLOSE:
3674 command = nsGkAtoms::Close;
3675 break;
3676 case APPCOMMAND_FIND:
3677 command = nsGkAtoms::Find;
3678 break;
3679 case APPCOMMAND_HELP:
3680 command = nsGkAtoms::Help;
3681 break;
3682 case APPCOMMAND_NEW:
3683 command = nsGkAtoms::New;
3684 break;
3685 case APPCOMMAND_OPEN:
3686 command = nsGkAtoms::Open;
3687 break;
3688 case APPCOMMAND_PRINT:
3689 command = nsGkAtoms::Print;
3690 break;
3691 case APPCOMMAND_SAVE:
3692 command = nsGkAtoms::Save;
3693 break;
3694 case APPCOMMAND_FORWARD_MAIL:
3695 command = nsGkAtoms::ForwardMail;
3696 break;
3697 case APPCOMMAND_REPLY_TO_MAIL:
3698 command = nsGkAtoms::ReplyToMail;
3699 break;
3700 case APPCOMMAND_SEND_MAIL:
3701 command = nsGkAtoms::SendMail;
3702 break;
3703 default:
3704 return false;
3706 nsCommandEvent event(true, nsGkAtoms::onAppCommand, command, this);
3708 InitEvent(event);
3709 return DispatchWindowEvent(&event);
3712 // Recursively dispatch synchronous paints for nsIWidget
3713 // descendants with invalidated rectangles.
3714 BOOL CALLBACK nsWindow::DispatchStarvedPaints(HWND aWnd, LPARAM aMsg)
3716 LONG_PTR proc = ::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
3717 if (proc == (LONG_PTR)&nsWindow::WindowProc) {
3718 // its one of our windows so check to see if it has a
3719 // invalidated rect. If it does. Dispatch a synchronous
3720 // paint.
3721 if (GetUpdateRect(aWnd, NULL, FALSE))
3722 VERIFY(::UpdateWindow(aWnd));
3724 return TRUE;
3727 // Check for pending paints and dispatch any pending paint
3728 // messages for any nsIWidget which is a descendant of the
3729 // top-level window that *this* window is embedded within.
3731 // Note: We do not dispatch pending paint messages for non
3732 // nsIWidget managed windows.
3733 void nsWindow::DispatchPendingEvents()
3735 if (mPainting) {
3736 NS_WARNING("We were asked to dispatch pending events during painting, "
3737 "denying since that's unsafe.");
3738 return;
3741 // We need to ensure that reflow events do not get starved.
3742 // At the same time, we don't want to recurse through here
3743 // as that would prevent us from dispatching starved paints.
3744 static int recursionBlocker = 0;
3745 if (recursionBlocker++ == 0) {
3746 NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
3747 --recursionBlocker;
3750 // Quickly check to see if there are any paint events pending,
3751 // but only dispatch them if it has been long enough since the
3752 // last paint completed.
3753 if (::GetQueueStatus(QS_PAINT) &&
3754 ((TimeStamp::Now() - mLastPaintEndTime).ToMilliseconds() >= 50)) {
3755 // Find the top level window.
3756 HWND topWnd = WinUtils::GetTopLevelHWND(mWnd);
3758 // Dispatch pending paints for topWnd and all its descendant windows.
3759 // Note: EnumChildWindows enumerates all descendant windows not just
3760 // the children (but not the window itself).
3761 nsWindow::DispatchStarvedPaints(topWnd, 0);
3762 ::EnumChildWindows(topWnd, nsWindow::DispatchStarvedPaints, 0);
3766 // Deal with plugin events
3767 bool nsWindow::DispatchPluginEvent(const MSG &aMsg)
3769 if (!PluginHasFocus())
3770 return false;
3772 nsPluginEvent event(true, NS_PLUGIN_INPUT_EVENT, this);
3773 nsIntPoint point(0, 0);
3774 InitEvent(event, &point);
3775 NPEvent pluginEvent;
3776 pluginEvent.event = aMsg.message;
3777 pluginEvent.wParam = aMsg.wParam;
3778 pluginEvent.lParam = aMsg.lParam;
3779 event.pluginEvent = (void *)&pluginEvent;
3780 event.retargetToFocusedDocument = true;
3781 return DispatchWindowEvent(&event);
3784 bool nsWindow::DispatchPluginEvent(UINT aMessage,
3785 WPARAM aWParam,
3786 LPARAM aLParam,
3787 bool aDispatchPendingEvents)
3789 bool ret = DispatchPluginEvent(WinUtils::InitMSG(aMessage, aWParam, aLParam));
3790 if (aDispatchPendingEvents) {
3791 DispatchPendingEvents();
3793 return ret;
3796 void nsWindow::RemoveMessageAndDispatchPluginEvent(UINT aFirstMsg,
3797 UINT aLastMsg, nsFakeCharMessage* aFakeCharMessage)
3799 MSG msg;
3800 if (aFakeCharMessage) {
3801 if (aFirstMsg > WM_CHAR || aLastMsg < WM_CHAR) {
3802 return;
3804 msg = aFakeCharMessage->GetCharMessage(mWnd);
3805 } else {
3806 WinUtils::GetMessage(&msg, mWnd, aFirstMsg, aLastMsg);
3808 DispatchPluginEvent(msg);
3811 // Deal with all sort of mouse event
3812 bool nsWindow::DispatchMouseEvent(uint32_t aEventType, WPARAM wParam,
3813 LPARAM lParam, bool aIsContextMenuKey,
3814 int16_t aButton, uint16_t aInputSource)
3816 bool result = false;
3818 UserActivity();
3820 if (!mWidgetListener) {
3821 return result;
3824 switch (aEventType) {
3825 case NS_MOUSE_BUTTON_DOWN:
3826 CaptureMouse(true);
3827 break;
3829 // NS_MOUSE_MOVE and NS_MOUSE_EXIT are here because we need to make sure capture flag
3830 // isn't left on after a drag where we wouldn't see a button up message (see bug 324131).
3831 case NS_MOUSE_BUTTON_UP:
3832 case NS_MOUSE_MOVE:
3833 case NS_MOUSE_EXIT:
3834 if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) && sIsInMouseCapture)
3835 CaptureMouse(false);
3836 break;
3838 default:
3839 break;
3841 } // switch
3843 nsIntPoint eventPoint;
3844 eventPoint.x = GET_X_LPARAM(lParam);
3845 eventPoint.y = GET_Y_LPARAM(lParam);
3847 nsMouseEvent event(true, aEventType, this, nsMouseEvent::eReal,
3848 aIsContextMenuKey
3849 ? nsMouseEvent::eContextMenuKey
3850 : nsMouseEvent::eNormal);
3851 if (aEventType == NS_CONTEXTMENU && aIsContextMenuKey) {
3852 nsIntPoint zero(0, 0);
3853 InitEvent(event, &zero);
3854 } else {
3855 InitEvent(event, &eventPoint);
3858 ModifierKeyState modifierKeyState;
3859 modifierKeyState.InitInputEvent(event);
3860 event.button = aButton;
3861 event.inputSource = aInputSource;
3863 nsIntPoint mpScreen = eventPoint + WidgetToScreenOffset();
3865 // Suppress mouse moves caused by widget creation
3866 if (aEventType == NS_MOUSE_MOVE)
3868 if ((sLastMouseMovePoint.x == mpScreen.x) && (sLastMouseMovePoint.y == mpScreen.y))
3869 return result;
3870 sLastMouseMovePoint.x = mpScreen.x;
3871 sLastMouseMovePoint.y = mpScreen.y;
3874 bool insideMovementThreshold = (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) < (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
3875 (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) < (short)::GetSystemMetrics(SM_CYDOUBLECLK));
3877 BYTE eventButton;
3878 switch (aButton) {
3879 case nsMouseEvent::eLeftButton:
3880 eventButton = VK_LBUTTON;
3881 break;
3882 case nsMouseEvent::eMiddleButton:
3883 eventButton = VK_MBUTTON;
3884 break;
3885 case nsMouseEvent::eRightButton:
3886 eventButton = VK_RBUTTON;
3887 break;
3888 default:
3889 eventButton = 0;
3890 break;
3893 // Doubleclicks are used to set the click count, then changed to mousedowns
3894 // We're going to time double-clicks from mouse *up* to next mouse *down*
3895 LONG curMsgTime = ::GetMessageTime();
3897 if (aEventType == NS_MOUSE_DOUBLECLICK) {
3898 event.message = NS_MOUSE_BUTTON_DOWN;
3899 event.button = aButton;
3900 sLastClickCount = 2;
3902 else if (aEventType == NS_MOUSE_BUTTON_UP) {
3903 // remember when this happened for the next mouse down
3904 sLastMousePoint.x = eventPoint.x;
3905 sLastMousePoint.y = eventPoint.y;
3906 sLastMouseButton = eventButton;
3908 else if (aEventType == NS_MOUSE_BUTTON_DOWN) {
3909 // now look to see if we want to convert this to a double- or triple-click
3910 if (((curMsgTime - sLastMouseDownTime) < (LONG)::GetDoubleClickTime()) && insideMovementThreshold &&
3911 eventButton == sLastMouseButton) {
3912 sLastClickCount ++;
3913 } else {
3914 // reset the click count, to count *this* click
3915 sLastClickCount = 1;
3917 // Set last Click time on MouseDown only
3918 sLastMouseDownTime = curMsgTime;
3920 else if (aEventType == NS_MOUSE_MOVE && !insideMovementThreshold) {
3921 sLastClickCount = 0;
3923 else if (aEventType == NS_MOUSE_EXIT) {
3924 event.exit = IsTopLevelMouseExit(mWnd) ? nsMouseEvent::eTopLevel : nsMouseEvent::eChild;
3926 else if (aEventType == NS_MOUSE_MOZHITTEST)
3928 event.mFlags.mOnlyChromeDispatch = true;
3930 event.clickCount = sLastClickCount;
3932 #ifdef NS_DEBUG_XX
3933 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
3934 ("Msg Time: %d Click Count: %d\n", curMsgTime, event.clickCount));
3935 #endif
3937 NPEvent pluginEvent;
3939 switch (aEventType)
3941 case NS_MOUSE_BUTTON_DOWN:
3942 switch (aButton) {
3943 case nsMouseEvent::eLeftButton:
3944 pluginEvent.event = WM_LBUTTONDOWN;
3945 break;
3946 case nsMouseEvent::eMiddleButton:
3947 pluginEvent.event = WM_MBUTTONDOWN;
3948 break;
3949 case nsMouseEvent::eRightButton:
3950 pluginEvent.event = WM_RBUTTONDOWN;
3951 break;
3952 default:
3953 break;
3955 break;
3956 case NS_MOUSE_BUTTON_UP:
3957 switch (aButton) {
3958 case nsMouseEvent::eLeftButton:
3959 pluginEvent.event = WM_LBUTTONUP;
3960 break;
3961 case nsMouseEvent::eMiddleButton:
3962 pluginEvent.event = WM_MBUTTONUP;
3963 break;
3964 case nsMouseEvent::eRightButton:
3965 pluginEvent.event = WM_RBUTTONUP;
3966 break;
3967 default:
3968 break;
3970 break;
3971 case NS_MOUSE_DOUBLECLICK:
3972 switch (aButton) {
3973 case nsMouseEvent::eLeftButton:
3974 pluginEvent.event = WM_LBUTTONDBLCLK;
3975 break;
3976 case nsMouseEvent::eMiddleButton:
3977 pluginEvent.event = WM_MBUTTONDBLCLK;
3978 break;
3979 case nsMouseEvent::eRightButton:
3980 pluginEvent.event = WM_RBUTTONDBLCLK;
3981 break;
3982 default:
3983 break;
3985 break;
3986 case NS_MOUSE_MOVE:
3987 pluginEvent.event = WM_MOUSEMOVE;
3988 break;
3989 case NS_MOUSE_EXIT:
3990 pluginEvent.event = WM_MOUSELEAVE;
3991 break;
3992 default:
3993 pluginEvent.event = WM_NULL;
3994 break;
3997 pluginEvent.wParam = wParam; // plugins NEED raw OS event flags!
3998 pluginEvent.lParam = lParam;
4000 event.pluginEvent = (void *)&pluginEvent;
4002 // call the event callback
4003 if (mWidgetListener) {
4004 if (nsToolkit::gMouseTrailer)
4005 nsToolkit::gMouseTrailer->Disable();
4006 if (aEventType == NS_MOUSE_MOVE) {
4007 if (nsToolkit::gMouseTrailer && !sIsInMouseCapture) {
4008 nsToolkit::gMouseTrailer->SetMouseTrailerWindow(mWnd);
4010 nsIntRect rect;
4011 GetBounds(rect);
4012 rect.x = 0;
4013 rect.y = 0;
4015 if (rect.Contains(event.refPoint)) {
4016 if (sCurrentWindow == NULL || sCurrentWindow != this) {
4017 if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
4018 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4019 sCurrentWindow->DispatchMouseEvent(NS_MOUSE_EXIT, wParam, pos, false,
4020 nsMouseEvent::eLeftButton, aInputSource);
4022 sCurrentWindow = this;
4023 if (!mInDtor) {
4024 LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
4025 sCurrentWindow->DispatchMouseEvent(NS_MOUSE_ENTER, wParam, pos, false,
4026 nsMouseEvent::eLeftButton, aInputSource);
4030 } else if (aEventType == NS_MOUSE_EXIT) {
4031 if (sCurrentWindow == this) {
4032 sCurrentWindow = nullptr;
4036 result = DispatchWindowEvent(&event);
4038 if (nsToolkit::gMouseTrailer)
4039 nsToolkit::gMouseTrailer->Enable();
4041 // Release the widget with NS_IF_RELEASE() just in case
4042 // the context menu key code in nsEventListenerManager::HandleEvent()
4043 // released it already.
4044 return result;
4047 return result;
4050 void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate)
4052 if (aIsActivate)
4053 sJustGotActivate = false;
4054 sJustGotDeactivate = false;
4056 // retrive the toplevel window or dialog
4057 HWND curWnd = mWnd;
4058 HWND toplevelWnd = NULL;
4059 while (curWnd) {
4060 toplevelWnd = curWnd;
4062 nsWindow *win = WinUtils::GetNSWindowPtr(curWnd);
4063 if (win) {
4064 nsWindowType wintype;
4065 win->GetWindowType(wintype);
4066 if (wintype == eWindowType_toplevel || wintype == eWindowType_dialog)
4067 break;
4070 curWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
4073 if (toplevelWnd) {
4074 nsWindow *win = WinUtils::GetNSWindowPtr(toplevelWnd);
4075 if (win && win->mWidgetListener) {
4076 if (aIsActivate) {
4077 win->mWidgetListener->WindowActivated();
4078 } else {
4079 if (!win->BlurEventsSuppressed()) {
4080 win->mWidgetListener->WindowDeactivated();
4087 bool nsWindow::IsTopLevelMouseExit(HWND aWnd)
4089 DWORD pos = ::GetMessagePos();
4090 POINT mp;
4091 mp.x = GET_X_LPARAM(pos);
4092 mp.y = GET_Y_LPARAM(pos);
4093 HWND mouseWnd = ::WindowFromPoint(mp);
4095 // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
4096 // (which includes the non-client area). If the mouse has moved into
4097 // the non-client area, we should treat it as a top-level exit.
4098 HWND mouseTopLevel = WinUtils::GetTopLevelHWND(mouseWnd);
4099 if (mouseWnd == mouseTopLevel)
4100 return true;
4102 return WinUtils::GetTopLevelHWND(aWnd) != mouseTopLevel;
4105 bool nsWindow::BlurEventsSuppressed()
4107 // are they suppressed in this window?
4108 if (mBlurSuppressLevel > 0)
4109 return true;
4111 // are they suppressed by any container widget?
4112 HWND parentWnd = ::GetParent(mWnd);
4113 if (parentWnd) {
4114 nsWindow *parent = WinUtils::GetNSWindowPtr(parentWnd);
4115 if (parent)
4116 return parent->BlurEventsSuppressed();
4118 return false;
4121 // In some circumstances (opening dependent windows) it makes more sense
4122 // (and fixes a crash bug) to not blur the parent window. Called from
4123 // nsFilePicker.
4124 void nsWindow::SuppressBlurEvents(bool aSuppress)
4126 if (aSuppress)
4127 ++mBlurSuppressLevel; // for this widget
4128 else {
4129 NS_ASSERTION(mBlurSuppressLevel > 0, "unbalanced blur event suppression");
4130 if (mBlurSuppressLevel > 0)
4131 --mBlurSuppressLevel;
4135 bool nsWindow::ConvertStatus(nsEventStatus aStatus)
4137 return aStatus == nsEventStatus_eConsumeNoDefault;
4140 /**************************************************************
4142 * SECTION: IPC
4144 * IPC related helpers.
4146 **************************************************************/
4148 // static
4149 bool
4150 nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult)
4152 switch(aMsg) {
4153 case WM_SETFOCUS:
4154 case WM_KILLFOCUS:
4155 case WM_ENABLE:
4156 case WM_WINDOWPOSCHANGING:
4157 case WM_WINDOWPOSCHANGED:
4158 case WM_PARENTNOTIFY:
4159 case WM_ACTIVATEAPP:
4160 case WM_NCACTIVATE:
4161 case WM_ACTIVATE:
4162 case WM_CHILDACTIVATE:
4163 case WM_IME_SETCONTEXT:
4164 case WM_IME_NOTIFY:
4165 case WM_SHOWWINDOW:
4166 case WM_CANCELMODE:
4167 case WM_MOUSEACTIVATE:
4168 case WM_CONTEXTMENU:
4169 aResult = 0;
4170 return true;
4172 case WM_SETTINGCHANGE:
4173 case WM_SETCURSOR:
4174 return false;
4177 #ifdef DEBUG
4178 char szBuf[200];
4179 sprintf(szBuf,
4180 "An unhandled ISMEX_SEND message was received during spin loop! (%X)", aMsg);
4181 NS_WARNING(szBuf);
4182 #endif
4184 return false;
4187 void
4188 nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam)
4190 NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(),
4191 "Failed to prevent a nonqueued message from running!");
4193 // Modal UI being displayed in windowless plugins.
4194 if (mozilla::ipc::RPCChannel::IsSpinLoopActive() &&
4195 (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
4196 LRESULT res;
4197 if (IsAsyncResponseEvent(msg, res)) {
4198 ReplyMessage(res);
4200 return;
4203 // Handle certain sync plugin events sent to the parent which
4204 // trigger ipc calls that result in deadlocks.
4206 DWORD dwResult = 0;
4207 bool handled = false;
4209 switch(msg) {
4210 // Windowless flash sending WM_ACTIVATE events to the main window
4211 // via calls to ShowWindow.
4212 case WM_ACTIVATE:
4213 if (lParam != 0 && LOWORD(wParam) == WA_ACTIVE &&
4214 IsWindow((HWND)lParam)) {
4215 // Check for Adobe Reader X sync activate message from their
4216 // helper window and ignore. Fixes an annoying focus problem.
4217 if ((InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
4218 PRUnichar szClass[10];
4219 HWND focusWnd = (HWND)lParam;
4220 if (IsWindowVisible(focusWnd) &&
4221 GetClassNameW(focusWnd, szClass,
4222 sizeof(szClass)/sizeof(PRUnichar)) &&
4223 !wcscmp(szClass, L"Edit") &&
4224 !WinUtils::IsOurProcessWindow(focusWnd)) {
4225 break;
4228 handled = true;
4230 break;
4231 // Plugins taking or losing focus triggering focus app messages.
4232 case WM_SETFOCUS:
4233 case WM_KILLFOCUS:
4234 // Windowed plugins that pass sys key events to defwndproc generate
4235 // WM_SYSCOMMAND events to the main window.
4236 case WM_SYSCOMMAND:
4237 // Windowed plugins that fire context menu selection events to parent
4238 // windows.
4239 case WM_CONTEXTMENU:
4240 // IME events fired as a result of synchronous focus changes
4241 case WM_IME_SETCONTEXT:
4242 handled = true;
4243 break;
4246 if (handled &&
4247 (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
4248 ReplyMessage(dwResult);
4252 /**************************************************************
4253 **************************************************************
4255 ** BLOCK: Native events
4257 ** Main Windows message handlers and OnXXX handlers for
4258 ** Windows event handling.
4260 **************************************************************
4261 **************************************************************/
4263 /**************************************************************
4265 * SECTION: Wind proc.
4267 * The main Windows event procedures and associated
4268 * message processing methods.
4270 **************************************************************/
4272 static bool
4273 DisplaySystemMenu(HWND hWnd, nsSizeMode sizeMode, bool isRtl, int32_t x, int32_t y)
4275 HMENU hMenu = GetSystemMenu(hWnd, FALSE);
4276 if (hMenu) {
4277 MENUITEMINFO mii;
4278 mii.cbSize = sizeof(MENUITEMINFO);
4279 mii.fMask = MIIM_STATE;
4280 mii.fType = 0;
4282 // update the options
4283 mii.fState = MF_ENABLED;
4284 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4285 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4286 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4287 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4288 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4290 mii.fState = MF_GRAYED;
4291 switch(sizeMode) {
4292 case nsSizeMode_Fullscreen:
4293 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4294 // intentional fall through
4295 case nsSizeMode_Maximized:
4296 SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
4297 SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
4298 SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
4299 break;
4300 case nsSizeMode_Minimized:
4301 SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
4302 break;
4303 case nsSizeMode_Normal:
4304 SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
4305 break;
4307 LPARAM cmd =
4308 TrackPopupMenu(hMenu,
4309 (TPM_LEFTBUTTON|TPM_RIGHTBUTTON|
4310 TPM_RETURNCMD|TPM_TOPALIGN|
4311 (isRtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
4312 x, y, 0, hWnd, NULL);
4313 if (cmd) {
4314 PostMessage(hWnd, WM_SYSCOMMAND, cmd, 0);
4315 return true;
4318 return false;
4321 inline static mozilla::HangMonitor::ActivityType ActivityTypeForMessage(UINT msg)
4323 if ((msg >= WM_KEYFIRST && msg <= WM_IME_KEYLAST) ||
4324 (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
4325 (msg >= MOZ_WM_MOUSEWHEEL_FIRST && msg <= MOZ_WM_MOUSEWHEEL_LAST) ||
4326 (msg >= NS_WM_IMEFIRST && msg <= NS_WM_IMELAST)) {
4327 return mozilla::HangMonitor::kUIActivity;
4330 // This may not actually be right, but we don't want to reset the timer if
4331 // we're not actually processing a UI message.
4332 return mozilla::HangMonitor::kActivityUIAVail;
4335 // The WndProc procedure for all nsWindows in this toolkit. This merely catches
4336 // exceptions and passes the real work to WindowProcInternal. See bug 587406
4337 // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
4338 LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4340 HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));
4342 return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg, wParam, lParam);
4345 LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
4347 if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
4348 // This message was sent to the FAKETRACKPOINTSCROLLABLE.
4349 if (msg == WM_HSCROLL) {
4350 // Route WM_HSCROLL messages to the main window.
4351 hWnd = ::GetParent(::GetParent(hWnd));
4352 } else {
4353 // Handle all other messages with its original window procedure.
4354 WNDPROC prevWindowProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
4355 return ::CallWindowProcW(prevWindowProc, hWnd, msg, wParam, lParam);
4359 if (msg == MOZ_WM_TRACE) {
4360 // This is a tracer event for measuring event loop latency.
4361 // See WidgetTraceEvent.cpp for more details.
4362 mozilla::SignalTracerThread();
4363 return 0;
4366 // Get the window which caused the event and ask it to process the message
4367 nsWindow *targetWindow = WinUtils::GetNSWindowPtr(hWnd);
4368 NS_ASSERTION(targetWindow, "nsWindow* is null!");
4369 if (!targetWindow)
4370 return ::DefWindowProcW(hWnd, msg, wParam, lParam);
4372 // Hold the window for the life of this method, in case it gets
4373 // destroyed during processing, unless we're in the dtor already.
4374 nsCOMPtr<nsISupports> kungFuDeathGrip;
4375 if (!targetWindow->mInDtor)
4376 kungFuDeathGrip = do_QueryInterface((nsBaseWidget*)targetWindow);
4378 targetWindow->IPCWindowProcHandler(msg, wParam, lParam);
4380 // Create this here so that we store the last rolled up popup until after
4381 // the event has been processed.
4382 nsAutoRollup autoRollup;
4384 LRESULT popupHandlingResult;
4385 if (DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult))
4386 return popupHandlingResult;
4388 // Call ProcessMessage
4389 LRESULT retValue;
4390 if (targetWindow->ProcessMessage(msg, wParam, lParam, &retValue)) {
4391 return retValue;
4394 LRESULT res = ::CallWindowProcW(targetWindow->GetPrevWindowProc(),
4395 hWnd, msg, wParam, lParam);
4397 return res;
4400 // The main windows message processing method for plugins.
4401 // The result means whether this method processed the native
4402 // event for plugin. If false, the native event should be
4403 // processed by the caller self.
4404 bool
4405 nsWindow::ProcessMessageForPlugin(const MSG &aMsg,
4406 LRESULT *aResult,
4407 bool &aCallDefWndProc)
4409 NS_PRECONDITION(aResult, "aResult must be non-null.");
4410 *aResult = 0;
4412 aCallDefWndProc = false;
4413 bool eventDispatched = false;
4414 switch (aMsg.message) {
4415 case WM_CHAR:
4416 case WM_SYSCHAR:
4417 *aResult = ProcessCharMessage(aMsg, &eventDispatched);
4418 break;
4420 case WM_KEYUP:
4421 case WM_SYSKEYUP:
4422 *aResult = ProcessKeyUpMessage(aMsg, &eventDispatched);
4423 break;
4425 case WM_KEYDOWN:
4426 case WM_SYSKEYDOWN:
4427 *aResult = ProcessKeyDownMessage(aMsg, &eventDispatched);
4428 break;
4430 case WM_DEADCHAR:
4431 case WM_SYSDEADCHAR:
4433 case WM_CUT:
4434 case WM_COPY:
4435 case WM_PASTE:
4436 case WM_CLEAR:
4437 case WM_UNDO:
4438 break;
4440 default:
4441 return false;
4444 if (!eventDispatched)
4445 aCallDefWndProc = !DispatchPluginEvent(aMsg);
4446 DispatchPendingEvents();
4447 return true;
4450 static void ForceFontUpdate()
4452 // update device context font cache
4453 // Dirty but easiest way:
4454 // Changing nsIPrefBranch entry which triggers callbacks
4455 // and flows into calling mDeviceContext->FlushFontCache()
4456 // to update the font cache in all the instance of Browsers
4457 static const char kPrefName[] = "font.internaluseonly.changed";
4458 bool fontInternalChange =
4459 Preferences::GetBool(kPrefName, false);
4460 Preferences::SetBool(kPrefName, !fontInternalChange);
4463 static bool CleartypeSettingChanged()
4465 static int currentQuality = -1;
4466 BYTE quality = cairo_win32_get_system_text_quality();
4468 if (currentQuality == quality)
4469 return false;
4471 if (currentQuality < 0) {
4472 currentQuality = quality;
4473 return false;
4475 currentQuality = quality;
4476 return true;
4479 // The main windows message processing method.
4480 bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
4481 LRESULT *aRetValue)
4483 // (Large blocks of code should be broken out into OnEvent handlers.)
4484 if (mWindowHook.Notify(mWnd, msg, wParam, lParam, aRetValue))
4485 return true;
4487 #if defined(EVENT_DEBUG_OUTPUT)
4488 // First param shows all events, second param indicates whether
4489 // to show mouse move events. See nsWindowDbg for details.
4490 PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS);
4491 #endif
4493 bool eatMessage;
4494 if (IMEHandler::ProcessMessage(this, msg, wParam, lParam, aRetValue,
4495 eatMessage)) {
4496 return mWnd ? eatMessage : true;
4499 if (MouseScrollHandler::ProcessMessage(this, msg, wParam, lParam, aRetValue,
4500 eatMessage)) {
4501 return mWnd ? eatMessage : true;
4504 if (PluginHasFocus()) {
4505 bool callDefaultWndProc;
4506 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam);
4507 if (ProcessMessageForPlugin(nativeMsg, aRetValue, callDefaultWndProc)) {
4508 return mWnd ? !callDefaultWndProc : true;
4512 bool result = false; // call the default nsWindow proc
4513 *aRetValue = 0;
4515 // Glass hit testing w/custom transparent margins
4516 LRESULT dwmHitResult;
4517 if (mCustomNonClient &&
4518 nsUXThemeData::CheckForCompositor() &&
4519 nsUXThemeData::dwmDwmDefWindowProcPtr(mWnd, msg, wParam, lParam, &dwmHitResult)) {
4520 *aRetValue = dwmHitResult;
4521 return true;
4524 switch (msg) {
4525 // WM_QUERYENDSESSION must be handled by all windows.
4526 // Otherwise Windows thinks the window can just be killed at will.
4527 case WM_QUERYENDSESSION:
4528 if (sCanQuit == TRI_UNKNOWN)
4530 // Ask if it's ok to quit, and store the answer until we
4531 // get WM_ENDSESSION signaling the round is complete.
4532 nsCOMPtr<nsIObserverService> obsServ =
4533 mozilla::services::GetObserverService();
4534 nsCOMPtr<nsISupportsPRBool> cancelQuit =
4535 do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
4536 cancelQuit->SetData(false);
4537 obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
4539 bool abortQuit;
4540 cancelQuit->GetData(&abortQuit);
4541 sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
4543 *aRetValue = sCanQuit ? TRUE : FALSE;
4544 result = true;
4545 break;
4547 case WM_ENDSESSION:
4548 case MOZ_WM_APP_QUIT:
4549 if (msg == MOZ_WM_APP_QUIT || (wParam == TRUE && sCanQuit == TRI_TRUE))
4551 // Let's fake a shutdown sequence without actually closing windows etc.
4552 // to avoid Windows killing us in the middle. A proper shutdown would
4553 // require having a chance to pump some messages. Unfortunately
4554 // Windows won't let us do that. Bug 212316.
4555 nsCOMPtr<nsIObserverService> obsServ =
4556 mozilla::services::GetObserverService();
4557 NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
4558 obsServ->NotifyObservers(nullptr, "quit-application-granted", nullptr);
4559 obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
4560 obsServ->NotifyObservers(nullptr, "quit-application", nullptr);
4561 obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get());
4562 obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get());
4563 obsServ->NotifyObservers(nullptr, "profile-before-change", context.get());
4564 obsServ->NotifyObservers(nullptr, "profile-before-change2", context.get());
4565 // Then a controlled but very quick exit.
4566 _exit(0);
4568 sCanQuit = TRI_UNKNOWN;
4569 result = true;
4570 break;
4572 case WM_SYSCOLORCHANGE:
4573 OnSysColorChanged();
4574 break;
4576 case WM_THEMECHANGED:
4578 // Update non-client margin offsets
4579 UpdateNonClientMargins();
4580 nsUXThemeData::InitTitlebarInfo();
4581 nsUXThemeData::UpdateNativeThemeInfo();
4583 NotifyThemeChanged();
4585 // Invalidate the window so that the repaint will
4586 // pick up the new theme.
4587 Invalidate(true, true, true);
4589 break;
4591 case WM_FONTCHANGE:
4593 // We only handle this message for the hidden window,
4594 // as we only need to update the (global) font list once
4595 // for any given change, not once per window!
4596 if (mWindowType != eWindowType_invisible) {
4597 break;
4600 nsresult rv;
4601 bool didChange = false;
4603 // update the global font list
4604 nsCOMPtr<nsIFontEnumerator> fontEnum = do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv);
4605 if (NS_SUCCEEDED(rv)) {
4606 fontEnum->UpdateFontList(&didChange);
4607 ForceFontUpdate();
4608 } //if (NS_SUCCEEDED(rv))
4610 break;
4612 case WM_NCCALCSIZE:
4614 if (mCustomNonClient) {
4615 // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
4616 // the proposed window rectangle for our window. During our
4617 // processing of the `WM_NCCALCSIZE` message, we are expected to
4618 // modify the `RECT` that `lParam` points to, so that its value upon
4619 // our return is the new client area. We must return 0 if `wParam`
4620 // is `FALSE`.
4622 // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
4623 // struct. This struct contains an array of 3 `RECT`s, the first of
4624 // which has the exact same meaning as the `RECT` that is pointed to
4625 // by `lParam` when `wParam` is `FALSE`. The remaining `RECT`s, in
4626 // conjunction with our return value, can
4627 // be used to specify portions of the source and destination window
4628 // rectangles that are valid and should be preserved. We opt not to
4629 // implement an elaborate client-area preservation technique, and
4630 // simply return 0, which means "preserve the entire old client area
4631 // and align it with the upper-left corner of our new client area".
4632 RECT *clientRect = wParam
4633 ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam))->rgrc[0]
4634 : (reinterpret_cast<RECT*>(lParam));
4635 clientRect->top += (mCaptionHeight - mNonClientOffset.top);
4636 clientRect->left += (mHorResizeMargin - mNonClientOffset.left);
4637 clientRect->right -= (mHorResizeMargin - mNonClientOffset.right);
4638 clientRect->bottom -= (mVertResizeMargin - mNonClientOffset.bottom);
4640 result = true;
4641 *aRetValue = 0;
4643 break;
4646 case WM_NCHITTEST:
4649 * If an nc client area margin has been moved, we are responsible
4650 * for calculating where the resize margins are and returning the
4651 * appropriate set of hit test constants. DwmDefWindowProc (above)
4652 * will handle hit testing on it's command buttons if we are on a
4653 * composited desktop.
4656 if (!mCustomNonClient)
4657 break;
4659 *aRetValue =
4660 ClientMarginHitTestPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
4661 result = true;
4662 break;
4665 case WM_SETTEXT:
4667 * WM_SETTEXT paints the titlebar area. Avoid this if we have a
4668 * custom titlebar we paint ourselves.
4671 if (!mCustomNonClient || mNonClientMargins.top == -1)
4672 break;
4675 // From msdn, the way around this is to disable the visible state
4676 // temporarily. We need the text to be set but we don't want the
4677 // redraw to occur.
4678 DWORD style = GetWindowLong(mWnd, GWL_STYLE);
4679 SetWindowLong(mWnd, GWL_STYLE, style & ~WS_VISIBLE);
4680 *aRetValue = CallWindowProcW(GetPrevWindowProc(), mWnd,
4681 msg, wParam, lParam);
4682 SetWindowLong(mWnd, GWL_STYLE, style);
4683 return true;
4686 case WM_NCACTIVATE:
4689 * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
4690 * through WM_NCPAINT via InvalidateNonClientRegion.
4693 if (!mCustomNonClient)
4694 break;
4696 // let the dwm handle nc painting on glass
4697 if(nsUXThemeData::CheckForCompositor())
4698 break;
4700 if (wParam == TRUE) {
4701 // going active
4702 *aRetValue = FALSE; // ignored
4703 result = true;
4704 UpdateGetWindowInfoCaptionStatus(true);
4705 // invalidate to trigger a paint
4706 InvalidateNonClientRegion();
4707 break;
4708 } else {
4709 // going inactive
4710 *aRetValue = TRUE; // go ahead and deactive
4711 result = true;
4712 UpdateGetWindowInfoCaptionStatus(false);
4713 // invalidate to trigger a paint
4714 InvalidateNonClientRegion();
4715 break;
4719 case WM_NCPAINT:
4722 * Reset the non-client paint region so that it excludes the
4723 * non-client areas we paint manually. Then call defwndproc
4724 * to do the actual painting.
4727 if (!mCustomNonClient)
4728 break;
4730 // let the dwm handle nc painting on glass
4731 if(nsUXThemeData::CheckForCompositor())
4732 break;
4734 HRGN paintRgn = ExcludeNonClientFromPaintRegion((HRGN)wParam);
4735 LRESULT res = CallWindowProcW(GetPrevWindowProc(), mWnd,
4736 msg, (WPARAM)paintRgn, lParam);
4737 if (paintRgn != (HRGN)wParam)
4738 DeleteObject(paintRgn);
4739 *aRetValue = res;
4740 result = true;
4742 break;
4744 case WM_POWERBROADCAST:
4745 switch (wParam)
4747 case PBT_APMSUSPEND:
4748 PostSleepWakeNotification(true);
4749 break;
4750 case PBT_APMRESUMEAUTOMATIC:
4751 case PBT_APMRESUMECRITICAL:
4752 case PBT_APMRESUMESUSPEND:
4753 PostSleepWakeNotification(false);
4754 break;
4756 break;
4758 case WM_MOVE: // Window moved
4760 RECT rect;
4761 ::GetWindowRect(mWnd, &rect);
4762 result = OnMove(rect.left, rect.top);
4764 break;
4766 case WM_CLOSE: // close request
4767 if (mWidgetListener)
4768 mWidgetListener->RequestWindowClose(this);
4769 result = true; // abort window closure
4770 break;
4772 case WM_DESTROY:
4773 // clean up.
4774 OnDestroy();
4775 result = true;
4776 break;
4778 case WM_PAINT:
4779 if (CleartypeSettingChanged()) {
4780 ForceFontUpdate();
4781 gfxFontCache *fc = gfxFontCache::GetCache();
4782 if (fc) {
4783 fc->Flush();
4786 *aRetValue = (int) OnPaint(NULL, 0);
4787 result = true;
4788 break;
4790 case WM_PRINTCLIENT:
4791 result = OnPaint((HDC) wParam, 0);
4792 break;
4794 case WM_HOTKEY:
4795 result = OnHotKey(wParam, lParam);
4796 break;
4798 case WM_SYSCHAR:
4799 case WM_CHAR:
4801 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam);
4802 result = ProcessCharMessage(nativeMsg, nullptr);
4803 DispatchPendingEvents();
4805 break;
4807 case WM_SYSKEYUP:
4808 case WM_KEYUP:
4810 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam);
4811 nativeMsg.time = ::GetMessageTime();
4812 result = ProcessKeyUpMessage(nativeMsg, nullptr);
4813 DispatchPendingEvents();
4815 break;
4817 case WM_SYSKEYDOWN:
4818 case WM_KEYDOWN:
4820 MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam);
4821 result = ProcessKeyDownMessage(nativeMsg, nullptr);
4822 DispatchPendingEvents();
4824 break;
4826 // say we've dealt with erase background if widget does
4827 // not need auto-erasing
4828 case WM_ERASEBKGND:
4829 if (!AutoErase((HDC)wParam)) {
4830 *aRetValue = 1;
4831 result = true;
4833 break;
4835 case WM_MOUSEMOVE:
4837 mMousePresent = true;
4839 // Suppress dispatch of pending events
4840 // when mouse moves are generated by widget
4841 // creation instead of user input.
4842 LPARAM lParamScreen = lParamToScreen(lParam);
4843 POINT mp;
4844 mp.x = GET_X_LPARAM(lParamScreen);
4845 mp.y = GET_Y_LPARAM(lParamScreen);
4846 bool userMovedMouse = false;
4847 if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
4848 userMovedMouse = true;
4851 result = DispatchMouseEvent(NS_MOUSE_MOVE, wParam, lParam,
4852 false, nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
4853 if (userMovedMouse) {
4854 DispatchPendingEvents();
4857 break;
4859 case WM_NCMOUSEMOVE:
4860 // If we receive a mouse move event on non-client chrome, make sure and
4861 // send an NS_MOUSE_EXIT event as well.
4862 if (mMousePresent && !sIsInMouseCapture)
4863 SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
4864 break;
4866 case WM_LBUTTONDOWN:
4868 result = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, wParam, lParam,
4869 false, nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
4870 DispatchPendingEvents();
4872 break;
4874 case WM_LBUTTONUP:
4876 result = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, wParam, lParam,
4877 false, nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
4878 DispatchPendingEvents();
4880 break;
4882 case WM_MOUSELEAVE:
4884 if (!mMousePresent)
4885 break;
4886 mMousePresent = false;
4888 // We need to check mouse button states and put them in for
4889 // wParam.
4890 WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0)
4891 | (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0)
4892 | (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
4893 // Synthesize an event position because we don't get one from
4894 // WM_MOUSELEAVE.
4895 LPARAM pos = lParamToClient(::GetMessagePos());
4896 DispatchMouseEvent(NS_MOUSE_EXIT, mouseState, pos, false,
4897 nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
4899 break;
4901 case WM_CONTEXTMENU:
4903 // if the context menu is brought up from the keyboard, |lParam|
4904 // will be -1.
4905 LPARAM pos;
4906 bool contextMenukey = false;
4907 if (lParam == -1)
4909 contextMenukey = true;
4910 pos = lParamToClient(GetMessagePos());
4912 else
4914 pos = lParamToClient(lParam);
4917 result = DispatchMouseEvent(NS_CONTEXTMENU, wParam, pos, contextMenukey,
4918 contextMenukey ?
4919 nsMouseEvent::eLeftButton :
4920 nsMouseEvent::eRightButton, MOUSE_INPUT_SOURCE());
4921 if (lParam != -1 && !result && mCustomNonClient &&
4922 DispatchMouseEvent(NS_MOUSE_MOZHITTEST, wParam, pos,
4923 false, nsMouseEvent::eLeftButton,
4924 MOUSE_INPUT_SOURCE())) {
4925 // Blank area hit, throw up the system menu.
4926 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
4927 result = true;
4930 break;
4932 case WM_LBUTTONDBLCLK:
4933 result = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, wParam, lParam, false,
4934 nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
4935 DispatchPendingEvents();
4936 break;
4938 case WM_MBUTTONDOWN:
4939 result = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, wParam, lParam, false,
4940 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4941 DispatchPendingEvents();
4942 break;
4944 case WM_MBUTTONUP:
4945 result = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, wParam, lParam, false,
4946 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4947 DispatchPendingEvents();
4948 break;
4950 case WM_MBUTTONDBLCLK:
4951 result = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, wParam, lParam, false,
4952 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4953 DispatchPendingEvents();
4954 break;
4956 case WM_NCMBUTTONDOWN:
4957 result = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, 0, lParamToClient(lParam), false,
4958 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4959 DispatchPendingEvents();
4960 break;
4962 case WM_NCMBUTTONUP:
4963 result = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, 0, lParamToClient(lParam), false,
4964 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4965 DispatchPendingEvents();
4966 break;
4968 case WM_NCMBUTTONDBLCLK:
4969 result = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, 0, lParamToClient(lParam), false,
4970 nsMouseEvent::eMiddleButton, MOUSE_INPUT_SOURCE());
4971 DispatchPendingEvents();
4972 break;
4974 case WM_RBUTTONDOWN:
4975 result = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, wParam, lParam, false,
4976 nsMouseEvent::eRightButton, MOUSE_INPUT_SOURCE());
4977 DispatchPendingEvents();
4978 break;
4980 case WM_RBUTTONUP:
4981 result = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, wParam, lParam, false,
4982 nsMouseEvent::eRightButton, MOUSE_INPUT_SOURCE());
4983 DispatchPendingEvents();
4984 break;
4986 case WM_RBUTTONDBLCLK:
4987 result = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, wParam, lParam, false,
4988 nsMouseEvent::eRightButton, MOUSE_INPUT_SOURCE());
4989 DispatchPendingEvents();
4990 break;
4992 case WM_NCRBUTTONDOWN:
4993 result = DispatchMouseEvent(NS_MOUSE_BUTTON_DOWN, 0, lParamToClient(lParam),
4994 false, nsMouseEvent::eRightButton,
4995 MOUSE_INPUT_SOURCE());
4996 DispatchPendingEvents();
4997 break;
4999 case WM_NCRBUTTONUP:
5000 result = DispatchMouseEvent(NS_MOUSE_BUTTON_UP, 0, lParamToClient(lParam),
5001 false, nsMouseEvent::eRightButton,
5002 MOUSE_INPUT_SOURCE());
5003 DispatchPendingEvents();
5004 break;
5006 case WM_NCRBUTTONDBLCLK:
5007 result = DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, 0, lParamToClient(lParam),
5008 false, nsMouseEvent::eRightButton,
5009 MOUSE_INPUT_SOURCE());
5010 DispatchPendingEvents();
5011 break;
5013 case WM_EXITSIZEMOVE:
5014 if (!sIsInMouseCapture) {
5015 NotifySizeMoveDone();
5017 break;
5019 case WM_NCLBUTTONDBLCLK:
5020 DispatchMouseEvent(NS_MOUSE_DOUBLECLICK, 0, lParamToClient(lParam),
5021 false, nsMouseEvent::eLeftButton,
5022 MOUSE_INPUT_SOURCE());
5023 result =
5024 DispatchMouseEvent(NS_MOUSE_BUTTON_UP, 0, lParamToClient(lParam),
5025 false, nsMouseEvent::eLeftButton,
5026 MOUSE_INPUT_SOURCE());
5027 DispatchPendingEvents();
5028 break;
5030 case WM_APPCOMMAND:
5032 uint32_t appCommand = GET_APPCOMMAND_LPARAM(lParam);
5033 uint32_t contentCommandMessage = NS_EVENT_NULL;
5034 // XXX After we implement KeyboardEvent.key, we should dispatch the
5035 // key event if (GET_DEVICE_LPARAM(lParam) == FAPPCOMMAND_KEY) is.
5036 switch (appCommand)
5038 case APPCOMMAND_BROWSER_BACKWARD:
5039 case APPCOMMAND_BROWSER_FORWARD:
5040 case APPCOMMAND_BROWSER_REFRESH:
5041 case APPCOMMAND_BROWSER_STOP:
5042 case APPCOMMAND_BROWSER_SEARCH:
5043 case APPCOMMAND_BROWSER_FAVORITES:
5044 case APPCOMMAND_BROWSER_HOME:
5045 case APPCOMMAND_CLOSE:
5046 case APPCOMMAND_FIND:
5047 case APPCOMMAND_HELP:
5048 case APPCOMMAND_NEW:
5049 case APPCOMMAND_OPEN:
5050 case APPCOMMAND_PRINT:
5051 case APPCOMMAND_SAVE:
5052 case APPCOMMAND_FORWARD_MAIL:
5053 case APPCOMMAND_REPLY_TO_MAIL:
5054 case APPCOMMAND_SEND_MAIL:
5055 // We shouldn't consume the message always because if we don't handle
5056 // the message, the sender (typically, utility of keyboard or mouse)
5057 // may send other key messages which indicate well known shortcut key.
5058 if (DispatchCommandEvent(appCommand)) {
5059 // tell the driver that we handled the event
5060 *aRetValue = 1;
5061 result = true;
5063 break;
5065 // Use content command for following commands:
5066 case APPCOMMAND_COPY:
5067 contentCommandMessage = NS_CONTENT_COMMAND_COPY;
5068 break;
5069 case APPCOMMAND_CUT:
5070 contentCommandMessage = NS_CONTENT_COMMAND_CUT;
5071 break;
5072 case APPCOMMAND_PASTE:
5073 contentCommandMessage = NS_CONTENT_COMMAND_PASTE;
5074 break;
5075 case APPCOMMAND_REDO:
5076 contentCommandMessage = NS_CONTENT_COMMAND_REDO;
5077 break;
5078 case APPCOMMAND_UNDO:
5079 contentCommandMessage = NS_CONTENT_COMMAND_UNDO;
5080 break;
5083 if (contentCommandMessage) {
5084 nsContentCommandEvent contentCommand(true, contentCommandMessage, this);
5085 DispatchWindowEvent(&contentCommand);
5086 // tell the driver that we handled the event
5087 *aRetValue = 1;
5088 result = true;
5090 // default = false - tell the driver that the event was not handled
5092 break;
5094 // The WM_ACTIVATE event is fired when a window is raised or lowered,
5095 // and the loword of wParam specifies which. But we don't want to tell
5096 // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
5097 // events are fired. Instead, set either the sJustGotActivate or
5098 // gJustGotDeactivate flags and activate/deactivate once the focus
5099 // events arrive.
5100 case WM_ACTIVATE:
5101 if (mWidgetListener) {
5102 int32_t fActive = LOWORD(wParam);
5104 if (WA_INACTIVE == fActive) {
5105 // when minimizing a window, the deactivation and focus events will
5106 // be fired in the reverse order. Instead, just deactivate right away.
5107 if (HIWORD(wParam))
5108 DispatchFocusToTopLevelWindow(false);
5109 else
5110 sJustGotDeactivate = true;
5112 if (mIsTopWidgetWindow)
5113 mLastKeyboardLayout = gKbdLayout.GetLayout();
5115 } else {
5116 StopFlashing();
5118 sJustGotActivate = true;
5119 nsMouseEvent event(true, NS_MOUSE_ACTIVATE, this,
5120 nsMouseEvent::eReal);
5121 InitEvent(event);
5122 ModifierKeyState modifierKeyState;
5123 modifierKeyState.InitInputEvent(event);
5124 DispatchWindowEvent(&event);
5125 if (sSwitchKeyboardLayout && mLastKeyboardLayout)
5126 ActivateKeyboardLayout(mLastKeyboardLayout, 0);
5129 break;
5131 case WM_MOUSEACTIVATE:
5132 if (mWindowType == eWindowType_popup) {
5133 // a popup with a parent owner should not be activated when clicked
5134 // but should still allow the mouse event to be fired, so the return
5135 // value is set to MA_NOACTIVATE. But if the owner isn't the frontmost
5136 // window, just use default processing so that the window is activated.
5137 HWND owner = ::GetWindow(mWnd, GW_OWNER);
5138 if (owner && owner == ::GetForegroundWindow()) {
5139 *aRetValue = MA_NOACTIVATE;
5140 result = true;
5143 break;
5145 case WM_WINDOWPOSCHANGING:
5147 LPWINDOWPOS info = (LPWINDOWPOS) lParam;
5148 OnWindowPosChanging(info);
5150 break;
5152 case WM_GETMINMAXINFO:
5154 MINMAXINFO* mmi = (MINMAXINFO*)lParam;
5155 // Set the constraints. The minimum size should also be constrained to the
5156 // default window maximum size so that it fits on screen.
5157 mmi->ptMinTrackSize.x =
5158 std::min((int32_t)mmi->ptMaxTrackSize.x,
5159 std::max((int32_t)mmi->ptMinTrackSize.x, mSizeConstraints.mMinSize.width));
5160 mmi->ptMinTrackSize.y =
5161 std::min((int32_t)mmi->ptMaxTrackSize.y,
5162 std::max((int32_t)mmi->ptMinTrackSize.y, mSizeConstraints.mMinSize.height));
5163 mmi->ptMaxTrackSize.x = std::min((int32_t)mmi->ptMaxTrackSize.x, mSizeConstraints.mMaxSize.width);
5164 mmi->ptMaxTrackSize.y = std::min((int32_t)mmi->ptMaxTrackSize.y, mSizeConstraints.mMaxSize.height);
5166 break;
5168 case WM_SETFOCUS:
5169 // If previous focused window isn't ours, it must have received the
5170 // redirected message. So, we should forget it.
5171 if (!WinUtils::IsOurProcessWindow(HWND(wParam))) {
5172 ForgetRedirectedKeyDownMessage();
5174 if (sJustGotActivate) {
5175 DispatchFocusToTopLevelWindow(true);
5177 break;
5179 case WM_KILLFOCUS:
5180 if (sJustGotDeactivate) {
5181 DispatchFocusToTopLevelWindow(false);
5183 break;
5185 case WM_WINDOWPOSCHANGED:
5187 WINDOWPOS *wp = (LPWINDOWPOS)lParam;
5188 OnWindowPosChanged(wp, result);
5190 break;
5192 case WM_INPUTLANGCHANGEREQUEST:
5193 *aRetValue = TRUE;
5194 result = false;
5195 break;
5197 case WM_INPUTLANGCHANGE:
5198 result = OnInputLangChange((HKL)lParam);
5199 break;
5201 case WM_DESTROYCLIPBOARD:
5203 nsIClipboard* clipboard;
5204 nsresult rv = CallGetService(kCClipboardCID, &clipboard);
5205 if(NS_SUCCEEDED(rv)) {
5206 clipboard->EmptyClipboard(nsIClipboard::kGlobalClipboard);
5207 NS_RELEASE(clipboard);
5210 break;
5212 #ifdef ACCESSIBILITY
5213 case WM_GETOBJECT:
5215 *aRetValue = 0;
5216 // Do explicit casting to make it working on 64bit systems (see bug 649236
5217 // for details).
5218 DWORD objId = static_cast<DWORD>(lParam);
5219 if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
5220 a11y::Accessible* rootAccessible = GetRootAccessible(); // Held by a11y cache
5221 if (rootAccessible) {
5222 IAccessible *msaaAccessible = NULL;
5223 rootAccessible->GetNativeInterface((void**)&msaaAccessible); // does an addref
5224 if (msaaAccessible) {
5225 *aRetValue = LresultFromObject(IID_IAccessible, wParam, msaaAccessible); // does an addref
5226 msaaAccessible->Release(); // release extra addref
5227 result = true; // We handled the WM_GETOBJECT message
5232 #endif
5234 case WM_SYSCOMMAND:
5236 WPARAM filteredWParam = (wParam &0xFFF0);
5237 // prevent Windows from trimming the working set. bug 76831
5238 if (!sTrimOnMinimize && filteredWParam == SC_MINIMIZE) {
5239 ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
5240 result = true;
5243 // Handle the system menu manually when we're in full screen mode
5244 // so we can set the appropriate options.
5245 if (filteredWParam == SC_KEYMENU && lParam == VK_SPACE &&
5246 mSizeMode == nsSizeMode_Fullscreen) {
5247 DisplaySystemMenu(mWnd, mSizeMode, mIsRTL,
5248 MOZ_SYSCONTEXT_X_POS,
5249 MOZ_SYSCONTEXT_Y_POS);
5250 result = true;
5253 break;
5255 case WM_DWMCOMPOSITIONCHANGED:
5256 // First, update the compositor state to latest one. All other methods
5257 // should use same state as here for consistency painting.
5258 nsUXThemeData::CheckForCompositor(true);
5260 UpdateNonClientMargins();
5261 RemovePropW(mWnd, kManageWindowInfoProperty);
5262 BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
5263 NotifyThemeChanged();
5264 UpdateGlass();
5265 Invalidate(true, true, true);
5266 break;
5268 case WM_UPDATEUISTATE:
5270 // If the UI state has changed, fire an event so the UI updates the
5271 // keyboard cues based on the system setting and how the window was
5272 // opened. For example, a dialog opened via a keyboard press on a button
5273 // should enable cues, whereas the same dialog opened via a mouse click of
5274 // the button should not.
5275 int32_t action = LOWORD(wParam);
5276 if (action == UIS_SET || action == UIS_CLEAR) {
5277 int32_t flags = HIWORD(wParam);
5278 UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
5279 UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
5280 if (flags & UISF_HIDEACCEL)
5281 showAccelerators = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
5282 if (flags & UISF_HIDEFOCUS)
5283 showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
5284 NotifyUIStateChanged(showAccelerators, showFocusRings);
5287 break;
5290 /* Gesture support events */
5291 case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
5292 // According to MS samples, this must be handled to enable
5293 // rotational support in multi-touch drivers.
5294 result = true;
5295 *aRetValue = TABLET_ROTATE_GESTURE_ENABLE;
5296 break;
5298 case WM_TOUCH:
5299 result = OnTouch(wParam, lParam);
5300 if (result) {
5301 *aRetValue = 0;
5303 break;
5305 case WM_GESTURE:
5306 result = OnGesture(wParam, lParam);
5307 break;
5309 case WM_GESTURENOTIFY:
5311 if (mWindowType != eWindowType_invisible &&
5312 mWindowType != eWindowType_plugin) {
5313 // A GestureNotify event is dispatched to decide which single-finger panning
5314 // direction should be active (including none) and if pan feedback should
5315 // be displayed. Java and plugin windows can make their own calls.
5316 GESTURENOTIFYSTRUCT * gestureinfo = (GESTURENOTIFYSTRUCT*)lParam;
5317 nsPointWin touchPoint;
5318 touchPoint = gestureinfo->ptsLocation;
5319 touchPoint.ScreenToClient(mWnd);
5320 nsGestureNotifyEvent gestureNotifyEvent(true, NS_GESTURENOTIFY_EVENT_START, this);
5321 gestureNotifyEvent.refPoint = touchPoint;
5322 nsEventStatus status;
5323 DispatchEvent(&gestureNotifyEvent, status);
5324 mDisplayPanFeedback = gestureNotifyEvent.displayPanFeedback;
5325 if (!mTouchWindow)
5326 mGesture.SetWinGestureSupport(mWnd, gestureNotifyEvent.panDirection);
5328 result = false; //should always bubble to DefWindowProc
5330 break;
5332 case WM_CLEAR:
5334 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_DELETE, this);
5335 DispatchWindowEvent(&command);
5336 result = true;
5338 break;
5340 case WM_CUT:
5342 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_CUT, this);
5343 DispatchWindowEvent(&command);
5344 result = true;
5346 break;
5348 case WM_COPY:
5350 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_COPY, this);
5351 DispatchWindowEvent(&command);
5352 result = true;
5354 break;
5356 case WM_PASTE:
5358 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_PASTE, this);
5359 DispatchWindowEvent(&command);
5360 result = true;
5362 break;
5364 case EM_UNDO:
5366 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_UNDO, this);
5367 DispatchWindowEvent(&command);
5368 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5369 result = true;
5371 break;
5373 case EM_REDO:
5375 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_REDO, this);
5376 DispatchWindowEvent(&command);
5377 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5378 result = true;
5380 break;
5382 case EM_CANPASTE:
5384 // Support EM_CANPASTE message only when wParam isn't specified or
5385 // is plain text format.
5386 if (wParam == 0 || wParam == CF_TEXT || wParam == CF_UNICODETEXT) {
5387 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_PASTE,
5388 this, true);
5389 DispatchWindowEvent(&command);
5390 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5391 result = true;
5394 break;
5396 case EM_CANUNDO:
5398 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_UNDO,
5399 this, true);
5400 DispatchWindowEvent(&command);
5401 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5402 result = true;
5404 break;
5406 case EM_CANREDO:
5408 nsContentCommandEvent command(true, NS_CONTENT_COMMAND_REDO,
5409 this, true);
5410 DispatchWindowEvent(&command);
5411 *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
5412 result = true;
5414 break;
5416 default:
5418 if (msg == nsAppShell::GetTaskbarButtonCreatedMessage())
5419 SetHasTaskbarIconBeenCreated();
5420 if (msg == sOOPPPluginFocusEvent) {
5421 if (wParam == 1) {
5422 // With OOPP, the plugin window exists in another process and is a child of
5423 // this window. This window is a placeholder plugin window for the dom. We
5424 // receive this event when the child window receives focus. (sent from
5425 // PluginInstanceParent.cpp)
5426 ::SendMessage(mWnd, WM_MOUSEACTIVATE, 0, 0); // See nsPluginNativeWindowWin.cpp
5427 } else {
5428 // WM_KILLFOCUS was received by the child process.
5429 if (sJustGotDeactivate) {
5430 DispatchFocusToTopLevelWindow(false);
5435 break;
5438 //*aRetValue = result;
5439 if (mWnd) {
5440 return result;
5442 else {
5443 //Events which caused mWnd destruction and aren't consumed
5444 //will crash during the Windows default processing.
5445 return true;
5449 /**************************************************************
5451 * SECTION: Broadcast messaging
5453 * Broadcast messages to all windows.
5455 **************************************************************/
5457 // Enumerate all child windows sending aMsg to each of them
5458 BOOL CALLBACK nsWindow::BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg)
5460 WNDPROC winProc = (WNDPROC)::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
5461 if (winProc == &nsWindow::WindowProc) {
5462 // it's one of our windows so go ahead and send a message to it
5463 ::CallWindowProcW(winProc, aWnd, aMsg, 0, 0);
5465 return TRUE;
5468 // Enumerate all top level windows specifying that the children of each
5469 // top level window should be enumerated. Do *not* send the message to
5470 // each top level window since it is assumed that the toolkit will send
5471 // aMsg to them directly.
5472 BOOL CALLBACK nsWindow::BroadcastMsg(HWND aTopWindow, LPARAM aMsg)
5474 // Iterate each of aTopWindows child windows sending the aMsg
5475 // to each of them.
5476 ::EnumChildWindows(aTopWindow, nsWindow::BroadcastMsgToChildren, aMsg);
5477 return TRUE;
5480 /**************************************************************
5482 * SECTION: Event processing helpers
5484 * Special processing for certain event types and
5485 * synthesized events.
5487 **************************************************************/
5489 int32_t
5490 nsWindow::ClientMarginHitTestPoint(int32_t mx, int32_t my)
5492 if (mSizeMode == nsSizeMode_Minimized ||
5493 mSizeMode == nsSizeMode_Fullscreen) {
5494 return HTCLIENT;
5497 // Calculations are done in screen coords
5498 RECT winRect;
5499 GetWindowRect(mWnd, &winRect);
5501 // hit return constants:
5502 // HTBORDER - non-resizable border
5503 // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
5504 // HTBOTTOMLEFT, HTBOTTOMRIGHT - resizable corner
5505 // HTTOPLEFT, HTTOPRIGHT - resizable corner
5506 // HTCAPTION - general title bar area
5507 // HTCLIENT - area considered the client
5508 // HTCLOSE - hovering over the close button
5509 // HTMAXBUTTON - maximize button
5510 // HTMINBUTTON - minimize button
5512 int32_t testResult = HTCLIENT;
5514 bool isResizable = (mBorderStyle & (eBorderStyle_all |
5515 eBorderStyle_resizeh |
5516 eBorderStyle_default)) > 0 ? true : false;
5517 if (mSizeMode == nsSizeMode_Maximized)
5518 isResizable = false;
5520 // Ensure being accessible to borders of window. Even if contents are in
5521 // this area, the area must behave as border.
5522 nsIntMargin nonClientSize(std::max(mCaptionHeight - mNonClientOffset.top,
5523 kResizableBorderMinSize),
5524 std::max(mHorResizeMargin - mNonClientOffset.right,
5525 kResizableBorderMinSize),
5526 std::max(mVertResizeMargin - mNonClientOffset.bottom,
5527 kResizableBorderMinSize),
5528 std::max(mHorResizeMargin - mNonClientOffset.left,
5529 kResizableBorderMinSize));
5531 bool allowContentOverride = mSizeMode == nsSizeMode_Maximized ||
5532 (mx >= winRect.left + nonClientSize.left &&
5533 mx <= winRect.right - nonClientSize.right &&
5534 my >= winRect.top + nonClientSize.top &&
5535 my <= winRect.bottom - nonClientSize.bottom);
5537 // The border size. If there is no content under mouse cursor, the border
5538 // size should be larger than the values in system settings. Otherwise,
5539 // contents under the mouse cursor should be able to override the behavior.
5540 // E.g., user must expect that Firefox button always opens the popup menu
5541 // even when the user clicks on the above edge of it.
5542 nsIntMargin borderSize(std::max(nonClientSize.top, mVertResizeMargin),
5543 std::max(nonClientSize.right, mHorResizeMargin),
5544 std::max(nonClientSize.bottom, mVertResizeMargin),
5545 std::max(nonClientSize.left, mHorResizeMargin));
5547 bool top = false;
5548 bool bottom = false;
5549 bool left = false;
5550 bool right = false;
5552 if (my >= winRect.top && my < winRect.top + borderSize.top) {
5553 top = true;
5554 } else if (my <= winRect.bottom && my > winRect.bottom - borderSize.bottom) {
5555 bottom = true;
5558 // (the 2x case here doubles the resize area for corners)
5559 int multiplier = (top || bottom) ? 2 : 1;
5560 if (mx >= winRect.left &&
5561 mx < winRect.left + (multiplier * borderSize.left)) {
5562 left = true;
5563 } else if (mx <= winRect.right &&
5564 mx > winRect.right - (multiplier * borderSize.right)) {
5565 right = true;
5568 if (isResizable) {
5569 if (top) {
5570 testResult = HTTOP;
5571 if (left)
5572 testResult = HTTOPLEFT;
5573 else if (right)
5574 testResult = HTTOPRIGHT;
5575 } else if (bottom) {
5576 testResult = HTBOTTOM;
5577 if (left)
5578 testResult = HTBOTTOMLEFT;
5579 else if (right)
5580 testResult = HTBOTTOMRIGHT;
5581 } else {
5582 if (left)
5583 testResult = HTLEFT;
5584 if (right)
5585 testResult = HTRIGHT;
5587 } else {
5588 if (top)
5589 testResult = HTCAPTION;
5590 else if (bottom || left || right)
5591 testResult = HTBORDER;
5594 if (!sIsInMouseCapture && allowContentOverride) {
5595 LPARAM lParam = MAKELPARAM(mx, my);
5596 LPARAM lParamClient = lParamToClient(lParam);
5597 bool result = DispatchMouseEvent(NS_MOUSE_MOZHITTEST, 0, lParamClient,
5598 false, nsMouseEvent::eLeftButton, MOUSE_INPUT_SOURCE());
5599 if (result) {
5600 // The mouse is over a blank area
5601 testResult = testResult == HTCLIENT ? HTCAPTION : testResult;
5603 } else {
5604 // There's content over the mouse pointer. Set HTCLIENT
5605 // to possibly override a resizer border.
5606 testResult = HTCLIENT;
5610 return testResult;
5613 void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode)
5615 if (aIsSleepMode == gIsSleepMode)
5616 return;
5618 gIsSleepMode = aIsSleepMode;
5620 nsCOMPtr<nsIObserverService> observerService =
5621 mozilla::services::GetObserverService();
5622 if (observerService)
5623 observerService->NotifyObservers(nullptr,
5624 aIsSleepMode ? NS_WIDGET_SLEEP_OBSERVER_TOPIC :
5625 NS_WIDGET_WAKE_OBSERVER_TOPIC, nullptr);
5628 // RemoveNextCharMessage() should be called by WM_KEYDOWN or WM_SYSKEYDOWM
5629 // message handler. If there is no WM_(SYS)CHAR message for it, this
5630 // method does nothing.
5631 // NOTE: WM_(SYS)CHAR message is posted by TranslateMessage() API which is
5632 // called in message loop. So, WM_(SYS)KEYDOWN message should have
5633 // WM_(SYS)CHAR message in the queue if the keydown event causes character
5634 // input.
5636 /* static */
5637 void nsWindow::RemoveNextCharMessage(HWND aWnd)
5639 MSG msg;
5640 if (WinUtils::PeekMessage(&msg, aWnd, WM_KEYFIRST, WM_KEYLAST,
5641 PM_NOREMOVE | PM_NOYIELD) &&
5642 (msg.message == WM_CHAR || msg.message == WM_SYSCHAR)) {
5643 WinUtils::GetMessage(&msg, aWnd, msg.message, msg.message);
5647 LRESULT nsWindow::ProcessCharMessage(const MSG &aMsg, bool *aEventDispatched)
5649 NS_PRECONDITION(aMsg.message == WM_CHAR || aMsg.message == WM_SYSCHAR,
5650 "message is not keydown event");
5651 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
5652 ("%s charCode=%d scanCode=%d\n",
5653 aMsg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
5654 aMsg.wParam, HIWORD(aMsg.lParam) & 0xFF));
5656 // These must be checked here too as a lone WM_CHAR could be received
5657 // if a child window didn't handle it (for example Alt+Space in a content window)
5658 ModifierKeyState modKeyState;
5659 NativeKey nativeKey(gKbdLayout, this, aMsg);
5660 gKbdLayout.InitNativeKey(nativeKey, modKeyState);
5661 return OnChar(aMsg, nativeKey, modKeyState, aEventDispatched);
5664 LRESULT nsWindow::ProcessKeyUpMessage(const MSG &aMsg, bool *aEventDispatched)
5666 NS_PRECONDITION(aMsg.message == WM_KEYUP || aMsg.message == WM_SYSKEYUP,
5667 "message is not keydown event");
5668 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
5669 ("%s VK=%d\n", aMsg.message == WM_SYSKEYDOWN ?
5670 "WM_SYSKEYUP" : "WM_KEYUP", aMsg.wParam));
5672 ModifierKeyState modKeyState;
5674 // Note: the original code passed (HIWORD(lParam)) to OnKeyUp as
5675 // scan code. However, this breaks Alt+Num pad input.
5676 // MSDN states the following:
5677 // Typically, ToAscii performs the translation based on the
5678 // virtual-key code. In some cases, however, bit 15 of the
5679 // uScanCode parameter may be used to distinguish between a key
5680 // press and a key release. The scan code is used for
5681 // translating ALT+number key combinations.
5683 // ignore [shift+]alt+space so the OS can handle it
5684 if (modKeyState.IsAlt() && !modKeyState.IsControl() &&
5685 IS_VK_DOWN(NS_VK_SPACE)) {
5686 return FALSE;
5689 if (!IMEHandler::IsComposingOn(this)) {
5690 return OnKeyUp(aMsg, modKeyState, aEventDispatched);
5693 return 0;
5696 LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg,
5697 bool *aEventDispatched)
5699 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
5700 ("%s VK=%d\n", aMsg.message == WM_SYSKEYDOWN ?
5701 "WM_SYSKEYDOWN" : "WM_KEYDOWN", aMsg.wParam));
5702 NS_PRECONDITION(aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN,
5703 "message is not keydown event");
5705 // If this method doesn't call OnKeyDown(), this method must clean up the
5706 // redirected message information itself. For more information, see above
5707 // comment of AutoForgetRedirectedKeyDownMessage struct definition in
5708 // nsWindow.h.
5709 AutoForgetRedirectedKeyDownMessage forgetRedirectedMessage(this, aMsg);
5711 ModifierKeyState modKeyState;
5713 // Note: the original code passed (HIWORD(lParam)) to OnKeyDown as
5714 // scan code. However, this breaks Alt+Num pad input.
5715 // MSDN states the following:
5716 // Typically, ToAscii performs the translation based on the
5717 // virtual-key code. In some cases, however, bit 15 of the
5718 // uScanCode parameter may be used to distinguish between a key
5719 // press and a key release. The scan code is used for
5720 // translating ALT+number key combinations.
5722 // ignore [shift+]alt+space so the OS can handle it
5723 if (modKeyState.IsAlt() && !modKeyState.IsControl() &&
5724 IS_VK_DOWN(NS_VK_SPACE))
5725 return FALSE;
5727 LRESULT result = 0;
5728 if (!IMEHandler::IsComposingOn(this)) {
5729 result = OnKeyDown(aMsg, modKeyState, aEventDispatched, nullptr);
5730 // OnKeyDown cleaned up the redirected message information itself, so,
5731 // we should do nothing.
5732 forgetRedirectedMessage.mCancel = true;
5735 if (aMsg.wParam == VK_MENU ||
5736 (aMsg.wParam == VK_F10 && !modKeyState.IsShift())) {
5737 // We need to let Windows handle this keypress,
5738 // by returning false, if there's a native menu
5739 // bar somewhere in our containing window hierarchy.
5740 // Otherwise we handle the keypress and don't pass
5741 // it on to Windows, by returning true.
5742 bool hasNativeMenu = false;
5743 HWND hWnd = mWnd;
5744 while (hWnd) {
5745 if (::GetMenu(hWnd)) {
5746 hasNativeMenu = true;
5747 break;
5749 hWnd = ::GetParent(hWnd);
5751 result = !hasNativeMenu;
5754 return result;
5757 nsresult
5758 nsWindow::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
5759 int32_t aNativeKeyCode,
5760 uint32_t aModifierFlags,
5761 const nsAString& aCharacters,
5762 const nsAString& aUnmodifiedCharacters)
5764 UINT keyboardLayoutListCount = ::GetKeyboardLayoutList(0, NULL);
5765 NS_ASSERTION(keyboardLayoutListCount > 0,
5766 "One keyboard layout must be installed at least");
5767 HKL keyboardLayoutListBuff[50];
5768 HKL* keyboardLayoutList =
5769 keyboardLayoutListCount < 50 ? keyboardLayoutListBuff :
5770 new HKL[keyboardLayoutListCount];
5771 keyboardLayoutListCount =
5772 ::GetKeyboardLayoutList(keyboardLayoutListCount, keyboardLayoutList);
5773 NS_ASSERTION(keyboardLayoutListCount > 0,
5774 "Failed to get all keyboard layouts installed on the system");
5776 nsPrintfCString layoutName("%08x", aNativeKeyboardLayout);
5777 HKL loadedLayout = LoadKeyboardLayoutA(layoutName.get(), KLF_NOTELLSHELL);
5778 if (loadedLayout == NULL) {
5779 if (keyboardLayoutListBuff != keyboardLayoutList) {
5780 delete [] keyboardLayoutList;
5782 return NS_ERROR_NOT_AVAILABLE;
5785 // Setup clean key state and load desired layout
5786 BYTE originalKbdState[256];
5787 ::GetKeyboardState(originalKbdState);
5788 BYTE kbdState[256];
5789 memset(kbdState, 0, sizeof(kbdState));
5790 // This changes the state of the keyboard for the current thread only,
5791 // and we'll restore it soon, so this should be OK.
5792 ::SetKeyboardState(kbdState);
5793 HKL oldLayout = gKbdLayout.GetLayout();
5794 gKbdLayout.LoadLayout(loadedLayout);
5796 uint8_t argumentKeySpecific = 0;
5797 switch (aNativeKeyCode) {
5798 case VK_SHIFT:
5799 aModifierFlags &= ~(nsIWidget::SHIFT_L | nsIWidget::SHIFT_R);
5800 argumentKeySpecific = VK_LSHIFT;
5801 break;
5802 case VK_LSHIFT:
5803 aModifierFlags &= ~nsIWidget::SHIFT_L;
5804 argumentKeySpecific = aNativeKeyCode;
5805 aNativeKeyCode = VK_SHIFT;
5806 break;
5807 case VK_RSHIFT:
5808 aModifierFlags &= ~nsIWidget::SHIFT_R;
5809 argumentKeySpecific = aNativeKeyCode;
5810 aNativeKeyCode = VK_SHIFT;
5811 break;
5812 case VK_CONTROL:
5813 aModifierFlags &= ~(nsIWidget::CTRL_L | nsIWidget::CTRL_R);
5814 argumentKeySpecific = VK_LCONTROL;
5815 break;
5816 case VK_LCONTROL:
5817 aModifierFlags &= ~nsIWidget::CTRL_L;
5818 argumentKeySpecific = aNativeKeyCode;
5819 aNativeKeyCode = VK_CONTROL;
5820 break;
5821 case VK_RCONTROL:
5822 aModifierFlags &= ~nsIWidget::CTRL_R;
5823 argumentKeySpecific = aNativeKeyCode;
5824 aNativeKeyCode = VK_CONTROL;
5825 break;
5826 case VK_MENU:
5827 aModifierFlags &= ~(nsIWidget::ALT_L | nsIWidget::ALT_R);
5828 argumentKeySpecific = VK_LMENU;
5829 break;
5830 case VK_LMENU:
5831 aModifierFlags &= ~nsIWidget::ALT_L;
5832 argumentKeySpecific = aNativeKeyCode;
5833 aNativeKeyCode = VK_MENU;
5834 break;
5835 case VK_RMENU:
5836 aModifierFlags &= ~nsIWidget::ALT_R;
5837 argumentKeySpecific = aNativeKeyCode;
5838 aNativeKeyCode = VK_MENU;
5839 break;
5840 case VK_CAPITAL:
5841 aModifierFlags &= ~nsIWidget::CAPS_LOCK;
5842 argumentKeySpecific = VK_CAPITAL;
5843 break;
5844 case VK_NUMLOCK:
5845 aModifierFlags &= ~nsIWidget::NUM_LOCK;
5846 argumentKeySpecific = VK_NUMLOCK;
5847 break;
5850 nsAutoTArray<KeyPair,10> keySequence;
5851 SetupKeyModifiersSequence(&keySequence, aModifierFlags);
5852 NS_ASSERTION(aNativeKeyCode >= 0 && aNativeKeyCode < 256,
5853 "Native VK key code out of range");
5854 keySequence.AppendElement(KeyPair(aNativeKeyCode, argumentKeySpecific));
5856 // Simulate the pressing of each modifier key and then the real key
5857 for (uint32_t i = 0; i < keySequence.Length(); ++i) {
5858 uint8_t key = keySequence[i].mGeneral;
5859 uint8_t keySpecific = keySequence[i].mSpecific;
5860 kbdState[key] = 0x81; // key is down and toggled on if appropriate
5861 if (keySpecific) {
5862 kbdState[keySpecific] = 0x81;
5864 ::SetKeyboardState(kbdState);
5865 ModifierKeyState modKeyState;
5866 UINT scanCode = ::MapVirtualKeyEx(argumentKeySpecific ?
5867 argumentKeySpecific : aNativeKeyCode,
5868 MAPVK_VK_TO_VSC, gKbdLayout.GetLayout());
5869 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5870 // Add extended key flag to the lParam for right control key and right alt
5871 // key.
5872 if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
5873 lParam |= 0x1000000;
5875 MSG msg = WinUtils::InitMSG(WM_KEYDOWN, key, lParam);
5876 if (i == keySequence.Length() - 1) {
5877 bool makeDeadCharMessage =
5878 gKbdLayout.IsDeadKey(key, modKeyState) && aCharacters.IsEmpty();
5879 nsAutoString chars(aCharacters);
5880 if (makeDeadCharMessage) {
5881 UniCharsAndModifiers deadChars =
5882 gKbdLayout.GetUniCharsAndModifiers(key, modKeyState);
5883 chars = deadChars.ToString();
5884 NS_ASSERTION(chars.Length() == 1,
5885 "Dead char must be only one character");
5887 if (chars.IsEmpty()) {
5888 OnKeyDown(msg, modKeyState, nullptr, nullptr);
5889 } else {
5890 nsFakeCharMessage fakeMsg = { chars.CharAt(0), scanCode,
5891 makeDeadCharMessage };
5892 OnKeyDown(msg, modKeyState, nullptr, &fakeMsg);
5893 for (uint32_t j = 1; j < chars.Length(); j++) {
5894 nsFakeCharMessage fakeMsg = { chars.CharAt(j), scanCode, false };
5895 MSG msg = fakeMsg.GetCharMessage(mWnd);
5896 NativeKey nativeKey(gKbdLayout, this, msg);
5897 OnChar(msg, nativeKey, modKeyState, nullptr);
5900 } else {
5901 OnKeyDown(msg, modKeyState, nullptr, nullptr);
5904 for (uint32_t i = keySequence.Length(); i > 0; --i) {
5905 uint8_t key = keySequence[i - 1].mGeneral;
5906 uint8_t keySpecific = keySequence[i - 1].mSpecific;
5907 kbdState[key] = 0; // key is up and toggled off if appropriate
5908 if (keySpecific) {
5909 kbdState[keySpecific] = 0;
5911 ::SetKeyboardState(kbdState);
5912 ModifierKeyState modKeyState;
5913 UINT scanCode = ::MapVirtualKeyEx(argumentKeySpecific ?
5914 argumentKeySpecific : aNativeKeyCode,
5915 MAPVK_VK_TO_VSC, gKbdLayout.GetLayout());
5916 LPARAM lParam = static_cast<LPARAM>(scanCode << 16);
5917 // Add extended key flag to the lParam for right control key and right alt
5918 // key.
5919 if (keySpecific == VK_RCONTROL || keySpecific == VK_RMENU) {
5920 lParam |= 0x1000000;
5922 MSG msg = WinUtils::InitMSG(WM_KEYUP, key, lParam);
5923 OnKeyUp(msg, modKeyState, nullptr);
5926 // Restore old key state and layout
5927 ::SetKeyboardState(originalKbdState);
5928 gKbdLayout.LoadLayout(oldLayout, true);
5930 // Don't unload the layout if it's installed actually.
5931 for (uint32_t i = 0; i < keyboardLayoutListCount; i++) {
5932 if (keyboardLayoutList[i] == loadedLayout) {
5933 loadedLayout = 0;
5934 break;
5937 if (keyboardLayoutListBuff != keyboardLayoutList) {
5938 delete [] keyboardLayoutList;
5940 if (loadedLayout) {
5941 ::UnloadKeyboardLayout(loadedLayout);
5943 return NS_OK;
5946 nsresult
5947 nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
5948 uint32_t aNativeMessage,
5949 uint32_t aModifierFlags)
5951 ::SetCursorPos(aPoint.x, aPoint.y);
5953 INPUT input;
5954 memset(&input, 0, sizeof(input));
5956 input.type = INPUT_MOUSE;
5957 input.mi.dwFlags = aNativeMessage;
5958 ::SendInput(1, &input, sizeof(INPUT));
5960 return NS_OK;
5963 nsresult
5964 nsWindow::SynthesizeNativeMouseScrollEvent(nsIntPoint aPoint,
5965 uint32_t aNativeMessage,
5966 double aDeltaX,
5967 double aDeltaY,
5968 double aDeltaZ,
5969 uint32_t aModifierFlags,
5970 uint32_t aAdditionalFlags)
5972 return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
5973 this, aPoint, aNativeMessage,
5974 (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL) ?
5975 static_cast<int32_t>(aDeltaY) : static_cast<int32_t>(aDeltaX),
5976 aModifierFlags, aAdditionalFlags);
5979 /**************************************************************
5981 * SECTION: OnXXX message handlers
5983 * For message handlers that need to be broken out or
5984 * implemented in specific platform code.
5986 **************************************************************/
5988 BOOL nsWindow::OnInputLangChange(HKL aHKL)
5990 #ifdef KE_DEBUG
5991 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("OnInputLanguageChange\n"));
5992 #endif
5993 gKbdLayout.LoadLayout(aHKL);
5994 return false; // always pass to child window
5997 void nsWindow::OnWindowPosChanged(WINDOWPOS *wp, bool& result)
5999 if (wp == nullptr)
6000 return;
6002 #ifdef WINSTATE_DEBUG_OUTPUT
6003 if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
6004 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("*** OnWindowPosChanged: [ top] "));
6005 } else {
6006 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("*** OnWindowPosChanged: [child] "));
6008 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("WINDOWPOS flags:"));
6009 if (wp->flags & SWP_FRAMECHANGED) {
6010 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_FRAMECHANGED "));
6012 if (wp->flags & SWP_SHOWWINDOW) {
6013 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_SHOWWINDOW "));
6015 if (wp->flags & SWP_NOSIZE) {
6016 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_NOSIZE "));
6018 if (wp->flags & SWP_HIDEWINDOW) {
6019 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_HIDEWINDOW "));
6021 if (wp->flags & SWP_NOZORDER) {
6022 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_NOZORDER "));
6024 if (wp->flags & SWP_NOACTIVATE) {
6025 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("SWP_NOACTIVATE "));
6027 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("\n"));
6028 #endif
6030 // Handle window size mode changes
6031 if (wp->flags & SWP_FRAMECHANGED && mSizeMode != nsSizeMode_Fullscreen) {
6033 // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
6034 // windows when fullscreen games disable desktop composition. If we're
6035 // minimized and not being activated, ignore the event and let windows
6036 // handle it.
6037 if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
6038 return;
6040 WINDOWPLACEMENT pl;
6041 pl.length = sizeof(pl);
6042 ::GetWindowPlacement(mWnd, &pl);
6044 // Windows has just changed the size mode of this window. The call to
6045 // SizeModeChanged will trigger a call into SetSizeMode where we will
6046 // set the min/max window state again or for nsSizeMode_Normal, call
6047 // SetWindow with a parameter of SW_RESTORE. There's no need however as
6048 // this window's mode has already changed. Updating mSizeMode here
6049 // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
6050 // to window docking. (bug 489258)
6051 if (pl.showCmd == SW_SHOWMAXIMIZED)
6052 mSizeMode = (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6053 else if (pl.showCmd == SW_SHOWMINIMIZED)
6054 mSizeMode = nsSizeMode_Minimized;
6055 else if (mFullscreenMode)
6056 mSizeMode = nsSizeMode_Fullscreen;
6057 else
6058 mSizeMode = nsSizeMode_Normal;
6060 // If !sTrimOnMinimize, we minimize windows using SW_SHOWMINIMIZED (See
6061 // SetSizeMode for internal calls, and WM_SYSCOMMAND for external). This
6062 // prevents the working set from being trimmed but keeps the window active.
6063 // After the window is minimized, we need to do some touch up work on the
6064 // active window. (bugs 76831 & 499816)
6065 if (!sTrimOnMinimize && nsSizeMode_Minimized == mSizeMode)
6066 ActivateOtherWindowHelper(mWnd);
6068 #ifdef WINSTATE_DEBUG_OUTPUT
6069 switch (mSizeMode) {
6070 case nsSizeMode_Normal:
6071 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6072 ("*** mSizeMode: nsSizeMode_Normal\n"));
6073 break;
6074 case nsSizeMode_Minimized:
6075 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6076 ("*** mSizeMode: nsSizeMode_Minimized\n"));
6077 break;
6078 case nsSizeMode_Maximized:
6079 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6080 ("*** mSizeMode: nsSizeMode_Maximized\n");
6081 break;
6082 default:
6083 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("*** mSizeMode: ??????\n");
6084 break;
6086 #endif
6088 if (mWidgetListener)
6089 mWidgetListener->SizeModeChanged(mSizeMode);
6091 // If window was restored, window activation was bypassed during the
6092 // SetSizeMode call originating from OnWindowPosChanging to avoid saving
6093 // pre-restore attributes. Force activation now to get correct attributes.
6094 if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
6095 DispatchFocusToTopLevelWindow(true);
6097 // Skip window size change events below on minimization.
6098 if (mSizeMode == nsSizeMode_Minimized)
6099 return;
6102 // Handle window size changes
6103 if (!(wp->flags & SWP_NOSIZE)) {
6104 RECT r;
6105 int32_t newWidth, newHeight;
6107 ::GetWindowRect(mWnd, &r);
6109 newWidth = r.right - r.left;
6110 newHeight = r.bottom - r.top;
6111 nsIntRect rect(wp->x, wp->y, newWidth, newHeight);
6113 #ifdef MOZ_XUL
6114 if (eTransparencyTransparent == mTransparencyMode)
6115 ResizeTranslucentWindow(newWidth, newHeight);
6116 #endif
6118 if (newWidth > mLastSize.width)
6120 RECT drect;
6122 // getting wider
6123 drect.left = wp->x + mLastSize.width;
6124 drect.top = wp->y;
6125 drect.right = drect.left + (newWidth - mLastSize.width);
6126 drect.bottom = drect.top + newHeight;
6128 ::RedrawWindow(mWnd, &drect, NULL,
6129 RDW_INVALIDATE |
6130 RDW_NOERASE |
6131 RDW_NOINTERNALPAINT |
6132 RDW_ERASENOW |
6133 RDW_ALLCHILDREN);
6135 if (newHeight > mLastSize.height)
6137 RECT drect;
6139 // getting taller
6140 drect.left = wp->x;
6141 drect.top = wp->y + mLastSize.height;
6142 drect.right = drect.left + newWidth;
6143 drect.bottom = drect.top + (newHeight - mLastSize.height);
6145 ::RedrawWindow(mWnd, &drect, NULL,
6146 RDW_INVALIDATE |
6147 RDW_NOERASE |
6148 RDW_NOINTERNALPAINT |
6149 RDW_ERASENOW |
6150 RDW_ALLCHILDREN);
6153 mBounds.width = newWidth;
6154 mBounds.height = newHeight;
6155 mLastSize.width = newWidth;
6156 mLastSize.height = newHeight;
6158 #ifdef WINSTATE_DEBUG_OUTPUT
6159 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6160 ("*** Resize window: %d x %d x %d x %d\n", wp->x, wp->y,
6161 newWidth, newHeight));
6162 #endif
6164 // If a maximized window is resized, recalculate the non-client margins.
6165 if (mSizeMode == nsSizeMode_Maximized) {
6166 if (UpdateNonClientMargins(nsSizeMode_Maximized, true)) {
6167 // gecko resize event already sent by UpdateNonClientMargins.
6168 result = true;
6169 return;
6173 // Recalculate the width and height based on the client area for gecko events.
6174 if (::GetClientRect(mWnd, &r)) {
6175 rect.width = r.right - r.left;
6176 rect.height = r.bottom - r.top;
6179 // Send a gecko resize event
6180 result = OnResize(rect);
6184 // static
6185 void nsWindow::ActivateOtherWindowHelper(HWND aWnd)
6187 // Find the next window that is enabled, visible, and not minimized.
6188 HWND hwndBelow = ::GetNextWindow(aWnd, GW_HWNDNEXT);
6189 while (hwndBelow && (!::IsWindowEnabled(hwndBelow) || !::IsWindowVisible(hwndBelow) ||
6190 ::IsIconic(hwndBelow))) {
6191 hwndBelow = ::GetNextWindow(hwndBelow, GW_HWNDNEXT);
6194 // Push ourselves to the bottom of the stack, then activate the
6195 // next window.
6196 ::SetWindowPos(aWnd, HWND_BOTTOM, 0, 0, 0, 0,
6197 SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
6198 if (hwndBelow)
6199 ::SetForegroundWindow(hwndBelow);
6201 // Play the minimize sound while we're here, since that is also
6202 // forgotten when we use SW_SHOWMINIMIZED.
6203 nsCOMPtr<nsISound> sound(do_CreateInstance("@mozilla.org/sound;1"));
6204 if (sound) {
6205 sound->PlaySystemSound(NS_LITERAL_STRING("Minimize"));
6209 void nsWindow::OnWindowPosChanging(LPWINDOWPOS& info)
6211 // Update non-client margins if the frame size is changing, and let the
6212 // browser know we are changing size modes, so alternative css can kick in.
6213 // If we're going into fullscreen mode, ignore this, since it'll reset
6214 // margins to normal mode.
6215 if ((info->flags & SWP_FRAMECHANGED && !(info->flags & SWP_NOSIZE)) &&
6216 mSizeMode != nsSizeMode_Fullscreen) {
6217 WINDOWPLACEMENT pl;
6218 pl.length = sizeof(pl);
6219 ::GetWindowPlacement(mWnd, &pl);
6220 nsSizeMode sizeMode;
6221 if (pl.showCmd == SW_SHOWMAXIMIZED)
6222 sizeMode = (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
6223 else if (pl.showCmd == SW_SHOWMINIMIZED)
6224 sizeMode = nsSizeMode_Minimized;
6225 else if (mFullscreenMode)
6226 sizeMode = nsSizeMode_Fullscreen;
6227 else
6228 sizeMode = nsSizeMode_Normal;
6230 if (mWidgetListener)
6231 mWidgetListener->SizeModeChanged(sizeMode);
6233 UpdateNonClientMargins(sizeMode, false);
6236 // enforce local z-order rules
6237 if (!(info->flags & SWP_NOZORDER)) {
6238 HWND hwndAfter = info->hwndInsertAfter;
6240 nsWindow *aboveWindow = 0;
6241 nsWindowZ placement;
6243 if (hwndAfter == HWND_BOTTOM)
6244 placement = nsWindowZBottom;
6245 else if (hwndAfter == HWND_TOP || hwndAfter == HWND_TOPMOST || hwndAfter == HWND_NOTOPMOST)
6246 placement = nsWindowZTop;
6247 else {
6248 placement = nsWindowZRelative;
6249 aboveWindow = WinUtils::GetNSWindowPtr(hwndAfter);
6252 if (mWidgetListener) {
6253 nsCOMPtr<nsIWidget> actualBelow = nullptr;
6254 if (mWidgetListener->ZLevelChanged(false, &placement,
6255 aboveWindow, getter_AddRefs(actualBelow))) {
6256 if (placement == nsWindowZBottom)
6257 info->hwndInsertAfter = HWND_BOTTOM;
6258 else if (placement == nsWindowZTop)
6259 info->hwndInsertAfter = HWND_TOP;
6260 else {
6261 info->hwndInsertAfter = (HWND)actualBelow->GetNativeData(NS_NATIVE_WINDOW);
6266 // prevent rude external programs from making hidden window visible
6267 if (mWindowType == eWindowType_invisible)
6268 info->flags &= ~SWP_SHOWWINDOW;
6271 void nsWindow::UserActivity()
6273 // Check if we have the idle service, if not we try to get it.
6274 if (!mIdleService) {
6275 mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
6278 // Check that we now have the idle service.
6279 if (mIdleService) {
6280 mIdleService->ResetIdleTimeOut(0);
6284 bool nsWindow::OnTouch(WPARAM wParam, LPARAM lParam)
6286 uint32_t cInputs = LOWORD(wParam);
6287 PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
6289 if (mGesture.GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs)) {
6290 nsTouchEvent* touchEventToSend = nullptr;
6291 nsTouchEvent* touchEndEventToSend = nullptr;
6292 nsEventStatus status;
6294 // Walk across the touch point array processing each contact point
6295 for (uint32_t i = 0; i < cInputs; i++) {
6296 uint32_t msg;
6298 if (pInputs[i].dwFlags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE)) {
6299 // Create a standard touch event to send
6300 if (!touchEventToSend) {
6301 touchEventToSend = new nsTouchEvent(true, NS_TOUCH_MOVE, this);
6302 touchEventToSend->time = ::GetMessageTime();
6303 ModifierKeyState modifierKeyState;
6304 modifierKeyState.InitInputEvent(*touchEventToSend);
6307 // Pres shell expects this event to be a NS_TOUCH_START if new contact
6308 // points have been added since the last event sent.
6309 if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) {
6310 touchEventToSend->message = msg = NS_TOUCH_START;
6311 } else {
6312 msg = NS_TOUCH_MOVE;
6314 } else if (pInputs[i].dwFlags & TOUCHEVENTF_UP) {
6315 // Pres shell expects removed contacts points to be delivered in a
6316 // separate NS_TOUCH_END event containing only the contact points
6317 // that were removed.
6318 if (!touchEndEventToSend) {
6319 touchEndEventToSend = new nsTouchEvent(true, NS_TOUCH_END, this);
6320 touchEndEventToSend->time = ::GetMessageTime();
6321 ModifierKeyState modifierKeyState;
6322 modifierKeyState.InitInputEvent(*touchEndEventToSend);
6324 msg = NS_TOUCH_END;
6325 } else {
6326 // Filter out spurious Windows events we don't understand, like palm
6327 // contact.
6328 continue;
6331 // Setup the touch point we'll append to the touch event array
6332 nsPointWin touchPoint;
6333 touchPoint.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
6334 touchPoint.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
6335 touchPoint.ScreenToClient(mWnd);
6336 nsCOMPtr<nsIDOMTouch> touch =
6337 new Touch(pInputs[i].dwID,
6338 touchPoint,
6339 /* radius, if known */
6340 pInputs[i].dwFlags & TOUCHINPUTMASKF_CONTACTAREA ?
6341 nsIntPoint(
6342 TOUCH_COORD_TO_PIXEL(pInputs[i].cxContact) / 2,
6343 TOUCH_COORD_TO_PIXEL(pInputs[i].cyContact) / 2) :
6344 nsIntPoint(1,1),
6345 /* rotation angle and force */
6346 0.0f, 0.0f);
6348 // Append to the appropriate event
6349 if (msg == NS_TOUCH_START || msg == NS_TOUCH_MOVE) {
6350 touchEventToSend->touches.AppendElement(touch);
6351 } else {
6352 touchEndEventToSend->touches.AppendElement(touch);
6356 // Dispatch touch start and move event if we have one.
6357 if (touchEventToSend) {
6358 DispatchEvent(touchEventToSend, status);
6359 delete touchEventToSend;
6362 // Dispatch touch end event if we have one.
6363 if (touchEndEventToSend) {
6364 DispatchEvent(touchEndEventToSend, status);
6365 delete touchEndEventToSend;
6369 delete [] pInputs;
6370 mGesture.CloseTouchInputHandle((HTOUCHINPUT)lParam);
6371 return true;
6374 static int32_t RoundDown(double aDouble)
6376 return aDouble > 0 ? static_cast<int32_t>(floor(aDouble)) :
6377 static_cast<int32_t>(ceil(aDouble));
6380 // Gesture event processing. Handles WM_GESTURE events.
6381 bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam)
6383 // Treatment for pan events which translate into scroll events:
6384 if (mGesture.IsPanEvent(lParam)) {
6385 if ( !mGesture.ProcessPanMessage(mWnd, wParam, lParam) )
6386 return false; // ignore
6388 nsEventStatus status;
6390 WheelEvent wheelEvent(true, NS_WHEEL_WHEEL, this);
6392 ModifierKeyState modifierKeyState;
6393 modifierKeyState.InitInputEvent(wheelEvent);
6395 wheelEvent.button = 0;
6396 wheelEvent.time = ::GetMessageTime();
6397 wheelEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
6399 bool endFeedback = true;
6401 if (mGesture.PanDeltaToPixelScroll(wheelEvent)) {
6402 DispatchEvent(&wheelEvent, status);
6405 if (mDisplayPanFeedback) {
6406 mGesture.UpdatePanFeedbackX(mWnd,
6407 DeprecatedAbs(RoundDown(wheelEvent.overflowDeltaX)),
6408 endFeedback);
6409 mGesture.UpdatePanFeedbackY(mWnd,
6410 DeprecatedAbs(RoundDown(wheelEvent.overflowDeltaY)),
6411 endFeedback);
6412 mGesture.PanFeedbackFinalize(mWnd, endFeedback);
6415 mGesture.CloseGestureInfoHandle((HGESTUREINFO)lParam);
6417 return true;
6420 // Other gestures translate into simple gesture events:
6421 nsSimpleGestureEvent event(true, 0, this, 0, 0.0);
6422 if ( !mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event) ) {
6423 return false; // fall through to DefWndProc
6426 // Polish up and send off the new event
6427 ModifierKeyState modifierKeyState;
6428 modifierKeyState.InitInputEvent(event);
6429 event.button = 0;
6430 event.time = ::GetMessageTime();
6431 event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
6433 nsEventStatus status;
6434 DispatchEvent(&event, status);
6435 if (status == nsEventStatus_eIgnore) {
6436 return false; // Ignored, fall through
6439 // Only close this if we process and return true.
6440 mGesture.CloseGestureInfoHandle((HGESTUREINFO)lParam);
6442 return true; // Handled
6445 /* static */
6446 bool nsWindow::IsRedirectedKeyDownMessage(const MSG &aMsg)
6448 return (aMsg.message == WM_KEYDOWN || aMsg.message == WM_SYSKEYDOWN) &&
6449 (sRedirectedKeyDown.message == aMsg.message &&
6450 WinUtils::GetScanCode(sRedirectedKeyDown.lParam) ==
6451 WinUtils::GetScanCode(aMsg.lParam));
6455 * nsWindow::OnKeyDown peeks into the message queue and pulls out
6456 * WM_CHAR messages for processing. During testing we don't want to
6457 * mess with the real message queue. Instead we pass a
6458 * pseudo-WM_CHAR-message using this structure, and OnKeyDown will use
6459 * that as if it was in the message queue, and refrain from actually
6460 * looking at or touching the message queue.
6462 LRESULT nsWindow::OnKeyDown(const MSG &aMsg,
6463 const ModifierKeyState &aModKeyState,
6464 bool *aEventDispatched,
6465 nsFakeCharMessage* aFakeCharMessage)
6467 NativeKey nativeKey(gKbdLayout, this, aMsg);
6468 gKbdLayout.InitNativeKey(nativeKey, aModKeyState);
6469 UniCharsAndModifiers inputtingChars =
6470 nativeKey.GetCommittedCharsAndModifiers();
6471 uint32_t DOMKeyCode = nativeKey.GetDOMKeyCode();
6473 #ifdef DEBUG
6474 //PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("In OnKeyDown virt: %d\n", DOMKeyCode));
6475 #endif
6477 static bool sRedirectedKeyDownEventPreventedDefault = false;
6478 bool noDefault;
6479 if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
6480 bool isIMEEnabled = IMEHandler::IsIMEEnabled(mInputContext);
6481 nsKeyEvent keydownEvent(true, NS_KEY_DOWN, this);
6482 keydownEvent.keyCode = DOMKeyCode;
6483 InitKeyEvent(keydownEvent, nativeKey, aModKeyState);
6484 noDefault = DispatchKeyEvent(keydownEvent, &aMsg);
6485 if (aEventDispatched) {
6486 *aEventDispatched = true;
6489 // If IMC wasn't associated to the window but is associated it now (i.e.,
6490 // focus is moved from a non-editable editor to an editor by keydown
6491 // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
6492 // inputting if IME is opened. But then, we should redirect the native
6493 // keydown message to IME.
6494 // However, note that if focus has been already moved to another
6495 // application, we shouldn't redirect the message to it because the keydown
6496 // message is processed by us, so, nobody shouldn't process it.
6497 HWND focusedWnd = ::GetFocus();
6498 if (!noDefault && !aFakeCharMessage && focusedWnd && !PluginHasFocus() &&
6499 !isIMEEnabled && IMEHandler::IsIMEEnabled(mInputContext)) {
6500 RemoveNextCharMessage(focusedWnd);
6502 INPUT keyinput;
6503 keyinput.type = INPUT_KEYBOARD;
6504 keyinput.ki.wVk = aMsg.wParam;
6505 keyinput.ki.wScan = WinUtils::GetScanCode(aMsg.lParam);
6506 keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
6507 if (WinUtils::IsExtendedScanCode(aMsg.lParam)) {
6508 keyinput.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
6510 keyinput.ki.time = 0;
6511 keyinput.ki.dwExtraInfo = 0;
6513 sRedirectedKeyDownEventPreventedDefault = noDefault;
6514 sRedirectedKeyDown = aMsg;
6516 ::SendInput(1, &keyinput, sizeof(keyinput));
6518 // Return here. We shouldn't dispatch keypress event for this WM_KEYDOWN.
6519 // If it's needed, it will be dispatched after next (redirected)
6520 // WM_KEYDOWN.
6521 return true;
6524 if (mOnDestroyCalled) {
6525 // If this was destroyed by the keydown event handler, we shouldn't
6526 // dispatch keypress event on this window.
6527 return true;
6529 } else {
6530 noDefault = sRedirectedKeyDownEventPreventedDefault;
6531 // If this is redirected keydown message, we have dispatched the keydown
6532 // event already.
6533 if (aEventDispatched) {
6534 *aEventDispatched = true;
6538 ForgetRedirectedKeyDownMessage();
6540 // If the key was processed by IME, we shouldn't dispatch keypress event.
6541 if (aMsg.wParam == VK_PROCESSKEY) {
6542 return noDefault;
6545 // If we won't be getting a WM_CHAR, WM_SYSCHAR or WM_DEADCHAR, synthesize a keypress
6546 // for almost all keys
6547 switch (DOMKeyCode) {
6548 case NS_VK_SHIFT:
6549 case NS_VK_CONTROL:
6550 case NS_VK_ALT:
6551 case NS_VK_CAPS_LOCK:
6552 case NS_VK_NUM_LOCK:
6553 case NS_VK_SCROLL_LOCK:
6554 case NS_VK_WIN:
6555 return noDefault;
6558 UINT virtualKeyCode = nativeKey.GetOriginalVirtualKeyCode();
6559 bool isDeadKey = gKbdLayout.IsDeadKey(virtualKeyCode, aModKeyState);
6560 EventFlags extraFlags;
6561 extraFlags.mDefaultPrevented = noDefault;
6562 MSG msg;
6563 BOOL gotMsg = aFakeCharMessage ||
6564 WinUtils::PeekMessage(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST,
6565 PM_NOREMOVE | PM_NOYIELD);
6566 // Enter and backspace are always handled here to avoid for example the
6567 // confusion between ctrl-enter and ctrl-J.
6568 if (DOMKeyCode == NS_VK_RETURN || DOMKeyCode == NS_VK_BACK ||
6569 ((aModKeyState.IsControl() || aModKeyState.IsAlt() || aModKeyState.IsWin())
6570 && !isDeadKey && KeyboardLayout::IsPrintableCharKey(virtualKeyCode)))
6572 // Remove a possible WM_CHAR or WM_SYSCHAR messages from the message queue.
6573 // They can be more than one because of:
6574 // * Dead-keys not pairing with base character
6575 // * Some keyboard layouts may map up to 4 characters to the single key
6576 bool anyCharMessagesRemoved = false;
6578 if (aFakeCharMessage) {
6579 RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST,
6580 aFakeCharMessage);
6581 anyCharMessagesRemoved = true;
6582 } else {
6583 while (gotMsg && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR))
6585 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6586 ("%s charCode=%d scanCode=%d\n", msg.message == WM_SYSCHAR ?
6587 "WM_SYSCHAR" : "WM_CHAR",
6588 msg.wParam, HIWORD(msg.lParam) & 0xFF));
6589 RemoveMessageAndDispatchPluginEvent(WM_KEYFIRST, WM_KEYLAST);
6590 anyCharMessagesRemoved = true;
6592 gotMsg = WinUtils::PeekMessage(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST,
6593 PM_NOREMOVE | PM_NOYIELD);
6597 if (!anyCharMessagesRemoved && DOMKeyCode == NS_VK_BACK &&
6598 IMEHandler::IsDoingKakuteiUndo(mWnd)) {
6599 NS_ASSERTION(!aFakeCharMessage,
6600 "We shouldn't be touching the real msg queue");
6601 RemoveMessageAndDispatchPluginEvent(WM_CHAR, WM_CHAR);
6604 else if (gotMsg &&
6605 (aFakeCharMessage ||
6606 msg.message == WM_CHAR || msg.message == WM_SYSCHAR || msg.message == WM_DEADCHAR)) {
6607 if (aFakeCharMessage) {
6608 MSG msg = aFakeCharMessage->GetCharMessage(mWnd);
6609 if (msg.message == WM_DEADCHAR) {
6610 return false;
6612 #ifdef DEBUG
6613 if (KeyboardLayout::IsPrintableCharKey(virtualKeyCode)) {
6614 nsPrintfCString log(
6615 "virtualKeyCode=0x%02X, inputtingChar={ mChars=[ 0x%04X, 0x%04X, "
6616 "0x%04X, 0x%04X, 0x%04X ], mLength=%d }, wParam=0x%04X",
6617 virtualKeyCode, inputtingChars.mChars[0], inputtingChars.mChars[1],
6618 inputtingChars.mChars[2], inputtingChars.mChars[3],
6619 inputtingChars.mChars[4], inputtingChars.mLength, msg.wParam);
6620 if (!inputtingChars.mLength) {
6621 log.Insert("length is zero: ", 0);
6622 NS_ERROR(log.get());
6623 NS_ABORT();
6624 } else if (inputtingChars.mChars[0] != msg.wParam) {
6625 log.Insert("character mismatch: ", 0);
6626 NS_ERROR(log.get());
6627 NS_ABORT();
6630 #endif // #ifdef DEBUG
6631 return OnChar(msg, nativeKey, aModKeyState, nullptr, &extraFlags);
6634 // If prevent default set for keydown, do same for keypress
6635 WinUtils::GetMessage(&msg, mWnd, msg.message, msg.message);
6637 if (msg.message == WM_DEADCHAR) {
6638 if (!PluginHasFocus())
6639 return false;
6641 // We need to send the removed message to focused plug-in.
6642 DispatchPluginEvent(msg);
6643 return noDefault;
6646 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6647 ("%s charCode=%d scanCode=%d\n",
6648 msg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
6649 msg.wParam, HIWORD(msg.lParam) & 0xFF));
6651 BOOL result = OnChar(msg, nativeKey, aModKeyState, nullptr, &extraFlags);
6652 // If a syschar keypress wasn't processed, Windows may want to
6653 // handle it to activate a native menu.
6654 if (!result && msg.message == WM_SYSCHAR)
6655 ::DefWindowProcW(mWnd, msg.message, msg.wParam, msg.lParam);
6656 return result;
6658 else if (!aModKeyState.IsControl() && !aModKeyState.IsAlt() &&
6659 !aModKeyState.IsWin() &&
6660 KeyboardLayout::IsPrintableCharKey(virtualKeyCode)) {
6661 // If this is simple KeyDown event but next message is not WM_CHAR,
6662 // this event may not input text, so we should ignore this event.
6663 // See bug 314130.
6664 return PluginHasFocus() && noDefault;
6667 if (isDeadKey) {
6668 return PluginHasFocus() && noDefault;
6671 UniCharsAndModifiers shiftedChars;
6672 UniCharsAndModifiers unshiftedChars;
6673 uint32_t shiftedLatinChar = 0;
6674 uint32_t unshiftedLatinChar = 0;
6676 if (!KeyboardLayout::IsPrintableCharKey(virtualKeyCode)) {
6677 inputtingChars.Clear();
6680 if (aModKeyState.IsControl() ^ aModKeyState.IsAlt()) {
6681 widget::ModifierKeyState capsLockState(
6682 aModKeyState.GetModifiers() & MODIFIER_CAPSLOCK);
6683 unshiftedChars =
6684 gKbdLayout.GetUniCharsAndModifiers(virtualKeyCode, capsLockState);
6685 capsLockState.Set(MODIFIER_SHIFT);
6686 shiftedChars =
6687 gKbdLayout.GetUniCharsAndModifiers(virtualKeyCode, capsLockState);
6689 // The current keyboard cannot input alphabets or numerics,
6690 // we should append them for Shortcut/Access keys.
6691 // E.g., for Cyrillic keyboard layout.
6692 capsLockState.Unset(MODIFIER_SHIFT);
6693 WidgetUtils::GetLatinCharCodeForKeyCode(DOMKeyCode,
6694 capsLockState.GetModifiers(),
6695 &unshiftedLatinChar,
6696 &shiftedLatinChar);
6698 // If the shiftedLatinChar isn't 0, the key code is NS_VK_[A-Z].
6699 if (shiftedLatinChar) {
6700 // If the produced characters of the key on current keyboard layout
6701 // are same as computed Latin characters, we shouldn't append the
6702 // Latin characters to alternativeCharCode.
6703 if (unshiftedLatinChar == unshiftedChars.mChars[0] &&
6704 shiftedLatinChar == shiftedChars.mChars[0]) {
6705 shiftedLatinChar = unshiftedLatinChar = 0;
6707 } else if (unshiftedLatinChar) {
6708 // If the shiftedLatinChar is 0, the keyCode doesn't produce
6709 // alphabet character. At that time, the character may be produced
6710 // with Shift key. E.g., on French keyboard layout, NS_VK_PERCENT
6711 // key produces LATIN SMALL LETTER U WITH GRAVE (U+00F9) without
6712 // Shift key but with Shift key, it produces '%'.
6713 // If the unshiftedLatinChar is produced by the key on current
6714 // keyboard layout, we shouldn't append it to alternativeCharCode.
6715 if (unshiftedLatinChar == unshiftedChars.mChars[0] ||
6716 unshiftedLatinChar == shiftedChars.mChars[0]) {
6717 unshiftedLatinChar = 0;
6721 // If the charCode is not ASCII character, we should replace the
6722 // charCode with ASCII character only when Ctrl is pressed.
6723 // But don't replace the charCode when the charCode is not same as
6724 // unmodified characters. In such case, Ctrl is sometimes used for a
6725 // part of character inputting key combination like Shift.
6726 if (aModKeyState.IsControl()) {
6727 uint32_t ch =
6728 aModKeyState.IsShift() ? shiftedLatinChar : unshiftedLatinChar;
6729 if (ch &&
6730 (!inputtingChars.mLength ||
6731 inputtingChars.UniCharsCaseInsensitiveEqual(
6732 aModKeyState.IsShift() ? shiftedChars : unshiftedChars))) {
6733 inputtingChars.Clear();
6734 inputtingChars.Append(ch, aModKeyState.GetModifiers());
6739 if (inputtingChars.mLength ||
6740 shiftedChars.mLength || unshiftedChars.mLength) {
6741 uint32_t num = std::max(inputtingChars.mLength,
6742 std::max(shiftedChars.mLength, unshiftedChars.mLength));
6743 uint32_t skipUniChars = num - inputtingChars.mLength;
6744 uint32_t skipShiftedChars = num - shiftedChars.mLength;
6745 uint32_t skipUnshiftedChars = num - unshiftedChars.mLength;
6746 UINT keyCode = !inputtingChars.mLength ? DOMKeyCode : 0;
6747 for (uint32_t cnt = 0; cnt < num; cnt++) {
6748 uint16_t uniChar, shiftedChar, unshiftedChar;
6749 uniChar = shiftedChar = unshiftedChar = 0;
6750 ModifierKeyState modKeyState(aModKeyState);
6751 if (skipUniChars <= cnt) {
6752 if (cnt - skipUniChars < inputtingChars.mLength) {
6753 // If key in combination with Alt and/or Ctrl produces a different
6754 // character than without them then do not report these flags
6755 // because it is separate keyboard layout shift state. If dead-key
6756 // and base character does not produce a valid composite character
6757 // then both produced dead-key character and following base
6758 // character may have different modifier flags, too.
6759 modKeyState.Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT |
6760 MODIFIER_ALTGRAPH | MODIFIER_CAPSLOCK);
6761 modKeyState.Set(inputtingChars.mModifiers[cnt - skipUniChars]);
6763 uniChar = inputtingChars.mChars[cnt - skipUniChars];
6765 if (skipShiftedChars <= cnt)
6766 shiftedChar = shiftedChars.mChars[cnt - skipShiftedChars];
6767 if (skipUnshiftedChars <= cnt)
6768 unshiftedChar = unshiftedChars.mChars[cnt - skipUnshiftedChars];
6769 nsAutoTArray<nsAlternativeCharCode, 5> altArray;
6771 if (shiftedChar || unshiftedChar) {
6772 nsAlternativeCharCode chars(unshiftedChar, shiftedChar);
6773 altArray.AppendElement(chars);
6775 if (cnt == num - 1) {
6776 if (unshiftedLatinChar || shiftedLatinChar) {
6777 nsAlternativeCharCode chars(unshiftedLatinChar, shiftedLatinChar);
6778 altArray.AppendElement(chars);
6781 // Typically, following virtual keycodes are used for a key which can
6782 // input the character. However, these keycodes are also used for
6783 // other keys on some keyboard layout. E.g., in spite of Shift+'1'
6784 // inputs '+' on Thai keyboard layout, a key which is at '=/+'
6785 // key on ANSI keyboard layout is VK_OEM_PLUS. Native applications
6786 // handle it as '+' key if Ctrl key is pressed.
6787 PRUnichar charForOEMKeyCode = 0;
6788 switch (virtualKeyCode) {
6789 case VK_OEM_PLUS: charForOEMKeyCode = '+'; break;
6790 case VK_OEM_COMMA: charForOEMKeyCode = ','; break;
6791 case VK_OEM_MINUS: charForOEMKeyCode = '-'; break;
6792 case VK_OEM_PERIOD: charForOEMKeyCode = '.'; break;
6794 if (charForOEMKeyCode &&
6795 charForOEMKeyCode != unshiftedChars.mChars[0] &&
6796 charForOEMKeyCode != shiftedChars.mChars[0] &&
6797 charForOEMKeyCode != unshiftedLatinChar &&
6798 charForOEMKeyCode != shiftedLatinChar) {
6799 nsAlternativeCharCode OEMChars(charForOEMKeyCode, charForOEMKeyCode);
6800 altArray.AppendElement(OEMChars);
6804 nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
6805 keypressEvent.mFlags.Union(extraFlags);
6806 keypressEvent.charCode = uniChar;
6807 keypressEvent.alternativeCharCodes.AppendElements(altArray);
6808 InitKeyEvent(keypressEvent, nativeKey, modKeyState);
6809 DispatchKeyEvent(keypressEvent, nullptr);
6811 } else {
6812 nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
6813 keypressEvent.mFlags.Union(extraFlags);
6814 keypressEvent.keyCode = DOMKeyCode;
6815 InitKeyEvent(keypressEvent, nativeKey, aModKeyState);
6816 DispatchKeyEvent(keypressEvent, nullptr);
6819 return noDefault;
6822 // OnKeyUp
6823 LRESULT nsWindow::OnKeyUp(const MSG &aMsg,
6824 const ModifierKeyState &aModKeyState,
6825 bool *aEventDispatched)
6827 // NOTE: VK_PROCESSKEY never comes with WM_KEYUP
6828 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
6829 ("nsWindow::OnKeyUp wParam(VK)=%d\n", aMsg.wParam));
6831 if (aEventDispatched)
6832 *aEventDispatched = true;
6833 nsKeyEvent keyupEvent(true, NS_KEY_UP, this);
6834 NativeKey nativeKey(gKbdLayout, this, aMsg);
6835 gKbdLayout.InitNativeKey(nativeKey, aModKeyState);
6836 keyupEvent.keyCode = nativeKey.GetDOMKeyCode();
6837 InitKeyEvent(keyupEvent, nativeKey, aModKeyState);
6838 // Set defaultPrevented of the key event if the VK_MENU is not a system key
6839 // release, so that the menu bar does not trigger. This helps avoid
6840 // triggering the menu bar for ALT key accelerators used in assistive
6841 // technologies such as Window-Eyes and ZoomText or for switching open state
6842 // of IME.
6843 keyupEvent.mFlags.mDefaultPrevented =
6844 (aMsg.wParam == VK_MENU && aMsg.message != WM_SYSKEYUP);
6845 return DispatchKeyEvent(keyupEvent, &aMsg);
6848 // OnChar
6849 LRESULT nsWindow::OnChar(const MSG &aMsg,
6850 const NativeKey& aNativeKey,
6851 const ModifierKeyState &aModKeyState,
6852 bool *aEventDispatched,
6853 const EventFlags *aExtraFlags)
6855 // ignore [shift+]alt+space so the OS can handle it
6856 if (aModKeyState.IsAlt() && !aModKeyState.IsControl() &&
6857 IS_VK_DOWN(NS_VK_SPACE)) {
6858 return FALSE;
6861 uint32_t charCode = aMsg.wParam;
6862 // Ignore Ctrl+Enter (bug 318235)
6863 if (aModKeyState.IsControl() && charCode == 0xA) {
6864 return FALSE;
6867 // WM_CHAR with Control and Alt (== AltGr) down really means a normal character
6868 ModifierKeyState modKeyState(aModKeyState);
6869 if (modKeyState.IsAlt() && modKeyState.IsControl()) {
6870 modKeyState.Unset(MODIFIER_ALT | MODIFIER_CONTROL);
6873 if (IMEHandler::IsComposingOn(this)) {
6874 IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION);
6877 wchar_t uniChar;
6878 // Ctrl+A Ctrl+Z, see Programming Windows 3.1 page 110 for details
6879 if (modKeyState.IsControl() && charCode <= 0x1A) {
6880 // need to account for shift here. bug 16486
6881 if (modKeyState.IsShift()) {
6882 uniChar = charCode - 1 + 'A';
6883 } else {
6884 uniChar = charCode - 1 + 'a';
6886 } else if (modKeyState.IsControl() && charCode <= 0x1F) {
6887 // Fix for 50255 - <ctrl><[> and <ctrl><]> are not being processed.
6888 // also fixes ctrl+\ (x1c), ctrl+^ (x1e) and ctrl+_ (x1f)
6889 // for some reason the keypress handler need to have the uniChar code set
6890 // with the addition of a upper case A not the lower case.
6891 uniChar = charCode - 1 + 'A';
6892 } else { // 0x20 - SPACE, 0x3D - EQUALS
6893 if (charCode < 0x20 || (charCode == 0x3D && modKeyState.IsControl())) {
6894 uniChar = 0;
6895 } else {
6896 uniChar = charCode;
6900 // Keep the characters unshifted for shortcuts and accesskeys and make sure
6901 // that numbers are always passed as such (among others: bugs 50255 and 351310)
6902 if (uniChar && (modKeyState.IsControl() || modKeyState.IsAlt())) {
6903 UINT virtualKeyCode = ::MapVirtualKeyEx(aNativeKey.GetScanCode(),
6904 MAPVK_VSC_TO_VK,
6905 gKbdLayout.GetLayout());
6906 UINT unshiftedCharCode =
6907 virtualKeyCode >= '0' && virtualKeyCode <= '9' ? virtualKeyCode :
6908 modKeyState.IsShift() ? ::MapVirtualKeyEx(virtualKeyCode,
6909 MAPVK_VK_TO_CHAR,
6910 gKbdLayout.GetLayout()) : 0;
6911 // ignore diacritics (top bit set) and key mapping errors (char code 0)
6912 if ((INT)unshiftedCharCode > 0)
6913 uniChar = unshiftedCharCode;
6916 // Fix for bug 285161 (and 295095) which was caused by the initial fix for bug 178110.
6917 // When pressing (alt|ctrl)+char, the char must be lowercase unless shift is
6918 // pressed too.
6919 if (!modKeyState.IsShift() &&
6920 (aModKeyState.IsAlt() || aModKeyState.IsControl())) {
6921 uniChar = towlower(uniChar);
6924 nsKeyEvent keypressEvent(true, NS_KEY_PRESS, this);
6925 if (aExtraFlags) {
6926 keypressEvent.mFlags.Union(*aExtraFlags);
6928 keypressEvent.charCode = uniChar;
6929 if (!keypressEvent.charCode) {
6930 keypressEvent.keyCode = aNativeKey.GetDOMKeyCode();
6932 InitKeyEvent(keypressEvent, aNativeKey, modKeyState);
6933 bool result = DispatchKeyEvent(keypressEvent, &aMsg);
6934 if (aEventDispatched)
6935 *aEventDispatched = true;
6936 return result;
6939 void
6940 nsWindow::SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray, uint32_t aModifiers)
6942 for (uint32_t i = 0; i < ArrayLength(sModifierKeyMap); ++i) {
6943 const uint32_t* map = sModifierKeyMap[i];
6944 if (aModifiers & map[0]) {
6945 aArray->AppendElement(KeyPair(map[1], map[2]));
6950 static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam)
6952 *((HWND*)lParam) = hwnd;
6953 return FALSE;
6956 static void InvalidatePluginAsWorkaround(nsWindow *aWindow, const nsIntRect &aRect)
6958 aWindow->Invalidate(aRect);
6960 // XXX - Even more evil workaround!! See bug 762948, flash's bottom
6961 // level sandboxed window doesn't seem to get our invalidate. We send
6962 // an invalidate to it manually. This is totally specialized for this
6963 // bug, for other child window structures this will just be a more or
6964 // less bogus invalidate but since that should not have any bad
6965 // side-effects this will have to do for now.
6966 HWND current = (HWND)aWindow->GetNativeData(NS_NATIVE_WINDOW);
6968 RECT windowRect;
6969 RECT parentRect;
6971 ::GetWindowRect(current, &parentRect);
6973 HWND next = current;
6975 do {
6976 current = next;
6978 ::EnumChildWindows(current, &EnumFirstChild, (LPARAM)&next);
6980 ::GetWindowRect(next, &windowRect);
6981 // This is relative to the screen, adjust it to be relative to the
6982 // window we're reconfiguring.
6983 windowRect.left -= parentRect.left;
6984 windowRect.top -= parentRect.top;
6985 } while (next != current && windowRect.top == 0 && windowRect.left == 0);
6987 if (windowRect.top == 0 && windowRect.left == 0) {
6988 RECT rect;
6989 rect.left = aRect.x;
6990 rect.top = aRect.y;
6991 rect.right = aRect.XMost();
6992 rect.bottom = aRect.YMost();
6994 ::InvalidateRect(next, &rect, FALSE);
6998 nsresult
6999 nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
7001 // XXXroc we could use BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
7002 // here, if that helps in some situations. So far I haven't seen a
7003 // need.
7004 for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
7005 const Configuration& configuration = aConfigurations[i];
7006 nsWindow* w = static_cast<nsWindow*>(configuration.mChild);
7007 NS_ASSERTION(w->GetParent() == this,
7008 "Configured widget is not a child");
7009 nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true);
7010 NS_ENSURE_SUCCESS(rv, rv);
7011 nsIntRect bounds;
7012 w->GetBounds(bounds);
7013 if (bounds.Size() != configuration.mBounds.Size()) {
7014 w->Resize(configuration.mBounds.x, configuration.mBounds.y,
7015 configuration.mBounds.width, configuration.mBounds.height,
7016 true);
7017 } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
7018 w->Move(configuration.mBounds.x, configuration.mBounds.y);
7021 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
7022 gfxWindowsPlatform::RENDER_DIRECT2D ||
7023 GetLayerManager()->GetBackendType() != LAYERS_BASIC) {
7024 // XXX - Workaround for Bug 587508. This will invalidate the part of the
7025 // plugin window that might be touched by moving content somehow. The
7026 // underlying problem should be found and fixed!
7027 nsIntRegion r;
7028 r.Sub(bounds, configuration.mBounds);
7029 r.MoveBy(-bounds.x,
7030 -bounds.y);
7031 nsIntRect toInvalidate = r.GetBounds();
7033 InvalidatePluginAsWorkaround(w, toInvalidate);
7036 rv = w->SetWindowClipRegion(configuration.mClipRegion, false);
7037 NS_ENSURE_SUCCESS(rv, rv);
7039 return NS_OK;
7042 static HRGN
7043 CreateHRGNFromArray(const nsTArray<nsIntRect>& aRects)
7045 int32_t size = sizeof(RGNDATAHEADER) + sizeof(RECT)*aRects.Length();
7046 nsAutoTArray<uint8_t,100> buf;
7047 if (!buf.SetLength(size))
7048 return NULL;
7049 RGNDATA* data = reinterpret_cast<RGNDATA*>(buf.Elements());
7050 RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
7051 data->rdh.dwSize = sizeof(data->rdh);
7052 data->rdh.iType = RDH_RECTANGLES;
7053 data->rdh.nCount = aRects.Length();
7054 nsIntRect bounds;
7055 for (uint32_t i = 0; i < aRects.Length(); ++i) {
7056 const nsIntRect& r = aRects[i];
7057 bounds.UnionRect(bounds, r);
7058 ::SetRect(&rects[i], r.x, r.y, r.XMost(), r.YMost());
7060 ::SetRect(&data->rdh.rcBound, bounds.x, bounds.y, bounds.XMost(), bounds.YMost());
7061 return ::ExtCreateRegion(NULL, buf.Length(), data);
7064 static void
7065 ArrayFromRegion(const nsIntRegion& aRegion, nsTArray<nsIntRect>& aRects)
7067 const nsIntRect* r;
7068 for (nsIntRegionRectIterator iter(aRegion); (r = iter.Next());) {
7069 aRects.AppendElement(*r);
7073 nsresult
7074 nsWindow::SetWindowClipRegion(const nsTArray<nsIntRect>& aRects,
7075 bool aIntersectWithExisting)
7077 if (!aIntersectWithExisting) {
7078 if (!StoreWindowClipRegion(aRects))
7079 return NS_OK;
7080 } else {
7081 // In this case still early return if nothing changed.
7082 if (mClipRects && mClipRectCount == aRects.Length() &&
7083 memcmp(mClipRects,
7084 aRects.Elements(),
7085 sizeof(nsIntRect)*mClipRectCount) == 0) {
7086 return NS_OK;
7089 // get current rects
7090 nsTArray<nsIntRect> currentRects;
7091 GetWindowClipRegion(&currentRects);
7092 // create region from them
7093 nsIntRegion currentRegion = RegionFromArray(currentRects);
7094 // create region from new rects
7095 nsIntRegion newRegion = RegionFromArray(aRects);
7096 // intersect regions
7097 nsIntRegion intersection;
7098 intersection.And(currentRegion, newRegion);
7099 // create int rect array from intersection
7100 nsTArray<nsIntRect> rects;
7101 ArrayFromRegion(intersection, rects);
7102 // store
7103 if (!StoreWindowClipRegion(rects))
7104 return NS_OK;
7107 HRGN dest = CreateHRGNFromArray(aRects);
7108 if (!dest)
7109 return NS_ERROR_OUT_OF_MEMORY;
7111 if (aIntersectWithExisting) {
7112 HRGN current = ::CreateRectRgn(0, 0, 0, 0);
7113 if (current) {
7114 if (::GetWindowRgn(mWnd, current) != 0 /*ERROR*/) {
7115 ::CombineRgn(dest, dest, current, RGN_AND);
7117 ::DeleteObject(current);
7121 // If a plugin is not visible, especially if it is in a background tab,
7122 // it should not be able to steal keyboard focus. This code checks whether
7123 // the region that the plugin is being clipped to is NULLREGION. If it is,
7124 // the plugin window gets disabled.
7125 if(mWindowType == eWindowType_plugin) {
7126 if(NULLREGION == ::CombineRgn(dest, dest, dest, RGN_OR)) {
7127 ::ShowWindow(mWnd, SW_HIDE);
7128 ::EnableWindow(mWnd, FALSE);
7129 } else {
7130 ::EnableWindow(mWnd, TRUE);
7131 ::ShowWindow(mWnd, SW_SHOW);
7134 if (!::SetWindowRgn(mWnd, dest, TRUE)) {
7135 ::DeleteObject(dest);
7136 return NS_ERROR_FAILURE;
7138 return NS_OK;
7141 // WM_DESTROY event handler
7142 void nsWindow::OnDestroy()
7144 mOnDestroyCalled = true;
7146 // Make sure we don't get destroyed in the process of tearing down.
7147 nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
7149 // Dispatch the destroy notification.
7150 if (!mInDtor)
7151 NotifyWindowDestroyed();
7153 // Prevent the widget from sending additional events.
7154 mWidgetListener = nullptr;
7155 mAttachedWidgetListener = nullptr;
7157 // Free our subclass and clear |this| stored in the window props. We will no longer
7158 // receive events from Windows after this point.
7159 SubclassWindow(FALSE);
7161 // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow can be
7162 // cleared. (It's used in tracking windows for mouse events.)
7163 if (sCurrentWindow == this)
7164 sCurrentWindow = nullptr;
7166 // Disconnects us from our parent, will call our GetParent().
7167 nsBaseWidget::Destroy();
7169 // Release references to children, device context, toolkit, and app shell.
7170 nsBaseWidget::OnDestroy();
7172 // Clear our native parent handle.
7173 // XXX Windows will take care of this in the proper order, and SetParent(nullptr)'s
7174 // remove child on the parent already took place in nsBaseWidget's Destroy call above.
7175 //SetParent(nullptr);
7176 mParent = nullptr;
7178 // We have to destroy the native drag target before we null out our window pointer.
7179 EnableDragDrop(false);
7181 // If we're going away and for some reason we're still the rollup widget, rollup and
7182 // turn off capture.
7183 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
7184 nsCOMPtr<nsIWidget> rollupWidget;
7185 if (rollupListener) {
7186 rollupWidget = rollupListener->GetRollupWidget();
7188 if (this == rollupWidget) {
7189 if ( rollupListener )
7190 rollupListener->Rollup(0, nullptr);
7191 CaptureRollupEvents(nullptr, false);
7194 IMEHandler::OnDestroyWindow(this);
7196 // Turn off mouse trails if enabled.
7197 MouseTrailer* mtrailer = nsToolkit::gMouseTrailer;
7198 if (mtrailer) {
7199 if (mtrailer->GetMouseTrailerWindow() == mWnd)
7200 mtrailer->DestroyTimer();
7202 if (mtrailer->GetCaptureWindow() == mWnd)
7203 mtrailer->SetCaptureWindow(nullptr);
7206 // Free GDI window class objects
7207 if (mBrush) {
7208 VERIFY(::DeleteObject(mBrush));
7209 mBrush = NULL;
7213 // Destroy any custom cursor resources.
7214 if (mCursor == -1)
7215 SetCursor(eCursor_standard);
7217 #ifdef MOZ_XUL
7218 // Reset transparency
7219 if (eTransparencyTransparent == mTransparencyMode)
7220 SetupTranslucentWindowMemoryBitmap(eTransparencyOpaque);
7221 #endif
7223 // Finalize panning feedback to possibly restore window displacement
7224 mGesture.PanFeedbackFinalize(mWnd, true);
7226 // Clear the main HWND.
7227 mWnd = NULL;
7230 // OnMove
7231 bool nsWindow::OnMove(int32_t aX, int32_t aY)
7233 mBounds.x = aX;
7234 mBounds.y = aY;
7236 return mWidgetListener ? mWidgetListener->WindowMoved(this, aX, aY) : false;
7239 // Send a resize message to the listener
7240 bool nsWindow::OnResize(nsIntRect &aWindowRect)
7242 #ifdef CAIRO_HAS_D2D_SURFACE
7243 if (mD2DWindowSurface) {
7244 mD2DWindowSurface = NULL;
7245 Invalidate();
7247 #endif
7249 bool result = mWidgetListener ?
7250 mWidgetListener->WindowResized(this, aWindowRect.width, aWindowRect.height) : false;
7252 // If there is an attached view, inform it as well as the normal widget listener.
7253 if (mAttachedWidgetListener) {
7254 return mAttachedWidgetListener->WindowResized(this, aWindowRect.width, aWindowRect.height);
7257 return result;
7260 bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam)
7262 return true;
7265 // Can be overriden. Controls auto-erase of background.
7266 bool nsWindow::AutoErase(HDC dc)
7268 return false;
7271 void
7272 nsWindow::AllowD3D9Callback(nsWindow *aWindow)
7274 if (aWindow->mLayerManager && !aWindow->ShouldUseOffMainThreadCompositing()) {
7275 aWindow->mLayerManager->Destroy();
7276 aWindow->mLayerManager = NULL;
7280 void
7281 nsWindow::AllowD3D9WithReinitializeCallback(nsWindow *aWindow)
7283 if (aWindow->mLayerManager && !aWindow->ShouldUseOffMainThreadCompositing()) {
7284 aWindow->mLayerManager->Destroy();
7285 aWindow->mLayerManager = NULL;
7286 (void) aWindow->GetLayerManager();
7290 void
7291 nsWindow::StartAllowingD3D9(bool aReinitialize)
7293 sAllowD3D9 = true;
7295 LayerManagerPrefs prefs;
7296 GetLayerManagerPrefs(&prefs);
7297 if (prefs.mDisableAcceleration) {
7298 // The guarantee here is, if there's *any* chance that after we
7299 // throw out our layer managers we'd create at least one new,
7300 // accelerated one, we *will* throw out all the current layer
7301 // managers. We early-return here because currently, if
7302 // |disableAcceleration|, we will always use basic managers and
7303 // it's a waste to recreate them. If we're using OMTC we don't want to
7304 // recreate out layer manager and its compositor either. This is even
7305 // more wasteful.
7307 // NB: the above implies that it's eminently possible for us to
7308 // skip this early return but still recreate basic managers.
7309 // That's OK. It's *not* OK to take this early return when we
7310 // *might* have created an accelerated manager.
7311 return;
7314 if (aReinitialize) {
7315 EnumAllWindows(AllowD3D9WithReinitializeCallback);
7316 } else {
7317 EnumAllWindows(AllowD3D9Callback);
7321 bool
7322 nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
7323 if (sHasBogusPopupsDropShadowOnMultiMonitor == TRI_UNKNOWN) {
7324 // Since any change in the preferences requires a restart, this can be
7325 // done just once.
7326 // Check for Direct2D first.
7327 sHasBogusPopupsDropShadowOnMultiMonitor =
7328 gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
7329 gfxWindowsPlatform::RENDER_DIRECT2D ? TRI_TRUE : TRI_FALSE;
7330 if (!sHasBogusPopupsDropShadowOnMultiMonitor) {
7331 // Otherwise check if Direct3D 9 may be used.
7332 LayerManagerPrefs prefs;
7333 GetLayerManagerPrefs(&prefs);
7334 if (!prefs.mDisableAcceleration && !prefs.mPreferOpenGL) {
7335 nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
7336 if (gfxInfo) {
7337 int32_t status;
7338 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status))) {
7339 if (status == nsIGfxInfo::FEATURE_NO_INFO || prefs.mForceAcceleration)
7341 sHasBogusPopupsDropShadowOnMultiMonitor = TRI_TRUE;
7348 return !!sHasBogusPopupsDropShadowOnMultiMonitor;
7351 void
7352 nsWindow::OnSysColorChanged()
7354 if (mWindowType == eWindowType_invisible) {
7355 ::EnumThreadWindows(GetCurrentThreadId(), nsWindow::BroadcastMsg, WM_SYSCOLORCHANGE);
7357 else {
7358 // Note: This is sent for child windows as well as top-level windows.
7359 // The Win32 toolkit normally only sends these events to top-level windows.
7360 // But we cycle through all of the childwindows and send it to them as well
7361 // so all presentations get notified properly.
7362 // See nsWindow::GlobalMsgWindowProc.
7363 NotifySysColorChanged();
7367 /**************************************************************
7368 **************************************************************
7370 ** BLOCK: IME management and accessibility
7372 ** Handles managing IME input and accessibility.
7374 **************************************************************
7375 **************************************************************/
7377 NS_IMETHODIMP
7378 nsWindow::NotifyIME(NotificationToIME aNotification)
7380 return IMEHandler::NotifyIME(this, aNotification);
7383 NS_IMETHODIMP_(void)
7384 nsWindow::SetInputContext(const InputContext& aContext,
7385 const InputContextAction& aAction)
7387 InputContext newInputContext = aContext;
7388 IMEHandler::SetInputContext(this, newInputContext, aAction);
7389 mInputContext = newInputContext;
7392 NS_IMETHODIMP_(InputContext)
7393 nsWindow::GetInputContext()
7395 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7396 if (IMEHandler::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
7397 mInputContext.mIMEState.mOpen = IMEState::OPEN;
7398 } else {
7399 mInputContext.mIMEState.mOpen = IMEState::CLOSED;
7401 return mInputContext;
7404 NS_IMETHODIMP
7405 nsWindow::GetToggledKeyState(uint32_t aKeyCode, bool* aLEDState)
7407 #ifdef DEBUG_KBSTATE
7408 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("GetToggledKeyState\n"));
7409 #endif
7410 NS_ENSURE_ARG_POINTER(aLEDState);
7411 *aLEDState = (::GetKeyState(aKeyCode) & 1) != 0;
7412 return NS_OK;
7415 NS_IMETHODIMP
7416 nsWindow::NotifyIMEOfTextChange(uint32_t aStart,
7417 uint32_t aOldEnd,
7418 uint32_t aNewEnd)
7420 return IMEHandler::NotifyIMEOfTextChange(aStart, aOldEnd, aNewEnd);
7423 nsIMEUpdatePreference
7424 nsWindow::GetIMEUpdatePreference()
7426 return IMEHandler::GetUpdatePreference();
7429 #ifdef ACCESSIBILITY
7431 #ifdef DEBUG_WMGETOBJECT
7432 #define NS_LOG_WMGETOBJECT_WNDACC(aWnd) \
7433 a11y::Accessible* acc = aWnd ? aWind->GetAccessible() : nullptr; \
7434 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, (" acc: %p", acc)); \
7435 if (acc) { \
7436 nsAutoString name; \
7437 acc->GetName(name); \
7438 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, \
7439 (", accname: %s", NS_ConvertUTF16toUTF8(name).get())); \
7440 nsCOMPtr<nsIAccessibleDocument> doc = do_QueryObject(acc); \
7441 void *hwnd = nullptr; \
7442 doc->GetWindowHandle(&hwnd); \
7443 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, (", acc hwnd: %d", hwnd)); \
7446 #define NS_LOG_WMGETOBJECT_THISWND \
7448 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, \
7449 ("\n*******Get Doc Accessible*******\nOrig Window: ")); \
7450 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, \
7451 ("\n {\n HWND: %d, parent HWND: %d, wndobj: %p,\n", \
7452 mWnd, ::GetParent(mWnd), this)); \
7453 NS_LOG_WMGETOBJECT_WNDACC(this) \
7454 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("\n }\n")); \
7457 #define NS_LOG_WMGETOBJECT_WND(aMsg, aHwnd) \
7459 nsWindow* wnd = WinUtils::GetNSWindowPtr(aHwnd); \
7460 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, \
7461 ("Get " aMsg ":\n {\n HWND: %d, parent HWND: %d, wndobj: %p,\n", \
7462 aHwnd, ::GetParent(aHwnd), wnd)); \
7463 NS_LOG_WMGETOBJECT_WNDACC(wnd); \
7464 PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("\n }\n")); \
7466 #else
7467 #define NS_LOG_WMGETOBJECT_THISWND
7468 #define NS_LOG_WMGETOBJECT_WND(aMsg, aHwnd)
7469 #endif // DEBUG_WMGETOBJECT
7471 a11y::Accessible*
7472 nsWindow::GetRootAccessible()
7474 // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
7475 if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled)
7476 return nullptr;
7478 if (mInDtor || mOnDestroyCalled || mWindowType == eWindowType_invisible) {
7479 return nullptr;
7482 NS_LOG_WMGETOBJECT_THISWND
7483 NS_LOG_WMGETOBJECT_WND("This Window", mWnd);
7485 return GetAccessible();
7487 #endif
7489 /**************************************************************
7490 **************************************************************
7492 ** BLOCK: Transparency
7494 ** Window transparency helpers.
7496 **************************************************************
7497 **************************************************************/
7499 #ifdef MOZ_XUL
7501 void nsWindow::ResizeTranslucentWindow(int32_t aNewWidth, int32_t aNewHeight, bool force)
7503 if (!force && aNewWidth == mBounds.width && aNewHeight == mBounds.height)
7504 return;
7506 #ifdef CAIRO_HAS_D2D_SURFACE
7507 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
7508 gfxWindowsPlatform::RENDER_DIRECT2D) {
7509 nsRefPtr<gfxD2DSurface> newSurface =
7510 new gfxD2DSurface(gfxIntSize(aNewWidth, aNewHeight), gfxASurface::ImageFormatARGB32);
7511 mTransparentSurface = newSurface;
7512 mMemoryDC = nullptr;
7513 } else
7514 #endif
7516 nsRefPtr<gfxWindowsSurface> newSurface =
7517 new gfxWindowsSurface(gfxIntSize(aNewWidth, aNewHeight), gfxASurface::ImageFormatARGB32);
7518 mTransparentSurface = newSurface;
7519 mMemoryDC = newSurface->GetDC();
7523 void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode)
7525 if (aMode == mTransparencyMode)
7526 return;
7528 // stop on dialogs and popups!
7529 HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
7530 nsWindow* parent = WinUtils::GetNSWindowPtr(hWnd);
7532 if (!parent)
7534 NS_WARNING("Trying to use transparent chrome in an embedded context");
7535 return;
7538 if (parent != this) {
7539 NS_WARNING("Setting SetWindowTranslucencyInner on a parent this is not us!");
7542 if (aMode == eTransparencyTransparent) {
7543 // If we're switching to the use of a transparent window, hide the chrome
7544 // on our parent.
7545 HideWindowChrome(true);
7546 } else if (mHideChrome && mTransparencyMode == eTransparencyTransparent) {
7547 // if we're switching out of transparent, re-enable our parent's chrome.
7548 HideWindowChrome(false);
7551 LONG_PTR style = ::GetWindowLongPtrW(hWnd, GWL_STYLE),
7552 exStyle = ::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
7554 if (parent->mIsVisible)
7555 style |= WS_VISIBLE;
7556 if (parent->mSizeMode == nsSizeMode_Maximized)
7557 style |= WS_MAXIMIZE;
7558 else if (parent->mSizeMode == nsSizeMode_Minimized)
7559 style |= WS_MINIMIZE;
7561 if (aMode == eTransparencyTransparent)
7562 exStyle |= WS_EX_LAYERED;
7563 else
7564 exStyle &= ~WS_EX_LAYERED;
7566 VERIFY_WINDOW_STYLE(style);
7567 ::SetWindowLongPtrW(hWnd, GWL_STYLE, style);
7568 ::SetWindowLongPtrW(hWnd, GWL_EXSTYLE, exStyle);
7570 if (HasGlass())
7571 memset(&mGlassMargins, 0, sizeof mGlassMargins);
7572 mTransparencyMode = aMode;
7574 SetupTranslucentWindowMemoryBitmap(aMode);
7575 UpdateGlass();
7578 void nsWindow::SetupTranslucentWindowMemoryBitmap(nsTransparencyMode aMode)
7580 if (eTransparencyTransparent == aMode) {
7581 ResizeTranslucentWindow(mBounds.width, mBounds.height, true);
7582 } else {
7583 mTransparentSurface = nullptr;
7584 mMemoryDC = NULL;
7588 void nsWindow::ClearTranslucentWindow()
7590 if (mTransparentSurface) {
7591 nsRefPtr<gfxContext> thebesContext = new gfxContext(mTransparentSurface);
7592 thebesContext->SetOperator(gfxContext::OPERATOR_CLEAR);
7593 thebesContext->Paint();
7594 UpdateTranslucentWindow();
7598 nsresult nsWindow::UpdateTranslucentWindow()
7600 if (mBounds.IsEmpty())
7601 return NS_OK;
7603 ::GdiFlush();
7605 BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
7606 SIZE winSize = { mBounds.width, mBounds.height };
7607 POINT srcPos = { 0, 0 };
7608 HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
7609 RECT winRect;
7610 ::GetWindowRect(hWnd, &winRect);
7612 #ifdef CAIRO_HAS_D2D_SURFACE
7613 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
7614 gfxWindowsPlatform::RENDER_DIRECT2D) {
7615 mMemoryDC = static_cast<gfxD2DSurface*>(mTransparentSurface.get())->
7616 GetDC(true);
7618 #endif
7619 // perform the alpha blend
7620 bool updateSuccesful =
7621 ::UpdateLayeredWindow(hWnd, NULL, (POINT*)&winRect, &winSize, mMemoryDC, &srcPos, 0, &bf, ULW_ALPHA);
7623 #ifdef CAIRO_HAS_D2D_SURFACE
7624 if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() ==
7625 gfxWindowsPlatform::RENDER_DIRECT2D) {
7626 nsIntRect r(0, 0, 0, 0);
7627 static_cast<gfxD2DSurface*>(mTransparentSurface.get())->ReleaseDC(&r);
7629 #endif
7631 if (!updateSuccesful) {
7632 return NS_ERROR_FAILURE;
7635 return NS_OK;
7638 #endif //MOZ_XUL
7640 /**************************************************************
7641 **************************************************************
7643 ** BLOCK: Popup rollup hooks
7645 ** Deals with CaptureRollup on popup windows.
7647 **************************************************************
7648 **************************************************************/
7650 // Schedules a timer for a window, so we can rollup after processing the hook event
7651 void nsWindow::ScheduleHookTimer(HWND aWnd, UINT aMsgId)
7653 // In some cases multiple hooks may be scheduled
7654 // so ignore any other requests once one timer is scheduled
7655 if (sHookTimerId == 0) {
7656 // Remember the window handle and the message ID to be used later
7657 sRollupMsgId = aMsgId;
7658 sRollupMsgWnd = aWnd;
7659 // Schedule native timer for doing the rollup after
7660 // this event is done being processed
7661 sHookTimerId = ::SetTimer(NULL, 0, 0, (TIMERPROC)HookTimerForPopups);
7662 NS_ASSERTION(sHookTimerId, "Timer couldn't be created.");
7666 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7667 int gLastMsgCode = 0;
7668 extern MSGFEventMsgInfo gMSGFEvents[];
7669 #endif
7671 // Process Menu messages, rollup when popup is clicked.
7672 LRESULT CALLBACK nsWindow::MozSpecialMsgFilter(int code, WPARAM wParam, LPARAM lParam)
7674 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7675 if (sProcessHook) {
7676 MSG* pMsg = (MSG*)lParam;
7678 int inx = 0;
7679 while (gMSGFEvents[inx].mId != code && gMSGFEvents[inx].mStr != NULL) {
7680 inx++;
7682 if (code != gLastMsgCode) {
7683 if (gMSGFEvents[inx].mId == code) {
7684 #ifdef DEBUG
7685 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
7686 ("MozSpecialMessageProc - code: 0x%X - %s hw: %p\n",
7687 code, gMSGFEvents[inx].mStr, pMsg->hwnd));
7688 #endif
7689 } else {
7690 #ifdef DEBUG
7691 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
7692 ("MozSpecialMessageProc - code: 0x%X - %d hw: %p\n",
7693 code, gMSGFEvents[inx].mId, pMsg->hwnd));
7694 #endif
7696 gLastMsgCode = code;
7698 PrintEvent(pMsg->message, FALSE, FALSE);
7700 #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7702 if (sProcessHook && code == MSGF_MENU) {
7703 MSG* pMsg = (MSG*)lParam;
7704 ScheduleHookTimer( pMsg->hwnd, pMsg->message);
7707 return ::CallNextHookEx(sMsgFilterHook, code, wParam, lParam);
7710 // Process all mouse messages. Roll up when a click is in a native window
7711 // that doesn't have an nsIWidget.
7712 LRESULT CALLBACK nsWindow::MozSpecialMouseProc(int code, WPARAM wParam, LPARAM lParam)
7714 if (sProcessHook) {
7715 switch (WinUtils::GetNativeMessage(wParam)) {
7716 case WM_LBUTTONDOWN:
7717 case WM_RBUTTONDOWN:
7718 case WM_MBUTTONDOWN:
7719 case WM_MOUSEWHEEL:
7720 case WM_MOUSEHWHEEL:
7722 MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
7723 nsIWidget* mozWin = WinUtils::GetNSWindowPtr(ms->hwnd);
7724 if (mozWin) {
7725 // If this window is windowed plugin window, the mouse events are not
7726 // sent to us.
7727 if (static_cast<nsWindow*>(mozWin)->mWindowType == eWindowType_plugin)
7728 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7729 } else {
7730 ScheduleHookTimer(ms->hwnd, (UINT)wParam);
7732 break;
7736 return ::CallNextHookEx(sCallMouseHook, code, wParam, lParam);
7739 // Process all messages. Roll up when the window is moving, or
7740 // is resizing or when maximized or mininized.
7741 LRESULT CALLBACK nsWindow::MozSpecialWndProc(int code, WPARAM wParam, LPARAM lParam)
7743 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7744 if (sProcessHook) {
7745 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7746 PrintEvent(cwpt->message, FALSE, FALSE);
7748 #endif
7750 if (sProcessHook) {
7751 CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
7752 if (cwpt->message == WM_MOVING ||
7753 cwpt->message == WM_SIZING ||
7754 cwpt->message == WM_GETMINMAXINFO) {
7755 ScheduleHookTimer(cwpt->hwnd, (UINT)cwpt->message);
7759 return ::CallNextHookEx(sCallProcHook, code, wParam, lParam);
7762 // Register the special "hooks" for dropdown processing.
7763 void nsWindow::RegisterSpecialDropdownHooks()
7765 NS_ASSERTION(!sMsgFilterHook, "sMsgFilterHook must be NULL!");
7766 NS_ASSERTION(!sCallProcHook, "sCallProcHook must be NULL!");
7768 DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
7770 // Install msg hook for moving the window and resizing
7771 if (!sMsgFilterHook) {
7772 DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
7773 sMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, MozSpecialMsgFilter, NULL, GetCurrentThreadId());
7774 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7775 if (!sMsgFilterHook) {
7776 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
7777 ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
7779 #endif
7782 // Install msg hook for menus
7783 if (!sCallProcHook) {
7784 DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
7785 sCallProcHook = SetWindowsHookEx(WH_CALLWNDPROC, MozSpecialWndProc, NULL, GetCurrentThreadId());
7786 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7787 if (!sCallProcHook) {
7788 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
7789 ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
7791 #endif
7794 // Install msg hook for the mouse
7795 if (!sCallMouseHook) {
7796 DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
7797 sCallMouseHook = SetWindowsHookEx(WH_MOUSE, MozSpecialMouseProc, NULL, GetCurrentThreadId());
7798 #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
7799 if (!sCallMouseHook) {
7800 PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
7801 ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
7803 #endif
7807 // Unhook special message hooks for dropdowns.
7808 void nsWindow::UnregisterSpecialDropdownHooks()
7810 DISPLAY_NMM_PRT("***************** De-installing Msg Hooks ***************\n");
7812 if (sCallProcHook) {
7813 DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
7814 if (!::UnhookWindowsHookEx(sCallProcHook)) {
7815 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
7817 sCallProcHook = NULL;
7820 if (sMsgFilterHook) {
7821 DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
7822 if (!::UnhookWindowsHookEx(sMsgFilterHook)) {
7823 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
7825 sMsgFilterHook = NULL;
7828 if (sCallMouseHook) {
7829 DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
7830 if (!::UnhookWindowsHookEx(sCallMouseHook)) {
7831 DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
7833 sCallMouseHook = NULL;
7837 // This timer is designed to only fire one time at most each time a "hook" function
7838 // is used to rollup the dropdown. In some cases, the timer may be scheduled from the
7839 // hook, but that hook event or a subsequent event may roll up the dropdown before
7840 // this timer function is executed.
7842 // For example, if an MFC control takes focus, the combobox will lose focus and rollup
7843 // before this function fires.
7844 VOID CALLBACK nsWindow::HookTimerForPopups(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
7846 if (sHookTimerId != 0) {
7847 // if the window is NULL then we need to use the ID to kill the timer
7848 BOOL status = ::KillTimer(NULL, sHookTimerId);
7849 NS_ASSERTION(status, "Hook Timer was not killed.");
7850 sHookTimerId = 0;
7853 if (sRollupMsgId != 0) {
7854 // Note: DealWithPopups does the check to make sure that the rollup widget is set.
7855 LRESULT popupHandlingResult;
7856 nsAutoRollup autoRollup;
7857 DealWithPopups(sRollupMsgWnd, sRollupMsgId, 0, 0, &popupHandlingResult);
7858 sRollupMsgId = 0;
7859 sRollupMsgWnd = NULL;
7863 BOOL CALLBACK nsWindow::ClearResourcesCallback(HWND aWnd, LPARAM aMsg)
7865 nsWindow *window = WinUtils::GetNSWindowPtr(aWnd);
7866 if (window) {
7867 window->ClearCachedResources();
7869 return TRUE;
7872 void
7873 nsWindow::ClearCachedResources()
7875 #ifdef CAIRO_HAS_D2D_SURFACE
7876 mD2DWindowSurface = nullptr;
7877 #endif
7878 if (mLayerManager &&
7879 mLayerManager->GetBackendType() == LAYERS_BASIC) {
7880 static_cast<BasicLayerManager*>(mLayerManager.get())->
7881 ClearCachedResources();
7883 ::EnumChildWindows(mWnd, nsWindow::ClearResourcesCallback, 0);
7886 static bool IsDifferentThreadWindow(HWND aWnd)
7888 return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd, NULL);
7891 bool
7892 nsWindow::EventIsInsideWindow(UINT Msg, nsWindow* aWindow)
7894 RECT r;
7896 if (Msg == WM_ACTIVATEAPP)
7897 // don't care about activation/deactivation
7898 return false;
7900 ::GetWindowRect(aWindow->mWnd, &r);
7901 DWORD pos = ::GetMessagePos();
7902 POINT mp;
7903 mp.x = GET_X_LPARAM(pos);
7904 mp.y = GET_Y_LPARAM(pos);
7906 // was the event inside this window?
7907 return (bool) PtInRect(&r, mp);
7910 // Handle events that may cause a popup (combobox, XPMenu, etc) to need to rollup.
7911 bool
7912 nsWindow::DealWithPopups(HWND inWnd, UINT inMsg, WPARAM inWParam, LPARAM inLParam, LRESULT* outResult)
7914 NS_ASSERTION(outResult, "Bad outResult");
7916 *outResult = MA_NOACTIVATE;
7918 if (!::IsWindowVisible(inWnd))
7919 return false;
7920 nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
7921 NS_ENSURE_TRUE(rollupListener, false);
7922 nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
7923 if (!rollupWidget)
7924 return false;
7926 inMsg = WinUtils::GetNativeMessage(inMsg);
7927 if (inMsg == WM_LBUTTONDOWN || inMsg == WM_RBUTTONDOWN || inMsg == WM_MBUTTONDOWN ||
7928 inMsg == WM_MOUSEWHEEL || inMsg == WM_MOUSEHWHEEL || inMsg == WM_ACTIVATE ||
7929 (inMsg == WM_KILLFOCUS && IsDifferentThreadWindow((HWND)inWParam)) ||
7930 inMsg == WM_NCRBUTTONDOWN ||
7931 inMsg == WM_MOVING ||
7932 inMsg == WM_SIZING ||
7933 inMsg == WM_NCLBUTTONDOWN ||
7934 inMsg == WM_NCMBUTTONDOWN ||
7935 inMsg == WM_MOUSEACTIVATE ||
7936 inMsg == WM_ACTIVATEAPP ||
7937 inMsg == WM_MENUSELECT) {
7938 // Rollup if the event is outside the popup.
7939 bool rollup = !nsWindow::EventIsInsideWindow(inMsg, (nsWindow*)(rollupWidget.get()));
7941 if (rollup && (inMsg == WM_MOUSEWHEEL || inMsg == WM_MOUSEHWHEEL)) {
7942 rollup = rollupListener->ShouldRollupOnMouseWheelEvent();
7943 *outResult = MA_ACTIVATE;
7946 // If we're dealing with menus, we probably have submenus and we don't
7947 // want to rollup if the click is in a parent menu of the current submenu.
7948 uint32_t popupsToRollup = UINT32_MAX;
7949 if (rollup) {
7950 nsAutoTArray<nsIWidget*, 5> widgetChain;
7951 uint32_t sameTypeCount = rollupListener->GetSubmenuWidgetChain(&widgetChain);
7952 for ( uint32_t i = 0; i < widgetChain.Length(); ++i ) {
7953 nsIWidget* widget = widgetChain[i];
7954 if ( nsWindow::EventIsInsideWindow(inMsg, (nsWindow*)widget) ) {
7955 // don't roll up if the mouse event occurred within a menu of the
7956 // same type. If the mouse event occurred in a menu higher than
7957 // that, roll up, but pass the number of popups to Rollup so
7958 // that only those of the same type close up.
7959 if (i < sameTypeCount) {
7960 rollup = false;
7961 } else {
7962 popupsToRollup = sameTypeCount;
7964 break;
7966 } // foreach parent menu widget
7969 if (inMsg == WM_MOUSEACTIVATE) {
7970 // Prevent the click inside the popup from causing a change in window
7971 // activation. Since the popup is shown non-activated, we need to eat
7972 // any requests to activate the window while it is displayed. Windows
7973 // will automatically activate the popup on the mousedown otherwise.
7974 if (!rollup) {
7975 return true;
7976 } else {
7977 UINT uMsg = HIWORD(inLParam);
7978 if (uMsg == WM_MOUSEMOVE) {
7979 // WM_MOUSEACTIVATE cause by moving the mouse - X-mouse (eg. TweakUI)
7980 // must be enabled in Windows.
7981 rollup = rollupListener->ShouldRollupOnMouseActivate();
7982 if (!rollup) {
7983 return true;
7988 // if we've still determined that we should still rollup everything, do it.
7989 else if (rollup) {
7990 // only need to deal with the last rollup for left mouse down events.
7991 NS_ASSERTION(!mLastRollup, "mLastRollup is null");
7992 bool consumeRollupEvent =
7993 rollupListener->Rollup(popupsToRollup, inMsg == WM_LBUTTONDOWN ? &mLastRollup : nullptr);
7994 NS_IF_ADDREF(mLastRollup);
7996 // Tell hook to stop processing messages
7997 sProcessHook = false;
7998 sRollupMsgId = 0;
7999 sRollupMsgWnd = NULL;
8001 // return TRUE tells Windows that the event is consumed,
8002 // false allows the event to be dispatched
8004 // So if we are NOT supposed to be consuming events, let it go through
8005 if (consumeRollupEvent && inMsg != WM_RBUTTONDOWN) {
8006 *outResult = MA_ACTIVATE;
8008 // However, don't activate panels
8009 if (inMsg == WM_MOUSEACTIVATE) {
8010 nsWindow* activateWindow = WinUtils::GetNSWindowPtr(inWnd);
8011 if (activateWindow) {
8012 nsWindowType wintype;
8013 activateWindow->GetWindowType(wintype);
8014 if (wintype == eWindowType_popup && activateWindow->PopupType() == ePopupTypePanel) {
8015 *outResult = popupsToRollup != UINT32_MAX ? MA_NOACTIVATEANDEAT : MA_NOACTIVATE;
8019 return true;
8021 // if we are only rolling up some popups, don't activate and don't let
8022 // the event go through. This prevents clicks menus higher in the
8023 // chain from opening when a context menu is open
8024 if (popupsToRollup != UINT32_MAX && inMsg == WM_MOUSEACTIVATE) {
8025 *outResult = MA_NOACTIVATEANDEAT;
8026 return true;
8029 } // if event that might trigger a popup to rollup
8031 return false;
8034 /**************************************************************
8035 **************************************************************
8037 ** BLOCK: Misc. utility methods and functions.
8039 ** General use.
8041 **************************************************************
8042 **************************************************************/
8044 // Note that the result of GetTopLevelWindow method can be different from the
8045 // result of WinUtils::GetTopLevelHWND(). The result can be non-floating
8046 // window. Because our top level window may be contained in another window
8047 // which is not managed by us.
8048 nsWindow* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup)
8050 nsWindow* curWindow = this;
8052 while (true) {
8053 if (aStopOnDialogOrPopup) {
8054 switch (curWindow->mWindowType) {
8055 case eWindowType_dialog:
8056 case eWindowType_popup:
8057 return curWindow;
8058 default:
8059 break;
8063 // Retrieve the top level parent or owner window
8064 nsWindow* parentWindow = curWindow->GetParentWindow(true);
8066 if (!parentWindow)
8067 return curWindow;
8069 curWindow = parentWindow;
8073 static BOOL CALLBACK gEnumWindowsProc(HWND hwnd, LPARAM lParam)
8075 DWORD pid;
8076 ::GetWindowThreadProcessId(hwnd, &pid);
8077 if (pid == GetCurrentProcessId() && ::IsWindowVisible(hwnd))
8079 gWindowsVisible = true;
8080 return FALSE;
8082 return TRUE;
8085 bool nsWindow::CanTakeFocus()
8087 gWindowsVisible = false;
8088 EnumWindows(gEnumWindowsProc, 0);
8089 if (!gWindowsVisible) {
8090 return true;
8091 } else {
8092 HWND fgWnd = ::GetForegroundWindow();
8093 if (!fgWnd) {
8094 return true;
8096 DWORD pid;
8097 GetWindowThreadProcessId(fgWnd, &pid);
8098 if (pid == GetCurrentProcessId()) {
8099 return true;
8102 return false;
8105 void nsWindow::GetMainWindowClass(nsAString& aClass)
8107 NS_PRECONDITION(aClass.IsEmpty(), "aClass should be empty string");
8108 nsresult rv = Preferences::GetString("ui.window_class_override", &aClass);
8109 if (NS_FAILED(rv) || aClass.IsEmpty()) {
8110 aClass.AssignASCII(sDefaultMainWindowClass);
8114 LPARAM nsWindow::lParamToScreen(LPARAM lParam)
8116 POINT pt;
8117 pt.x = GET_X_LPARAM(lParam);
8118 pt.y = GET_Y_LPARAM(lParam);
8119 ::ClientToScreen(mWnd, &pt);
8120 return MAKELPARAM(pt.x, pt.y);
8123 LPARAM nsWindow::lParamToClient(LPARAM lParam)
8125 POINT pt;
8126 pt.x = GET_X_LPARAM(lParam);
8127 pt.y = GET_Y_LPARAM(lParam);
8128 ::ScreenToClient(mWnd, &pt);
8129 return MAKELPARAM(pt.x, pt.y);
8132 void nsWindow::PickerOpen()
8134 mPickerDisplayCount++;
8137 void nsWindow::PickerClosed()
8139 NS_ASSERTION(mPickerDisplayCount > 0, "mPickerDisplayCount out of sync!");
8140 if (!mPickerDisplayCount)
8141 return;
8142 mPickerDisplayCount--;
8143 if (!mPickerDisplayCount && mDestroyCalled) {
8144 Destroy();
8148 /**************************************************************
8149 **************************************************************
8151 ** BLOCK: ChildWindow impl.
8153 ** Child window overrides.
8155 **************************************************************
8156 **************************************************************/
8158 // return the style for a child nsWindow
8159 DWORD ChildWindow::WindowStyle()
8161 DWORD style = WS_CLIPCHILDREN | nsWindow::WindowStyle();
8162 if (!(style & WS_POPUP))
8163 style |= WS_CHILD; // WS_POPUP and WS_CHILD are mutually exclusive.
8164 VERIFY_WINDOW_STYLE(style);
8165 return style;