forked from mirrors/gecko-dev
		
	 7496fb37dd
			
		
	
	
		7496fb37dd
		
	
	
	
	
		
			
			MozReview-Commit-ID: 3bzhX9bfR4s --HG-- extra : rebase_source : 52619ebf8fa230bc03d499fdb81f632296e2997b
		
			
				
	
	
		
			8553 lines
		
	
	
	
		
			269 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			8553 lines
		
	
	
	
		
			269 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim:set ts=2 sts=2 sw=2 et cin: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| /*
 | |
|  * nsWindow - Native window management and event handling.
 | |
|  * 
 | |
|  * nsWindow is organized into a set of major blocks and
 | |
|  * block subsections. The layout is as follows:
 | |
|  *
 | |
|  *  Includes
 | |
|  *  Variables
 | |
|  *  nsIWidget impl.
 | |
|  *     nsIWidget methods and utilities
 | |
|  *  nsSwitchToUIThread impl.
 | |
|  *     nsSwitchToUIThread methods and utilities
 | |
|  *  Moz events
 | |
|  *     Event initialization
 | |
|  *     Event dispatching
 | |
|  *  Native events
 | |
|  *     Wndproc(s)
 | |
|  *     Event processing
 | |
|  *     OnEvent event handlers
 | |
|  *  IME management and accessibility
 | |
|  *  Transparency
 | |
|  *  Popup hook handling
 | |
|  *  Misc. utilities
 | |
|  *  Child window impl.
 | |
|  *
 | |
|  * Search for "BLOCK:" to find major blocks.
 | |
|  * Search for "SECTION:" to find specific sections.
 | |
|  *
 | |
|  * Blocks should be split out into separate files if they
 | |
|  * become unmanageable.
 | |
|  *
 | |
|  * Related source:
 | |
|  *
 | |
|  *  nsWindowDefs.h     - Definitions, macros, structs, enums
 | |
|  *                       and general setup.
 | |
|  *  nsWindowDbg.h/.cpp - Debug related code and directives.
 | |
|  *  nsWindowGfx.h/.cpp - Graphics and painting.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Includes
 | |
|  **
 | |
|  ** Include headers.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| #include "gfx2DGlue.h"
 | |
| #include "gfxEnv.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "gfxPrefs.h"
 | |
| #include "mozilla/AutoRestore.h"
 | |
| #include "mozilla/Logging.h"
 | |
| #include "mozilla/MathAlgorithms.h"
 | |
| #include "mozilla/MiscEvents.h"
 | |
| #include "mozilla/MouseEvents.h"
 | |
| #include "mozilla/TouchEvents.h"
 | |
| 
 | |
| #include "mozilla/ipc/MessageChannel.h"
 | |
| #include <algorithm>
 | |
| #include <limits>
 | |
| 
 | |
| #include "nsWindow.h"
 | |
| #include "nsAppRunner.h"
 | |
| 
 | |
| #include <shellapi.h>
 | |
| #include <windows.h>
 | |
| #include <wtsapi32.h>
 | |
| #include <process.h>
 | |
| #include <commctrl.h>
 | |
| #include <unknwn.h>
 | |
| #include <psapi.h>
 | |
| 
 | |
| #include "mozilla/Logging.h"
 | |
| #include "prtime.h"
 | |
| #include "prenv.h"
 | |
| 
 | |
| #include "mozilla/WidgetTraceEvent.h"
 | |
| #include "nsIAppShell.h"
 | |
| #include "nsISupportsPrimitives.h"
 | |
| #include "nsIKeyEventInPluginCallback.h"
 | |
| #include "nsITheme.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsIScreenManager.h"
 | |
| #include "imgIContainer.h"
 | |
| #include "nsIFile.h"
 | |
| #include "nsIRollupListener.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsIClipboard.h"
 | |
| #include "WinMouseScrollHandler.h"
 | |
| #include "nsFontMetrics.h"
 | |
| #include "nsIFontEnumerator.h"
 | |
| #include "nsFont.h"
 | |
| #include "nsRect.h"
 | |
| #include "nsThreadUtils.h"
 | |
| #include "nsNativeCharsetUtils.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsCRT.h"
 | |
| #include "nsAppDirectoryServiceDefs.h"
 | |
| #include "nsWidgetsCID.h"
 | |
| #include "nsTHashtable.h"
 | |
| #include "nsHashKeys.h"
 | |
| #include "nsString.h"
 | |
| #include "mozilla/Services.h"
 | |
| #include "nsNativeThemeWin.h"
 | |
| #include "nsWindowsDllInterceptor.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsView.h"
 | |
| #include "nsIWindowMediator.h"
 | |
| #include "nsIServiceManager.h"
 | |
| #include "nsWindowGfx.h"
 | |
| #include "gfxWindowsPlatform.h"
 | |
| #include "Layers.h"
 | |
| #include "nsPrintfCString.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "nsISound.h"
 | |
| #include "SystemTimeConverter.h"
 | |
| #include "WinTaskbar.h"
 | |
| #include "WidgetUtils.h"
 | |
| #include "nsIWidgetListener.h"
 | |
| #include "mozilla/dom/MouseEventBinding.h"
 | |
| #include "mozilla/dom/Touch.h"
 | |
| #include "mozilla/gfx/2D.h"
 | |
| #include "nsToolkitCompsCID.h"
 | |
| #include "nsIAppStartup.h"
 | |
| #include "mozilla/WindowsVersion.h"
 | |
| #include "mozilla/TextEvents.h" // For WidgetKeyboardEvent
 | |
| #include "mozilla/TextEventDispatcherListener.h"
 | |
| #include "mozilla/widget/nsAutoRollup.h"
 | |
| #include "mozilla/widget/WinNativeEventData.h"
 | |
| #include "mozilla/widget/PlatformWidgetTypes.h"
 | |
| #include "nsThemeConstants.h"
 | |
| #include "nsBidiKeyboard.h"
 | |
| #include "nsThemeConstants.h"
 | |
| #include "gfxConfig.h"
 | |
| #include "InProcessWinCompositorWidget.h"
 | |
| #include "ScreenHelperWin.h"
 | |
| 
 | |
| #include "nsIGfxInfo.h"
 | |
| #include "nsUXThemeConstants.h"
 | |
| #include "KeyboardLayout.h"
 | |
| #include "nsNativeDragTarget.h"
 | |
| #include <mmsystem.h> // needed for WIN32_LEAN_AND_MEAN
 | |
| #include <zmouse.h>
 | |
| #include <richedit.h>
 | |
| 
 | |
| #if defined(ACCESSIBILITY)
 | |
| 
 | |
| #ifdef DEBUG
 | |
| #include "mozilla/a11y/Logging.h"
 | |
| #endif
 | |
| 
 | |
| #include "oleidl.h"
 | |
| #include <winuser.h>
 | |
| #include "nsAccessibilityService.h"
 | |
| #include "mozilla/a11y/DocAccessible.h"
 | |
| #include "mozilla/a11y/LazyInstantiator.h"
 | |
| #include "mozilla/a11y/Platform.h"
 | |
| #if !defined(WINABLEAPI)
 | |
| #include <winable.h>
 | |
| #endif // !defined(WINABLEAPI)
 | |
| #endif // defined(ACCESSIBILITY)
 | |
| 
 | |
| #include "nsIWinTaskbar.h"
 | |
| #define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1"
 | |
| 
 | |
| #include "nsIWindowsUIUtils.h"
 | |
| 
 | |
| #include "nsWindowDefs.h"
 | |
| 
 | |
| #include "nsCrashOnException.h"
 | |
| #include "nsIXULRuntime.h"
 | |
| 
 | |
| #include "nsIContent.h"
 | |
| 
 | |
| #include "mozilla/BackgroundHangMonitor.h"
 | |
| #include "WinIMEHandler.h"
 | |
| 
 | |
| #include "npapi.h"
 | |
| 
 | |
| #include <d3d11.h>
 | |
| 
 | |
| #include "InkCollector.h"
 | |
| 
 | |
| // ERROR from wingdi.h (below) gets undefined by some code.
 | |
| // #define ERROR               0
 | |
| // #define RGN_ERROR ERROR
 | |
| #define ERROR 0
 | |
| 
 | |
| #if !defined(SM_CONVERTIBLESLATEMODE)
 | |
| #define SM_CONVERTIBLESLATEMODE 0x2003
 | |
| #endif
 | |
| 
 | |
| #if !defined(WM_DPICHANGED)
 | |
| #define WM_DPICHANGED 0x02E0
 | |
| #endif
 | |
| 
 | |
| #include "mozilla/gfx/DeviceManagerDx.h"
 | |
| #include "mozilla/layers/InputAPZContext.h"
 | |
| #include "mozilla/layers/KnowsCompositor.h"
 | |
| #include "InputData.h"
 | |
| 
 | |
| #include "mozilla/Telemetry.h"
 | |
| #include "mozilla/plugins/PluginProcessParent.h"
 | |
| #include "mozilla/webrender/WebRenderAPI.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::dom;
 | |
| using namespace mozilla::gfx;
 | |
| using namespace mozilla::layers;
 | |
| using namespace mozilla::widget;
 | |
| using namespace mozilla::plugins;
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Variables
 | |
|  **
 | |
|  ** nsWindow Class static initializations and global variables. 
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsWindow statics
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| bool            nsWindow::sDropShadowEnabled      = true;
 | |
| uint32_t        nsWindow::sInstanceCount          = 0;
 | |
| bool            nsWindow::sSwitchKeyboardLayout   = false;
 | |
| BOOL            nsWindow::sIsOleInitialized       = FALSE;
 | |
| HCURSOR         nsWindow::sHCursor                = nullptr;
 | |
| imgIContainer*  nsWindow::sCursorImgContainer     = nullptr;
 | |
| nsWindow*       nsWindow::sCurrentWindow          = nullptr;
 | |
| bool            nsWindow::sJustGotDeactivate      = false;
 | |
| bool            nsWindow::sJustGotActivate        = false;
 | |
| bool            nsWindow::sIsInMouseCapture       = false;
 | |
| 
 | |
| // imported in nsWidgetFactory.cpp
 | |
| TriStateBool    nsWindow::sCanQuit                = TRI_UNKNOWN;
 | |
| 
 | |
| // Hook Data Memebers for Dropdowns. sProcessHook Tells the
 | |
| // hook methods whether they should be processing the hook
 | |
| // messages.
 | |
| HHOOK           nsWindow::sMsgFilterHook          = nullptr;
 | |
| HHOOK           nsWindow::sCallProcHook           = nullptr;
 | |
| HHOOK           nsWindow::sCallMouseHook          = nullptr;
 | |
| bool            nsWindow::sProcessHook            = false;
 | |
| UINT            nsWindow::sRollupMsgId            = 0;
 | |
| HWND            nsWindow::sRollupMsgWnd           = nullptr;
 | |
| UINT            nsWindow::sHookTimerId            = 0;
 | |
| 
 | |
| // Mouse Clicks - static variable definitions for figuring
 | |
| // out 1 - 3 Clicks.
 | |
| POINT           nsWindow::sLastMousePoint         = {0};
 | |
| POINT           nsWindow::sLastMouseMovePoint     = {0};
 | |
| LONG            nsWindow::sLastMouseDownTime      = 0L;
 | |
| LONG            nsWindow::sLastClickCount         = 0L;
 | |
| BYTE            nsWindow::sLastMouseButton        = 0;
 | |
| 
 | |
| bool            nsWindow::sHaveInitializedPrefs   = false;
 | |
| 
 | |
| TriStateBool nsWindow::sHasBogusPopupsDropShadowOnMultiMonitor = TRI_UNKNOWN;
 | |
| 
 | |
| static SystemTimeConverter<DWORD>&
 | |
| TimeConverter() {
 | |
|   static SystemTimeConverter<DWORD> timeConverterSingleton;
 | |
|   return timeConverterSingleton;
 | |
| }
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| class CurrentWindowsTimeGetter {
 | |
| public:
 | |
|   explicit CurrentWindowsTimeGetter(HWND aWnd)
 | |
|     : mWnd(aWnd)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   DWORD GetCurrentTime() const
 | |
|   {
 | |
|     return ::GetTickCount();
 | |
|   }
 | |
| 
 | |
|   void GetTimeAsyncForPossibleBackwardsSkew(const TimeStamp& aNow)
 | |
|   {
 | |
|     DWORD currentTime = GetCurrentTime();
 | |
|     if (sBackwardsSkewStamp && currentTime == sLastPostTime) {
 | |
|       // There's already one inflight with this timestamp. Don't
 | |
|       // send a duplicate.
 | |
|       return;
 | |
|     }
 | |
|     sBackwardsSkewStamp = Some(aNow);
 | |
|     sLastPostTime = currentTime;
 | |
|     static_assert(sizeof(WPARAM) >= sizeof(DWORD), "Can't fit a DWORD in a WPARAM");
 | |
|     ::PostMessage(mWnd, MOZ_WM_SKEWFIX, sLastPostTime, 0);
 | |
|   }
 | |
| 
 | |
|   static bool GetAndClearBackwardsSkewStamp(DWORD aPostTime, TimeStamp* aOutSkewStamp)
 | |
|   {
 | |
|     if (aPostTime != sLastPostTime) {
 | |
|       // The SKEWFIX message is stale; we've sent a new one since then.
 | |
|       // Ignore this one.
 | |
|       return false;
 | |
|     }
 | |
|     MOZ_ASSERT(sBackwardsSkewStamp);
 | |
|     *aOutSkewStamp = sBackwardsSkewStamp.value();
 | |
|     sBackwardsSkewStamp = Nothing();
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   static Maybe<TimeStamp> sBackwardsSkewStamp;
 | |
|   static DWORD sLastPostTime;
 | |
|   HWND mWnd;
 | |
| };
 | |
| 
 | |
| Maybe<TimeStamp> CurrentWindowsTimeGetter::sBackwardsSkewStamp;
 | |
| DWORD CurrentWindowsTimeGetter::sLastPostTime = 0;
 | |
| 
 | |
| } // namespace mozilla
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: globals variables
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| static const char *sScreenManagerContractID       = "@mozilla.org/gfx/screenmanager;1";
 | |
| 
 | |
| extern mozilla::LazyLogModule gWindowsLog;
 | |
| 
 | |
| // Global used in Show window enumerations.
 | |
| static bool     gWindowsVisible                   = false;
 | |
| 
 | |
| // True if we have sent a notification that we are suspending/sleeping.
 | |
| static bool     gIsSleepMode                      = false;
 | |
| 
 | |
| static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 | |
| 
 | |
| // General purpose user32.dll hook object
 | |
| static WindowsDllInterceptor sUser32Intercept;
 | |
| 
 | |
| // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
 | |
| // the default window border Windows paints. Glass will be extended inward
 | |
| // this distance to remove the border.
 | |
| static const int32_t kGlassMarginAdjustment = 2;
 | |
| 
 | |
| // When the client area is extended out into the default window frame area,
 | |
| // this is the minimum amount of space along the edge of resizable windows
 | |
| // we will always display a resize cursor in, regardless of the underlying
 | |
| // content.
 | |
| static const int32_t kResizableBorderMinSize = 3;
 | |
| 
 | |
| // Cached pointer events enabler value, True if pointer events are enabled.
 | |
| static bool gIsPointerEventsEnabled = false;
 | |
| 
 | |
| // We should never really try to accelerate windows bigger than this. In some
 | |
| // cases this might lead to no D3D9 acceleration where we could have had it
 | |
| // but D3D9 does not reliably report when it supports bigger windows. 8192
 | |
| // is as safe as we can get, we know at least D3D10 hardware always supports
 | |
| // this, other hardware we expect to report correctly in D3D9.
 | |
| #define MAX_ACCELERATED_DIMENSION 8192
 | |
| 
 | |
| // On window open (as well as after), Windows has an unfortunate habit of
 | |
| // sending rather a lot of WM_NCHITTEST messages. Because we have to do point
 | |
| // to DOM target conversions for these, we cache responses for a given
 | |
| // coordinate this many milliseconds:
 | |
| #define HITTEST_CACHE_LIFETIME_MS 50
 | |
| 
 | |
| #if defined(ACCESSIBILITY)
 | |
| 
 | |
| namespace mozilla {
 | |
| 
 | |
| /**
 | |
|  * Windows touchscreen code works by setting a global WH_GETMESSAGE hook and
 | |
|  * injecting tiptsf.dll. The touchscreen process then posts registered messages
 | |
|  * to our main thread. The tiptsf hook picks up those registered messages and
 | |
|  * uses them as commands, some of which call into UIA, which then calls into
 | |
|  * MSAA, which then sends WM_GETOBJECT to us.
 | |
|  *
 | |
|  * We can get ahead of this by installing our own thread-local WH_GETMESSAGE
 | |
|  * hook. Since thread-local hooks are called ahead of global hooks, we will
 | |
|  * see these registered messages before tiptsf does. At this point we can then
 | |
|  * raise a flag that blocks a11y before invoking CallNextHookEx which will then
 | |
|  * invoke the global tiptsf hook. Then when we see WM_GETOBJECT, we check the
 | |
|  * flag by calling TIPMessageHandler::IsA11yBlocked().
 | |
|  *
 | |
|  * For Windows 8, we also hook tiptsf!ProcessCaretEvents, which is an a11y hook
 | |
|  * function that also calls into UIA.
 | |
|  */
 | |
| class TIPMessageHandler
 | |
| {
 | |
| public:
 | |
|   ~TIPMessageHandler()
 | |
|   {
 | |
|     if (mHook) {
 | |
|       ::UnhookWindowsHookEx(mHook);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   static void Initialize()
 | |
|   {
 | |
|     if (!IsWin8OrLater()) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (sInstance) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     sInstance = new TIPMessageHandler();
 | |
|     ClearOnShutdown(&sInstance);
 | |
|   }
 | |
| 
 | |
|   static bool IsA11yBlocked()
 | |
|   {
 | |
|     if (!sInstance) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return sInstance->mA11yBlockCount > 0;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   TIPMessageHandler()
 | |
|     : mHook(nullptr)
 | |
|     , mA11yBlockCount(0)
 | |
|   {
 | |
|     MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|     // Registered messages used by tiptsf
 | |
|     mMessages[0] = ::RegisterWindowMessage(L"ImmersiveFocusNotification");
 | |
|     mMessages[1] = ::RegisterWindowMessage(L"TipCloseMenus");
 | |
|     mMessages[2] = ::RegisterWindowMessage(L"TabletInputPanelOpening");
 | |
|     mMessages[3] = ::RegisterWindowMessage(L"IHM Pen or Touch Event noticed");
 | |
|     mMessages[4] = ::RegisterWindowMessage(L"ProgrammabilityCaretVisibility");
 | |
|     mMessages[5] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPHidden");
 | |
|     mMessages[6] = ::RegisterWindowMessage(L"CaretTrackingUpdateIPInfo");
 | |
| 
 | |
|     mHook = ::SetWindowsHookEx(WH_GETMESSAGE, &TIPHook, nullptr,
 | |
|                                ::GetCurrentThreadId());
 | |
|     MOZ_ASSERT(mHook);
 | |
| 
 | |
|     // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
 | |
|     // first initialized.
 | |
|     if (!IsWin10OrLater() && GetModuleHandle(L"tiptsf.dll") &&
 | |
|         !sProcessCaretEventsStub) {
 | |
|       sTipTsfInterceptor.Init("tiptsf.dll");
 | |
|       DebugOnly<bool> ok = sProcessCaretEventsStub.Set(sTipTsfInterceptor,
 | |
|                                                        "ProcessCaretEvents",
 | |
|                                                        &ProcessCaretEventsHook);
 | |
|       MOZ_ASSERT(ok);
 | |
|     }
 | |
| 
 | |
|     if (!sSendMessageTimeoutWStub) {
 | |
|       sUser32Intercept.Init("user32.dll");
 | |
|       DebugOnly<bool> hooked = sSendMessageTimeoutWStub.Set(sUser32Intercept,
 | |
|                                                             "SendMessageTimeoutW",
 | |
|                                                             &SendMessageTimeoutWHook);
 | |
|       MOZ_ASSERT(hooked);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   class MOZ_RAII A11yInstantiationBlocker
 | |
|   {
 | |
|   public:
 | |
|     A11yInstantiationBlocker()
 | |
|     {
 | |
|       if (!TIPMessageHandler::sInstance) {
 | |
|         return;
 | |
|       }
 | |
|       ++TIPMessageHandler::sInstance->mA11yBlockCount;
 | |
|     }
 | |
| 
 | |
|     ~A11yInstantiationBlocker()
 | |
|     {
 | |
|       if (!TIPMessageHandler::sInstance) {
 | |
|         return;
 | |
|       }
 | |
|       MOZ_ASSERT(TIPMessageHandler::sInstance->mA11yBlockCount > 0);
 | |
|       --TIPMessageHandler::sInstance->mA11yBlockCount;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   friend class A11yInstantiationBlocker;
 | |
| 
 | |
|   static LRESULT CALLBACK TIPHook(int aCode, WPARAM aWParam, LPARAM aLParam)
 | |
|   {
 | |
|     if (aCode < 0 || !sInstance) {
 | |
|       return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
 | |
|     }
 | |
| 
 | |
|     MSG* msg = reinterpret_cast<MSG*>(aLParam);
 | |
|     UINT& msgCode = msg->message;
 | |
| 
 | |
|     for (uint32_t i = 0; i < ArrayLength(sInstance->mMessages); ++i) {
 | |
|       if (msgCode == sInstance->mMessages[i]) {
 | |
|         A11yInstantiationBlocker block;
 | |
|         return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
 | |
|   }
 | |
| 
 | |
|   static void CALLBACK ProcessCaretEventsHook(HWINEVENTHOOK aWinEventHook,
 | |
|                                               DWORD aEvent, HWND aHwnd,
 | |
|                                               LONG aObjectId, LONG aChildId,
 | |
|                                               DWORD aGeneratingTid,
 | |
|                                               DWORD aEventTime)
 | |
|   {
 | |
|     A11yInstantiationBlocker block;
 | |
|     sProcessCaretEventsStub(aWinEventHook, aEvent, aHwnd, aObjectId, aChildId,
 | |
|                             aGeneratingTid, aEventTime);
 | |
|   }
 | |
| 
 | |
|   static LRESULT WINAPI SendMessageTimeoutWHook(HWND aHwnd, UINT aMsgCode,
 | |
|                                                 WPARAM aWParam, LPARAM aLParam,
 | |
|                                                 UINT aFlags, UINT aTimeout,
 | |
|                                                 PDWORD_PTR aMsgResult)
 | |
|   {
 | |
|     // We don't want to handle this unless the message is a WM_GETOBJECT that we
 | |
|     // want to block, and the aHwnd is a nsWindow that belongs to the current
 | |
|     // thread.
 | |
|     if (!aMsgResult || aMsgCode != WM_GETOBJECT ||
 | |
|         static_cast<DWORD>(aLParam) != OBJID_CLIENT ||
 | |
|         !WinUtils::GetNSWindowPtr(aHwnd) ||
 | |
|         ::GetWindowThreadProcessId(aHwnd, nullptr) != ::GetCurrentThreadId() ||
 | |
|         !IsA11yBlocked()) {
 | |
|       return sSendMessageTimeoutWStub(aHwnd, aMsgCode, aWParam, aLParam,
 | |
|                                       aFlags, aTimeout, aMsgResult);
 | |
|     }
 | |
| 
 | |
|     // In this case we want to fake the result that would happen if we had
 | |
|     // decided not to handle WM_GETOBJECT in our WndProc. We hand the message
 | |
|     // off to DefWindowProc to accomplish this.
 | |
|     *aMsgResult = static_cast<DWORD_PTR>(::DefWindowProcW(aHwnd, aMsgCode,
 | |
|                                                           aWParam, aLParam));
 | |
| 
 | |
|     return static_cast<LRESULT>(TRUE);
 | |
|   }
 | |
| 
 | |
|   static WindowsDllInterceptor sTipTsfInterceptor;
 | |
|   static WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
 | |
|     sProcessCaretEventsStub;
 | |
|   static WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
 | |
|     sSendMessageTimeoutWStub;
 | |
|   static StaticAutoPtr<TIPMessageHandler> sInstance;
 | |
| 
 | |
|   HHOOK                 mHook;
 | |
|   UINT                  mMessages[7];
 | |
|   uint32_t              mA11yBlockCount;
 | |
| };
 | |
| 
 | |
| WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
 | |
| WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
 | |
|   TIPMessageHandler::sProcessCaretEventsStub;
 | |
| WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
 | |
|   TIPMessageHandler::sSendMessageTimeoutWStub;
 | |
| StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
 | |
| 
 | |
| } // namespace mozilla
 | |
| 
 | |
| #endif // defined(ACCESSIBILITY)
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: nsIWidget impl.
 | |
|  **
 | |
|  ** nsIWidget interface implementation, broken down into
 | |
|  ** sections.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsWindow construction and destruction
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| nsWindow::nsWindow(bool aIsChildWindow)
 | |
|   : nsWindowBase()
 | |
|   , mResizeState(NOT_RESIZING)
 | |
|   , mIsChildWindow(aIsChildWindow)
 | |
| {
 | |
|   mIconSmall            = nullptr;
 | |
|   mIconBig              = nullptr;
 | |
|   mWnd                  = nullptr;
 | |
|   mTransitionWnd        = nullptr;
 | |
|   mPaintDC              = nullptr;
 | |
|   mPrevWndProc          = nullptr;
 | |
|   mNativeDragTarget     = nullptr;
 | |
|   mInDtor               = false;
 | |
|   mIsVisible            = false;
 | |
|   mIsTopWidgetWindow    = false;
 | |
|   mUnicodeWidget        = true;
 | |
|   mDisplayPanFeedback   = false;
 | |
|   mTouchWindow          = false;
 | |
|   mFutureMarginsToUse   = false;
 | |
|   mCustomNonClient      = false;
 | |
|   mHideChrome           = false;
 | |
|   mFullscreenMode       = false;
 | |
|   mMousePresent         = false;
 | |
|   mDestroyCalled        = false;
 | |
|   mIsEarlyBlankWindow   = false;
 | |
|   mHasTaskbarIconBeenCreated = false;
 | |
|   mMouseTransparent     = false;
 | |
|   mPickerDisplayCount   = 0;
 | |
|   mWindowType           = eWindowType_child;
 | |
|   mBorderStyle          = eBorderStyle_default;
 | |
|   mOldSizeMode          = nsSizeMode_Normal;
 | |
|   mLastSizeMode         = nsSizeMode_Normal;
 | |
|   mLastSize.width       = 0;
 | |
|   mLastSize.height      = 0;
 | |
|   mOldStyle             = 0;
 | |
|   mOldExStyle           = 0;
 | |
|   mPainting             = 0;
 | |
|   mLastKeyboardLayout   = 0;
 | |
|   mBlurSuppressLevel    = 0;
 | |
|   mLastPaintEndTime     = TimeStamp::Now();
 | |
|   mCachedHitTestPoint.x = 0;
 | |
|   mCachedHitTestPoint.y = 0;
 | |
|   mCachedHitTestTime    = TimeStamp::Now();
 | |
|   mCachedHitTestResult  = 0;
 | |
| #ifdef MOZ_XUL
 | |
|   mTransparencyMode     = eTransparencyOpaque;
 | |
|   memset(&mGlassMargins, 0, sizeof mGlassMargins);
 | |
| #endif
 | |
|   DWORD background      = ::GetSysColor(COLOR_BTNFACE);
 | |
|   mBrush                = ::CreateSolidBrush(NSRGB_2_COLOREF(background));
 | |
|   mSendingSetText       = false;
 | |
|   mDefaultScale         = -1.0; // not yet set, will be calculated on first use
 | |
| 
 | |
|   mTaskbarPreview = nullptr;
 | |
| 
 | |
|   mCompositorWidgetDelegate = nullptr;
 | |
| 
 | |
|   // Global initialization
 | |
|   if (!sInstanceCount) {
 | |
|     // Global app registration id for Win7 and up. See
 | |
|     // WinTaskbar.cpp for details.
 | |
|     mozilla::widget::WinTaskbar::RegisterAppUserModelID();
 | |
|     KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
 | |
| #if defined(ACCESSIBILITY)
 | |
|     mozilla::TIPMessageHandler::Initialize();
 | |
| #endif // defined(ACCESSIBILITY)
 | |
|     if (SUCCEEDED(::OleInitialize(nullptr))) {
 | |
|       sIsOleInitialized = TRUE;
 | |
|     }
 | |
|     NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
 | |
|     MouseScrollHandler::Initialize();
 | |
|     // Init theme data
 | |
|     nsUXThemeData::UpdateNativeThemeInfo();
 | |
|     RedirectedKeyDownMessageManager::Forget();
 | |
|     if (mPointerEvents.ShouldEnableInkCollector()) {
 | |
|       InkCollector::sInkCollector = new InkCollector();
 | |
|     }
 | |
| 
 | |
|     Preferences::AddBoolVarCache(&gIsPointerEventsEnabled,
 | |
|                                  "dom.w3c_pointer_events.enabled",
 | |
|                                  gIsPointerEventsEnabled);
 | |
|   } // !sInstanceCount
 | |
| 
 | |
|   mIdleService = nullptr;
 | |
| 
 | |
|   mSizeConstraintsScale = GetDefaultScale().scale;
 | |
| 
 | |
|   sInstanceCount++;
 | |
| }
 | |
| 
 | |
| nsWindow::~nsWindow()
 | |
| {
 | |
|   mInDtor = true;
 | |
| 
 | |
|   // If the widget was released without calling Destroy() then the native window still
 | |
|   // exists, and we need to destroy it.
 | |
|   // Destroy() will early-return if it was already called. In any case it is important
 | |
|   // to call it before destroying mPresentLock (cf. 1156182).
 | |
|   Destroy();
 | |
| 
 | |
|   // Free app icon resources.  This must happen after `OnDestroy` (see bug 708033).
 | |
|   if (mIconSmall)
 | |
|     ::DestroyIcon(mIconSmall);
 | |
| 
 | |
|   if (mIconBig)
 | |
|     ::DestroyIcon(mIconBig);
 | |
| 
 | |
|   sInstanceCount--;
 | |
| 
 | |
|   // Global shutdown
 | |
|   if (sInstanceCount == 0) {
 | |
|     if (InkCollector::sInkCollector) {
 | |
|       InkCollector::sInkCollector->Shutdown();
 | |
|       InkCollector::sInkCollector = nullptr;
 | |
|     }
 | |
|     IMEHandler::Terminate();
 | |
|     NS_IF_RELEASE(sCursorImgContainer);
 | |
|     if (sIsOleInitialized) {
 | |
|       ::OleFlushClipboard();
 | |
|       ::OleUninitialize();
 | |
|       sIsOleInitialized = FALSE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   NS_IF_RELEASE(mNativeDragTarget);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::Create, nsIWidget::Destroy
 | |
|  *
 | |
|  * Creating and destroying windows for this widget.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Allow Derived classes to modify the height that is passed
 | |
| // when the window is created or resized.
 | |
| int32_t nsWindow::GetHeight(int32_t aProposedHeight)
 | |
| {
 | |
|   return aProposedHeight;
 | |
| }
 | |
| 
 | |
| static bool
 | |
| ShouldCacheTitleBarInfo(nsWindowType aWindowType, nsBorderStyle aBorderStyle)
 | |
| {
 | |
|   return (aWindowType == eWindowType_toplevel)  &&
 | |
|          (aBorderStyle == eBorderStyle_default  ||
 | |
|             aBorderStyle == eBorderStyle_all)   &&
 | |
|       (!nsUXThemeData::sTitlebarInfoPopulatedThemed ||
 | |
|        !nsUXThemeData::sTitlebarInfoPopulatedAero);
 | |
| }
 | |
| 
 | |
| // Create the proper widget
 | |
| nsresult
 | |
| nsWindow::Create(nsIWidget* aParent,
 | |
|                  nsNativeWidget aNativeParent,
 | |
|                  const LayoutDeviceIntRect& aRect,
 | |
|                  nsWidgetInitData* aInitData)
 | |
| {
 | |
|   nsWidgetInitData defaultInitData;
 | |
|   if (!aInitData)
 | |
|     aInitData = &defaultInitData;
 | |
| 
 | |
|   mUnicodeWidget = aInitData->mUnicode;
 | |
| 
 | |
|   nsIWidget *baseParent = aInitData->mWindowType == eWindowType_dialog ||
 | |
|                           aInitData->mWindowType == eWindowType_toplevel ||
 | |
|                           aInitData->mWindowType == eWindowType_invisible ?
 | |
|                           nullptr : aParent;
 | |
| 
 | |
|   mIsTopWidgetWindow = (nullptr == baseParent);
 | |
|   mBounds = aRect;
 | |
| 
 | |
|   // Ensure that the toolkit is created.
 | |
|   nsToolkit::GetToolkit();
 | |
| 
 | |
|   BaseCreate(baseParent, aInitData);
 | |
| 
 | |
|   HWND parent;
 | |
|   if (aParent) { // has a nsIWidget parent
 | |
|     parent = aParent ? (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
 | |
|     mParent = aParent;
 | |
|   } else { // has a nsNative parent
 | |
|     parent = (HWND)aNativeParent;
 | |
|     mParent = aNativeParent ?
 | |
|       WinUtils::GetNSWindowPtr((HWND)aNativeParent) : nullptr;
 | |
|   }
 | |
| 
 | |
|   mIsRTL = aInitData->mRTL;
 | |
|   mOpeningAnimationSuppressed = aInitData->mIsAnimationSuppressed;
 | |
| 
 | |
|   DWORD style = WindowStyle();
 | |
|   DWORD extendedStyle = WindowExStyle();
 | |
| 
 | |
|   if (mWindowType == eWindowType_popup) {
 | |
|     if (!aParent) {
 | |
|       parent = nullptr;
 | |
|     }
 | |
| 
 | |
|     if (!IsWin8OrLater() &&
 | |
|         HasBogusPopupsDropShadowOnMultiMonitor() &&
 | |
|         ShouldUseOffMainThreadCompositing()) {
 | |
|       extendedStyle |= WS_EX_COMPOSITED;
 | |
|     }
 | |
| 
 | |
|     if (aInitData->mMouseTransparent) {
 | |
|       // This flag makes the window transparent to mouse events
 | |
|       mMouseTransparent = true;
 | |
|       extendedStyle |= WS_EX_TRANSPARENT;
 | |
|     }
 | |
|   } else if (mWindowType == eWindowType_invisible) {
 | |
|     // Make sure CreateWindowEx succeeds at creating a toplevel window
 | |
|     style &= ~0x40000000; // WS_CHILDWINDOW
 | |
|   } else {
 | |
|     // See if the caller wants to explictly set clip children and clip siblings
 | |
|     if (aInitData->clipChildren) {
 | |
|       style |= WS_CLIPCHILDREN;
 | |
|     } else {
 | |
|       style &= ~WS_CLIPCHILDREN;
 | |
|     }
 | |
|     if (aInitData->clipSiblings) {
 | |
|       style |= WS_CLIPSIBLINGS;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   const wchar_t* className;
 | |
|   if (aInitData->mDropShadow) {
 | |
|     className = GetWindowPopupClass();
 | |
|   } else {
 | |
|     className = GetWindowClass();
 | |
|   }
 | |
|   // Plugins are created in the disabled state so that they can't
 | |
|   // steal focus away from our main window.  This is especially
 | |
|   // important if the plugin has loaded in a background tab.
 | |
|   if (aInitData->mWindowType == eWindowType_plugin ||
 | |
|       aInitData->mWindowType == eWindowType_plugin_ipc_chrome ||
 | |
|       aInitData->mWindowType == eWindowType_plugin_ipc_content) {
 | |
|     style |= WS_DISABLED;
 | |
|   }
 | |
|   mWnd = ::CreateWindowExW(extendedStyle,
 | |
|                            className,
 | |
|                            L"",
 | |
|                            style,
 | |
|                            aRect.X(),
 | |
|                            aRect.Y(),
 | |
|                            aRect.Width(),
 | |
|                            GetHeight(aRect.Height()),
 | |
|                            parent,
 | |
|                            nullptr,
 | |
|                            nsToolkit::mDllInstance,
 | |
|                            nullptr);
 | |
| 
 | |
|   if (!mWnd) {
 | |
|     NS_WARNING("nsWindow CreateWindowEx failed.");
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   // If mDefaultScale is set before mWnd has been set, it will have the scale of the
 | |
|   // primary monitor, rather than the monitor that the window is actually on. For
 | |
|   // non-popup windows this gets corrected by the WM_DPICHANGED message which resets
 | |
|   // mDefaultScale, but for popup windows we don't reset mDefaultScale on that message.
 | |
|   // In order to ensure that popup windows spawned on a non-primary monitor end up
 | |
|   // with the correct scale, we reset mDefaultScale here so that it gets recomputed
 | |
|   // using the correct monitor now that we have a mWnd.
 | |
|   mDefaultScale = -1.0;
 | |
| 
 | |
|   if (mIsRTL) {
 | |
|     DWORD dwAttribute = TRUE;    
 | |
|     DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
 | |
|   }
 | |
| 
 | |
|   if (mOpeningAnimationSuppressed) {
 | |
|     SuppressAnimation(true);
 | |
|   }
 | |
| 
 | |
|   if (!IsPlugin() &&
 | |
|       mWindowType != eWindowType_invisible &&
 | |
|       MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) {
 | |
|     // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977)
 | |
|     //
 | |
|     // We create two zero-sized windows as descendants of the top-level window,
 | |
|     // like so:
 | |
|     //
 | |
|     //   Top-level window (MozillaWindowClass)
 | |
|     //     FAKETRACKPOINTSCROLLCONTAINER (MozillaWindowClass)
 | |
|     //       FAKETRACKPOINTSCROLLABLE (MozillaWindowClass)
 | |
|     //
 | |
|     // We need to have the middle window, otherwise the Trackpoint driver
 | |
|     // will fail to deliver scroll messages.  WM_MOUSEWHEEL messages are
 | |
|     // sent to the FAKETRACKPOINTSCROLLABLE, which then propagate up the
 | |
|     // window hierarchy until they are handled by nsWindow::WindowProc.
 | |
|     // WM_HSCROLL messages are also sent to the FAKETRACKPOINTSCROLLABLE,
 | |
|     // but these do not propagate automatically, so we have the window
 | |
|     // procedure pretend that they were dispatched to the top-level window
 | |
|     // instead.
 | |
|     //
 | |
|     // The FAKETRACKPOINTSCROLLABLE needs to have the specific window styles it
 | |
|     // is given below so that it catches the Trackpoint driver's heuristics.
 | |
|     HWND scrollContainerWnd = ::CreateWindowW
 | |
|       (className, L"FAKETRACKPOINTSCROLLCONTAINER",
 | |
|        WS_CHILD | WS_VISIBLE,
 | |
|        0, 0, 0, 0, mWnd, nullptr, nsToolkit::mDllInstance, nullptr);
 | |
|     HWND scrollableWnd = ::CreateWindowW
 | |
|       (className, L"FAKETRACKPOINTSCROLLABLE",
 | |
|        WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_TABSTOP | 0x30,
 | |
|        0, 0, 0, 0, scrollContainerWnd, nullptr, nsToolkit::mDllInstance,
 | |
|        nullptr);
 | |
| 
 | |
|     // Give the FAKETRACKPOINTSCROLLABLE window a specific ID so that
 | |
|     // WindowProcInternal can distinguish it from the top-level window
 | |
|     // easily.
 | |
|     ::SetWindowLongPtrW(scrollableWnd, GWLP_ID, eFakeTrackPointScrollableID);
 | |
| 
 | |
|     // Make FAKETRACKPOINTSCROLLABLE use nsWindow::WindowProc, and store the
 | |
|     // old window procedure in its "user data".
 | |
|     WNDPROC oldWndProc;
 | |
|     if (mUnicodeWidget)
 | |
|       oldWndProc = (WNDPROC)::SetWindowLongPtrW(scrollableWnd, GWLP_WNDPROC,
 | |
|                                                 (LONG_PTR)nsWindow::WindowProc);
 | |
|     else
 | |
|       oldWndProc = (WNDPROC)::SetWindowLongPtrA(scrollableWnd, GWLP_WNDPROC,
 | |
|                                                 (LONG_PTR)nsWindow::WindowProc);
 | |
|     ::SetWindowLongPtrW(scrollableWnd, GWLP_USERDATA, (LONG_PTR)oldWndProc);
 | |
|   }
 | |
| 
 | |
|   SubclassWindow(TRUE);
 | |
| 
 | |
|   // Starting with Windows XP, a process always runs within a terminal services
 | |
|   // session. In order to play nicely with RDP, fast user switching, and the
 | |
|   // lock screen, we should be handling WM_WTSSESSION_CHANGE. We must register
 | |
|   // our HWND in order to receive this message.
 | |
|   DebugOnly<BOOL> wtsRegistered = ::WTSRegisterSessionNotification(mWnd,
 | |
|                                                        NOTIFY_FOR_THIS_SESSION);
 | |
|   NS_ASSERTION(wtsRegistered, "WTSRegisterSessionNotification failed!\n");
 | |
| 
 | |
|   mDefaultIMC.Init(this);
 | |
|   IMEHandler::InitInputContext(this, mInputContext);
 | |
| 
 | |
|   // Do some initialization work, but only if (a) it hasn't already been done,
 | |
|   // and (b) this is the hidden window (which is conveniently created before
 | |
|   // any visible windows but after the profile has been initialized).
 | |
|   if (!sHaveInitializedPrefs && mWindowType == eWindowType_invisible) {
 | |
|     sSwitchKeyboardLayout =
 | |
|       Preferences::GetBool("intl.keyboard.per_window_layout", false);
 | |
|     sHaveInitializedPrefs = true;
 | |
|   }
 | |
| 
 | |
|   // Query for command button metric data for rendering the titlebar. We
 | |
|   // only do this once on the first window that has an actual titlebar
 | |
|   if (ShouldCacheTitleBarInfo(mWindowType, mBorderStyle)) {
 | |
|     nsUXThemeData::UpdateTitlebarInfo(mWnd);
 | |
|   }
 | |
| 
 | |
|   static bool a11yPrimed = false;
 | |
|   if (!a11yPrimed &&
 | |
|       mWindowType == eWindowType_toplevel) {
 | |
|     a11yPrimed = true;
 | |
|     if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
 | |
|       ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0);
 | |
|     }
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // Close this nsWindow
 | |
| void nsWindow::Destroy()
 | |
| {
 | |
|   // WM_DESTROY has already fired, avoid calling it twice
 | |
|   if (mOnDestroyCalled)
 | |
|     return;
 | |
| 
 | |
|   // Don't destroy windows that have file pickers open, we'll tear these down
 | |
|   // later once the picker is closed.
 | |
|   mDestroyCalled = true;
 | |
|   if (mPickerDisplayCount)
 | |
|     return;
 | |
| 
 | |
|   // During the destruction of all of our children, make sure we don't get deleted.
 | |
|   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
 | |
| 
 | |
|   /**
 | |
|    * On windows the LayerManagerOGL destructor wants the widget to be around for
 | |
|    * cleanup. It also would like to have the HWND intact, so we nullptr it here.
 | |
|    */
 | |
|   DestroyLayerManager();
 | |
| 
 | |
|   /* We should clear our cached resources now and not wait for the GC to
 | |
|    * delete the nsWindow. */
 | |
|   ClearCachedResources();
 | |
| 
 | |
|   // The DestroyWindow function destroys the specified window. The function sends WM_DESTROY
 | |
|   // and WM_NCDESTROY messages to the window to deactivate it and remove the keyboard focus
 | |
|   // from it. The function also destroys the window's menu, flushes the thread message queue,
 | |
|   // destroys timers, removes clipboard ownership, and breaks the clipboard viewer chain (if
 | |
|   // the window is at the top of the viewer chain).
 | |
|   //
 | |
|   // If the specified window is a parent or owner window, DestroyWindow automatically destroys
 | |
|   // the associated child or owned windows when it destroys the parent or owner window. The
 | |
|   // function first destroys child or owned windows, and then it destroys the parent or owner
 | |
|   // window.
 | |
|   VERIFY(::DestroyWindow(mWnd));
 | |
|   
 | |
|   // Our windows can be subclassed which may prevent us receiving WM_DESTROY. If OnDestroy()
 | |
|   // didn't get called, call it now.
 | |
|   if (false == mOnDestroyCalled) {
 | |
|     MSGResult msgResult;
 | |
|     mWindowHook.Notify(mWnd, WM_DESTROY, 0, 0, msgResult);
 | |
|     OnDestroy();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Window class utilities
 | |
|  *
 | |
|  * Utilities for calculating the proper window class name for
 | |
|  * Create window.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| const wchar_t*
 | |
| nsWindow::RegisterWindowClass(const wchar_t* aClassName,
 | |
|                               UINT aExtraStyle, LPWSTR aIconID) const
 | |
| {
 | |
|   WNDCLASSW wc;
 | |
|   if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) {
 | |
|     // already registered
 | |
|     return aClassName;
 | |
|   }
 | |
| 
 | |
|   wc.style         = CS_DBLCLKS | aExtraStyle;
 | |
|   wc.lpfnWndProc   = WinUtils::NonClientDpiScalingDefWindowProcW;
 | |
|   wc.cbClsExtra    = 0;
 | |
|   wc.cbWndExtra    = 0;
 | |
|   wc.hInstance     = nsToolkit::mDllInstance;
 | |
|   wc.hIcon         = aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr;
 | |
|   wc.hCursor       = nullptr;
 | |
|   wc.hbrBackground = mBrush;
 | |
|   wc.lpszMenuName  = nullptr;
 | |
|   wc.lpszClassName = aClassName;
 | |
| 
 | |
|   if (!::RegisterClassW(&wc)) {
 | |
|     // For older versions of Win32 (i.e., not XP), the registration may
 | |
|     // fail with aExtraStyle, so we have to re-register without it.
 | |
|     wc.style = CS_DBLCLKS;
 | |
|     ::RegisterClassW(&wc);
 | |
|   }
 | |
|   return aClassName;
 | |
| }
 | |
| 
 | |
| static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);
 | |
| 
 | |
| // Return the proper window class for everything except popups.
 | |
| const wchar_t*
 | |
| nsWindow::GetWindowClass() const
 | |
| {
 | |
|   switch (mWindowType) {
 | |
|   case eWindowType_invisible:
 | |
|     return RegisterWindowClass(kClassNameHidden, 0, gStockApplicationIcon);
 | |
|   case eWindowType_dialog:
 | |
|     return RegisterWindowClass(kClassNameDialog, 0, 0);
 | |
|   default:
 | |
|     return RegisterWindowClass(GetMainWindowClass(), 0, gStockApplicationIcon);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Return the proper popup window class
 | |
| const wchar_t*
 | |
| nsWindow::GetWindowPopupClass() const
 | |
| {
 | |
|   return RegisterWindowClass(kClassNameDropShadow,
 | |
|                              CS_XP_DROPSHADOW, gStockApplicationIcon);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Window styles utilities
 | |
|  *
 | |
|  * Return the proper windows styles and extended styles.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Return nsWindow styles
 | |
| DWORD nsWindow::WindowStyle()
 | |
| {
 | |
|   DWORD style;
 | |
| 
 | |
|   switch (mWindowType) {
 | |
|     case eWindowType_plugin:
 | |
|     case eWindowType_plugin_ipc_chrome:
 | |
|     case eWindowType_plugin_ipc_content:
 | |
|     case eWindowType_child:
 | |
|       style = WS_OVERLAPPED;
 | |
|       break;
 | |
| 
 | |
|     case eWindowType_dialog:
 | |
|       style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU | DS_3DLOOK |
 | |
|               DS_MODALFRAME | WS_CLIPCHILDREN;
 | |
|       if (mBorderStyle != eBorderStyle_default)
 | |
|         style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
 | |
|       break;
 | |
| 
 | |
|     case eWindowType_popup:
 | |
|       style = WS_POPUP;
 | |
|       if (!HasGlass()) {
 | |
|         style |= WS_OVERLAPPED;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       NS_ERROR("unknown border style");
 | |
|       // fall through
 | |
| 
 | |
|     case eWindowType_toplevel:
 | |
|     case eWindowType_invisible:
 | |
|       style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU |
 | |
|               WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPCHILDREN;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   if (mBorderStyle != eBorderStyle_default && mBorderStyle != eBorderStyle_all) {
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_border))
 | |
|       style &= ~WS_BORDER;
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_title)) {
 | |
|       style &= ~WS_DLGFRAME;
 | |
|       style |= WS_POPUP;
 | |
|       style &= ~WS_CHILD;
 | |
|     }
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_close))
 | |
|       style &= ~0;
 | |
|     // XXX The close box can only be removed by changing the window class,
 | |
|     // as far as I know   --- roc+moz@cs.cmu.edu
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none ||
 | |
|       !(mBorderStyle & (eBorderStyle_menu | eBorderStyle_close)))
 | |
|       style &= ~WS_SYSMENU;
 | |
|     // Looks like getting rid of the system menu also does away with the
 | |
|     // close box. So, we only get rid of the system menu if you want neither it
 | |
|     // nor the close box. How does the Windows "Dialog" window class get just
 | |
|     // closebox and no sysmenu? Who knows.
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_resizeh))
 | |
|       style &= ~WS_THICKFRAME;
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_minimize))
 | |
|       style &= ~WS_MINIMIZEBOX;
 | |
| 
 | |
|     if (mBorderStyle == eBorderStyle_none || !(mBorderStyle & eBorderStyle_maximize))
 | |
|       style &= ~WS_MAXIMIZEBOX;
 | |
| 
 | |
|     if (IsPopupWithTitleBar()) {
 | |
|       style |= WS_CAPTION;
 | |
|       if (mBorderStyle & eBorderStyle_close) {
 | |
|         style |= WS_SYSMENU;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mIsChildWindow) {
 | |
|     style |= WS_CLIPCHILDREN;
 | |
|     if (!(style & WS_POPUP)) {
 | |
|       style |= WS_CHILD; // WS_POPUP and WS_CHILD are mutually exclusive.
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   VERIFY_WINDOW_STYLE(style);
 | |
|   return style;
 | |
| }
 | |
| 
 | |
| // Return nsWindow extended styles
 | |
| DWORD nsWindow::WindowExStyle()
 | |
| {
 | |
|   switch (mWindowType)
 | |
|   {
 | |
|     case eWindowType_plugin:
 | |
|     case eWindowType_plugin_ipc_chrome:
 | |
|     case eWindowType_plugin_ipc_content:
 | |
|     case eWindowType_child:
 | |
|       return 0;
 | |
| 
 | |
|     case eWindowType_dialog:
 | |
|       return WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME;
 | |
| 
 | |
|     case eWindowType_popup:
 | |
|     {
 | |
|       DWORD extendedStyle = WS_EX_TOOLWINDOW;
 | |
|       if (mPopupLevel == ePopupLevelTop)
 | |
|         extendedStyle |= WS_EX_TOPMOST;
 | |
|       return extendedStyle;
 | |
|     }
 | |
|     default:
 | |
|       NS_ERROR("unknown border style");
 | |
|       // fall through
 | |
| 
 | |
|     case eWindowType_toplevel:
 | |
|     case eWindowType_invisible:
 | |
|       return WS_EX_WINDOWEDGE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Window subclassing utilities
 | |
|  *
 | |
|  * Set or clear window subclasses on native windows. Used in
 | |
|  * Create and Destroy.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Subclass (or remove the subclass from) this component's nsWindow
 | |
| void nsWindow::SubclassWindow(BOOL bState)
 | |
| {
 | |
|   if (bState) {
 | |
|     if (!mWnd || !IsWindow(mWnd)) {
 | |
|       NS_ERROR("Invalid window handle");
 | |
|     }
 | |
| 
 | |
|     if (mUnicodeWidget) {
 | |
|       mPrevWndProc =
 | |
|         reinterpret_cast<WNDPROC>(
 | |
|           SetWindowLongPtrW(mWnd,
 | |
|                             GWLP_WNDPROC,
 | |
|                             reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
 | |
|     } else {
 | |
|       mPrevWndProc =
 | |
|         reinterpret_cast<WNDPROC>(
 | |
|           SetWindowLongPtrA(mWnd,
 | |
|                             GWLP_WNDPROC,
 | |
|                             reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
 | |
|     }
 | |
|     NS_ASSERTION(mPrevWndProc, "Null standard window procedure");
 | |
|     // connect the this pointer to the nsWindow handle
 | |
|     WinUtils::SetNSWindowBasePtr(mWnd, this);
 | |
|   } else {
 | |
|     if (IsWindow(mWnd)) {
 | |
|       if (mUnicodeWidget) {
 | |
|         SetWindowLongPtrW(mWnd,
 | |
|                           GWLP_WNDPROC,
 | |
|                           reinterpret_cast<LONG_PTR>(mPrevWndProc));
 | |
|       } else {
 | |
|         SetWindowLongPtrA(mWnd,
 | |
|                           GWLP_WNDPROC,
 | |
|                           reinterpret_cast<LONG_PTR>(mPrevWndProc));
 | |
|       }
 | |
|     }
 | |
|     WinUtils::SetNSWindowBasePtr(mWnd, nullptr);
 | |
|     mPrevWndProc = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetParent, nsIWidget::GetParent
 | |
|  *
 | |
|  * Set or clear the parent widgets using window properties, and
 | |
|  * handles calculating native parent handles.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Get and set parent widgets
 | |
| void
 | |
| nsWindow::SetParent(nsIWidget *aNewParent)
 | |
| {
 | |
|   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
 | |
|   nsIWidget* parent = GetParent();
 | |
|   if (parent) {
 | |
|     parent->RemoveChild(this);
 | |
|   }
 | |
| 
 | |
|   mParent = aNewParent;
 | |
| 
 | |
|   if (aNewParent) {
 | |
|     ReparentNativeWidget(aNewParent);
 | |
|     aNewParent->AddChild(this);
 | |
|     return;
 | |
|   }
 | |
|   if (mWnd) {
 | |
|     // If we have no parent, SetParent should return the desktop.
 | |
|     VERIFY(::SetParent(mWnd, nullptr));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::ReparentNativeWidget(nsIWidget* aNewParent)
 | |
| {
 | |
|   MOZ_ASSERT(aNewParent, "null widget");
 | |
| 
 | |
|   mParent = aNewParent;
 | |
|   if (mWindowType == eWindowType_popup) {
 | |
|     return;
 | |
|   }
 | |
|   HWND newParent = (HWND)aNewParent->GetNativeData(NS_NATIVE_WINDOW);
 | |
|   NS_ASSERTION(newParent, "Parent widget has a null native window handle");
 | |
|   if (newParent && mWnd) {
 | |
|     ::SetParent(mWnd, newParent);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsIWidget* nsWindow::GetParent(void)
 | |
| {
 | |
|   if (mIsTopWidgetWindow) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   if (mInDtor || mOnDestroyCalled) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return mParent;
 | |
| }
 | |
| 
 | |
| static int32_t RoundDown(double aDouble)
 | |
| {
 | |
|   return aDouble > 0 ? static_cast<int32_t>(floor(aDouble)) :
 | |
|                        static_cast<int32_t>(ceil(aDouble));
 | |
| }
 | |
| 
 | |
| float nsWindow::GetDPI()
 | |
| {
 | |
|   float dpi = 96.0f;
 | |
|   nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
 | |
|   if (screen) {
 | |
|     screen->GetDpi(&dpi);
 | |
|   }
 | |
|   return dpi;
 | |
| }
 | |
| 
 | |
| double nsWindow::GetDefaultScaleInternal()
 | |
| {
 | |
|   if (mDefaultScale <= 0.0) {
 | |
|     mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
 | |
|   }
 | |
|   return mDefaultScale;
 | |
| }
 | |
| 
 | |
| int32_t nsWindow::LogToPhys(double aValue)
 | |
| {
 | |
|   return WinUtils::LogToPhys(::MonitorFromWindow(mWnd,
 | |
|                                                  MONITOR_DEFAULTTOPRIMARY),
 | |
|                              aValue);
 | |
| }
 | |
| 
 | |
| nsWindow*
 | |
| nsWindow::GetParentWindow(bool aIncludeOwner)
 | |
| {
 | |
|   return static_cast<nsWindow*>(GetParentWindowBase(aIncludeOwner));
 | |
| }
 | |
| 
 | |
| nsWindowBase*
 | |
| nsWindow::GetParentWindowBase(bool aIncludeOwner)
 | |
| {
 | |
|   if (mIsTopWidgetWindow) {
 | |
|     // Must use a flag instead of mWindowType to tell if the window is the
 | |
|     // owned by the topmost widget, because a child window can be embedded inside
 | |
|     // a HWND which is not associated with a nsIWidget.
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // If this widget has already been destroyed, pretend we have no parent.
 | |
|   // This corresponds to code in Destroy which removes the destroyed
 | |
|   // widget from its parent's child list.
 | |
|   if (mInDtor || mOnDestroyCalled)
 | |
|     return nullptr;
 | |
| 
 | |
| 
 | |
|   // aIncludeOwner set to true implies walking the parent chain to retrieve the
 | |
|   // root owner. aIncludeOwner set to false implies the search will stop at the
 | |
|   // true parent (default).
 | |
|   nsWindow* widget = nullptr;
 | |
|   if (mWnd) {
 | |
|     HWND parent = nullptr;
 | |
|     if (aIncludeOwner)
 | |
|       parent = ::GetParent(mWnd);
 | |
|     else
 | |
|       parent = ::GetAncestor(mWnd, GA_PARENT);
 | |
| 
 | |
|     if (parent) {
 | |
|       widget = WinUtils::GetNSWindowPtr(parent);
 | |
|       if (widget) {
 | |
|         // If the widget is in the process of being destroyed then
 | |
|         // do NOT return it
 | |
|         if (widget->mInDtor) {
 | |
|           widget = nullptr;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return static_cast<nsWindowBase*>(widget);
 | |
| }
 | |
|  
 | |
| BOOL CALLBACK
 | |
| nsWindow::EnumAllChildWindProc(HWND aWnd, LPARAM aParam)
 | |
| {
 | |
|   nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
 | |
|   if (wnd) {
 | |
|     reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| BOOL CALLBACK
 | |
| nsWindow::EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam)
 | |
| {
 | |
|   nsWindow *wnd = WinUtils::GetNSWindowPtr(aWnd);
 | |
|   if (wnd) {
 | |
|     reinterpret_cast<nsTArray<nsWindow*>*>(aParam)->AppendElement(wnd);
 | |
|   }
 | |
|   EnumChildWindows(aWnd, EnumAllChildWindProc, aParam);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /* static*/ nsTArray<nsWindow*>
 | |
| nsWindow::EnumAllWindows()
 | |
| {
 | |
|   nsTArray<nsWindow*> windows;
 | |
|   EnumThreadWindows(GetCurrentThreadId(),
 | |
|                     EnumAllThreadWindowProc,
 | |
|                     reinterpret_cast<LPARAM>(&windows));
 | |
|   return windows;
 | |
| }
 | |
| 
 | |
| static already_AddRefed<SourceSurface>
 | |
| CreateSourceSurfaceForGfxSurface(gfxASurface* aSurface)
 | |
| {
 | |
|   MOZ_ASSERT(aSurface);
 | |
|   return Factory::CreateSourceSurfaceForCairoSurface(
 | |
|            aSurface->CairoSurface(), aSurface->GetSize(),
 | |
|            aSurface->GetSurfaceFormat());
 | |
| }
 | |
| 
 | |
| nsWindow::ScrollSnapshot*
 | |
| nsWindow::EnsureSnapshotSurface(ScrollSnapshot& aSnapshotData,
 | |
|                                 const mozilla::gfx::IntSize& aSize)
 | |
| {
 | |
|   // If the surface doesn't exist or is the wrong size then create new one.
 | |
|   if (!aSnapshotData.surface || aSnapshotData.surface->GetSize() != aSize) {
 | |
|     aSnapshotData.surface = new gfxWindowsSurface(aSize, kScrollCaptureFormat);
 | |
|     aSnapshotData.surfaceHasSnapshot = false;
 | |
|   }
 | |
| 
 | |
|   return &aSnapshotData;
 | |
| }
 | |
| 
 | |
| already_AddRefed<SourceSurface>
 | |
| nsWindow::CreateScrollSnapshot()
 | |
| {
 | |
|   RECT clip = { 0 };
 | |
|   int rgnType = ::GetWindowRgnBox(mWnd, &clip);
 | |
|   if (rgnType == RGN_ERROR) {
 | |
|     // We failed to get the clip assume that we need a full fallback.
 | |
|     clip.left = 0;
 | |
|     clip.top = 0;
 | |
|     clip.right = mBounds.Width();
 | |
|     clip.bottom = mBounds.Height();
 | |
|     return GetFallbackScrollSnapshot(clip);
 | |
|   }
 | |
| 
 | |
|   // Check that the window is in a position to snapshot. We don't check for
 | |
|   // clipped width as that doesn't currently matter for APZ scrolling.
 | |
|   if (clip.top || clip.bottom != mBounds.Height()) {
 | |
|     return GetFallbackScrollSnapshot(clip);
 | |
|   }
 | |
| 
 | |
|   HDC windowDC = ::GetDC(mWnd);
 | |
|   if (!windowDC) {
 | |
|     return GetFallbackScrollSnapshot(clip);
 | |
|   }
 | |
|   auto releaseDC = MakeScopeExit([&] {
 | |
|     ::ReleaseDC(mWnd, windowDC);
 | |
|   });
 | |
| 
 | |
|   gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
 | |
|   ScrollSnapshot* snapshot;
 | |
|   if (clip.left || clip.right != mBounds.Width()) {
 | |
|     // Can't do a full snapshot, so use the partial snapshot.
 | |
|     snapshot = EnsureSnapshotSurface(mPartialSnapshot, snapshotSize);
 | |
|   } else {
 | |
|     snapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
 | |
|   }
 | |
| 
 | |
|   // Note that we know that the clip is full height.
 | |
|   if (!::BitBlt(snapshot->surface->GetDC(), clip.left, 0, clip.right - clip.left,
 | |
|                 clip.bottom, windowDC, clip.left, 0, SRCCOPY)) {
 | |
|     return GetFallbackScrollSnapshot(clip);
 | |
|   }
 | |
|   ::GdiFlush();
 | |
|   snapshot->surface->Flush();
 | |
|   snapshot->surfaceHasSnapshot = true;
 | |
|   snapshot->clip = clip;
 | |
|   mCurrentSnapshot = snapshot;
 | |
| 
 | |
|   return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
 | |
| }
 | |
| 
 | |
| already_AddRefed<SourceSurface>
 | |
| nsWindow::GetFallbackScrollSnapshot(const RECT& aRequiredClip)
 | |
| {
 | |
|   gfx::IntSize snapshotSize(mBounds.Width(), mBounds.Height());
 | |
| 
 | |
|   // If the current snapshot is the correct size and covers the required clip,
 | |
|   // just keep that by returning null.
 | |
|   // Note: we know the clip is always full height.
 | |
|   if (mCurrentSnapshot &&
 | |
|       mCurrentSnapshot->surface->GetSize() == snapshotSize &&
 | |
|       mCurrentSnapshot->clip.left <= aRequiredClip.left &&
 | |
|       mCurrentSnapshot->clip.right >= aRequiredClip.right) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Otherwise we'll use the full snapshot, making sure it is big enough first.
 | |
|   mCurrentSnapshot = EnsureSnapshotSurface(mFullSnapshot, snapshotSize);
 | |
| 
 | |
|   // If there is no snapshot, create a default.
 | |
|   if (!mCurrentSnapshot->surfaceHasSnapshot) {
 | |
|     gfx::SurfaceFormat format = mCurrentSnapshot->surface->GetSurfaceFormat();
 | |
|     RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForCairoSurface(
 | |
|       mCurrentSnapshot->surface->CairoSurface(),
 | |
|       mCurrentSnapshot->surface->GetSize(), &format);
 | |
| 
 | |
|     DefaultFillScrollCapture(dt);
 | |
|   }
 | |
| 
 | |
|   return CreateSourceSurfaceForGfxSurface(mCurrentSnapshot->surface);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::Show
 | |
|  *
 | |
|  * Hide or show this component.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::Show(bool bState)
 | |
| {
 | |
|   if (mWindowType == eWindowType_popup) {
 | |
|     // See bug 603793. When we try to draw D3D9/10 windows with a drop shadow
 | |
|     // without the DWM on a secondary monitor, windows fails to composite
 | |
|     // our windows correctly. We therefor switch off the drop shadow for
 | |
|     // pop-up windows when the DWM is disabled and two monitors are
 | |
|     // connected.
 | |
|     if (HasBogusPopupsDropShadowOnMultiMonitor() &&
 | |
|         WinUtils::GetMonitorCount() > 1 &&
 | |
|         !nsUXThemeData::CheckForCompositor())
 | |
|     {
 | |
|       if (sDropShadowEnabled) {
 | |
|         ::SetClassLongA(mWnd, GCL_STYLE, 0);
 | |
|         sDropShadowEnabled = false;
 | |
|       }
 | |
|     } else {
 | |
|       if (!sDropShadowEnabled) {
 | |
|         ::SetClassLongA(mWnd, GCL_STYLE, CS_DROPSHADOW);
 | |
|         sDropShadowEnabled = true;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // WS_EX_COMPOSITED conflicts with the WS_EX_LAYERED style and causes
 | |
|     // some popup menus to become invisible.
 | |
|     LONG_PTR exStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
 | |
|     if (exStyle & WS_EX_LAYERED) {
 | |
|       ::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, exStyle & ~WS_EX_COMPOSITED);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool syncInvalidate = false;
 | |
| 
 | |
|   bool wasVisible = mIsVisible;
 | |
|   // Set the status now so that anyone asking during ShowWindow or
 | |
|   // SetWindowPos would get the correct answer.
 | |
|   mIsVisible = bState;
 | |
| 
 | |
|   // We may have cached an out of date visible state. This can happen
 | |
|   // when session restore sets the full screen mode.
 | |
|   if (mIsVisible)
 | |
|     mOldStyle |= WS_VISIBLE;
 | |
|   else
 | |
|     mOldStyle &= ~WS_VISIBLE;
 | |
| 
 | |
|   if (!mIsVisible && wasVisible) {
 | |
|       ClearCachedResources();
 | |
|   }
 | |
| 
 | |
|   if (mWnd) {
 | |
|     if (bState) {
 | |
|       if (!wasVisible && mWindowType == eWindowType_toplevel) {
 | |
|         // speed up the initial paint after show for
 | |
|         // top level windows:
 | |
|         syncInvalidate = true;
 | |
| 
 | |
|         // Set the cursor before showing the window to avoid the default wait
 | |
|         // cursor.
 | |
|         SetCursor(eCursor_standard);
 | |
| 
 | |
|         switch (mSizeMode) {
 | |
|           case nsSizeMode_Fullscreen:
 | |
|             ::ShowWindow(mWnd, SW_SHOW);
 | |
|             break;
 | |
|           case nsSizeMode_Maximized :
 | |
|             ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
 | |
|             break;
 | |
|           case nsSizeMode_Minimized :
 | |
|             ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
 | |
|             break;
 | |
|           default:
 | |
|             if (CanTakeFocus()) {
 | |
|               ::ShowWindow(mWnd, SW_SHOWNORMAL);
 | |
|             } else {
 | |
|               ::ShowWindow(mWnd, SW_SHOWNOACTIVATE);
 | |
|               Unused << GetAttention(2);
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|       } else {
 | |
|         DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
 | |
|         if (wasVisible)
 | |
|           flags |= SWP_NOZORDER;
 | |
| 
 | |
|         if (mWindowType == eWindowType_popup) {
 | |
|           // ensure popups are the topmost of the TOPMOST
 | |
|           // layer. Remember not to set the SWP_NOZORDER
 | |
|           // flag as that might allow the taskbar to overlap
 | |
|           // the popup.
 | |
|           flags |= SWP_NOACTIVATE;
 | |
|           HWND owner = ::GetWindow(mWnd, GW_OWNER);
 | |
|           ::SetWindowPos(mWnd, owner ? 0 : HWND_TOPMOST, 0, 0, 0, 0, flags);
 | |
|         } else {
 | |
|           if (mWindowType == eWindowType_dialog && !CanTakeFocus())
 | |
|             flags |= SWP_NOACTIVATE;
 | |
| 
 | |
|           ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (!wasVisible && (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)) {
 | |
|         // when a toplevel window or dialog is shown, initialize the UI state
 | |
|         ::SendMessageW(mWnd, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEFOCUS | UISF_HIDEACCEL), 0);
 | |
|       }
 | |
|     } else {
 | |
|       // Clear contents to avoid ghosting of old content if we display
 | |
|       // this window again.
 | |
|       if (wasVisible && mTransparencyMode == eTransparencyTransparent) {
 | |
|         if (mCompositorWidgetDelegate) {
 | |
|           mCompositorWidgetDelegate->ClearTransparentWindow();
 | |
|         }
 | |
|       }
 | |
|       if (mWindowType != eWindowType_dialog) {
 | |
|         ::ShowWindow(mWnd, SW_HIDE);
 | |
|       } else {
 | |
|         ::SetWindowPos(mWnd, 0, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE |
 | |
|                        SWP_NOZORDER | SWP_NOACTIVATE);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_XUL
 | |
|   if (!wasVisible && bState) {
 | |
|     Invalidate();
 | |
|     if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
 | |
|       ::UpdateWindow(mWnd);
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (mOpeningAnimationSuppressed) {
 | |
|     SuppressAnimation(false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::IsVisible
 | |
|  *
 | |
|  * Returns the visibility state.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Return true if the whether the component is visible, false otherwise
 | |
| bool nsWindow::IsVisible() const
 | |
| {
 | |
|   return mIsVisible;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Window clipping utilities
 | |
|  *
 | |
|  * Used in Size and Move operations for setting the proper
 | |
|  * window clipping regions for window transparency.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // XP and Vista visual styles sometimes require window clipping regions to be applied for proper
 | |
| // transparency. These routines are called on size and move operations.
 | |
| // XXX this is apparently still needed in Windows 7 and later
 | |
| void nsWindow::ClearThemeRegion()
 | |
| {
 | |
|   if (!HasGlass() &&
 | |
|       (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
 | |
|        (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
 | |
|     SetWindowRgn(mWnd, nullptr, false);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsWindow::SetThemeRegion()
 | |
| {
 | |
|   // Popup types that have a visual styles region applied (bug 376408). This can be expanded
 | |
|   // for other window types as needed. The regions are applied generically to the base window
 | |
|   // so default constants are used for part and state. At some point we might need part and
 | |
|   // state values from nsNativeThemeWin's GetThemePartAndState, but currently windows that
 | |
|   // change shape based on state haven't come up.
 | |
|   if (!HasGlass() &&
 | |
|       (mWindowType == eWindowType_popup && !IsPopupWithTitleBar() &&
 | |
|        (mPopupType == ePopupTypeTooltip || mPopupType == ePopupTypePanel))) {
 | |
|     HRGN hRgn = nullptr;
 | |
|     RECT rect = {0,0,mBounds.Width(),mBounds.Height()};
 | |
|     
 | |
|     HDC dc = ::GetDC(mWnd);
 | |
|     GetThemeBackgroundRegion(nsUXThemeData::GetTheme(eUXTooltip), dc, TTP_STANDARD, TS_NORMAL, &rect, &hRgn);
 | |
|     if (hRgn) {
 | |
|       if (!SetWindowRgn(mWnd, hRgn, false)) // do not delete or alter hRgn if accepted.
 | |
|         DeleteObject(hRgn);
 | |
|     }
 | |
|     ::ReleaseDC(mWnd, dc);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Touch and APZ-related functions
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void nsWindow::RegisterTouchWindow() {
 | |
|   mTouchWindow = true;
 | |
|   ::RegisterTouchWindow(mWnd, TWF_WANTPALM);
 | |
|   ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
 | |
| }
 | |
| 
 | |
| BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
 | |
|   nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
 | |
|   if (win) {
 | |
|     ::RegisterTouchWindow(aWnd, TWF_WANTPALM);
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::Move, nsIWidget::Resize,
 | |
|  * nsIWidget::Size, nsIWidget::BeginResizeDrag
 | |
|  *
 | |
|  * Repositioning and sizing a window.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints)
 | |
| {
 | |
|   SizeConstraints c = aConstraints;
 | |
|   if (mWindowType != eWindowType_popup) {
 | |
|     c.mMinSize.width = std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
 | |
|     c.mMinSize.height = std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
 | |
|   }
 | |
|   KnowsCompositor* knowsCompositor = GetLayerManager()->AsKnowsCompositor();
 | |
|   if (knowsCompositor) {
 | |
|     int32_t maxSize = knowsCompositor->GetMaxTextureSize();
 | |
|     // We can't make ThebesLayers bigger than this anyway.. no point it letting
 | |
|     // a window grow bigger as we won't be able to draw content there in
 | |
|     // general.
 | |
|     c.mMaxSize.width = std::min(c.mMaxSize.width, maxSize);
 | |
|     c.mMaxSize.height = std::min(c.mMaxSize.height, maxSize);
 | |
|   }
 | |
| 
 | |
|   mSizeConstraintsScale = GetDefaultScale().scale;
 | |
| 
 | |
|   nsBaseWidget::SetSizeConstraints(c);
 | |
| }
 | |
| 
 | |
| const SizeConstraints
 | |
| nsWindow::GetSizeConstraints()
 | |
| {
 | |
|   double scale = GetDefaultScale().scale;
 | |
|   if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
 | |
|     return mSizeConstraints;
 | |
|   }
 | |
|   scale /= mSizeConstraintsScale;
 | |
|   SizeConstraints c = mSizeConstraints;
 | |
|   if (c.mMinSize.width != NS_MAXSIZE) {
 | |
|     c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
 | |
|   }
 | |
|   if (c.mMinSize.height != NS_MAXSIZE) {
 | |
|     c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
 | |
|   }
 | |
|   if (c.mMaxSize.width != NS_MAXSIZE) {
 | |
|     c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
 | |
|   }
 | |
|   if (c.mMaxSize.height != NS_MAXSIZE) {
 | |
|     c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
 | |
|   }
 | |
|   return c;
 | |
| }
 | |
| 
 | |
| // Move this component
 | |
| void
 | |
| nsWindow::Move(double aX, double aY)
 | |
| {
 | |
|   if (mWindowType == eWindowType_toplevel ||
 | |
|       mWindowType == eWindowType_dialog) {
 | |
|     SetSizeMode(nsSizeMode_Normal);
 | |
|   }
 | |
| 
 | |
|   // for top-level windows only, convert coordinates from desktop pixels
 | |
|   // (the "parent" coordinate space) to the window's device pixel space
 | |
|   double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
 | |
|   int32_t x = NSToIntRound(aX * scale);
 | |
|   int32_t y = NSToIntRound(aY * scale);
 | |
| 
 | |
|   // Check to see if window needs to be moved first
 | |
|   // to avoid a costly call to SetWindowPos. This check
 | |
|   // can not be moved to the calling code in nsView, because
 | |
|   // some platforms do not position child windows correctly
 | |
| 
 | |
|   // Only perform this check for non-popup windows, since the positioning can
 | |
|   // in fact change even when the x/y do not.  We always need to perform the
 | |
|   // check. See bug #97805 for details.
 | |
|   if (mWindowType != eWindowType_popup && mBounds.IsEqualXY(x, y))
 | |
|   {
 | |
|     // Nothing to do, since it is already positioned correctly.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mBounds.MoveTo(x, y);
 | |
| 
 | |
|   if (mWnd) {
 | |
| #ifdef DEBUG
 | |
|     // complain if a window is moved offscreen (legal, but potentially worrisome)
 | |
|     if (mIsTopWidgetWindow) { // only a problem for top-level windows
 | |
|       // Make sure this window is actually on the screen before we move it
 | |
|       // XXX: Needs multiple monitor support
 | |
|       HDC dc = ::GetDC(mWnd);
 | |
|       if (dc) {
 | |
|         if (::GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) {
 | |
|           RECT workArea;
 | |
|           ::SystemParametersInfo(SPI_GETWORKAREA, 0, &workArea, 0);
 | |
|           // no annoying assertions. just mention the issue.
 | |
|           if (x < 0 || x >= workArea.right || y < 0 || y >= workArea.bottom) {
 | |
|             MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|                    ("window moved to offscreen position\n"));
 | |
|           }
 | |
|         }
 | |
|       ::ReleaseDC(mWnd, dc);
 | |
|       }
 | |
|     }
 | |
| #endif
 | |
|     ClearThemeRegion();
 | |
| 
 | |
|     UINT flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE;
 | |
|     // Workaround SetWindowPos bug with D3D9. If our window has a clip
 | |
|     // region, some drivers or OSes may incorrectly copy into the clipped-out
 | |
|     // area.
 | |
|     if (IsPlugin() &&
 | |
|         !mLayerManager &&
 | |
|         mClipRects &&
 | |
|         (mClipRectCount != 1 || !mClipRects[0].IsEqualInterior(LayoutDeviceIntRect(0, 0, mBounds.Width(), mBounds.Height())))) {
 | |
|       flags |= SWP_NOCOPYBITS;
 | |
|     }
 | |
|     double oldScale = mDefaultScale;
 | |
|     mResizeState = IN_SIZEMOVE;
 | |
|     VERIFY(::SetWindowPos(mWnd, nullptr, x, y, 0, 0, flags));
 | |
|     mResizeState = NOT_RESIZING;
 | |
|     if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
 | |
|       ChangedDPI();
 | |
|     }
 | |
| 
 | |
|     SetThemeRegion();
 | |
|   }
 | |
|   NotifyRollupGeometryChange();
 | |
| }
 | |
| 
 | |
| // Resize this component
 | |
| void
 | |
| nsWindow::Resize(double aWidth, double aHeight, bool aRepaint)
 | |
| {
 | |
|   // for top-level windows only, convert coordinates from desktop pixels
 | |
|   // (the "parent" coordinate space) to the window's device pixel space
 | |
|   double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
 | |
|   int32_t width = NSToIntRound(aWidth * scale);
 | |
|   int32_t height = NSToIntRound(aHeight * scale);
 | |
| 
 | |
|   NS_ASSERTION((width >= 0) , "Negative width passed to nsWindow::Resize");
 | |
|   NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
 | |
| 
 | |
|   ConstrainSize(&width, &height);
 | |
| 
 | |
|   // Avoid unnecessary resizing calls
 | |
|   if (mBounds.IsEqualSize(width, height)) {
 | |
|     if (aRepaint) {
 | |
|       Invalidate();
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Set cached value for lightweight and printing
 | |
|   mBounds.SizeTo(width, height);
 | |
| 
 | |
|   if (mWnd) {
 | |
|     UINT  flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE;
 | |
| 
 | |
|     if (!aRepaint) {
 | |
|       flags |= SWP_NOREDRAW;
 | |
|     }
 | |
| 
 | |
|     ClearThemeRegion();
 | |
|     double oldScale = mDefaultScale;
 | |
|     VERIFY(::SetWindowPos(mWnd, nullptr, 0, 0,
 | |
|                           width, GetHeight(height), flags));
 | |
|     if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
 | |
|       ChangedDPI();
 | |
|     }
 | |
|     SetThemeRegion();
 | |
|   }
 | |
| 
 | |
|   if (aRepaint)
 | |
|     Invalidate();
 | |
| 
 | |
|   NotifyRollupGeometryChange();
 | |
| }
 | |
| 
 | |
| // Resize this component
 | |
| void
 | |
| nsWindow::Resize(double aX, double aY, double aWidth,
 | |
|                  double aHeight, bool aRepaint)
 | |
| {
 | |
|   // for top-level windows only, convert coordinates from desktop pixels
 | |
|   // (the "parent" coordinate space) to the window's device pixel space
 | |
|   double scale = BoundsUseDesktopPixels() ? GetDesktopToDeviceScale().scale : 1.0;
 | |
|   int32_t x = NSToIntRound(aX * scale);
 | |
|   int32_t y = NSToIntRound(aY * scale);
 | |
|   int32_t width = NSToIntRound(aWidth * scale);
 | |
|   int32_t height = NSToIntRound(aHeight * scale);
 | |
| 
 | |
|   NS_ASSERTION((width >= 0),  "Negative width passed to nsWindow::Resize");
 | |
|   NS_ASSERTION((height >= 0), "Negative height passed to nsWindow::Resize");
 | |
| 
 | |
|   ConstrainSize(&width, &height);
 | |
| 
 | |
|   // Avoid unnecessary resizing calls
 | |
|   if (mBounds.IsEqualRect(x, y, width, height)) {
 | |
|     if (aRepaint) {
 | |
|       Invalidate();
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Set cached value for lightweight and printing
 | |
|   mBounds.SetRect(x, y, width, height);
 | |
| 
 | |
|   if (mWnd) {
 | |
|     UINT  flags = SWP_NOZORDER | SWP_NOACTIVATE;
 | |
|     if (!aRepaint) {
 | |
|       flags |= SWP_NOREDRAW;
 | |
|     }
 | |
| 
 | |
|     ClearThemeRegion();
 | |
|     double oldScale = mDefaultScale;
 | |
|     VERIFY(::SetWindowPos(mWnd, nullptr, x, y,
 | |
|                           width, GetHeight(height), flags));
 | |
|     if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
 | |
|       ChangedDPI();
 | |
|     }
 | |
|     if (mTransitionWnd) {
 | |
|       // If we have a fullscreen transition window, we need to make
 | |
|       // it topmost again, otherwise the taskbar may be raised by
 | |
|       // the system unexpectedly when we leave fullscreen state.
 | |
|       ::SetWindowPos(mTransitionWnd, HWND_TOPMOST, 0, 0, 0, 0,
 | |
|                      SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
 | |
|     }
 | |
|     SetThemeRegion();
 | |
|   }
 | |
| 
 | |
|   if (aRepaint)
 | |
|     Invalidate();
 | |
| 
 | |
|   NotifyRollupGeometryChange();
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::BeginResizeDrag(WidgetGUIEvent* aEvent,
 | |
|                           int32_t aHorizontal,
 | |
|                           int32_t aVertical)
 | |
| {
 | |
|   NS_ENSURE_ARG_POINTER(aEvent);
 | |
| 
 | |
|   if (aEvent->mClass != eMouseEventClass) {
 | |
|     // you can only begin a resize drag with a mouse event
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
| 
 | |
|   if (aEvent->AsMouseEvent()->button != WidgetMouseEvent::eLeftButton) {
 | |
|     // you can only begin a resize drag with the left mouse button
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
|   }
 | |
| 
 | |
|   // work out what sizemode we're talking about
 | |
|   WPARAM syscommand;
 | |
|   if (aVertical < 0) {
 | |
|     if (aHorizontal < 0) {
 | |
|       syscommand = SC_SIZE | WMSZ_TOPLEFT;
 | |
|     } else if (aHorizontal == 0) {
 | |
|       syscommand = SC_SIZE | WMSZ_TOP;
 | |
|     } else {
 | |
|       syscommand = SC_SIZE | WMSZ_TOPRIGHT;
 | |
|     }
 | |
|   } else if (aVertical == 0) {
 | |
|     if (aHorizontal < 0) {
 | |
|       syscommand = SC_SIZE | WMSZ_LEFT;
 | |
|     } else if (aHorizontal == 0) {
 | |
|       return NS_ERROR_INVALID_ARG;
 | |
|     } else {
 | |
|       syscommand = SC_SIZE | WMSZ_RIGHT;
 | |
|     }
 | |
|   } else {
 | |
|     if (aHorizontal < 0) {
 | |
|       syscommand = SC_SIZE | WMSZ_BOTTOMLEFT;
 | |
|     } else if (aHorizontal == 0) {
 | |
|       syscommand = SC_SIZE | WMSZ_BOTTOM;
 | |
|     } else {
 | |
|       syscommand = SC_SIZE | WMSZ_BOTTOMRIGHT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // resizing doesn't work if the mouse is already captured
 | |
|   CaptureMouse(false);
 | |
| 
 | |
|   // find the top-level window
 | |
|   HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd, true);
 | |
| 
 | |
|   // tell Windows to start the resize
 | |
|   ::PostMessage(toplevelWnd, WM_SYSCOMMAND, syscommand,
 | |
|                 POINTTOPOINTS(aEvent->mRefPoint));
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Window Z-order and state.
 | |
|  *
 | |
|  * nsIWidget::PlaceBehind, nsIWidget::SetSizeMode,
 | |
|  * nsIWidget::ConstrainPosition
 | |
|  *
 | |
|  * Z-order, positioning, restore, minimize, and maximize.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Position the window behind the given window
 | |
| void
 | |
| nsWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement,
 | |
|                       nsIWidget *aWidget, bool aActivate)
 | |
| {
 | |
|   HWND behind = HWND_TOP;
 | |
|   if (aPlacement == eZPlacementBottom)
 | |
|     behind = HWND_BOTTOM;
 | |
|   else if (aPlacement == eZPlacementBelow && aWidget)
 | |
|     behind = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
 | |
|   UINT flags = SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOSIZE;
 | |
|   if (!aActivate)
 | |
|     flags |= SWP_NOACTIVATE;
 | |
| 
 | |
|   if (!CanTakeFocus() && behind == HWND_TOP)
 | |
|   {
 | |
|     // Can't place the window to top so place it behind the foreground window
 | |
|     // (as long as it is not topmost)
 | |
|     HWND wndAfter = ::GetForegroundWindow();
 | |
|     if (!wndAfter)
 | |
|       behind = HWND_BOTTOM;
 | |
|     else if (!(GetWindowLongPtrW(wndAfter, GWL_EXSTYLE) & WS_EX_TOPMOST))
 | |
|       behind = wndAfter;
 | |
|     flags |= SWP_NOACTIVATE;
 | |
|   }
 | |
| 
 | |
|   ::SetWindowPos(mWnd, behind, 0, 0, 0, 0, flags);
 | |
| }
 | |
| 
 | |
| static UINT
 | |
| GetCurrentShowCmd(HWND aWnd)
 | |
| {
 | |
|   WINDOWPLACEMENT pl;
 | |
|   pl.length = sizeof(pl);
 | |
|   ::GetWindowPlacement(aWnd, &pl);
 | |
|   return pl.showCmd;
 | |
| }
 | |
| 
 | |
| // Maximize, minimize or restore the window.
 | |
| void
 | |
| nsWindow::SetSizeMode(nsSizeMode aMode)
 | |
| {
 | |
|   // Let's not try and do anything if we're already in that state.
 | |
|   // (This is needed to prevent problems when calling window.minimize(), which
 | |
|   // calls us directly, and then the OS triggers another call to us.)
 | |
|   if (aMode == mSizeMode)
 | |
|     return;
 | |
| 
 | |
|   // save the requested state
 | |
|   mLastSizeMode = mSizeMode;
 | |
|   nsBaseWidget::SetSizeMode(aMode);
 | |
|   if (mIsVisible) {
 | |
|     int mode;
 | |
| 
 | |
|     switch (aMode) {
 | |
|       case nsSizeMode_Fullscreen :
 | |
|         mode = SW_SHOW;
 | |
|         break;
 | |
| 
 | |
|       case nsSizeMode_Maximized :
 | |
|         mode = SW_MAXIMIZE;
 | |
|         break;
 | |
| 
 | |
|       case nsSizeMode_Minimized :
 | |
|         mode = SW_MINIMIZE;
 | |
|         break;
 | |
| 
 | |
|       default :
 | |
|         mode = SW_RESTORE;
 | |
|     }
 | |
| 
 | |
|     // Don't call ::ShowWindow if we're trying to "restore" a window that is
 | |
|     // already in a normal state.  Prevents a bug where snapping to one side
 | |
|     // of the screen and then minimizing would cause Windows to forget our
 | |
|     // window's correct restored position/size.
 | |
|     if(!(GetCurrentShowCmd(mWnd) == SW_SHOWNORMAL && mode == SW_RESTORE)) {
 | |
|       ::ShowWindow(mWnd, mode);
 | |
|     }
 | |
|     // we activate here to ensure that the right child window is focused
 | |
|     if (mode == SW_MAXIMIZE || mode == SW_SHOW)
 | |
|       DispatchFocusToTopLevelWindow(true);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::SuppressAnimation(bool aSuppress)
 | |
| {
 | |
|   DWORD dwAttribute = aSuppress ? TRUE : FALSE;
 | |
|   DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
 | |
|                         &dwAttribute, sizeof dwAttribute);
 | |
| }
 | |
| 
 | |
| // Constrain a potential move to fit onscreen
 | |
| // Position (aX, aY) is specified in Windows screen (logical) pixels,
 | |
| // except when using per-monitor DPI, in which case it's device pixels.
 | |
| void
 | |
| nsWindow::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY)
 | |
| {
 | |
|   if (!mIsTopWidgetWindow) // only a problem for top-level windows
 | |
|     return;
 | |
| 
 | |
|   double dpiScale = GetDesktopToDeviceScale().scale;
 | |
| 
 | |
|   // We need to use the window size in the kind of pixels used for window-
 | |
|   // manipulation APIs.
 | |
|   int32_t logWidth = std::max<int32_t>(NSToIntRound(mBounds.Width() / dpiScale), 1);
 | |
|   int32_t logHeight = std::max<int32_t>(NSToIntRound(mBounds.Height() / dpiScale), 1);
 | |
| 
 | |
|   /* get our playing field. use the current screen, or failing that
 | |
|   for any reason, use device caps for the default screen. */
 | |
|   RECT screenRect;
 | |
| 
 | |
|   nsCOMPtr<nsIScreenManager> screenmgr = do_GetService(sScreenManagerContractID);
 | |
|   if (!screenmgr) {
 | |
|     return;
 | |
|   }
 | |
|   nsCOMPtr<nsIScreen> screen;
 | |
|   int32_t left, top, width, height;
 | |
| 
 | |
|   screenmgr->ScreenForRect(*aX, *aY, logWidth, logHeight,
 | |
|                            getter_AddRefs(screen));
 | |
|   if (mSizeMode != nsSizeMode_Fullscreen) {
 | |
|     // For normalized windows, use the desktop work area.
 | |
|     nsresult rv = screen->GetAvailRectDisplayPix(&left, &top, &width, &height);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return;
 | |
|     }
 | |
|   } else {
 | |
|     // For full screen windows, use the desktop.
 | |
|     nsresult rv = screen->GetRectDisplayPix(&left, &top, &width, &height);
 | |
|     if (NS_FAILED(rv)) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
|   screenRect.left = left;
 | |
|   screenRect.right = left + width;
 | |
|   screenRect.top = top;
 | |
|   screenRect.bottom = top + height;
 | |
| 
 | |
|   if (aAllowSlop) {
 | |
|     if (*aX < screenRect.left - logWidth + kWindowPositionSlop)
 | |
|       *aX = screenRect.left - logWidth + kWindowPositionSlop;
 | |
|     else if (*aX >= screenRect.right - kWindowPositionSlop)
 | |
|       *aX = screenRect.right - kWindowPositionSlop;
 | |
| 
 | |
|     if (*aY < screenRect.top - logHeight + kWindowPositionSlop)
 | |
|       *aY = screenRect.top - logHeight + kWindowPositionSlop;
 | |
|     else if (*aY >= screenRect.bottom - kWindowPositionSlop)
 | |
|       *aY = screenRect.bottom - kWindowPositionSlop;
 | |
| 
 | |
|   } else {
 | |
| 
 | |
|     if (*aX < screenRect.left)
 | |
|       *aX = screenRect.left;
 | |
|     else if (*aX >= screenRect.right - logWidth)
 | |
|       *aX = screenRect.right - logWidth;
 | |
| 
 | |
|     if (*aY < screenRect.top)
 | |
|       *aY = screenRect.top;
 | |
|     else if (*aY >= screenRect.bottom - logHeight)
 | |
|       *aY = screenRect.bottom - logHeight;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::Enable, nsIWidget::IsEnabled
 | |
|  *
 | |
|  * Enabling and disabling the widget.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Enable/disable this component
 | |
| void
 | |
| nsWindow::Enable(bool bState)
 | |
| {
 | |
|   if (mWnd) {
 | |
|     ::EnableWindow(mWnd, bState);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Return the current enable state
 | |
| bool nsWindow::IsEnabled() const
 | |
| {
 | |
|   return !mWnd ||
 | |
|          (::IsWindowEnabled(mWnd) &&
 | |
|           ::IsWindowEnabled(::GetAncestor(mWnd, GA_ROOT)));
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetFocus
 | |
|  *
 | |
|  * Give the focus to this widget.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SetFocus(bool aRaise)
 | |
| {
 | |
|   if (mWnd) {
 | |
| #ifdef WINSTATE_DEBUG_OUTPUT
 | |
|     if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
 | |
|       MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|              ("*** SetFocus: [  top] raise=%d\n", aRaise));
 | |
|     } else {
 | |
|       MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|              ("*** SetFocus: [child] raise=%d\n", aRaise));
 | |
|     }
 | |
| #endif
 | |
|     // Uniconify, if necessary
 | |
|     HWND toplevelWnd = WinUtils::GetTopLevelHWND(mWnd);
 | |
|     if (aRaise && ::IsIconic(toplevelWnd)) {
 | |
|       ::ShowWindow(toplevelWnd, SW_RESTORE);
 | |
|     }
 | |
|     ::SetFocus(mWnd);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Bounds
 | |
|  *
 | |
|  * GetBounds, GetClientBounds, GetScreenBounds,
 | |
|  * GetRestoredBounds, GetClientOffset
 | |
|  * SetDrawsInTitlebar, SetNonClientMargins
 | |
|  *
 | |
|  * Bound calculations.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Return the window's full dimensions in screen coordinates.
 | |
| // If the window has a parent, converts the origin to an offset
 | |
| // of the parent's screen origin.
 | |
| LayoutDeviceIntRect
 | |
| nsWindow::GetBounds()
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     return mBounds;
 | |
|   }
 | |
| 
 | |
|   RECT r;
 | |
|   VERIFY(::GetWindowRect(mWnd, &r));
 | |
| 
 | |
|   LayoutDeviceIntRect rect;
 | |
| 
 | |
|   // assign size
 | |
|   rect.SizeTo(r.right - r.left, r.bottom - r.top);
 | |
| 
 | |
|   // popup window bounds' are in screen coordinates, not relative to parent
 | |
|   // window
 | |
|   if (mWindowType == eWindowType_popup) {
 | |
|     rect.MoveTo(r.left, r.top);
 | |
|     return rect;
 | |
|   }
 | |
| 
 | |
|   // chrome on parent:
 | |
|   //  ___      5,5   (chrome start)
 | |
|   // |  ____   10,10 (client start)
 | |
|   // | |  ____ 20,20 (child start)
 | |
|   // | | |
 | |
|   // 20,20 - 5,5 = 15,15 (??)
 | |
|   // minus GetClientOffset:
 | |
|   // 15,15 - 5,5 = 10,10
 | |
|   //
 | |
|   // no chrome on parent:
 | |
|   //  ______   10,10 (win start)
 | |
|   // |  ____   20,20 (child start)
 | |
|   // | |
 | |
|   // 20,20 - 10,10 = 10,10
 | |
|   //
 | |
|   // walking the chain:
 | |
|   //  ___      5,5   (chrome start)
 | |
|   // |  ___    10,10 (client start)
 | |
|   // | |  ___  20,20 (child start)
 | |
|   // | | |  __ 30,30 (child start)
 | |
|   // | | | |
 | |
|   // 30,30 - 20,20 = 10,10 (offset from second child to first)
 | |
|   // 20,20 - 5,5 = 15,15 + 10,10 = 25,25 (??)
 | |
|   // minus GetClientOffset:
 | |
|   // 25,25 - 5,5 = 20,20 (offset from second child to parent client)
 | |
| 
 | |
|   // convert coordinates if parent exists
 | |
|   HWND parent = ::GetParent(mWnd);
 | |
|   if (parent) {
 | |
|     RECT pr;
 | |
|     VERIFY(::GetWindowRect(parent, &pr));
 | |
|     r.left -= pr.left;
 | |
|     r.top  -= pr.top;
 | |
|     // adjust for chrome
 | |
|     nsWindow* pWidget = static_cast<nsWindow*>(GetParent());
 | |
|     if (pWidget && pWidget->IsTopLevelWidget()) {
 | |
|       LayoutDeviceIntPoint clientOffset = pWidget->GetClientOffset();
 | |
|       r.left -= clientOffset.x;
 | |
|       r.top  -= clientOffset.y;
 | |
|     }
 | |
|   }
 | |
|   rect.MoveTo(r.left, r.top);
 | |
|   return rect;
 | |
| }
 | |
| 
 | |
| // Get this component dimension
 | |
| LayoutDeviceIntRect
 | |
| nsWindow::GetClientBounds()
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     return LayoutDeviceIntRect(0, 0, 0, 0);
 | |
|   }
 | |
| 
 | |
|   RECT r;
 | |
|   VERIFY(::GetClientRect(mWnd, &r));
 | |
| 
 | |
|   LayoutDeviceIntRect bounds = GetBounds();
 | |
|   LayoutDeviceIntRect rect;
 | |
|   rect.MoveTo(bounds.TopLeft() + GetClientOffset());
 | |
|   rect.SizeTo(r.right - r.left, r.bottom - r.top);
 | |
|   return rect;
 | |
| }
 | |
| 
 | |
| // Like GetBounds, but don't offset by the parent
 | |
| LayoutDeviceIntRect
 | |
| nsWindow::GetScreenBounds()
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     return mBounds;
 | |
|   }
 | |
| 
 | |
|   RECT r;
 | |
|   VERIFY(::GetWindowRect(mWnd, &r));
 | |
| 
 | |
|   return LayoutDeviceIntRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::GetRestoredBounds(LayoutDeviceIntRect &aRect)
 | |
| {
 | |
|   if (SizeMode() == nsSizeMode_Normal) {
 | |
|     aRect = GetScreenBounds();
 | |
|     return NS_OK;
 | |
|   }
 | |
|   if (!mWnd) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   WINDOWPLACEMENT pl = { sizeof(WINDOWPLACEMENT) };
 | |
|   VERIFY(::GetWindowPlacement(mWnd, &pl));
 | |
|   const RECT& r = pl.rcNormalPosition;
 | |
| 
 | |
|   HMONITOR monitor = ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTONULL);
 | |
|   if (!monitor) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   MONITORINFO mi = { sizeof(MONITORINFO) };
 | |
|   VERIFY(::GetMonitorInfo(monitor, &mi));
 | |
| 
 | |
|   aRect.SetRect(r.left, r.top, r.right - r.left, r.bottom - r.top);
 | |
|   aRect.MoveBy(mi.rcWork.left - mi.rcMonitor.left,
 | |
|                mi.rcWork.top - mi.rcMonitor.top);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // Return the x,y offset of the client area from the origin of the window. If
 | |
| // the window is borderless returns (0,0).
 | |
| LayoutDeviceIntPoint
 | |
| nsWindow::GetClientOffset()
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     return LayoutDeviceIntPoint(0, 0);
 | |
|   }
 | |
| 
 | |
|   RECT r1;
 | |
|   GetWindowRect(mWnd, &r1);
 | |
|   LayoutDeviceIntPoint pt = WidgetToScreenOffset();
 | |
|   return LayoutDeviceIntPoint(pt.x - r1.left, pt.y - r1.top);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::SetDrawsInTitlebar(bool aState)
 | |
| {
 | |
|   nsWindow * window = GetTopLevelWindow(true);
 | |
|   if (window && window != this) {
 | |
|     return window->SetDrawsInTitlebar(aState);
 | |
|   }
 | |
| 
 | |
|   if (aState) {
 | |
|     // top, right, bottom, left for nsIntMargin
 | |
|     LayoutDeviceIntMargin margins(0, -1, -1, -1);
 | |
|     SetNonClientMargins(margins);
 | |
|   }
 | |
|   else {
 | |
|     LayoutDeviceIntMargin margins(-1, -1, -1, -1);
 | |
|     SetNonClientMargins(margins);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::ResetLayout()
 | |
| {
 | |
|   // This will trigger a frame changed event, triggering
 | |
|   // nc calc size and a sizemode gecko event.
 | |
|   SetWindowPos(mWnd, 0, 0, 0, 0, 0,
 | |
|                SWP_FRAMECHANGED|SWP_NOACTIVATE|SWP_NOMOVE|
 | |
|                SWP_NOOWNERZORDER|SWP_NOSIZE|SWP_NOZORDER);
 | |
| 
 | |
|   // If hidden, just send the frame changed event for now.
 | |
|   if (!mIsVisible)
 | |
|     return;
 | |
| 
 | |
|   // Send a gecko size event to trigger reflow.
 | |
|   RECT clientRc = {0};
 | |
|   GetClientRect(mWnd, &clientRc);
 | |
|   OnResize(WinUtils::ToIntRect(clientRc).Size());
 | |
| 
 | |
|   // Invalidate and update
 | |
|   Invalidate();
 | |
| }
 | |
| 
 | |
| // Internally track the caption status via a window property. Required
 | |
| // due to our internal handling of WM_NCACTIVATE when custom client
 | |
| // margins are set.
 | |
| static const wchar_t kManageWindowInfoProperty[] = L"ManageWindowInfoProperty";
 | |
| typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
 | |
| static WindowsDllInterceptor::FuncHookType<GetWindowInfoPtr> sGetWindowInfoPtrStub;
 | |
| 
 | |
| BOOL WINAPI
 | |
| GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
 | |
| {
 | |
|   if (!sGetWindowInfoPtrStub) {
 | |
|     NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!");
 | |
|     return FALSE;
 | |
|   }
 | |
|   int windowStatus = 
 | |
|     reinterpret_cast<LONG_PTR>(GetPropW(hWnd, kManageWindowInfoProperty));
 | |
|   // No property set, return the default data.
 | |
|   if (!windowStatus)
 | |
|     return sGetWindowInfoPtrStub(hWnd, pwi);
 | |
|   // Call GetWindowInfo and update dwWindowStatus with our
 | |
|   // internally tracked value. 
 | |
|   BOOL result = sGetWindowInfoPtrStub(hWnd, pwi);
 | |
|   if (result && pwi)
 | |
|     pwi->dwWindowStatus = (windowStatus == 1 ? 0 : WS_ACTIVECAPTION);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption)
 | |
| {
 | |
|   if (!mWnd)
 | |
|     return;
 | |
| 
 | |
|   sUser32Intercept.Init("user32.dll");
 | |
|   sGetWindowInfoPtrStub.Set(sUser32Intercept, "GetWindowInfo",
 | |
|                             &GetWindowInfoHook);
 | |
|   if (!sGetWindowInfoPtrStub) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Update our internally tracked caption status
 | |
|   SetPropW(mWnd, kManageWindowInfoProperty,
 | |
|     reinterpret_cast<HANDLE>(static_cast<INT_PTR>(aActiveCaption) + 1));
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Called when the window layout changes: full screen mode transitions,
 | |
|  * theme changes, and composition changes. Calculates the new non-client
 | |
|  * margins and fires off a frame changed event, which triggers an nc calc
 | |
|  * size windows event, kicking the changes in.
 | |
|  *
 | |
|  * The offsets calculated here are based on the value of `mNonClientMargins`
 | |
|  * which is specified in the "chromemargins" attribute of the window.  For
 | |
|  * each margin, the value specified has the following meaning:
 | |
|  *    -1 - leave the default frame in place
 | |
|  *     0 - remove the frame
 | |
|  *    >0 - frame size equals min(0, (default frame size - margin value))
 | |
|  *
 | |
|  * This function calculates and populates `mNonClientOffset`.
 | |
|  * In our processing of `WM_NCCALCSIZE`, the frame size will be calculated
 | |
|  * as (default frame size - offset).  For example, if the left frame should
 | |
|  * be 1 pixel narrower than the default frame size, `mNonClientOffset.left`
 | |
|  * will equal 1.
 | |
|  *
 | |
|  * For maximized, fullscreen, and minimized windows, the values stored in
 | |
|  * `mNonClientMargins` are ignored, and special processing takes place.
 | |
|  *
 | |
|  * For non-glass windows, we only allow frames to be their default size
 | |
|  * or removed entirely.
 | |
|  */
 | |
| bool
 | |
| nsWindow::UpdateNonClientMargins(int32_t aSizeMode, bool aReflowWindow)
 | |
| {
 | |
|   if (!mCustomNonClient)
 | |
|     return false;
 | |
| 
 | |
|   if (aSizeMode == -1) {
 | |
|     aSizeMode = mSizeMode;
 | |
|   }
 | |
| 
 | |
|   bool hasCaption = (mBorderStyle
 | |
|                     & (eBorderStyle_all
 | |
|                      | eBorderStyle_title
 | |
|                      | eBorderStyle_menu
 | |
|                      | eBorderStyle_default));
 | |
| 
 | |
|   // mCaptionHeight is the default size of the NC area at
 | |
|   // the top of the window. If the window has a caption,
 | |
|   // the size is calculated as the sum of:
 | |
|   //      SM_CYFRAME        - The thickness of the sizing border
 | |
|   //                          around a resizable window
 | |
|   //      SM_CXPADDEDBORDER - The amount of border padding
 | |
|   //                          for captioned windows
 | |
|   //      SM_CYCAPTION      - The height of the caption area
 | |
|   //
 | |
|   // If the window does not have a caption, mCaptionHeight will be equal to
 | |
|   // `GetSystemMetrics(SM_CYFRAME)`
 | |
|   mCaptionHeight = GetSystemMetrics(SM_CYFRAME)
 | |
|                  + (hasCaption ? GetSystemMetrics(SM_CYCAPTION)
 | |
|                                  + GetSystemMetrics(SM_CXPADDEDBORDER)
 | |
|                                : 0);
 | |
| 
 | |
|   // mHorResizeMargin is the size of the default NC areas on the
 | |
|   // left and right sides of our window.  It is calculated as
 | |
|   // the sum of:
 | |
|   //      SM_CXFRAME        - The thickness of the sizing border
 | |
|   //      SM_CXPADDEDBORDER - The amount of border padding
 | |
|   //                          for captioned windows
 | |
|   //
 | |
|   // If the window does not have a caption, mHorResizeMargin will be equal to
 | |
|   // `GetSystemMetrics(SM_CXFRAME)`
 | |
|   mHorResizeMargin = GetSystemMetrics(SM_CXFRAME)
 | |
|                    + (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
 | |
| 
 | |
|   // mVertResizeMargin is the size of the default NC area at the
 | |
|   // bottom of the window. It is calculated as the sum of:
 | |
|   //      SM_CYFRAME        - The thickness of the sizing border
 | |
|   //      SM_CXPADDEDBORDER - The amount of border padding
 | |
|   //                          for captioned windows.
 | |
|   //
 | |
|   // If the window does not have a caption, mVertResizeMargin will be equal to
 | |
|   // `GetSystemMetrics(SM_CYFRAME)`
 | |
|   mVertResizeMargin = GetSystemMetrics(SM_CYFRAME)
 | |
|                     + (hasCaption ? GetSystemMetrics(SM_CXPADDEDBORDER) : 0);
 | |
| 
 | |
|   if (aSizeMode == nsSizeMode_Minimized) {
 | |
|     // Use default frame size for minimized windows
 | |
|     mNonClientOffset.top = 0;
 | |
|     mNonClientOffset.left = 0;
 | |
|     mNonClientOffset.right = 0;
 | |
|     mNonClientOffset.bottom = 0;
 | |
|   } else if (aSizeMode == nsSizeMode_Fullscreen) {
 | |
|     // Remove the default frame from the top of our fullscreen window.  This
 | |
|     // makes the whole caption part of our client area, allowing us to draw
 | |
|     // in the whole caption area.  Additionally remove the default frame from
 | |
|     // the left, right, and bottom.
 | |
|     mNonClientOffset.top = mCaptionHeight;
 | |
|     mNonClientOffset.bottom = mVertResizeMargin;
 | |
|     mNonClientOffset.left = mHorResizeMargin;
 | |
|     mNonClientOffset.right = mHorResizeMargin;
 | |
|   } else if (aSizeMode == nsSizeMode_Maximized) {
 | |
|     // Remove the default frame from the top of our maximized window.  This
 | |
|     // makes the whole caption part of our client area, allowing us to draw
 | |
|     // in the whole caption area.  Use default frame size on left, right, and
 | |
|     // bottom. The reason this works is that, for maximized windows,
 | |
|     // Windows positions them so that their frames fall off the screen.
 | |
|     // This gives the illusion of windows having no frames when they are
 | |
|     // maximized.  If we try to mess with the frame sizes by setting these
 | |
|     // offsets to positive values, our client area will fall off the screen.
 | |
|     mNonClientOffset.top = mCaptionHeight;
 | |
|     mNonClientOffset.bottom = 0;
 | |
|     mNonClientOffset.left = 0;
 | |
|     mNonClientOffset.right = 0;
 | |
| 
 | |
|     APPBARDATA appBarData;
 | |
|     appBarData.cbSize = sizeof(appBarData);
 | |
|     UINT taskbarState = SHAppBarMessage(ABM_GETSTATE, &appBarData);
 | |
|     if (ABS_AUTOHIDE & taskbarState) {
 | |
|       UINT edge = -1;
 | |
|       appBarData.hWnd = FindWindow(L"Shell_TrayWnd", nullptr);
 | |
|       if (appBarData.hWnd) {
 | |
|         HMONITOR taskbarMonitor = ::MonitorFromWindow(appBarData.hWnd,
 | |
|                                                       MONITOR_DEFAULTTOPRIMARY);
 | |
|         HMONITOR windowMonitor = ::MonitorFromWindow(mWnd,
 | |
|                                                      MONITOR_DEFAULTTONEAREST);
 | |
|         if (taskbarMonitor == windowMonitor) {
 | |
|           SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData);
 | |
|           edge = appBarData.uEdge;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (ABE_LEFT == edge) {
 | |
|         mNonClientOffset.left -= 1;
 | |
|       } else if (ABE_RIGHT == edge) {
 | |
|         mNonClientOffset.right -= 1;
 | |
|       } else if (ABE_BOTTOM == edge || ABE_TOP == edge) {
 | |
|         mNonClientOffset.bottom -= 1;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     bool glass = nsUXThemeData::CheckForCompositor();
 | |
| 
 | |
|     // We're dealing with a "normal" window (not maximized, minimized, or
 | |
|     // fullscreen), so process `mNonClientMargins` and set `mNonClientOffset`
 | |
|     // accordingly.
 | |
|     //
 | |
|     // Setting `mNonClientOffset` to 0 has the effect of leaving the default
 | |
|     // frame intact.  Setting it to a value greater than 0 reduces the frame
 | |
|     // size by that amount.
 | |
| 
 | |
|     if (mNonClientMargins.top > 0 && glass) {
 | |
|       mNonClientOffset.top = std::min(mCaptionHeight, mNonClientMargins.top);
 | |
|     } else if (mNonClientMargins.top == 0) {
 | |
|       mNonClientOffset.top = mCaptionHeight;
 | |
|     } else {
 | |
|       mNonClientOffset.top = 0;
 | |
|     }
 | |
| 
 | |
|     if (mNonClientMargins.bottom > 0 && glass) {
 | |
|       mNonClientOffset.bottom = std::min(mVertResizeMargin, mNonClientMargins.bottom);
 | |
|     } else if (mNonClientMargins.bottom == 0) {
 | |
|       mNonClientOffset.bottom = mVertResizeMargin;
 | |
|     } else {
 | |
|       mNonClientOffset.bottom = 0;
 | |
|     }
 | |
| 
 | |
|     if (mNonClientMargins.left > 0 && glass) {
 | |
|       mNonClientOffset.left = std::min(mHorResizeMargin, mNonClientMargins.left);
 | |
|     } else if (mNonClientMargins.left == 0) {
 | |
|       mNonClientOffset.left = mHorResizeMargin;
 | |
|     } else {
 | |
|       mNonClientOffset.left = 0;
 | |
|     }
 | |
| 
 | |
|     if (mNonClientMargins.right > 0 && glass) {
 | |
|       mNonClientOffset.right = std::min(mHorResizeMargin, mNonClientMargins.right);
 | |
|     } else if (mNonClientMargins.right == 0) {
 | |
|       mNonClientOffset.right = mHorResizeMargin;
 | |
|     } else {
 | |
|       mNonClientOffset.right = 0;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aReflowWindow) {
 | |
|     // Force a reflow of content based on the new client
 | |
|     // dimensions.
 | |
|     ResetLayout();
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SetNonClientMargins(LayoutDeviceIntMargin &margins)
 | |
| {
 | |
|   if (!mIsTopWidgetWindow ||
 | |
|       mBorderStyle == eBorderStyle_none)
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
| 
 | |
|   if (mHideChrome) {
 | |
|     mFutureMarginsOnceChromeShows = margins;
 | |
|     mFutureMarginsToUse = true;
 | |
|     return NS_OK;
 | |
|   }
 | |
|   mFutureMarginsToUse = false;
 | |
| 
 | |
|   // Request for a reset
 | |
|   if (margins.top == -1 && margins.left == -1 &&
 | |
|       margins.right == -1 && margins.bottom == -1) {
 | |
|     mCustomNonClient = false;
 | |
|     mNonClientMargins = margins;
 | |
|     // Force a reflow of content based on the new client
 | |
|     // dimensions.
 | |
|     ResetLayout();
 | |
| 
 | |
|     int windowStatus =
 | |
|       reinterpret_cast<LONG_PTR>(GetPropW(mWnd, kManageWindowInfoProperty));
 | |
|     if (windowStatus) {
 | |
|       ::SendMessageW(mWnd, WM_NCACTIVATE, 1 != windowStatus, 0);
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (margins.top < -1 || margins.bottom < -1 ||
 | |
|       margins.left < -1 || margins.right < -1)
 | |
|     return NS_ERROR_INVALID_ARG;
 | |
| 
 | |
|   mNonClientMargins = margins;
 | |
|   mCustomNonClient = true;
 | |
|   if (!UpdateNonClientMargins()) {
 | |
|     NS_WARNING("UpdateNonClientMargins failed!");
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::InvalidateNonClientRegion()
 | |
| {
 | |
|   // +-+-----------------------+-+
 | |
|   // | | app non-client chrome | |
 | |
|   // | +-----------------------+ |
 | |
|   // | |   app client chrome   | | }
 | |
|   // | +-----------------------+ | }
 | |
|   // | |      app content      | | } area we don't want to invalidate
 | |
|   // | +-----------------------+ | }
 | |
|   // | |   app client chrome   | | }
 | |
|   // | +-----------------------+ | 
 | |
|   // +---------------------------+ <
 | |
|   //  ^                         ^    windows non-client chrome
 | |
|   // client area = app *
 | |
|   RECT rect;
 | |
|   GetWindowRect(mWnd, &rect);
 | |
|   MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
 | |
|   HRGN winRgn = CreateRectRgnIndirect(&rect);
 | |
| 
 | |
|   // Subtract app client chrome and app content leaving
 | |
|   // windows non-client chrome and app non-client chrome
 | |
|   // in winRgn.
 | |
|   GetWindowRect(mWnd, &rect);
 | |
|   rect.top += mCaptionHeight;
 | |
|   rect.right -= mHorResizeMargin;
 | |
|   rect.bottom -= mHorResizeMargin;
 | |
|   rect.left += mVertResizeMargin;
 | |
|   MapWindowPoints(nullptr, mWnd, (LPPOINT)&rect, 2);
 | |
|   HRGN clientRgn = CreateRectRgnIndirect(&rect);
 | |
|   CombineRgn(winRgn, winRgn, clientRgn, RGN_DIFF);
 | |
|   DeleteObject(clientRgn);
 | |
| 
 | |
|   // triggers ncpaint and paint events for the two areas
 | |
|   RedrawWindow(mWnd, nullptr, winRgn, RDW_FRAME | RDW_INVALIDATE);
 | |
|   DeleteObject(winRgn);
 | |
| }
 | |
| 
 | |
| HRGN
 | |
| nsWindow::ExcludeNonClientFromPaintRegion(HRGN aRegion)
 | |
| {
 | |
|   RECT rect;
 | |
|   HRGN rgn = nullptr;
 | |
|   if (aRegion == (HRGN)1) { // undocumented value indicating a full refresh
 | |
|     GetWindowRect(mWnd, &rect);
 | |
|     rgn = CreateRectRgnIndirect(&rect);
 | |
|   } else {
 | |
|     rgn = aRegion;
 | |
|   }
 | |
|   GetClientRect(mWnd, &rect);
 | |
|   MapWindowPoints(mWnd, nullptr, (LPPOINT)&rect, 2);
 | |
|   HRGN nonClientRgn = CreateRectRgnIndirect(&rect);
 | |
|   CombineRgn(rgn, rgn, nonClientRgn, RGN_DIFF);
 | |
|   DeleteObject(nonClientRgn);
 | |
|   return rgn;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetBackgroundColor
 | |
|  *
 | |
|  * Sets the window background paint color.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void nsWindow::SetBackgroundColor(const nscolor &aColor)
 | |
| {
 | |
|   if (mBrush)
 | |
|     ::DeleteObject(mBrush);
 | |
| 
 | |
|   mBrush = ::CreateSolidBrush(NSRGB_2_COLOREF(aColor));
 | |
|   if (mWnd != nullptr) {
 | |
|     ::SetClassLongPtrW(mWnd, GCLP_HBRBACKGROUND, (LONG_PTR)mBrush);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetCursor
 | |
|  *
 | |
|  * SetCursor and related utilities for manging cursor state.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Set this component cursor
 | |
| void
 | |
| nsWindow::SetCursor(nsCursor aCursor)
 | |
| {
 | |
|   // Only change cursor if it's changing
 | |
| 
 | |
|   //XXX mCursor isn't always right.  Scrollbars and others change it, too.
 | |
|   //XXX If we want this optimization we need a better way to do it.
 | |
|   //if (aCursor != mCursor) {
 | |
|   HCURSOR newCursor = nullptr;
 | |
| 
 | |
|   switch (aCursor) {
 | |
|     case eCursor_select:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_IBEAM);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_wait:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_WAIT);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_hyperlink:
 | |
|     {
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_HAND);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case eCursor_standard:
 | |
|     case eCursor_context_menu: // XXX See bug 258960.
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_ARROW);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_n_resize:
 | |
|     case eCursor_s_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_w_resize:
 | |
|     case eCursor_e_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_nw_resize:
 | |
|     case eCursor_se_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_ne_resize:
 | |
|     case eCursor_sw_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_crosshair:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_CROSS);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_move:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_help:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_HELP);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_copy: // CSS3
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COPY));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_alias:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ALIAS));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_cell:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_CELL));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_grab:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRAB));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_grabbing:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_GRABBING));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_spinning:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_APPSTARTING);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_zoom_in:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMIN));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_zoom_out:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ZOOMOUT));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_not_allowed:
 | |
|     case eCursor_no_drop:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_NO);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_col_resize:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_COLRESIZE));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_row_resize:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_ROWRESIZE));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_vertical_text:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_VERTICALTEXT));
 | |
|       break;
 | |
| 
 | |
|     case eCursor_all_scroll:
 | |
|       // XXX not 100% appropriate perhaps
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZEALL);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_nesw_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENESW);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_nwse_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENWSE);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_ns_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZENS);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_ew_resize:
 | |
|       newCursor = ::LoadCursor(nullptr, IDC_SIZEWE);
 | |
|       break;
 | |
| 
 | |
|     case eCursor_none:
 | |
|       newCursor = ::LoadCursor(nsToolkit::mDllInstance, MAKEINTRESOURCE(IDC_NONE));
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       NS_ERROR("Invalid cursor type");
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   if (nullptr != newCursor) {
 | |
|     mCursor = aCursor;
 | |
|     HCURSOR oldCursor = ::SetCursor(newCursor);
 | |
|     
 | |
|     if (sHCursor == oldCursor) {
 | |
|       NS_IF_RELEASE(sCursorImgContainer);
 | |
|       if (sHCursor != nullptr)
 | |
|         ::DestroyIcon(sHCursor);
 | |
|       sHCursor = nullptr;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Setting the actual cursor
 | |
| nsresult
 | |
| nsWindow::SetCursor(imgIContainer* aCursor,
 | |
|                     uint32_t aHotspotX, uint32_t aHotspotY)
 | |
| {
 | |
|   if (sCursorImgContainer == aCursor && sHCursor) {
 | |
|     ::SetCursor(sHCursor);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   int32_t width;
 | |
|   int32_t height;
 | |
| 
 | |
|   nsresult rv;
 | |
|   rv = aCursor->GetWidth(&width);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
|   rv = aCursor->GetHeight(&height);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // Reject cursors greater than 128 pixels in either direction, to prevent
 | |
|   // spoofing.
 | |
|   // XXX ideally we should rescale. Also, we could modify the API to
 | |
|   // allow trusted content to set larger cursors.
 | |
|   if (width > 128 || height > 128)
 | |
|     return NS_ERROR_NOT_AVAILABLE;
 | |
| 
 | |
|   HCURSOR cursor;
 | |
|   double scale = GetDefaultScale().scale;
 | |
|   IntSize size = RoundedToInt(Size(width * scale, height * scale));
 | |
|   rv = nsWindowGfx::CreateIcon(aCursor, true, aHotspotX, aHotspotY, size, &cursor);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   mCursor = eCursorInvalid;
 | |
|   ::SetCursor(cursor);
 | |
| 
 | |
|   NS_IF_RELEASE(sCursorImgContainer);
 | |
|   sCursorImgContainer = aCursor;
 | |
|   NS_ADDREF(sCursorImgContainer);
 | |
| 
 | |
|   if (sHCursor != nullptr)
 | |
|     ::DestroyIcon(sHCursor);
 | |
|   sHCursor = cursor;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::Get/SetTransparencyMode
 | |
|  *
 | |
|  * Manage the transparency mode of the window containing this
 | |
|  * widget. Only works for popup and dialog windows when the
 | |
|  * Desktop Window Manager compositor is not enabled.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| #ifdef MOZ_XUL
 | |
| nsTransparencyMode nsWindow::GetTransparencyMode()
 | |
| {
 | |
|   return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
 | |
| }
 | |
| 
 | |
| void nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
 | |
| {
 | |
|   nsWindow* window = GetTopLevelWindow(true);
 | |
|   MOZ_ASSERT(window);
 | |
| 
 | |
|   if (!window || window->DestroyCalled()) {
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   if (nsWindowType::eWindowType_toplevel == window->mWindowType &&
 | |
|       mTransparencyMode != aMode &&
 | |
|       !nsUXThemeData::CheckForCompositor()) {
 | |
|       NS_WARNING("Cannot set transparency mode on top-level windows.");
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   window->SetWindowTranslucencyInner(aMode);
 | |
| }
 | |
| 
 | |
| void nsWindow::UpdateOpaqueRegion(const LayoutDeviceIntRegion& aOpaqueRegion)
 | |
| {
 | |
|   if (!HasGlass() || GetParent())
 | |
|     return;
 | |
| 
 | |
|   // If there is no opaque region or hidechrome=true, set margins
 | |
|   // to support a full sheet of glass. Comments in MSDN indicate
 | |
|   // all values must be set to -1 to get a full sheet of glass.
 | |
|   MARGINS margins = { -1, -1, -1, -1 };
 | |
|   if (!aOpaqueRegion.IsEmpty()) {
 | |
|     LayoutDeviceIntRect pluginBounds;
 | |
|     for (nsIWidget* child = GetFirstChild(); child; child = child->GetNextSibling()) {
 | |
|       if (child->IsPlugin()) {
 | |
|         // Collect the bounds of all plugins for GetLargestRectangle.
 | |
|         LayoutDeviceIntRect childBounds = child->GetBounds();
 | |
|         pluginBounds.UnionRect(pluginBounds, childBounds);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     LayoutDeviceIntRect clientBounds = GetClientBounds();
 | |
| 
 | |
|     // Find the largest rectangle and use that to calculate the inset. Our top
 | |
|     // priority is to include the bounds of all plugins.
 | |
|     LayoutDeviceIntRect largest =
 | |
|       aOpaqueRegion.GetLargestRectangle(pluginBounds);
 | |
|     margins.cxLeftWidth = largest.X();
 | |
|     margins.cxRightWidth = clientBounds.Width() - largest.XMost();
 | |
|     margins.cyBottomHeight = clientBounds.Height() - largest.YMost();
 | |
|     if (mCustomNonClient) {
 | |
|       // The minimum glass height must be the caption buttons height,
 | |
|       // otherwise the buttons are drawn incorrectly.
 | |
|       largest.MoveToY(std::max<uint32_t>(largest.Y(),
 | |
|                         nsUXThemeData::GetCommandButtonBoxMetrics().cy));
 | |
|     }
 | |
|     margins.cyTopHeight = largest.Y();
 | |
|   }
 | |
| 
 | |
|   // Only update glass area if there are changes
 | |
|   if (memcmp(&mGlassMargins, &margins, sizeof mGlassMargins)) {
 | |
|     mGlassMargins = margins;
 | |
|     UpdateGlass();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
| *
 | |
| * SECTION: nsIWidget::UpdateWindowDraggingRegion
 | |
| *
 | |
| * For setting the draggable titlebar region from CSS
 | |
| * with -moz-window-dragging: drag.
 | |
| *
 | |
| **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::UpdateWindowDraggingRegion(const LayoutDeviceIntRegion& aRegion)
 | |
| {
 | |
|   if (mDraggableRegion != aRegion) {
 | |
|     mDraggableRegion = aRegion;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsWindow::UpdateGlass()
 | |
| {
 | |
|   MARGINS margins = mGlassMargins;
 | |
| 
 | |
|   // DWMNCRP_USEWINDOWSTYLE - The non-client rendering area is
 | |
|   //                          rendered based on the window style.
 | |
|   // DWMNCRP_ENABLED        - The non-client area rendering is
 | |
|   //                          enabled; the window style is ignored.
 | |
|   DWMNCRENDERINGPOLICY policy = DWMNCRP_USEWINDOWSTYLE;
 | |
|   switch (mTransparencyMode) {
 | |
|   case eTransparencyBorderlessGlass:
 | |
|     // Only adjust if there is some opaque rectangle
 | |
|     if (margins.cxLeftWidth >= 0) {
 | |
|       margins.cxLeftWidth += kGlassMarginAdjustment;
 | |
|       margins.cyTopHeight += kGlassMarginAdjustment;
 | |
|       margins.cxRightWidth += kGlassMarginAdjustment;
 | |
|       margins.cyBottomHeight += kGlassMarginAdjustment;
 | |
|     }
 | |
|     // Fall through
 | |
|   case eTransparencyGlass:
 | |
|     policy = DWMNCRP_ENABLED;
 | |
|     break;
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|          ("glass margins: left:%d top:%d right:%d bottom:%d\n",
 | |
|           margins.cxLeftWidth, margins.cyTopHeight,
 | |
|           margins.cxRightWidth, margins.cyBottomHeight));
 | |
| 
 | |
|   // Extends the window frame behind the client area
 | |
|   if (nsUXThemeData::CheckForCompositor()) {
 | |
|     DwmExtendFrameIntoClientArea(mWnd, &margins);
 | |
|     DwmSetWindowAttribute(mWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof policy);
 | |
|   }
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::HideWindowChrome
 | |
|  *
 | |
|  * Show or hide window chrome.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::HideWindowChrome(bool aShouldHide)
 | |
| {
 | |
|   HWND hwnd = WinUtils::GetTopLevelHWND(mWnd, true);
 | |
|   if (!WinUtils::GetNSWindowPtr(hwnd))
 | |
|   {
 | |
|     NS_WARNING("Trying to hide window decorations in an embedded context");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mHideChrome == aShouldHide)
 | |
|     return;
 | |
| 
 | |
|   DWORD_PTR style, exStyle;
 | |
|   mHideChrome = aShouldHide;
 | |
|   if (aShouldHide) {
 | |
|     DWORD_PTR tempStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
 | |
|     DWORD_PTR tempExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
 | |
| 
 | |
|     style = tempStyle & ~(WS_CAPTION | WS_THICKFRAME);
 | |
|     exStyle = tempExStyle & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
 | |
|                               WS_EX_CLIENTEDGE | WS_EX_STATICEDGE);
 | |
| 
 | |
|     mOldStyle = tempStyle;
 | |
|     mOldExStyle = tempExStyle;
 | |
|   }
 | |
|   else {
 | |
|     if (!mOldStyle || !mOldExStyle) {
 | |
|       mOldStyle = ::GetWindowLongPtrW(hwnd, GWL_STYLE);
 | |
|       mOldExStyle = ::GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
 | |
|     }
 | |
| 
 | |
|     style = mOldStyle;
 | |
|     exStyle = mOldExStyle;
 | |
|     if (mFutureMarginsToUse) {
 | |
|       SetNonClientMargins(mFutureMarginsOnceChromeShows);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   VERIFY_WINDOW_STYLE(style);
 | |
|   ::SetWindowLongPtrW(hwnd, GWL_STYLE, style);
 | |
|   ::SetWindowLongPtrW(hwnd, GWL_EXSTYLE, exStyle);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsWindow::Invalidate
 | |
|  *
 | |
|  * Invalidate an area of the client for painting.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Invalidate this component visible area
 | |
| void
 | |
| nsWindow::Invalidate(bool aEraseBackground,
 | |
|                      bool aUpdateNCArea,
 | |
|                      bool aIncludeChildren)
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| #ifdef WIDGET_DEBUG_OUTPUT
 | |
|   debug_DumpInvalidate(stdout,
 | |
|                        this,
 | |
|                        nullptr,
 | |
|                        "noname",
 | |
|                        (int32_t) mWnd);
 | |
| #endif // WIDGET_DEBUG_OUTPUT
 | |
| 
 | |
|   DWORD flags = RDW_INVALIDATE;
 | |
|   if (aEraseBackground) {
 | |
|     flags |= RDW_ERASE;
 | |
|   }
 | |
|   if (aUpdateNCArea) {
 | |
|     flags |= RDW_FRAME;
 | |
|   }
 | |
|   if (aIncludeChildren) {
 | |
|     flags |= RDW_ALLCHILDREN;
 | |
|   }
 | |
| 
 | |
|   VERIFY(::RedrawWindow(mWnd, nullptr, nullptr, flags));
 | |
| }
 | |
| 
 | |
| // Invalidate this component visible area
 | |
| void
 | |
| nsWindow::Invalidate(const LayoutDeviceIntRect& aRect)
 | |
| {
 | |
|   if (mWnd) {
 | |
| #ifdef WIDGET_DEBUG_OUTPUT
 | |
|     debug_DumpInvalidate(stdout,
 | |
|                          this,
 | |
|                          &aRect,
 | |
|                          "noname",
 | |
|                          (int32_t) mWnd);
 | |
| #endif // WIDGET_DEBUG_OUTPUT
 | |
| 
 | |
|     RECT rect;
 | |
| 
 | |
|     rect.left   = aRect.X();
 | |
|     rect.top    = aRect.Y();
 | |
|     rect.right  = aRect.XMost();
 | |
|     rect.bottom = aRect.YMost();
 | |
| 
 | |
|     VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
 | |
|   }
 | |
| }
 | |
| 
 | |
| static LRESULT CALLBACK
 | |
| FullscreenTransitionWindowProc(HWND hWnd, UINT uMsg,
 | |
|                                WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   switch (uMsg) {
 | |
|     case WM_FULLSCREEN_TRANSITION_BEFORE:
 | |
|     case WM_FULLSCREEN_TRANSITION_AFTER: {
 | |
|       DWORD duration = (DWORD)lParam;
 | |
|       DWORD flags = AW_BLEND;
 | |
|       if (uMsg == WM_FULLSCREEN_TRANSITION_AFTER) {
 | |
|         flags |= AW_HIDE;
 | |
|       }
 | |
|       ::AnimateWindow(hWnd, duration, flags);
 | |
|       // The message sender should have added ref for us.
 | |
|       NS_DispatchToMainThread(
 | |
|         already_AddRefed<nsIRunnable>((nsIRunnable*)wParam));
 | |
|       break;
 | |
|     }
 | |
|     case WM_DESTROY:
 | |
|       ::PostQuitMessage(0);
 | |
|       break;
 | |
|     default:
 | |
|       return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| struct FullscreenTransitionInitData
 | |
| {
 | |
|   nsIntRect mBounds;
 | |
|   HANDLE mSemaphore;
 | |
|   HANDLE mThread;
 | |
|   HWND mWnd;
 | |
| 
 | |
|   FullscreenTransitionInitData()
 | |
|     : mSemaphore(nullptr)
 | |
|     , mThread(nullptr)
 | |
|     , mWnd(nullptr) { }
 | |
| 
 | |
|   ~FullscreenTransitionInitData()
 | |
|   {
 | |
|     if (mSemaphore) {
 | |
|       ::CloseHandle(mSemaphore);
 | |
|     }
 | |
|     if (mThread) {
 | |
|       ::CloseHandle(mThread);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| static DWORD WINAPI
 | |
| FullscreenTransitionThreadProc(LPVOID lpParam)
 | |
| {
 | |
|   // Initialize window class
 | |
|   static bool sInitialized = false;
 | |
|   if (!sInitialized) {
 | |
|     WNDCLASSW wc = {};
 | |
|     wc.lpfnWndProc = ::FullscreenTransitionWindowProc;
 | |
|     wc.hInstance = nsToolkit::mDllInstance;
 | |
|     wc.hbrBackground = ::CreateSolidBrush(RGB(0, 0, 0));
 | |
|     wc.lpszClassName = kClassNameTransition;
 | |
|     ::RegisterClassW(&wc);
 | |
|     sInitialized = true;
 | |
|   }
 | |
| 
 | |
|   auto data = static_cast<FullscreenTransitionInitData*>(lpParam);
 | |
|   HWND wnd = ::CreateWindowW(
 | |
|     kClassNameTransition, L"", 0, 0, 0, 0, 0,
 | |
|     nullptr, nullptr, nsToolkit::mDllInstance, nullptr);
 | |
|   if (!wnd) {
 | |
|     ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   // Since AnimateWindow blocks the thread of the transition window,
 | |
|   // we need to hide the cursor for that window, otherwise the system
 | |
|   // would show the busy pointer to the user.
 | |
|   ::ShowCursor(false);
 | |
|   ::SetWindowLongW(wnd, GWL_STYLE, 0);
 | |
|   ::SetWindowLongW(wnd, GWL_EXSTYLE, WS_EX_LAYERED |
 | |
|                    WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE);
 | |
|   ::SetWindowPos(wnd, HWND_TOPMOST, data->mBounds.X(), data->mBounds.Y(),
 | |
|                  data->mBounds.Width(), data->mBounds.Height(), 0);
 | |
|   data->mWnd = wnd;
 | |
|   ::ReleaseSemaphore(data->mSemaphore, 1, nullptr);
 | |
|   // The initialization data may no longer be valid
 | |
|   // after we release the semaphore.
 | |
|   data = nullptr;
 | |
| 
 | |
|   MSG msg;
 | |
|   while (::GetMessageW(&msg, nullptr, 0, 0)) {
 | |
|     ::TranslateMessage(&msg);
 | |
|     ::DispatchMessage(&msg);
 | |
|   }
 | |
|   ::ShowCursor(true);
 | |
|   ::DestroyWindow(wnd);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| class FullscreenTransitionData final : public nsISupports
 | |
| {
 | |
| public:
 | |
|   NS_DECL_ISUPPORTS
 | |
| 
 | |
|   explicit FullscreenTransitionData(HWND aWnd)
 | |
|     : mWnd(aWnd)
 | |
|   {
 | |
|     MOZ_ASSERT(NS_IsMainThread(), "FullscreenTransitionData "
 | |
|                "should be constructed in the main thread");
 | |
|   }
 | |
| 
 | |
|   const HWND mWnd;
 | |
| 
 | |
| private:
 | |
|   ~FullscreenTransitionData()
 | |
|   {
 | |
|     MOZ_ASSERT(NS_IsMainThread(), "FullscreenTransitionData "
 | |
|                "should be deconstructed in the main thread");
 | |
|     ::PostMessageW(mWnd, WM_DESTROY, 0, 0);
 | |
|   }
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS0(FullscreenTransitionData)
 | |
| 
 | |
| /* virtual */ bool
 | |
| nsWindow::PrepareForFullscreenTransition(nsISupports** aData)
 | |
| {
 | |
|   // We don't support fullscreen transition when composition is not
 | |
|   // enabled, which could make the transition broken and annoying.
 | |
|   // See bug 1184201.
 | |
|   if (!nsUXThemeData::CheckForCompositor()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   FullscreenTransitionInitData initData;
 | |
|   nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
 | |
|   int32_t x, y, width, height;
 | |
|   screen->GetRectDisplayPix(&x, &y, &width, &height);
 | |
|   MOZ_ASSERT(BoundsUseDesktopPixels(),
 | |
|              "Should only be called on top-level window");
 | |
|   double scale = GetDesktopToDeviceScale().scale; // XXX or GetDefaultScale() ?
 | |
|   initData.mBounds.SetRect(NSToIntRound(x * scale),
 | |
|                            NSToIntRound(y * scale),
 | |
|                            NSToIntRound(width * scale),
 | |
|                            NSToIntRound(height * scale));
 | |
| 
 | |
|   // Create a semaphore for synchronizing the window handle which will
 | |
|   // be created by the transition thread and used by the main thread for
 | |
|   // posting the transition messages.
 | |
|   initData.mSemaphore = ::CreateSemaphore(nullptr, 0, 1, nullptr);
 | |
|   if (initData.mSemaphore) {
 | |
|     initData.mThread = ::CreateThread(
 | |
|       nullptr, 0, FullscreenTransitionThreadProc, &initData, 0, nullptr);
 | |
|     if (initData.mThread) {
 | |
|       ::WaitForSingleObject(initData.mSemaphore, INFINITE);
 | |
|     }
 | |
|   }
 | |
|   if (!initData.mWnd) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mTransitionWnd = initData.mWnd;
 | |
| 
 | |
|   auto data = new FullscreenTransitionData(initData.mWnd);
 | |
|   *aData = data;
 | |
|   NS_ADDREF(data);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* virtual */ void
 | |
| nsWindow::PerformFullscreenTransition(FullscreenTransitionStage aStage,
 | |
|                                       uint16_t aDuration, nsISupports* aData,
 | |
|                                       nsIRunnable* aCallback)
 | |
| {
 | |
|   auto data = static_cast<FullscreenTransitionData*>(aData);
 | |
|   nsCOMPtr<nsIRunnable> callback = aCallback;
 | |
|   UINT msg = aStage == eBeforeFullscreenToggle ?
 | |
|     WM_FULLSCREEN_TRANSITION_BEFORE : WM_FULLSCREEN_TRANSITION_AFTER;
 | |
|   WPARAM wparam = (WPARAM)callback.forget().take();
 | |
|   ::PostMessage(data->mWnd, msg, wparam, (LPARAM)aDuration);
 | |
| }
 | |
| 
 | |
| /* virtual */ void
 | |
| nsWindow::CleanupFullscreenTransition()
 | |
| {
 | |
|   MOZ_ASSERT(NS_IsMainThread(), "CleanupFullscreenTransition "
 | |
|              "should only run on the main thread");
 | |
| 
 | |
|   mTransitionWnd = nullptr;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::MakeFullScreen(bool aFullScreen, nsIScreen* aTargetScreen)
 | |
| {
 | |
|   // taskbarInfo will be nullptr pre Windows 7 until Bug 680227 is resolved.
 | |
|   nsCOMPtr<nsIWinTaskbar> taskbarInfo =
 | |
|     do_GetService(NS_TASKBAR_CONTRACTID);
 | |
| 
 | |
|   mFullscreenMode = aFullScreen;
 | |
|   if (aFullScreen) {
 | |
|     if (mSizeMode == nsSizeMode_Fullscreen)
 | |
|       return NS_OK;
 | |
|     mOldSizeMode = mSizeMode;
 | |
|     SetSizeMode(nsSizeMode_Fullscreen);
 | |
| 
 | |
|     // Notify the taskbar that we will be entering full screen mode.
 | |
|     if (taskbarInfo) {
 | |
|       taskbarInfo->PrepareFullScreenHWND(mWnd, TRUE);
 | |
|     }
 | |
|   } else {
 | |
|     if (mSizeMode != nsSizeMode_Fullscreen)
 | |
|       return NS_OK;
 | |
|     SetSizeMode(mOldSizeMode);
 | |
|   }
 | |
| 
 | |
|   // If we are going fullscreen, the window size continues to change
 | |
|   // and the window will be reflow again then.
 | |
|   UpdateNonClientMargins(mSizeMode, /* Reflow */ !aFullScreen);
 | |
| 
 | |
|   // Will call hide chrome, reposition window. Note this will
 | |
|   // also cache dimensions for restoration, so it should only
 | |
|   // be called once per fullscreen request.
 | |
|   nsBaseWidget::InfallibleMakeFullScreen(aFullScreen, aTargetScreen);
 | |
| 
 | |
|   if (mIsVisible && !aFullScreen && mOldSizeMode == nsSizeMode_Normal) {
 | |
|     // Ensure the window exiting fullscreen get activated. Window
 | |
|     // activation might be bypassed in SetSizeMode.
 | |
|     DispatchFocusToTopLevelWindow(true);
 | |
|   }
 | |
| 
 | |
|   // Notify the taskbar that we have exited full screen mode.
 | |
|   if (!aFullScreen && taskbarInfo) {
 | |
|     taskbarInfo->PrepareFullScreenHWND(mWnd, FALSE);
 | |
|   }
 | |
| 
 | |
|   if (mWidgetListener) {
 | |
|     mWidgetListener->SizeModeChanged(mSizeMode);
 | |
|     mWidgetListener->FullscreenChanged(aFullScreen);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Native data storage
 | |
|  *
 | |
|  * nsIWidget::GetNativeData
 | |
|  * nsIWidget::FreeNativeData
 | |
|  *
 | |
|  * Set or clear native data based on a constant.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Return some native data according to aDataType
 | |
| void* nsWindow::GetNativeData(uint32_t aDataType)
 | |
| {
 | |
|   switch (aDataType) {
 | |
|     case NS_NATIVE_TMP_WINDOW:
 | |
|       return (void*)::CreateWindowExW(mIsRTL ? WS_EX_LAYOUTRTL : 0,
 | |
|                                       GetWindowClass(),
 | |
|                                       L"",
 | |
|                                       WS_CHILD,
 | |
|                                       CW_USEDEFAULT,
 | |
|                                       CW_USEDEFAULT,
 | |
|                                       CW_USEDEFAULT,
 | |
|                                       CW_USEDEFAULT,
 | |
|                                       mWnd,
 | |
|                                       nullptr,
 | |
|                                       nsToolkit::mDllInstance,
 | |
|                                       nullptr);
 | |
|     case NS_NATIVE_PLUGIN_ID:
 | |
|     case NS_NATIVE_PLUGIN_PORT:
 | |
|     case NS_NATIVE_WIDGET:
 | |
|     case NS_NATIVE_WINDOW:
 | |
|       return (void*)mWnd;
 | |
|     case NS_NATIVE_SHAREABLE_WINDOW:
 | |
|       return (void*) WinUtils::GetTopLevelHWND(mWnd);
 | |
|     case NS_NATIVE_GRAPHIC:
 | |
|       MOZ_ASSERT_UNREACHABLE("Not supported on Windows:");
 | |
|       return nullptr;
 | |
|     case NS_RAW_NATIVE_IME_CONTEXT: {
 | |
|       void* pseudoIMEContext = GetPseudoIMEContext();
 | |
|       if (pseudoIMEContext) {
 | |
|         return pseudoIMEContext;
 | |
|       }
 | |
|       MOZ_FALLTHROUGH;
 | |
|     }
 | |
|     case NS_NATIVE_TSF_THREAD_MGR:
 | |
|     case NS_NATIVE_TSF_CATEGORY_MGR:
 | |
|     case NS_NATIVE_TSF_DISPLAY_ATTR_MGR:
 | |
|       return IMEHandler::GetNativeData(this, aDataType);
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| static void
 | |
| SetChildStyleAndParent(HWND aChildWindow, HWND aParentWindow)
 | |
| {
 | |
|     // Make sure the window is styled to be a child window.
 | |
|     LONG_PTR style = GetWindowLongPtr(aChildWindow, GWL_STYLE);
 | |
|     style |= WS_CHILD;
 | |
|     style &= ~WS_POPUP;
 | |
|     SetWindowLongPtr(aChildWindow, GWL_STYLE, style);
 | |
| 
 | |
|     // Do the reparenting. Note that this call will probably cause a sync native
 | |
|     // message to the process that owns the child window.
 | |
|     ::SetParent(aChildWindow, aParentWindow);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::SetNativeData(uint32_t aDataType, uintptr_t aVal)
 | |
| {
 | |
|   switch (aDataType) {
 | |
|     case NS_NATIVE_CHILD_WINDOW:
 | |
|     case NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW:
 | |
|       {
 | |
|         HWND childHwnd = reinterpret_cast<HWND>(aVal);
 | |
|         DWORD childProc = 0;
 | |
|         GetWindowThreadProcessId(childHwnd, &childProc);
 | |
|         if (!PluginProcessParent::IsPluginProcessId(static_cast<base::ProcessId>(childProc))) {
 | |
|           MOZ_ASSERT_UNREACHABLE("SetNativeData window origin was not a plugin process.");
 | |
|           break;
 | |
|         }
 | |
|         HWND parentHwnd =
 | |
|           aDataType == NS_NATIVE_CHILD_WINDOW ? mWnd : WinUtils::GetTopLevelHWND(mWnd);
 | |
|         SetChildStyleAndParent(childHwnd, parentHwnd);
 | |
|         break;
 | |
|       }
 | |
|     default:
 | |
|       NS_ERROR("SetNativeData called with unsupported data type.");
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Free some native data according to aDataType
 | |
| void nsWindow::FreeNativeData(void * data, uint32_t aDataType)
 | |
| {
 | |
|   switch (aDataType)
 | |
|   {
 | |
|     case NS_NATIVE_GRAPHIC:
 | |
|     case NS_NATIVE_WIDGET:
 | |
|     case NS_NATIVE_WINDOW:
 | |
|     case NS_NATIVE_PLUGIN_PORT:
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetTitle
 | |
|  *
 | |
|  * Set the main windows title text.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SetTitle(const nsAString& aTitle)
 | |
| {
 | |
|   const nsString& strTitle = PromiseFlatString(aTitle);
 | |
|   AutoRestore<bool> sendingText(mSendingSetText);
 | |
|   mSendingSetText = true;
 | |
|   ::SendMessageW(mWnd, WM_SETTEXT, (WPARAM)0, (LPARAM)(LPCWSTR)strTitle.get());
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::SetIcon
 | |
|  *
 | |
|  * Set the main windows icon.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::SetIcon(const nsAString& aIconSpec)
 | |
| {
 | |
|   // Assume the given string is a local identifier for an icon file.
 | |
| 
 | |
|   nsCOMPtr<nsIFile> iconFile;
 | |
|   ResolveIconName(aIconSpec, NS_LITERAL_STRING(".ico"),
 | |
|                   getter_AddRefs(iconFile));
 | |
|   if (!iconFile)
 | |
|     return;
 | |
| 
 | |
|   nsAutoString iconPath;
 | |
|   iconFile->GetPath(iconPath);
 | |
| 
 | |
|   // XXX this should use MZLU (see bug 239279)
 | |
| 
 | |
|   ::SetLastError(0);
 | |
| 
 | |
|   HICON bigIcon = (HICON)::LoadImageW(nullptr,
 | |
|                                       (LPCWSTR)iconPath.get(),
 | |
|                                       IMAGE_ICON,
 | |
|                                       ::GetSystemMetrics(SM_CXICON),
 | |
|                                       ::GetSystemMetrics(SM_CYICON),
 | |
|                                       LR_LOADFROMFILE );
 | |
|   HICON smallIcon = (HICON)::LoadImageW(nullptr,
 | |
|                                         (LPCWSTR)iconPath.get(),
 | |
|                                         IMAGE_ICON,
 | |
|                                         ::GetSystemMetrics(SM_CXSMICON),
 | |
|                                         ::GetSystemMetrics(SM_CYSMICON),
 | |
|                                         LR_LOADFROMFILE );
 | |
| 
 | |
|   if (bigIcon) {
 | |
|     HICON icon = (HICON) ::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_BIG, (LPARAM)bigIcon);
 | |
|     if (icon)
 | |
|       ::DestroyIcon(icon);
 | |
|     mIconBig = bigIcon;
 | |
|   }
 | |
| #ifdef DEBUG_SetIcon
 | |
|   else {
 | |
|     NS_LossyConvertUTF16toASCII cPath(iconPath);
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|            ("\nIcon load error; icon=%s, rc=0x%08X\n\n", 
 | |
|             cPath.get(), ::GetLastError()));
 | |
|   }
 | |
| #endif
 | |
|   if (smallIcon) {
 | |
|     HICON icon = (HICON) ::SendMessageW(mWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)smallIcon);
 | |
|     if (icon)
 | |
|       ::DestroyIcon(icon);
 | |
|     mIconSmall = smallIcon;
 | |
|   }
 | |
| #ifdef DEBUG_SetIcon
 | |
|   else {
 | |
|     NS_LossyConvertUTF16toASCII cPath(iconPath);
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|            ("\nSmall icon load error; icon=%s, rc=0x%08X\n\n", 
 | |
|             cPath.get(), ::GetLastError()));
 | |
|   }
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::WidgetToScreenOffset
 | |
|  *
 | |
|  * Return this widget's origin in screen coordinates.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset()
 | |
| {
 | |
|   POINT point;
 | |
|   point.x = 0;
 | |
|   point.y = 0;
 | |
|   ::ClientToScreen(mWnd, &point);
 | |
|   return LayoutDeviceIntPoint(point.x, point.y);
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntSize
 | |
| nsWindow::ClientToWindowSize(const LayoutDeviceIntSize& aClientSize)
 | |
| {
 | |
|   if (mWindowType == eWindowType_popup && !IsPopupWithTitleBar())
 | |
|     return aClientSize;
 | |
| 
 | |
|   // just use (200, 200) as the position
 | |
|   RECT r;
 | |
|   r.left = 200;
 | |
|   r.top = 200;
 | |
|   r.right = 200 + aClientSize.width;
 | |
|   r.bottom = 200 + aClientSize.height;
 | |
|   ::AdjustWindowRectEx(&r, WindowStyle(), false, WindowExStyle());
 | |
| 
 | |
|   return LayoutDeviceIntSize(r.right - r.left, r.bottom - r.top);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::EnableDragDrop
 | |
|  *
 | |
|  * Enables/Disables drag and drop of files on this widget.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::EnableDragDrop(bool aEnable)
 | |
| {
 | |
|   if (!mWnd) {
 | |
|     // Return early if the window already closed
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (aEnable) {
 | |
|     if (!mNativeDragTarget) {
 | |
|       mNativeDragTarget = new nsNativeDragTarget(this);
 | |
|       mNativeDragTarget->AddRef();
 | |
|       if (SUCCEEDED(::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget,
 | |
|                                            TRUE, FALSE))) {
 | |
|         ::RegisterDragDrop(mWnd, (LPDROPTARGET)mNativeDragTarget);
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (mWnd && mNativeDragTarget) {
 | |
|       ::RevokeDragDrop(mWnd);
 | |
|       ::CoLockObjectExternal((LPUNKNOWN)mNativeDragTarget, FALSE, TRUE);
 | |
|       mNativeDragTarget->DragCancel();
 | |
|       NS_RELEASE(mNativeDragTarget);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::CaptureMouse
 | |
|  *
 | |
|  * Enables/Disables system mouse capture.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void nsWindow::CaptureMouse(bool aCapture)
 | |
| {
 | |
|   TRACKMOUSEEVENT mTrack;
 | |
|   mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
 | |
|   mTrack.dwHoverTime = 0;
 | |
|   mTrack.hwndTrack = mWnd;
 | |
|   if (aCapture) {
 | |
|     mTrack.dwFlags = TME_CANCEL | TME_LEAVE;
 | |
|     ::SetCapture(mWnd);
 | |
|   } else {
 | |
|     mTrack.dwFlags = TME_LEAVE;
 | |
|     ::ReleaseCapture();
 | |
|   }
 | |
|   sIsInMouseCapture = aCapture;
 | |
|   TrackMouseEvent(&mTrack);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::CaptureRollupEvents
 | |
|  *
 | |
|  * Dealing with event rollup on destroy for popups. Enables &
 | |
|  * Disables system capture of any and all events that would
 | |
|  * cause a dropdown to be rolled up.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::CaptureRollupEvents(nsIRollupListener* aListener, bool aDoCapture)
 | |
| {
 | |
|   if (aDoCapture) {
 | |
|     gRollupListener = aListener;
 | |
|     if (!sMsgFilterHook && !sCallProcHook && !sCallMouseHook) {
 | |
|       RegisterSpecialDropdownHooks();
 | |
|     }
 | |
|     sProcessHook = true;
 | |
|   } else {
 | |
|     gRollupListener = nullptr;
 | |
|     sProcessHook = false;
 | |
|     UnregisterSpecialDropdownHooks();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::GetAttention
 | |
|  *
 | |
|  * Bring this window to the user's attention.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Draw user's attention to this window until it comes to foreground.
 | |
| nsresult
 | |
| nsWindow::GetAttention(int32_t aCycleCount)
 | |
| {
 | |
|   // Got window?
 | |
|   if (!mWnd)
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
| 
 | |
|   HWND flashWnd = WinUtils::GetTopLevelHWND(mWnd, false, false);
 | |
|   HWND fgWnd = ::GetForegroundWindow();
 | |
|   // Don't flash if the flash count is 0 or if the foreground window is our
 | |
|   // window handle or that of our owned-most window.
 | |
|   if (aCycleCount == 0 || 
 | |
|       flashWnd == fgWnd ||
 | |
|       flashWnd == WinUtils::GetTopLevelHWND(fgWnd, false, false)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   DWORD defaultCycleCount = 0;
 | |
|   ::SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &defaultCycleCount, 0);
 | |
| 
 | |
|   FLASHWINFO flashInfo = { sizeof(FLASHWINFO), flashWnd,
 | |
|     FLASHW_ALL, aCycleCount > 0 ? aCycleCount : defaultCycleCount, 0 };
 | |
|   ::FlashWindowEx(&flashInfo);
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void nsWindow::StopFlashing()
 | |
| {
 | |
|   HWND flashWnd = mWnd;
 | |
|   while (HWND ownerWnd = ::GetWindow(flashWnd, GW_OWNER)) {
 | |
|     flashWnd = ownerWnd;
 | |
|   }
 | |
| 
 | |
|   FLASHWINFO flashInfo = { sizeof(FLASHWINFO), flashWnd,
 | |
|     FLASHW_STOP, 0, 0 };
 | |
|   ::FlashWindowEx(&flashInfo);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::HasPendingInputEvent
 | |
|  *
 | |
|  * Ask whether there user input events pending.  All input events are
 | |
|  * included, including those not targeted at this nsIwidget instance.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| bool
 | |
| nsWindow::HasPendingInputEvent()
 | |
| {
 | |
|   // If there is pending input or the user is currently
 | |
|   // moving the window then return true.
 | |
|   // Note: When the user is moving the window WIN32 spins
 | |
|   // a separate event loop and input events are not
 | |
|   // reported to the application.
 | |
|   if (HIWORD(GetQueueStatus(QS_INPUT)))
 | |
|     return true;
 | |
|   GUITHREADINFO guiInfo;
 | |
|   guiInfo.cbSize = sizeof(GUITHREADINFO);
 | |
|   if (!GetGUIThreadInfo(GetCurrentThreadId(), &guiInfo))
 | |
|     return false;
 | |
|   return GUI_INMOVESIZE == (guiInfo.flags & GUI_INMOVESIZE);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::GetLayerManager
 | |
|  *
 | |
|  * Get the layer manager associated with this widget.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| LayerManager*
 | |
| nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
 | |
|                           LayersBackend aBackendHint,
 | |
|                           LayerManagerPersistence aPersistence)
 | |
| {
 | |
|   RECT windowRect;
 | |
|   ::GetClientRect(mWnd, &windowRect);
 | |
| 
 | |
|   // Try OMTC first.
 | |
|   if (!mLayerManager && ShouldUseOffMainThreadCompositing()) {
 | |
|     gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
 | |
| 
 | |
|     // e10s uses the parameter to pass in the shadow manager from the TabChild
 | |
|     // so we don't expect to see it there since this doesn't support e10s.
 | |
|     NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s");
 | |
|     CreateCompositor();
 | |
|   }
 | |
| 
 | |
|   if (!mLayerManager) {
 | |
|     MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild);
 | |
|     MOZ_ASSERT(!mCompositorWidgetDelegate);
 | |
| 
 | |
|     // Ensure we have a widget proxy even if we're not using the compositor,
 | |
|     // since all our transparent window handling lives there.
 | |
|     WinCompositorWidgetInitData initData(
 | |
|       reinterpret_cast<uintptr_t>(mWnd),
 | |
|       reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
 | |
|       mTransparencyMode);
 | |
|     // If we're not using the compositor, the options don't actually matter.
 | |
|     CompositorOptions options(false, false);
 | |
|     mBasicLayersSurface = new InProcessWinCompositorWidget(initData, options, this);
 | |
|     mCompositorWidgetDelegate = mBasicLayersSurface;
 | |
|     mLayerManager = CreateBasicLayerManager();
 | |
|   }
 | |
| 
 | |
|   NS_ASSERTION(mLayerManager, "Couldn't provide a valid layer manager.");
 | |
| 
 | |
|   return mLayerManager;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsBaseWidget::SetCompositorWidgetDelegate
 | |
|  *
 | |
|  * Called to connect the nsWindow to the delegate providing
 | |
|  * platform compositing API access.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate)
 | |
| {
 | |
|     if (delegate) {
 | |
|         mCompositorWidgetDelegate = delegate->AsPlatformSpecificDelegate();
 | |
|         MOZ_ASSERT(mCompositorWidgetDelegate,
 | |
|                    "nsWindow::SetCompositorWidgetDelegate called with a non-PlatformCompositorWidgetDelegate");
 | |
|     } else {
 | |
|         mCompositorWidgetDelegate = nullptr;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: nsIWidget::OnDefaultButtonLoaded
 | |
|  *
 | |
|  * Called after the dialog is loaded and it has a default button.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| nsresult
 | |
| nsWindow::OnDefaultButtonLoaded(const LayoutDeviceIntRect& aButtonRect)
 | |
| {
 | |
|   if (aButtonRect.IsEmpty())
 | |
|     return NS_OK;
 | |
| 
 | |
|   // Don't snap when we are not active.
 | |
|   HWND activeWnd = ::GetActiveWindow();
 | |
|   if (activeWnd != ::GetForegroundWindow() ||
 | |
|       WinUtils::GetTopLevelHWND(mWnd, true) !=
 | |
|         WinUtils::GetTopLevelHWND(activeWnd, true)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   bool isAlwaysSnapCursor =
 | |
|     Preferences::GetBool("ui.cursor_snapping.always_enabled", false);
 | |
| 
 | |
|   if (!isAlwaysSnapCursor) {
 | |
|     BOOL snapDefaultButton;
 | |
|     if (!::SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0,
 | |
|                                 &snapDefaultButton, 0) || !snapDefaultButton)
 | |
|       return NS_OK;
 | |
|   }
 | |
| 
 | |
|   LayoutDeviceIntRect widgetRect = GetScreenBounds();
 | |
|   LayoutDeviceIntRect buttonRect(aButtonRect + widgetRect.TopLeft());
 | |
| 
 | |
|   LayoutDeviceIntPoint centerOfButton(buttonRect.X() + buttonRect.Width() / 2,
 | |
|                                       buttonRect.Y() + buttonRect.Height() / 2);
 | |
|   // The center of the button can be outside of the widget.
 | |
|   // E.g., it could be hidden by scrolling.
 | |
|   if (!widgetRect.Contains(centerOfButton)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!::SetCursorPos(centerOfButton.x, centerOfButton.y)) {
 | |
|     NS_ERROR("SetCursorPos failed");
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::UpdateThemeGeometries(const nsTArray<ThemeGeometry>& aThemeGeometries)
 | |
| {
 | |
|   RefPtr<LayerManager> layerManager = GetLayerManager();
 | |
|   if (!layerManager) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsIntRegion clearRegion;
 | |
|   if (!HasGlass() || !nsUXThemeData::CheckForCompositor()) {
 | |
|     // Make sure and clear old regions we've set previously. Note HasGlass can be false
 | |
|     // for glass desktops if the window we are rendering to doesn't make use of glass
 | |
|     // (e.g. fullscreen browsing).
 | |
|     layerManager->SetRegionToClear(clearRegion);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // On Win10, force show the top border:
 | |
|   if (IsWin10OrLater() && mCustomNonClient && mSizeMode == nsSizeMode_Normal) {
 | |
|     RECT rect;
 | |
|     ::GetWindowRect(mWnd, &rect);
 | |
|     // We want 1 pixel of border for every whole 100% of scaling
 | |
|     double borderSize = std::min(1, RoundDown(GetDesktopToDeviceScale().scale));
 | |
|     clearRegion.Or(clearRegion, gfx::IntRect::Truncate(0, 0, rect.right - rect.left, borderSize));
 | |
|   }
 | |
| 
 | |
|   mWindowButtonsRect = Nothing();
 | |
| 
 | |
|   if (!IsWin10OrLater()) {
 | |
|     for (size_t i = 0; i < aThemeGeometries.Length(); i++) {
 | |
|       if (aThemeGeometries[i].mType == nsNativeThemeWin::eThemeGeometryTypeWindowButtons) {
 | |
|         LayoutDeviceIntRect bounds = aThemeGeometries[i].mRect;
 | |
|         // Extend the bounds by one pixel to the right, because that's how much
 | |
|         // the actual window button shape extends past the client area of the
 | |
|         // window (and overlaps the right window frame).
 | |
|         bounds.SetWidth(bounds.Width() + 1);
 | |
|         if (!mWindowButtonsRect) {
 | |
|           mWindowButtonsRect = Some(bounds);
 | |
|         }
 | |
|         clearRegion.Or(clearRegion, gfx::IntRect::Truncate(bounds.X(), bounds.Y(), bounds.Width(), bounds.Height() - 2.0));
 | |
|         clearRegion.Or(clearRegion, gfx::IntRect::Truncate(bounds.X() + 1.0, bounds.YMost() - 2.0, bounds.Width() - 2.0, 1.0));
 | |
|         clearRegion.Or(clearRegion, gfx::IntRect::Truncate(bounds.X() + 2.0, bounds.YMost() - 1.0, bounds.Width() - 4.0, 1.0));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   layerManager->SetRegionToClear(clearRegion);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::AddWindowOverlayWebRenderCommands(layers::WebRenderBridgeChild* aWrBridge,
 | |
|                                             wr::DisplayListBuilder& aBuilder,
 | |
|                                             wr::IpcResourceUpdateQueue& aResources)
 | |
| {
 | |
|   if (mWindowButtonsRect) {
 | |
|     wr::LayoutRect rect = wr::ToLayoutRect(*mWindowButtonsRect);
 | |
|     nsTArray<wr::ComplexClipRegion> roundedClip;
 | |
|     roundedClip.AppendElement(wr::ToComplexClipRegion(
 | |
|       RoundedRect(ThebesRect(mWindowButtonsRect->ToUnknownRect()),
 | |
|                   RectCornerRadii(0, 0, 3, 3))));
 | |
|     wr::WrClipId clipId =
 | |
|       aBuilder.DefineClip(Nothing(), rect, &roundedClip);
 | |
|     aBuilder.PushClip(clipId);
 | |
|     aBuilder.PushClearRect(rect);
 | |
|     aBuilder.PopClip();
 | |
|   }
 | |
| }
 | |
| 
 | |
| uint32_t
 | |
| nsWindow::GetMaxTouchPoints() const
 | |
| {
 | |
|   return WinUtils::GetMaxTouchPoints();
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::SetWindowClass(const nsAString& xulWinType)
 | |
| {
 | |
|   mIsEarlyBlankWindow = xulWinType.EqualsLiteral("navigator:blank");
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Moz Events
 | |
|  **
 | |
|  ** Moz GUI event management. 
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Mozilla event initialization
 | |
|  *
 | |
|  * Helpers for initializing moz events.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Event initialization
 | |
| void nsWindow::InitEvent(WidgetGUIEvent& event, LayoutDeviceIntPoint* aPoint)
 | |
| {
 | |
|   if (nullptr == aPoint) {     // use the point from the event
 | |
|     // get the message position in client coordinates
 | |
|     if (mWnd != nullptr) {
 | |
|       DWORD pos = ::GetMessagePos();
 | |
|       POINT cpos;
 | |
| 
 | |
|       cpos.x = GET_X_LPARAM(pos);
 | |
|       cpos.y = GET_Y_LPARAM(pos);
 | |
| 
 | |
|       ::ScreenToClient(mWnd, &cpos);
 | |
|       event.mRefPoint = LayoutDeviceIntPoint(cpos.x, cpos.y);
 | |
|     } else {
 | |
|       event.mRefPoint = LayoutDeviceIntPoint(0, 0);
 | |
|     }
 | |
|   } else {
 | |
|     // use the point override if provided
 | |
|     event.mRefPoint = *aPoint;
 | |
|   }
 | |
| 
 | |
|   event.AssignEventTime(CurrentMessageWidgetEventTime());
 | |
| }
 | |
| 
 | |
| WidgetEventTime
 | |
| nsWindow::CurrentMessageWidgetEventTime() const
 | |
| {
 | |
|   LONG messageTime = ::GetMessageTime();
 | |
|   return WidgetEventTime(messageTime, GetMessageTimeStamp(messageTime));
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Moz event dispatch helpers
 | |
|  *
 | |
|  * Helpers for dispatching different types of moz events.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Main event dispatch. Invokes callback and ProcessEvent method on
 | |
| // Event Listener object. Part of nsIWidget.
 | |
| nsresult
 | |
| nsWindow::DispatchEvent(WidgetGUIEvent* event, nsEventStatus& aStatus)
 | |
| {
 | |
| #ifdef WIDGET_DEBUG_OUTPUT
 | |
|   debug_DumpEvent(stdout,
 | |
|                   event->mWidget,
 | |
|                   event,
 | |
|                   "something",
 | |
|                   (int32_t) mWnd);
 | |
| #endif // WIDGET_DEBUG_OUTPUT
 | |
| 
 | |
|   aStatus = nsEventStatus_eIgnore;
 | |
| 
 | |
|   // Top level windows can have a view attached which requires events be sent
 | |
|   // to the underlying base window and the view. Added when we combined the
 | |
|   // base chrome window with the main content child for nc client area (title
 | |
|   // bar) rendering.
 | |
|   if (mAttachedWidgetListener) {
 | |
|     aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
 | |
|   }
 | |
|   else if (mWidgetListener) {
 | |
|     aStatus = mWidgetListener->HandleEvent(event, mUseAttachedEvents);
 | |
|   }
 | |
| 
 | |
|   // the window can be destroyed during processing of seemingly innocuous events like, say,
 | |
|   // mousedowns due to the magic of scripting. mousedowns will return nsEventStatus_eIgnore,
 | |
|   // which causes problems with the deleted window. therefore:
 | |
|   if (mOnDestroyCalled)
 | |
|     aStatus = nsEventStatus_eConsumeNoDefault;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchStandardEvent(EventMessage aMsg)
 | |
| {
 | |
|   WidgetGUIEvent event(true, aMsg, this);
 | |
|   InitEvent(event);
 | |
| 
 | |
|   bool result = DispatchWindowEvent(&event);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchKeyboardEvent(WidgetKeyboardEvent* event)
 | |
| {
 | |
|   nsEventStatus status = DispatchInputEvent(event);
 | |
|   return ConvertStatus(status);
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchContentCommandEvent(WidgetContentCommandEvent* aEvent)
 | |
| {
 | |
|   nsEventStatus status;
 | |
|   DispatchEvent(aEvent, status);
 | |
|   return ConvertStatus(status);
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchWheelEvent(WidgetWheelEvent* aEvent)
 | |
| {
 | |
|   nsEventStatus status = DispatchInputEvent(aEvent->AsInputEvent());
 | |
|   return ConvertStatus(status);
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event)
 | |
| {
 | |
|   nsEventStatus status;
 | |
|   DispatchEvent(event, status);
 | |
|   return ConvertStatus(status);
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchWindowEvent(WidgetGUIEvent* event,
 | |
|                                    nsEventStatus& aStatus)
 | |
| {
 | |
|   DispatchEvent(event, aStatus);
 | |
|   return ConvertStatus(aStatus);
 | |
| }
 | |
| 
 | |
| // Recursively dispatch synchronous paints for nsIWidget
 | |
| // descendants with invalidated rectangles.
 | |
| BOOL CALLBACK nsWindow::DispatchStarvedPaints(HWND aWnd, LPARAM aMsg)
 | |
| {
 | |
|   LONG_PTR proc = ::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
 | |
|   if (proc == (LONG_PTR)&nsWindow::WindowProc) {
 | |
|     // its one of our windows so check to see if it has a
 | |
|     // invalidated rect. If it does. Dispatch a synchronous
 | |
|     // paint.
 | |
|     if (GetUpdateRect(aWnd, nullptr, FALSE))
 | |
|       VERIFY(::UpdateWindow(aWnd));
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| // Check for pending paints and dispatch any pending paint
 | |
| // messages for any nsIWidget which is a descendant of the
 | |
| // top-level window that *this* window is embedded within.
 | |
| //
 | |
| // Note: We do not dispatch pending paint messages for non
 | |
| // nsIWidget managed windows.
 | |
| void nsWindow::DispatchPendingEvents()
 | |
| {
 | |
|   if (mPainting) {
 | |
|     NS_WARNING("We were asked to dispatch pending events during painting, "
 | |
|                "denying since that's unsafe.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // We need to ensure that reflow events do not get starved.
 | |
|   // At the same time, we don't want to recurse through here
 | |
|   // as that would prevent us from dispatching starved paints.
 | |
|   static int recursionBlocker = 0;
 | |
|   if (recursionBlocker++ == 0) {
 | |
|     NS_ProcessPendingEvents(nullptr, PR_MillisecondsToInterval(100));
 | |
|     --recursionBlocker;
 | |
|   }
 | |
| 
 | |
|   // Quickly check to see if there are any paint events pending,
 | |
|   // but only dispatch them if it has been long enough since the
 | |
|   // last paint completed.
 | |
|   if (::GetQueueStatus(QS_PAINT) &&
 | |
|       ((TimeStamp::Now() - mLastPaintEndTime).ToMilliseconds() >= 50)) {
 | |
|     // Find the top level window.
 | |
|     HWND topWnd = WinUtils::GetTopLevelHWND(mWnd);
 | |
| 
 | |
|     // Dispatch pending paints for topWnd and all its descendant windows.
 | |
|     // Note: EnumChildWindows enumerates all descendant windows not just
 | |
|     // the children (but not the window itself).
 | |
|     nsWindow::DispatchStarvedPaints(topWnd, 0);
 | |
|     ::EnumChildWindows(topWnd, nsWindow::DispatchStarvedPaints, 0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsWindow::DispatchPluginEvent(UINT aMessage,
 | |
|                                      WPARAM aWParam,
 | |
|                                      LPARAM aLParam,
 | |
|                                      bool aDispatchPendingEvents)
 | |
| {
 | |
|   bool ret = nsWindowBase::DispatchPluginEvent(
 | |
|                WinUtils::InitMSG(aMessage, aWParam, aLParam, mWnd));
 | |
|   if (aDispatchPendingEvents && !Destroyed()) {
 | |
|     DispatchPendingEvents();
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| bool nsWindow::TouchEventShouldStartDrag(EventMessage aEventMessage,
 | |
|                                          LayoutDeviceIntPoint aEventPoint)
 | |
| {
 | |
|   // Allow users to start dragging by double-tapping.
 | |
|   if (aEventMessage == eMouseDoubleClick) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // In chrome UI, allow touchdownstartsdrag attributes
 | |
|   // to cause any touchdown event to trigger a drag.
 | |
|   if (aEventMessage == eMouseDown) {
 | |
|     WidgetMouseEvent hittest(true, eMouseHitTest, this,
 | |
|                              WidgetMouseEvent::eReal);
 | |
|     hittest.mRefPoint = aEventPoint;
 | |
|     hittest.mIgnoreRootScrollFrame = true;
 | |
|     hittest.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 | |
|     DispatchInputEvent(&hittest);
 | |
| 
 | |
|     EventTarget* target = hittest.GetDOMEventTarget();
 | |
|     if (target) {
 | |
|       nsCOMPtr<nsIContent> node = do_QueryInterface(target);
 | |
| 
 | |
|       // Check if the element or any parent element has the
 | |
|       // attribute we're looking for.
 | |
|       while (node) {
 | |
|         if (node->IsElement()) {
 | |
|           nsAutoString startDrag;
 | |
|           node->AsElement()->GetAttribute(
 | |
|             NS_LITERAL_STRING("touchdownstartsdrag"), startDrag);
 | |
|           if (!startDrag.IsEmpty()) {
 | |
|             return true;
 | |
|           }
 | |
|         }
 | |
|         node = node->GetParent();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // Deal with all sort of mouse event
 | |
| bool
 | |
| nsWindow::DispatchMouseEvent(EventMessage aEventMessage, WPARAM wParam,
 | |
|                              LPARAM lParam, bool aIsContextMenuKey,
 | |
|                              int16_t aButton, uint16_t aInputSource,
 | |
|                              WinPointerInfo* aPointerInfo)
 | |
| {
 | |
|   enum
 | |
|   {
 | |
|     eUnset,
 | |
|     ePrecise,
 | |
|     eTouch
 | |
|   };
 | |
|   static int sTouchInputActiveState = eUnset;
 | |
|   bool result = false;
 | |
| 
 | |
|   UserActivity();
 | |
| 
 | |
|   if (!mWidgetListener) {
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
 | |
|   LayoutDeviceIntPoint mpScreen = eventPoint + WidgetToScreenOffset();
 | |
| 
 | |
|   // Suppress mouse moves caused by widget creation. Make sure to do this early
 | |
|   // so that we update sLastMouseMovePoint even for touch-induced mousemove events.
 | |
|   if (aEventMessage == eMouseMove) {
 | |
|     if ((sLastMouseMovePoint.x == mpScreen.x) && (sLastMouseMovePoint.y == mpScreen.y)) {
 | |
|       return result;
 | |
|     }
 | |
|     sLastMouseMovePoint.x = mpScreen.x;
 | |
|     sLastMouseMovePoint.y = mpScreen.y;
 | |
|   }
 | |
| 
 | |
|   if (WinUtils::GetIsMouseFromTouch(aEventMessage)) {
 | |
|     if (aEventMessage == eMouseDown) {
 | |
|       Telemetry::Accumulate(Telemetry::FX_TOUCH_USED, 1);
 | |
|     }
 | |
| 
 | |
|     // Fire an observer when the user initially touches a touch screen. Front end
 | |
|     // uses this to modify UX.
 | |
|     if (sTouchInputActiveState != eTouch) {
 | |
|       sTouchInputActiveState = eTouch;
 | |
|       nsCOMPtr<nsIObserverService> obsServ =
 | |
|         mozilla::services::GetObserverService();
 | |
|       obsServ->NotifyObservers(nullptr, "touch-input-detected", nullptr);
 | |
|     }
 | |
| 
 | |
|     if (mTouchWindow) {
 | |
|       // If mTouchWindow is true, then we must have APZ enabled and be
 | |
|       // feeding it raw touch events. In that case we only want to
 | |
|       // send touch-generated mouse events to content if they should
 | |
|       // start a touch-based drag-and-drop gesture, such as on
 | |
|       // double-tapping or when tapping elements marked with the
 | |
|       // touchdownstartsdrag attribute in chrome UI.
 | |
|       MOZ_ASSERT(mAPZC);
 | |
|       if (TouchEventShouldStartDrag(aEventMessage, eventPoint)) {
 | |
|         aEventMessage = eMouseTouchDrag;
 | |
|       } else {
 | |
|         return result;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     // Fire an observer when the user initially uses a mouse or pen.
 | |
|     if (sTouchInputActiveState != ePrecise) {
 | |
|       sTouchInputActiveState = ePrecise;
 | |
|       nsCOMPtr<nsIObserverService> obsServ =
 | |
|         mozilla::services::GetObserverService();
 | |
|       obsServ->NotifyObservers(nullptr, "precise-input-detected", nullptr);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   uint32_t pointerId = aPointerInfo ? aPointerInfo->pointerId :
 | |
|                                       MOUSE_POINTERID();
 | |
| 
 | |
|   // Since it is unclear whether a user will use the digitizer,
 | |
|   // Postpone initialization until first PEN message will be found.
 | |
|   if (MouseEvent_Binding::MOZ_SOURCE_PEN == aInputSource
 | |
|       // Messages should be only at topLevel window.
 | |
|       && nsWindowType::eWindowType_toplevel == mWindowType
 | |
|       // Currently this scheme is used only when pointer events is enabled.
 | |
|       && gfxPrefs::PointerEventsEnabled() && InkCollector::sInkCollector) {
 | |
|     InkCollector::sInkCollector->SetTarget(mWnd);
 | |
|     InkCollector::sInkCollector->SetPointerId(pointerId);
 | |
|   }
 | |
| 
 | |
|   switch (aEventMessage) {
 | |
|     case eMouseDown:
 | |
|       CaptureMouse(true);
 | |
|       break;
 | |
| 
 | |
|     // eMouseMove and eMouseExitFromWidget are here because we need to make
 | |
|     // sure capture flag isn't left on after a drag where we wouldn't see a
 | |
|     // button up message (see bug 324131).
 | |
|     case eMouseUp:
 | |
|     case eMouseMove:
 | |
|     case eMouseExitFromWidget:
 | |
|       if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON)) && sIsInMouseCapture)
 | |
|         CaptureMouse(false);
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
| 
 | |
|   } // switch
 | |
| 
 | |
|   WidgetMouseEvent event(true, aEventMessage, this, WidgetMouseEvent::eReal,
 | |
|                          aIsContextMenuKey ? WidgetMouseEvent::eContextMenuKey :
 | |
|                                              WidgetMouseEvent::eNormal);
 | |
|   if (aEventMessage == eContextMenu && aIsContextMenuKey) {
 | |
|     LayoutDeviceIntPoint zero(0, 0);
 | |
|     InitEvent(event, &zero);
 | |
|   } else {
 | |
|     InitEvent(event, &eventPoint);
 | |
|   }
 | |
| 
 | |
|   ModifierKeyState modifierKeyState;
 | |
|   modifierKeyState.InitInputEvent(event);
 | |
| 
 | |
|   // eContextMenu with Shift state is special.  It won't fire "contextmenu"
 | |
|   // event in the web content for blocking web content to prevent its default.
 | |
|   // However, Shift+F10 is a standard shortcut key on Windows.  Therefore,
 | |
|   // this should not block web page to prevent its default.  I.e., it should
 | |
|   // behave same as ContextMenu key without Shift key.
 | |
|   // XXX Should we allow to block web page to prevent its default with
 | |
|   //     Ctrl+Shift+F10 or Alt+Shift+F10 instead?
 | |
|   if (aEventMessage == eContextMenu && aIsContextMenuKey && event.IsShift() &&
 | |
|       NativeKey::LastKeyOrCharMSG().message == WM_SYSKEYDOWN &&
 | |
|       NativeKey::LastKeyOrCharMSG().wParam == VK_F10) {
 | |
|     event.mModifiers &= ~MODIFIER_SHIFT;
 | |
|   }
 | |
| 
 | |
|   event.button    = aButton;
 | |
|   event.inputSource = aInputSource;
 | |
|   if (aPointerInfo) {
 | |
|     // Mouse events from Windows WM_POINTER*. Fill more information in
 | |
|     // WidgetMouseEvent.
 | |
|     event.AssignPointerHelperData(*aPointerInfo);
 | |
|     event.pressure = aPointerInfo->mPressure;
 | |
|     event.buttons = aPointerInfo->mButtons;
 | |
|   } else {
 | |
|     // If we get here the mouse events must be from non-touch sources, so
 | |
|     // convert it to pointer events as well
 | |
|     event.convertToPointer = true;
 | |
|     event.pointerId = pointerId;
 | |
|   }
 | |
| 
 | |
|   bool insideMovementThreshold = (DeprecatedAbs(sLastMousePoint.x - eventPoint.x) < (short)::GetSystemMetrics(SM_CXDOUBLECLK)) &&
 | |
|                                    (DeprecatedAbs(sLastMousePoint.y - eventPoint.y) < (short)::GetSystemMetrics(SM_CYDOUBLECLK));
 | |
| 
 | |
|   BYTE eventButton;
 | |
|   switch (aButton) {
 | |
|     case WidgetMouseEvent::eLeftButton:
 | |
|       eventButton = VK_LBUTTON;
 | |
|       break;
 | |
|     case WidgetMouseEvent::eMiddleButton:
 | |
|       eventButton = VK_MBUTTON;
 | |
|       break;
 | |
|     case WidgetMouseEvent::eRightButton:
 | |
|       eventButton = VK_RBUTTON;
 | |
|       break;
 | |
|     default:
 | |
|       eventButton = 0;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   // Doubleclicks are used to set the click count, then changed to mousedowns
 | |
|   // We're going to time double-clicks from mouse *up* to next mouse *down*
 | |
|   LONG curMsgTime = ::GetMessageTime();
 | |
| 
 | |
|   switch (aEventMessage) {
 | |
|     case eMouseDoubleClick:
 | |
|       event.mMessage = eMouseDown;
 | |
|       event.button = aButton;
 | |
|       sLastClickCount = 2;
 | |
|       sLastMouseDownTime = curMsgTime;
 | |
|       break;
 | |
|     case eMouseUp:
 | |
|       // remember when this happened for the next mouse down
 | |
|       sLastMousePoint.x = eventPoint.x;
 | |
|       sLastMousePoint.y = eventPoint.y;
 | |
|       sLastMouseButton = eventButton;
 | |
|       break;
 | |
|     case eMouseDown:
 | |
|       // now look to see if we want to convert this to a double- or triple-click
 | |
|       if (((curMsgTime - sLastMouseDownTime) < (LONG)::GetDoubleClickTime()) &&
 | |
|           insideMovementThreshold &&
 | |
|           eventButton == sLastMouseButton) {
 | |
|         sLastClickCount ++;
 | |
|       } else {
 | |
|         // reset the click count, to count *this* click
 | |
|         sLastClickCount = 1;
 | |
|       }
 | |
|       // Set last Click time on MouseDown only
 | |
|       sLastMouseDownTime = curMsgTime;
 | |
|       break;
 | |
|     case eMouseMove:
 | |
|       if (!insideMovementThreshold) {
 | |
|         sLastClickCount = 0;
 | |
|       }
 | |
|       break;
 | |
|     case eMouseExitFromWidget:
 | |
|       event.mExitFrom =
 | |
|         IsTopLevelMouseExit(mWnd) ? WidgetMouseEvent::eTopLevel :
 | |
|                                     WidgetMouseEvent::eChild;
 | |
|       break;
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
|   event.mClickCount = sLastClickCount;
 | |
| 
 | |
| #ifdef NS_DEBUG_XX
 | |
|   MOZ_LOG(gWindowsLog, LogLevel::Info,
 | |
|          ("Msg Time: %d Click Count: %d\n", curMsgTime, event.mClickCount));
 | |
| #endif
 | |
| 
 | |
|   NPEvent pluginEvent;
 | |
| 
 | |
|   switch (aEventMessage) {
 | |
|     case eMouseDown:
 | |
|       switch (aButton) {
 | |
|         case WidgetMouseEvent::eLeftButton:
 | |
|           pluginEvent.event = WM_LBUTTONDOWN;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eMiddleButton:
 | |
|           pluginEvent.event = WM_MBUTTONDOWN;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eRightButton:
 | |
|           pluginEvent.event = WM_RBUTTONDOWN;
 | |
|           break;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|       break;
 | |
|     case eMouseUp:
 | |
|       switch (aButton) {
 | |
|         case WidgetMouseEvent::eLeftButton:
 | |
|           pluginEvent.event = WM_LBUTTONUP;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eMiddleButton:
 | |
|           pluginEvent.event = WM_MBUTTONUP;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eRightButton:
 | |
|           pluginEvent.event = WM_RBUTTONUP;
 | |
|           break;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|       break;
 | |
|     case eMouseDoubleClick:
 | |
|       switch (aButton) {
 | |
|         case WidgetMouseEvent::eLeftButton:
 | |
|           pluginEvent.event = WM_LBUTTONDBLCLK;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eMiddleButton:
 | |
|           pluginEvent.event = WM_MBUTTONDBLCLK;
 | |
|           break;
 | |
|         case WidgetMouseEvent::eRightButton:
 | |
|           pluginEvent.event = WM_RBUTTONDBLCLK;
 | |
|           break;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|       break;
 | |
|     case eMouseMove:
 | |
|       pluginEvent.event = WM_MOUSEMOVE;
 | |
|       break;
 | |
|     case eMouseExitFromWidget:
 | |
|       pluginEvent.event = WM_MOUSELEAVE;
 | |
|       break;
 | |
|     default:
 | |
|       pluginEvent.event = WM_NULL;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   pluginEvent.wParam = wParam;     // plugins NEED raw OS event flags!
 | |
|   pluginEvent.lParam = lParam;
 | |
| 
 | |
|   event.mPluginEvent.Copy(pluginEvent);
 | |
| 
 | |
|   // call the event callback
 | |
|   if (mWidgetListener) {
 | |
|     if (aEventMessage == eMouseMove) {
 | |
|       LayoutDeviceIntRect rect = GetBounds();
 | |
|       rect.MoveTo(0, 0);
 | |
| 
 | |
|       if (rect.Contains(event.mRefPoint)) {
 | |
|         if (sCurrentWindow == nullptr || sCurrentWindow != this) {
 | |
|           if ((nullptr != sCurrentWindow) && (!sCurrentWindow->mInDtor)) {
 | |
|             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
 | |
|             sCurrentWindow->DispatchMouseEvent(eMouseExitFromWidget,
 | |
|                                                wParam, pos, false, 
 | |
|                                                WidgetMouseEvent::eLeftButton,
 | |
|                                                aInputSource, aPointerInfo);
 | |
|           }
 | |
|           sCurrentWindow = this;
 | |
|           if (!mInDtor) {
 | |
|             LPARAM pos = sCurrentWindow->lParamToClient(lParamToScreen(lParam));
 | |
|             sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
 | |
|                                                wParam, pos, false,
 | |
|                                                WidgetMouseEvent::eLeftButton,
 | |
|                                                aInputSource, aPointerInfo);
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     } else if (aEventMessage == eMouseExitFromWidget) {
 | |
|       if (sCurrentWindow == this) {
 | |
|         sCurrentWindow = nullptr;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     result = ConvertStatus(DispatchInputEvent(&event));
 | |
| 
 | |
|     // Release the widget with NS_IF_RELEASE() just in case
 | |
|     // the context menu key code in EventListenerManager::HandleEvent()
 | |
|     // released it already.
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| void nsWindow::DispatchFocusToTopLevelWindow(bool aIsActivate)
 | |
| {
 | |
|   if (aIsActivate)
 | |
|     sJustGotActivate = false;
 | |
|   sJustGotDeactivate = false;
 | |
| 
 | |
|   // retrive the toplevel window or dialog
 | |
|   HWND curWnd = mWnd;
 | |
|   HWND toplevelWnd = nullptr;
 | |
|   while (curWnd) {
 | |
|     toplevelWnd = curWnd;
 | |
| 
 | |
|     nsWindow *win = WinUtils::GetNSWindowPtr(curWnd);
 | |
|     if (win) {
 | |
|       nsWindowType wintype = win->WindowType();
 | |
|       if (wintype == eWindowType_toplevel || wintype == eWindowType_dialog)
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     curWnd = ::GetParent(curWnd); // Parent or owner (if has no parent)
 | |
|   }
 | |
| 
 | |
|   if (toplevelWnd) {
 | |
|     nsWindow *win = WinUtils::GetNSWindowPtr(toplevelWnd);
 | |
|     if (win && win->mWidgetListener) {
 | |
|       if (aIsActivate) {
 | |
|         win->mWidgetListener->WindowActivated();
 | |
|       } else {
 | |
|         if (!win->BlurEventsSuppressed()) {
 | |
|           win->mWidgetListener->WindowDeactivated();
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| HWND nsWindow::WindowAtMouse()
 | |
| {
 | |
|   DWORD pos = ::GetMessagePos();
 | |
|   POINT mp;
 | |
|   mp.x = GET_X_LPARAM(pos);
 | |
|   mp.y = GET_Y_LPARAM(pos);
 | |
|   return ::WindowFromPoint(mp);
 | |
| }
 | |
| 
 | |
| bool nsWindow::IsTopLevelMouseExit(HWND aWnd)
 | |
| {
 | |
|   HWND mouseWnd = WindowAtMouse();
 | |
| 
 | |
|   // WinUtils::GetTopLevelHWND() will return a HWND for the window frame
 | |
|   // (which includes the non-client area).  If the mouse has moved into
 | |
|   // the non-client area, we should treat it as a top-level exit.
 | |
|   HWND mouseTopLevel = WinUtils::GetTopLevelHWND(mouseWnd);
 | |
|   if (mouseWnd == mouseTopLevel)
 | |
|     return true;
 | |
| 
 | |
|   return WinUtils::GetTopLevelHWND(aWnd) != mouseTopLevel;
 | |
| }
 | |
| 
 | |
| bool nsWindow::BlurEventsSuppressed()
 | |
| {
 | |
|   // are they suppressed in this window?
 | |
|   if (mBlurSuppressLevel > 0)
 | |
|     return true;
 | |
| 
 | |
|   // are they suppressed by any container widget?
 | |
|   HWND parentWnd = ::GetParent(mWnd);
 | |
|   if (parentWnd) {
 | |
|     nsWindow *parent = WinUtils::GetNSWindowPtr(parentWnd);
 | |
|     if (parent)
 | |
|       return parent->BlurEventsSuppressed();
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // In some circumstances (opening dependent windows) it makes more sense
 | |
| // (and fixes a crash bug) to not blur the parent window. Called from
 | |
| // nsFilePicker.
 | |
| void nsWindow::SuppressBlurEvents(bool aSuppress)
 | |
| {
 | |
|   if (aSuppress)
 | |
|     ++mBlurSuppressLevel; // for this widget
 | |
|   else {
 | |
|     NS_ASSERTION(mBlurSuppressLevel > 0, "unbalanced blur event suppression");
 | |
|     if (mBlurSuppressLevel > 0)
 | |
|       --mBlurSuppressLevel;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsWindow::ConvertStatus(nsEventStatus aStatus)
 | |
| {
 | |
|   return aStatus == nsEventStatus_eConsumeNoDefault;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: IPC
 | |
|  *
 | |
|  * IPC related helpers.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // static
 | |
| bool
 | |
| nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult)
 | |
| {
 | |
|   switch(aMsg) {
 | |
|     case WM_SETFOCUS:
 | |
|     case WM_KILLFOCUS:
 | |
|     case WM_ENABLE:
 | |
|     case WM_WINDOWPOSCHANGING:
 | |
|     case WM_WINDOWPOSCHANGED:
 | |
|     case WM_PARENTNOTIFY:
 | |
|     case WM_ACTIVATEAPP:
 | |
|     case WM_NCACTIVATE:
 | |
|     case WM_ACTIVATE:
 | |
|     case WM_CHILDACTIVATE:
 | |
|     case WM_IME_SETCONTEXT:
 | |
|     case WM_IME_NOTIFY:
 | |
|     case WM_SHOWWINDOW:
 | |
|     case WM_CANCELMODE:
 | |
|     case WM_MOUSEACTIVATE:
 | |
|     case WM_CONTEXTMENU:
 | |
|       aResult = 0;
 | |
|     return true;
 | |
| 
 | |
|     case WM_SETTINGCHANGE:
 | |
|     case WM_SETCURSOR:
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   char szBuf[200];
 | |
|   sprintf(szBuf,
 | |
|     "An unhandled ISMEX_SEND message was received during spin loop! (%X)", aMsg);
 | |
|   NS_WARNING(szBuf);
 | |
| #endif
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam)
 | |
| {
 | |
|   MOZ_ASSERT_IF(msg != WM_GETOBJECT,
 | |
|                 !mozilla::ipc::MessageChannel::IsPumpingMessages() ||
 | |
|                 mozilla::ipc::SuppressedNeuteringRegion::IsNeuteringSuppressed());
 | |
| 
 | |
|   // Modal UI being displayed in windowless plugins.
 | |
|   if (mozilla::ipc::MessageChannel::IsSpinLoopActive() &&
 | |
|       (InSendMessageEx(nullptr) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
 | |
|     LRESULT res;
 | |
|     if (IsAsyncResponseEvent(msg, res)) {
 | |
|       ReplyMessage(res);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Handle certain sync plugin events sent to the parent which
 | |
|   // trigger ipc calls that result in deadlocks.
 | |
| 
 | |
|   DWORD dwResult = 0;
 | |
|   bool handled = false;
 | |
| 
 | |
|   switch(msg) {
 | |
|     // Windowless flash sending WM_ACTIVATE events to the main window
 | |
|     // via calls to ShowWindow.
 | |
|     case WM_ACTIVATE:
 | |
|       if (lParam != 0 && LOWORD(wParam) == WA_ACTIVE &&
 | |
|           IsWindow((HWND)lParam)) {
 | |
|         // Check for Adobe Reader X sync activate message from their
 | |
|         // helper window and ignore. Fixes an annoying focus problem.
 | |
|         if ((InSendMessageEx(nullptr) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
 | |
|           wchar_t szClass[10];
 | |
|           HWND focusWnd = (HWND)lParam;
 | |
|           if (IsWindowVisible(focusWnd) &&
 | |
|               GetClassNameW(focusWnd, szClass,
 | |
|                             sizeof(szClass)/sizeof(char16_t)) &&
 | |
|               !wcscmp(szClass, L"Edit") &&
 | |
|               !WinUtils::IsOurProcessWindow(focusWnd)) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         handled = true;
 | |
|       }
 | |
|     break;
 | |
|     // Plugins taking or losing focus triggering focus app messages.
 | |
|     case WM_SETFOCUS:
 | |
|     case WM_KILLFOCUS:
 | |
|     // Windowed plugins that pass sys key events to defwndproc generate
 | |
|     // WM_SYSCOMMAND events to the main window.
 | |
|     case WM_SYSCOMMAND:
 | |
|     // Windowed plugins that fire context menu selection events to parent
 | |
|     // windows.
 | |
|     case WM_CONTEXTMENU:
 | |
|     // IME events fired as a result of synchronous focus changes
 | |
|     case WM_IME_SETCONTEXT:
 | |
|       handled = true;
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   if (handled &&
 | |
|       (InSendMessageEx(nullptr) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) {
 | |
|     ReplyMessage(dwResult);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Native events
 | |
|  **
 | |
|  ** Main Windows message handlers and OnXXX handlers for
 | |
|  ** Windows event handling.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Wind proc.
 | |
|  *
 | |
|  * The main Windows event procedures and associated
 | |
|  * message processing methods.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| static bool
 | |
| DisplaySystemMenu(HWND hWnd, nsSizeMode sizeMode, bool isRtl, int32_t x, int32_t y)
 | |
| {
 | |
|   HMENU hMenu = GetSystemMenu(hWnd, FALSE);
 | |
|   if (hMenu) {
 | |
|     MENUITEMINFO mii;
 | |
|     mii.cbSize = sizeof(MENUITEMINFO);
 | |
|     mii.fMask = MIIM_STATE;
 | |
|     mii.fType = 0;
 | |
| 
 | |
|     // update the options
 | |
|     mii.fState = MF_ENABLED;
 | |
|     SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
 | |
|     SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
 | |
|     SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
 | |
|     SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
 | |
|     SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
 | |
| 
 | |
|     mii.fState = MF_GRAYED;
 | |
|     switch(sizeMode) {
 | |
|       case nsSizeMode_Fullscreen:
 | |
|         // intentional fall through
 | |
|       case nsSizeMode_Maximized:
 | |
|         SetMenuItemInfo(hMenu, SC_SIZE, FALSE, &mii);
 | |
|         SetMenuItemInfo(hMenu, SC_MOVE, FALSE, &mii);
 | |
|         SetMenuItemInfo(hMenu, SC_MAXIMIZE, FALSE, &mii);
 | |
|         break;
 | |
|       case nsSizeMode_Minimized:
 | |
|         SetMenuItemInfo(hMenu, SC_MINIMIZE, FALSE, &mii);
 | |
|         break;
 | |
|       case nsSizeMode_Normal:
 | |
|         SetMenuItemInfo(hMenu, SC_RESTORE, FALSE, &mii);
 | |
|         break;
 | |
|     }
 | |
|     LPARAM cmd =
 | |
|       TrackPopupMenu(hMenu,
 | |
|                      (TPM_LEFTBUTTON|TPM_RIGHTBUTTON|
 | |
|                       TPM_RETURNCMD|TPM_TOPALIGN|
 | |
|                       (isRtl ? TPM_RIGHTALIGN : TPM_LEFTALIGN)),
 | |
|                      x, y, 0, hWnd, nullptr);
 | |
|     if (cmd) {
 | |
|       PostMessage(hWnd, WM_SYSCOMMAND, cmd, 0);
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // The WndProc procedure for all nsWindows in this toolkit. This merely catches
 | |
| // exceptions and passes the real work to WindowProcInternal. See bug 587406
 | |
| // and http://msdn.microsoft.com/en-us/library/ms633573%28VS.85%29.aspx
 | |
| LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   mozilla::ipc::CancelCPOWs();
 | |
| 
 | |
|   BackgroundHangMonitor().NotifyActivity();
 | |
| 
 | |
|   return mozilla::CallWindowProcCrashProtected(WindowProcInternal, hWnd, msg, wParam, lParam);
 | |
| }
 | |
| 
 | |
| LRESULT CALLBACK nsWindow::WindowProcInternal(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   if (::GetWindowLongPtrW(hWnd, GWLP_ID) == eFakeTrackPointScrollableID) {
 | |
|     // This message was sent to the FAKETRACKPOINTSCROLLABLE.
 | |
|     if (msg == WM_HSCROLL) {
 | |
|       // Route WM_HSCROLL messages to the main window.
 | |
|       hWnd = ::GetParent(::GetParent(hWnd));
 | |
|     } else {
 | |
|       // Handle all other messages with its original window procedure.
 | |
|       WNDPROC prevWindowProc = (WNDPROC)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
 | |
|       return ::CallWindowProcW(prevWindowProc, hWnd, msg, wParam, lParam);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (msg == MOZ_WM_TRACE) {
 | |
|     // This is a tracer event for measuring event loop latency.
 | |
|     // See WidgetTraceEvent.cpp for more details.
 | |
|     mozilla::SignalTracerThread();
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   // Get the window which caused the event and ask it to process the message
 | |
|   nsWindow *targetWindow = WinUtils::GetNSWindowPtr(hWnd);
 | |
|   NS_ASSERTION(targetWindow, "nsWindow* is null!");
 | |
|   if (!targetWindow)
 | |
|     return ::DefWindowProcW(hWnd, msg, wParam, lParam);
 | |
| 
 | |
|   // Hold the window for the life of this method, in case it gets
 | |
|   // destroyed during processing, unless we're in the dtor already.
 | |
|   nsCOMPtr<nsIWidget> kungFuDeathGrip;
 | |
|   if (!targetWindow->mInDtor)
 | |
|     kungFuDeathGrip = targetWindow;
 | |
| 
 | |
|   targetWindow->IPCWindowProcHandler(msg, wParam, lParam);
 | |
| 
 | |
|   // Create this here so that we store the last rolled up popup until after
 | |
|   // the event has been processed.
 | |
|   nsAutoRollup autoRollup;
 | |
| 
 | |
|   LRESULT popupHandlingResult;
 | |
|   if (DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult))
 | |
|     return popupHandlingResult;
 | |
| 
 | |
|   // Call ProcessMessage
 | |
|   LRESULT retValue;
 | |
|   if (targetWindow->ProcessMessage(msg, wParam, lParam, &retValue)) {
 | |
|     return retValue;
 | |
|   }
 | |
| 
 | |
|   LRESULT res = ::CallWindowProcW(targetWindow->GetPrevWindowProc(),
 | |
|                                   hWnd, msg, wParam, lParam);
 | |
| 
 | |
|   return res;
 | |
| }
 | |
| 
 | |
| const char16_t*
 | |
| GetQuitType()
 | |
| {
 | |
|   if (Preferences::GetBool(PREF_WIN_REGISTER_APPLICATION_RESTART, false)) {
 | |
|     DWORD cchCmdLine = 0;
 | |
|     HRESULT rc =
 | |
|       ::GetApplicationRestartSettings(::GetCurrentProcess(), nullptr, &cchCmdLine, nullptr);
 | |
|     if (rc == S_OK) {
 | |
|       return u"os-restart";
 | |
|     }
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| // The main windows message processing method for plugins.
 | |
| // The result means whether this method processed the native
 | |
| // event for plugin. If false, the native event should be
 | |
| // processed by the caller self.
 | |
| bool
 | |
| nsWindow::ProcessMessageForPlugin(const MSG &aMsg,
 | |
|                                   MSGResult& aResult)
 | |
| {
 | |
|   aResult.mResult = 0;
 | |
|   aResult.mConsumed = true;
 | |
| 
 | |
|   bool eventDispatched = false;
 | |
|   switch (aMsg.message) {
 | |
|     case WM_CHAR:
 | |
|     case WM_SYSCHAR:
 | |
|       aResult.mResult = ProcessCharMessage(aMsg, &eventDispatched);
 | |
|       break;
 | |
| 
 | |
|     case WM_KEYUP:
 | |
|     case WM_SYSKEYUP:
 | |
|       aResult.mResult = ProcessKeyUpMessage(aMsg, &eventDispatched);
 | |
|       break;
 | |
| 
 | |
|     case WM_KEYDOWN:
 | |
|     case WM_SYSKEYDOWN:
 | |
|       aResult.mResult = ProcessKeyDownMessage(aMsg, &eventDispatched);
 | |
|       break;
 | |
| 
 | |
|     case WM_DEADCHAR:
 | |
|     case WM_SYSDEADCHAR:
 | |
| 
 | |
|     case WM_CUT:
 | |
|     case WM_COPY:
 | |
|     case WM_PASTE:
 | |
|     case WM_CLEAR:
 | |
|     case WM_UNDO:
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| 
 | |
|   if (!eventDispatched) {
 | |
|     aResult.mConsumed = nsWindowBase::DispatchPluginEvent(aMsg);
 | |
|   }
 | |
|   if (!Destroyed()) {
 | |
|     DispatchPendingEvents();
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static void ForceFontUpdate()
 | |
| {
 | |
|   // update device context font cache
 | |
|   // Dirty but easiest way:
 | |
|   // Changing nsIPrefBranch entry which triggers callbacks
 | |
|   // and flows into calling mDeviceContext->FlushFontCache()
 | |
|   // to update the font cache in all the instance of Browsers
 | |
|   static const char kPrefName[] = "font.internaluseonly.changed";
 | |
|   bool fontInternalChange =
 | |
|     Preferences::GetBool(kPrefName, false);
 | |
|   Preferences::SetBool(kPrefName, !fontInternalChange);
 | |
| }
 | |
| 
 | |
| static bool CleartypeSettingChanged()
 | |
| {
 | |
|   static int currentQuality = -1;
 | |
|   BYTE quality = cairo_win32_get_system_text_quality();
 | |
| 
 | |
|   if (currentQuality == quality)
 | |
|     return false;
 | |
| 
 | |
|   if (currentQuality < 0) {
 | |
|     currentQuality = quality;
 | |
|     return false;
 | |
|   }
 | |
|   currentQuality = quality;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::ExternalHandlerProcessMessage(UINT aMessage,
 | |
|                                         WPARAM& aWParam,
 | |
|                                         LPARAM& aLParam,
 | |
|                                         MSGResult& aResult)
 | |
| {
 | |
|   if (mWindowHook.Notify(mWnd, aMessage, aWParam, aLParam, aResult)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (IMEHandler::ProcessMessage(this, aMessage, aWParam, aLParam, aResult)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (MouseScrollHandler::ProcessMessage(this, aMessage, aWParam, aLParam,
 | |
|                                          aResult)) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (PluginHasFocus()) {
 | |
|     MSG nativeMsg = WinUtils::InitMSG(aMessage, aWParam, aLParam, mWnd);
 | |
|     if (ProcessMessageForPlugin(nativeMsg, aResult)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * the _exit() call is not a safe way to terminate your own process on
 | |
|  * Windows, because _exit runs DLL detach callbacks which run static
 | |
|  * destructors for xul.dll.
 | |
|  *
 | |
|  * This method terminates the current process without those issues.
 | |
|  */
 | |
| static void
 | |
| ExitThisProcessSafely()
 | |
| {
 | |
|   HANDLE process = GetCurrentProcess();
 | |
|   if (TerminateProcess(GetCurrentProcess(), 0)) {
 | |
|     // TerminateProcess is asynchronous, so we wait on our own process handle
 | |
|     WaitForSingleObject(process, INFINITE);
 | |
|   }
 | |
|   MOZ_CRASH("Just in case extremis crash in ExitThisProcessSafely.");
 | |
| }
 | |
| 
 | |
| // The main windows message processing method.
 | |
| bool
 | |
| nsWindow::ProcessMessage(UINT msg, WPARAM& wParam, LPARAM& lParam,
 | |
|                          LRESULT *aRetValue)
 | |
| {
 | |
| #if defined(EVENT_DEBUG_OUTPUT)
 | |
|   // First param shows all events, second param indicates whether
 | |
|   // to show mouse move events. See nsWindowDbg for details.
 | |
|   PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS);
 | |
| #endif
 | |
| 
 | |
|   MSGResult msgResult(aRetValue);
 | |
|   if (ExternalHandlerProcessMessage(msg, wParam, lParam, msgResult)) {
 | |
|     return (msgResult.mConsumed || !mWnd);
 | |
|   }
 | |
| 
 | |
|   bool result = false;    // call the default nsWindow proc
 | |
|   *aRetValue = 0;
 | |
| 
 | |
|   // Glass hit testing w/custom transparent margins
 | |
|   LRESULT dwmHitResult;
 | |
|   if (mCustomNonClient &&
 | |
|       nsUXThemeData::CheckForCompositor() &&
 | |
|       /* We don't do this for win10 glass with a custom titlebar,
 | |
|        * in order to avoid the caption buttons breaking. */
 | |
|       !(IsWin10OrLater() && HasGlass()) &&
 | |
|       DwmDefWindowProc(mWnd, msg, wParam, lParam, &dwmHitResult)) {
 | |
|     *aRetValue = dwmHitResult;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // (Large blocks of code should be broken out into OnEvent handlers.)
 | |
|   switch (msg) {
 | |
|     // WM_QUERYENDSESSION must be handled by all windows.
 | |
|     // Otherwise Windows thinks the window can just be killed at will.
 | |
|     case WM_QUERYENDSESSION:
 | |
|       if (sCanQuit == TRI_UNKNOWN)
 | |
|       {
 | |
|         // Ask if it's ok to quit, and store the answer until we
 | |
|         // get WM_ENDSESSION signaling the round is complete.
 | |
|         nsCOMPtr<nsIObserverService> obsServ =
 | |
|           mozilla::services::GetObserverService();
 | |
|         nsCOMPtr<nsISupportsPRBool> cancelQuit =
 | |
|           do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
 | |
|         cancelQuit->SetData(false);
 | |
| 
 | |
|         const char16_t* quitType = GetQuitType();
 | |
|         obsServ->NotifyObservers(cancelQuit, "quit-application-requested", quitType);
 | |
| 
 | |
|         bool abortQuit;
 | |
|         cancelQuit->GetData(&abortQuit);
 | |
|         sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
 | |
|       }
 | |
|       *aRetValue = sCanQuit ? TRUE : FALSE;
 | |
|       result = true;
 | |
|       break;
 | |
| 
 | |
|     case MOZ_WM_STARTA11Y:
 | |
| #if defined(ACCESSIBILITY)
 | |
|       (void*)GetAccessible();
 | |
|       result = true;
 | |
| #else
 | |
|       result = false;
 | |
| #endif
 | |
|       break;
 | |
| 
 | |
|     case WM_ENDSESSION:
 | |
|     case MOZ_WM_APP_QUIT:
 | |
|       if (msg == MOZ_WM_APP_QUIT || (wParam == TRUE && sCanQuit == TRI_TRUE))
 | |
|       {
 | |
|         // Let's fake a shutdown sequence without actually closing windows etc.
 | |
|         // to avoid Windows killing us in the middle. A proper shutdown would
 | |
|         // require having a chance to pump some messages. Unfortunately
 | |
|         // Windows won't let us do that. Bug 212316.
 | |
|         nsCOMPtr<nsIObserverService> obsServ =
 | |
|           mozilla::services::GetObserverService();
 | |
|         const char16_t* context = u"shutdown-persist";
 | |
|         const char16_t* syncShutdown = u"syncShutdown";
 | |
|         const char16_t* quitType = GetQuitType();
 | |
| 
 | |
|         obsServ->NotifyObservers(nullptr, "quit-application-granted", syncShutdown);
 | |
|         obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
 | |
|         obsServ->NotifyObservers(nullptr, "quit-application", quitType);
 | |
|         obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context);
 | |
|         obsServ->NotifyObservers(nullptr, "profile-change-teardown", context);
 | |
|         obsServ->NotifyObservers(nullptr, "profile-before-change", context);
 | |
|         obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context);
 | |
|         obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry", context);
 | |
|         ExitThisProcessSafely();
 | |
|       }
 | |
|       sCanQuit = TRI_UNKNOWN;
 | |
|       result = true;
 | |
|       break;
 | |
| 
 | |
|     case WM_SYSCOLORCHANGE:
 | |
|       OnSysColorChanged();
 | |
|       break;
 | |
| 
 | |
|     case WM_THEMECHANGED:
 | |
|     {
 | |
|       // Update non-client margin offsets 
 | |
|       UpdateNonClientMargins();
 | |
|       nsUXThemeData::UpdateNativeThemeInfo();
 | |
| 
 | |
|       NotifyThemeChanged();
 | |
| 
 | |
|       // Invalidate the window so that the repaint will
 | |
|       // pick up the new theme.
 | |
|       Invalidate(true, true, true);
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_WTSSESSION_CHANGE:
 | |
|     {
 | |
|       switch (wParam) {
 | |
|         case WTS_CONSOLE_CONNECT:
 | |
|         case WTS_REMOTE_CONNECT:
 | |
|         case WTS_SESSION_UNLOCK:
 | |
|           // When a session becomes visible, we should invalidate.
 | |
|           Invalidate(true, true, true);
 | |
|           break;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_FONTCHANGE:
 | |
|     {
 | |
|       // We only handle this message for the hidden window,
 | |
|       // as we only need to update the (global) font list once
 | |
|       // for any given change, not once per window!
 | |
|       if (mWindowType != eWindowType_invisible) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       nsresult rv;
 | |
|       bool didChange = false;
 | |
| 
 | |
|       // update the global font list
 | |
|       nsCOMPtr<nsIFontEnumerator> fontEnum = do_GetService("@mozilla.org/gfx/fontenumerator;1", &rv);
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         fontEnum->UpdateFontList(&didChange);
 | |
|         ForceFontUpdate();
 | |
|       } //if (NS_SUCCEEDED(rv))
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_SETTINGCHANGE:
 | |
|     {
 | |
|       if (wParam == SPI_SETKEYBOARDDELAY) {
 | |
|         // CaretBlinkTime is cached in nsLookAndFeel
 | |
|         NotifyThemeChanged();
 | |
|         break;
 | |
|       }
 | |
|       if (lParam) {
 | |
|         auto lParamString = reinterpret_cast<const wchar_t*>(lParam);
 | |
|         if (!wcscmp(lParamString, L"ImmersiveColorSet")) {
 | |
|           // This might be the Win10 dark mode setting; only way to tell
 | |
|           // is to actually force a theme change, since we don't get
 | |
|           // WM_THEMECHANGED or WM_SYSCOLORCHANGE when that happens.
 | |
|           if (IsWin10OrLater() && mWindowType == eWindowType_toplevel) {
 | |
|             nsIPresShell* presShell = mWidgetListener->GetPresShell();
 | |
|             if (presShell) {
 | |
|               presShell->ThemeChanged();
 | |
|             }
 | |
|           }
 | |
|           // WM_SYSCOLORCHANGE is not dispatched for accent color changes
 | |
|           OnSysColorChanged();
 | |
|           break;
 | |
|         }
 | |
|         if (IsWin10OrLater() && mWindowType == eWindowType_invisible) {
 | |
|           if (!wcscmp(lParamString, L"UserInteractionMode")) {
 | |
|             nsCOMPtr<nsIWindowsUIUtils> uiUtils(do_GetService("@mozilla.org/windows-ui-utils;1"));
 | |
|             if (uiUtils) {
 | |
|               uiUtils->UpdateTabletModeState();
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_NCCALCSIZE:
 | |
|     {
 | |
|       if (mCustomNonClient) {
 | |
|         // If `wParam` is `FALSE`, `lParam` points to a `RECT` that contains
 | |
|         // the proposed window rectangle for our window.  During our
 | |
|         // processing of the `WM_NCCALCSIZE` message, we are expected to
 | |
|         // modify the `RECT` that `lParam` points to, so that its value upon
 | |
|         // our return is the new client area.  We must return 0 if `wParam`
 | |
|         // is `FALSE`.
 | |
|         //
 | |
|         // If `wParam` is `TRUE`, `lParam` points to a `NCCALCSIZE_PARAMS`
 | |
|         // struct.  This struct contains an array of 3 `RECT`s, the first of
 | |
|         // which has the exact same meaning as the `RECT` that is pointed to
 | |
|         // by `lParam` when `wParam` is `FALSE`.  The remaining `RECT`s, in
 | |
|         // conjunction with our return value, can
 | |
|         // be used to specify portions of the source and destination window
 | |
|         // rectangles that are valid and should be preserved.  We opt not to
 | |
|         // implement an elaborate client-area preservation technique, and
 | |
|         // simply return 0, which means "preserve the entire old client area
 | |
|         // and align it with the upper-left corner of our new client area".
 | |
|         RECT *clientRect = wParam
 | |
|                          ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam))->rgrc[0]
 | |
|                          : (reinterpret_cast<RECT*>(lParam));
 | |
|         double scale = WinUtils::IsPerMonitorDPIAware()
 | |
|           ? WinUtils::LogToPhysFactor(mWnd) / WinUtils::SystemScaleFactor()
 | |
|           : 1.0;
 | |
|         clientRect->top +=
 | |
|           NSToIntRound((mCaptionHeight - mNonClientOffset.top) * scale);
 | |
|         clientRect->left +=
 | |
|           NSToIntRound((mHorResizeMargin - mNonClientOffset.left) * scale);
 | |
|         clientRect->right -=
 | |
|           NSToIntRound((mHorResizeMargin - mNonClientOffset.right) * scale);
 | |
|         clientRect->bottom -=
 | |
|           NSToIntRound((mVertResizeMargin - mNonClientOffset.bottom) * scale);
 | |
|         // Make client rect's width and height more than 0 to
 | |
|         // avoid problems of webrender and angle.
 | |
|         clientRect->right = std::max(clientRect->right, clientRect->left + 1);
 | |
|         clientRect->bottom = std::max(clientRect->bottom, clientRect->top + 1);
 | |
| 
 | |
|         result = true;
 | |
|         *aRetValue = 0;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_NCHITTEST:
 | |
|     {
 | |
|       if (mMouseTransparent) {
 | |
|         // Treat this window as transparent.
 | |
|         *aRetValue = HTTRANSPARENT;
 | |
|         result = true;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       /*
 | |
|        * If an nc client area margin has been moved, we are responsible
 | |
|        * for calculating where the resize margins are and returning the
 | |
|        * appropriate set of hit test constants. DwmDefWindowProc (above)
 | |
|        * will handle hit testing on it's command buttons if we are on a
 | |
|        * composited desktop.
 | |
|        */
 | |
| 
 | |
|       if (!mCustomNonClient)
 | |
|         break;
 | |
| 
 | |
|       *aRetValue =
 | |
|         ClientMarginHitTestPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
 | |
|       result = true;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_SETTEXT:
 | |
|       /*
 | |
|        * WM_SETTEXT paints the titlebar area. Avoid this if we have a
 | |
|        * custom titlebar we paint ourselves, or if we're the ones
 | |
|        * sending the message with an updated title
 | |
|        */
 | |
| 
 | |
|       if ((mSendingSetText && nsUXThemeData::CheckForCompositor()) ||
 | |
|           !mCustomNonClient || mNonClientMargins.top == -1)
 | |
|         break;
 | |
| 
 | |
|       {
 | |
|         // From msdn, the way around this is to disable the visible state
 | |
|         // temporarily. We need the text to be set but we don't want the
 | |
|         // redraw to occur. However, we need to make sure that we don't
 | |
|         // do this at the same time that a Present is happening.
 | |
|         //
 | |
|         // To do this we take mPresentLock in nsWindow::PreRender and
 | |
|         // if that lock is taken we wait before doing WM_SETTEXT
 | |
|         if (mCompositorWidgetDelegate) {
 | |
|           mCompositorWidgetDelegate->EnterPresentLock();
 | |
|         }
 | |
|         DWORD style = GetWindowLong(mWnd, GWL_STYLE);
 | |
|         SetWindowLong(mWnd, GWL_STYLE, style & ~WS_VISIBLE);
 | |
|         *aRetValue = CallWindowProcW(GetPrevWindowProc(), mWnd,
 | |
|                                      msg, wParam, lParam);
 | |
|         SetWindowLong(mWnd, GWL_STYLE, style);
 | |
|         if (mCompositorWidgetDelegate) {
 | |
|           mCompositorWidgetDelegate->LeavePresentLock();
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|     case WM_NCACTIVATE:
 | |
|     {
 | |
|       /*
 | |
|        * WM_NCACTIVATE paints nc areas. Avoid this and re-route painting
 | |
|        * through WM_NCPAINT via InvalidateNonClientRegion.
 | |
|        */
 | |
|       UpdateGetWindowInfoCaptionStatus(FALSE != wParam);
 | |
| 
 | |
|       if (!mCustomNonClient)
 | |
|         break;
 | |
| 
 | |
|       // There is a case that rendered result is not kept. Bug 1237617
 | |
|       if (wParam == TRUE &&
 | |
|           !gfxEnv::DisableForcePresent() &&
 | |
|           gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
 | |
|         NS_DispatchToMainThread(NewRunnableMethod("nsWindow::ForcePresent",
 | |
|                                                   this, &nsWindow::ForcePresent));
 | |
|       }
 | |
| 
 | |
|       // let the dwm handle nc painting on glass
 | |
|       // Never allow native painting if we are on fullscreen
 | |
|       if(mSizeMode != nsSizeMode_Fullscreen &&
 | |
|          nsUXThemeData::CheckForCompositor())
 | |
|         break;
 | |
| 
 | |
|       if (wParam == TRUE) {
 | |
|         // going active
 | |
|         *aRetValue = FALSE; // ignored
 | |
|         result = true;
 | |
|         // invalidate to trigger a paint
 | |
|         InvalidateNonClientRegion();
 | |
|         break;
 | |
|       } else {
 | |
|         // going inactive
 | |
|         *aRetValue = TRUE; // go ahead and deactive
 | |
|         result = true;
 | |
|         // invalidate to trigger a paint
 | |
|         InvalidateNonClientRegion();
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     case WM_NCPAINT:
 | |
|     {
 | |
|       /*
 | |
|        * Reset the non-client paint region so that it excludes the
 | |
|        * non-client areas we paint manually. Then call defwndproc
 | |
|        * to do the actual painting.
 | |
|        */
 | |
| 
 | |
|       if (!mCustomNonClient)
 | |
|         break;
 | |
| 
 | |
|       // let the dwm handle nc painting on glass
 | |
|       if(nsUXThemeData::CheckForCompositor())
 | |
|         break;
 | |
| 
 | |
|       HRGN paintRgn = ExcludeNonClientFromPaintRegion((HRGN)wParam);
 | |
|       LRESULT res = CallWindowProcW(GetPrevWindowProc(), mWnd,
 | |
|                                     msg, (WPARAM)paintRgn, lParam);
 | |
|       if (paintRgn != (HRGN)wParam)
 | |
|         DeleteObject(paintRgn);
 | |
|       *aRetValue = res;
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_POWERBROADCAST:
 | |
|       switch (wParam)
 | |
|       {
 | |
|         case PBT_APMSUSPEND:
 | |
|           PostSleepWakeNotification(true);
 | |
|           break;
 | |
|         case PBT_APMRESUMEAUTOMATIC:
 | |
|         case PBT_APMRESUMECRITICAL:
 | |
|         case PBT_APMRESUMESUSPEND:
 | |
|           PostSleepWakeNotification(false);
 | |
|           break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_CLOSE: // close request
 | |
|       if (mWidgetListener)
 | |
|         mWidgetListener->RequestWindowClose(this);
 | |
|       result = true; // abort window closure
 | |
|       break;
 | |
| 
 | |
|     case WM_DESTROY:
 | |
|       // clean up.
 | |
|       DestroyLayerManager();
 | |
|       OnDestroy();
 | |
|       result = true;
 | |
|       break;
 | |
| 
 | |
|     case WM_PAINT:
 | |
|       if (CleartypeSettingChanged()) {
 | |
|         ForceFontUpdate();
 | |
|         gfxFontCache *fc = gfxFontCache::GetCache();
 | |
|         if (fc) {
 | |
|           fc->Flush();
 | |
|         }
 | |
|       }
 | |
|       *aRetValue = (int) OnPaint(nullptr, 0);
 | |
|       result = true;
 | |
|       break;
 | |
| 
 | |
|     case WM_PRINTCLIENT:
 | |
|       result = OnPaint((HDC) wParam, 0);
 | |
|       break;
 | |
| 
 | |
|     case WM_HOTKEY:
 | |
|       result = OnHotKey(wParam, lParam);
 | |
|       break;
 | |
| 
 | |
|     case WM_SYSCHAR:
 | |
|     case WM_CHAR:
 | |
|     {
 | |
|       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
 | |
|       result = ProcessCharMessage(nativeMsg, nullptr);
 | |
|       DispatchPendingEvents();
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_SYSKEYUP:
 | |
|     case WM_KEYUP:
 | |
|     {
 | |
|       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
 | |
|       nativeMsg.time = ::GetMessageTime();
 | |
|       result = ProcessKeyUpMessage(nativeMsg, nullptr);
 | |
|       DispatchPendingEvents();
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_SYSKEYDOWN:
 | |
|     case WM_KEYDOWN:
 | |
|     {
 | |
|       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
 | |
|       result = ProcessKeyDownMessage(nativeMsg, nullptr);
 | |
|       DispatchPendingEvents();
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     // say we've dealt with erase background if widget does
 | |
|     // not need auto-erasing
 | |
|     case WM_ERASEBKGND:
 | |
|       if (!AutoErase((HDC)wParam)) {
 | |
|         *aRetValue = 1;
 | |
|         result = true;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_MOUSEMOVE:
 | |
|     {
 | |
|       if (!mMousePresent && !sIsInMouseCapture) {
 | |
|         // First MOUSEMOVE over the client area. Ask for MOUSELEAVE
 | |
|         TRACKMOUSEEVENT mTrack;
 | |
|         mTrack.cbSize = sizeof(TRACKMOUSEEVENT);
 | |
|         mTrack.dwFlags = TME_LEAVE;
 | |
|         mTrack.dwHoverTime = 0;
 | |
|         mTrack.hwndTrack = mWnd;
 | |
|         TrackMouseEvent(&mTrack);
 | |
|       }
 | |
|       mMousePresent = true;
 | |
| 
 | |
|       // Suppress dispatch of pending events
 | |
|       // when mouse moves are generated by widget
 | |
|       // creation instead of user input.
 | |
|       LPARAM lParamScreen = lParamToScreen(lParam);
 | |
|       POINT mp;
 | |
|       mp.x      = GET_X_LPARAM(lParamScreen);
 | |
|       mp.y      = GET_Y_LPARAM(lParamScreen);
 | |
|       bool userMovedMouse = false;
 | |
|       if ((sLastMouseMovePoint.x != mp.x) || (sLastMouseMovePoint.y != mp.y)) {
 | |
|         userMovedMouse = true;
 | |
|       }
 | |
| 
 | |
|       result = DispatchMouseEvent(eMouseMove, wParam, lParam,
 | |
|                                   false, WidgetMouseEvent::eLeftButton,
 | |
|                                   MOUSE_INPUT_SOURCE(),
 | |
|                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
 | |
|       if (userMovedMouse) {
 | |
|         DispatchPendingEvents();
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_NCMOUSEMOVE:
 | |
|       // If we receive a mouse move event on non-client chrome, make sure and
 | |
|       // send an eMouseExitFromWidget event as well.
 | |
|       if (mMousePresent && !sIsInMouseCapture)
 | |
|         SendMessage(mWnd, WM_MOUSELEAVE, 0, 0);
 | |
|     break;
 | |
| 
 | |
|     case WM_LBUTTONDOWN:
 | |
|     {
 | |
|       result = DispatchMouseEvent(eMouseDown, wParam, lParam,
 | |
|                                   false, WidgetMouseEvent::eLeftButton,
 | |
|                                   MOUSE_INPUT_SOURCE(),
 | |
|                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
 | |
|       DispatchPendingEvents();
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_LBUTTONUP:
 | |
|     {
 | |
|       result = DispatchMouseEvent(eMouseUp, wParam, lParam,
 | |
|                                   false, WidgetMouseEvent::eLeftButton,
 | |
|                                   MOUSE_INPUT_SOURCE(),
 | |
|                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
 | |
|       DispatchPendingEvents();
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_MOUSELEAVE:
 | |
|     {
 | |
|       if (!mMousePresent)
 | |
|         break;
 | |
|       mMousePresent = false;
 | |
| 
 | |
|       // Check if the mouse is over the fullscreen transition window, if so
 | |
|       // clear sLastMouseMovePoint. This way the WM_MOUSEMOVE we get after the
 | |
|       // transition window disappears will not be ignored, even if the mouse
 | |
|       // hasn't moved.
 | |
|       if (mTransitionWnd && WindowAtMouse() == mTransitionWnd) {
 | |
|         sLastMouseMovePoint = {0};
 | |
|       }
 | |
| 
 | |
|       // We need to check mouse button states and put them in for
 | |
|       // wParam.
 | |
|       WPARAM mouseState = (GetKeyState(VK_LBUTTON) ? MK_LBUTTON : 0)
 | |
|         | (GetKeyState(VK_MBUTTON) ? MK_MBUTTON : 0)
 | |
|         | (GetKeyState(VK_RBUTTON) ? MK_RBUTTON : 0);
 | |
|       // Synthesize an event position because we don't get one from
 | |
|       // WM_MOUSELEAVE.
 | |
|       LPARAM pos = lParamToClient(::GetMessagePos());
 | |
|       DispatchMouseEvent(eMouseExitFromWidget, mouseState, pos, false,
 | |
|                          WidgetMouseEvent::eLeftButton,
 | |
|                          MOUSE_INPUT_SOURCE());
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case MOZ_WM_PEN_LEAVES_HOVER_OF_DIGITIZER:
 | |
|     {
 | |
|       LPARAM pos = lParamToClient(::GetMessagePos());
 | |
|       MOZ_ASSERT(InkCollector::sInkCollector);
 | |
|       uint16_t pointerId = InkCollector::sInkCollector->GetPointerId();
 | |
|       if (pointerId != 0) {
 | |
|         WinPointerInfo pointerInfo;
 | |
|         pointerInfo.pointerId = pointerId;
 | |
|         DispatchMouseEvent(eMouseExitFromWidget, wParam, pos, false,
 | |
|                            WidgetMouseEvent::eLeftButton,
 | |
|                            MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
 | |
|         InkCollector::sInkCollector->ClearTarget();
 | |
|         InkCollector::sInkCollector->ClearPointerId();
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_CONTEXTMENU:
 | |
|     {
 | |
|       // If the context menu is brought up by a touch long-press, then
 | |
|       // the APZ code is responsible for dealing with this, so we don't
 | |
|       // need to do anything.
 | |
|       if (mTouchWindow && MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
 | |
|         MOZ_ASSERT(mAPZC); // since mTouchWindow is true, APZ must be enabled
 | |
|         result = true;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       // if the context menu is brought up from the keyboard, |lParam|
 | |
|       // will be -1.
 | |
|       LPARAM pos;
 | |
|       bool contextMenukey = false;
 | |
|       if (lParam == -1)
 | |
|       {
 | |
|         contextMenukey = true;
 | |
|         pos = lParamToClient(GetMessagePos());
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         pos = lParamToClient(lParam);
 | |
|       }
 | |
| 
 | |
|       result = DispatchMouseEvent(eContextMenu, wParam, pos, contextMenukey,
 | |
|                                   contextMenukey ?
 | |
|                                     WidgetMouseEvent::eLeftButton :
 | |
|                                     WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       if (lParam != -1 && !result && mCustomNonClient &&
 | |
|           mDraggableRegion.Contains(GET_X_LPARAM(pos), GET_Y_LPARAM(pos))) {
 | |
|         // Blank area hit, throw up the system menu.
 | |
|         DisplaySystemMenu(mWnd, mSizeMode, mIsRTL, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
 | |
|         result = true;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_POINTERLEAVE:
 | |
|     case WM_POINTERDOWN:
 | |
|     case WM_POINTERUP:
 | |
|     case WM_POINTERUPDATE:
 | |
|       result = OnPointerEvents(msg, wParam, lParam);
 | |
|       if (result) {
 | |
|         DispatchPendingEvents();
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_LBUTTONDBLCLK:
 | |
|       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eLeftButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_MBUTTONDOWN:
 | |
|       result = DispatchMouseEvent(eMouseDown, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_MBUTTONUP:
 | |
|       result = DispatchMouseEvent(eMouseUp, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_MBUTTONDBLCLK:
 | |
|       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCMBUTTONDOWN:
 | |
|       result = DispatchMouseEvent(eMouseDown, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCMBUTTONUP:
 | |
|       result = DispatchMouseEvent(eMouseUp, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCMBUTTONDBLCLK:
 | |
|       result = DispatchMouseEvent(eMouseDoubleClick, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eMiddleButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_RBUTTONDOWN:
 | |
|       result = DispatchMouseEvent(eMouseDown, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE(),
 | |
|                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_RBUTTONUP:
 | |
|       result = DispatchMouseEvent(eMouseUp, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE(),
 | |
|                                   mPointerEvents.GetCachedPointerInfo(msg, wParam));
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_RBUTTONDBLCLK:
 | |
|       result = DispatchMouseEvent(eMouseDoubleClick, wParam,
 | |
|                                   lParam, false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCRBUTTONDOWN:
 | |
|       result = DispatchMouseEvent(eMouseDown, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCRBUTTONUP:
 | |
|       result = DispatchMouseEvent(eMouseUp, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_NCRBUTTONDBLCLK:
 | |
|       result = DispatchMouseEvent(eMouseDoubleClick, 0,
 | |
|                                   lParamToClient(lParam), false,
 | |
|                                   WidgetMouseEvent::eRightButton,
 | |
|                                   MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     // Windows doesn't provide to customize the behavior of 4th nor 5th button
 | |
|     // of mouse.  If 5-button mouse works with standard mouse deriver of
 | |
|     // Windows, users cannot disable 4th button (browser back) nor 5th button
 | |
|     // (browser forward).  We should allow to do it with our prefs since we can
 | |
|     // prevent Windows to generate WM_APPCOMMAND message if WM_XBUTTONUP
 | |
|     // messages are not sent to DefWindowProc.
 | |
|     case WM_XBUTTONDOWN:
 | |
|     case WM_XBUTTONUP:
 | |
|     case WM_NCXBUTTONDOWN:
 | |
|     case WM_NCXBUTTONUP:
 | |
|       *aRetValue = TRUE;
 | |
|       switch (GET_XBUTTON_WPARAM(wParam)) {
 | |
|         case XBUTTON1:
 | |
|           result = !Preferences::GetBool("mousebutton.4th.enabled", true);
 | |
|           break;
 | |
|         case XBUTTON2:
 | |
|           result = !Preferences::GetBool("mousebutton.5th.enabled", true);
 | |
|           break;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_SIZING:
 | |
|     {
 | |
|       // When we get WM_ENTERSIZEMOVE we don't know yet if we're in a live
 | |
|       // resize or move event. Instead we wait for first VM_SIZING message
 | |
|       // within a ENTERSIZEMOVE to consider this a live resize event.
 | |
|       if (mResizeState == IN_SIZEMOVE) {
 | |
|         mResizeState = RESIZING;
 | |
|         NotifyLiveResizeStarted();
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_MOVING:
 | |
|       FinishLiveResizing(MOVING);
 | |
|       if (WinUtils::IsPerMonitorDPIAware()) {
 | |
|         // Sometimes, we appear to miss a WM_DPICHANGED message while moving
 | |
|         // a window around. Therefore, call ChangedDPI and ResetLayout here
 | |
|         // if it appears that the window's scaling is not what we expect.
 | |
|         // This causes the prescontext and appshell window management code to
 | |
|         // check the appUnitsPerDevPixel value and current widget size, and
 | |
|         // refresh them if necessary. If nothing has changed, these calls will
 | |
|         // return without actually triggering any extra reflow or painting.
 | |
|         if (WinUtils::LogToPhysFactor(mWnd) != mDefaultScale) {
 | |
|           ChangedDPI();
 | |
|           ResetLayout();
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_ENTERSIZEMOVE:
 | |
|     {
 | |
|       if (mResizeState == NOT_RESIZING) {
 | |
|         mResizeState = IN_SIZEMOVE;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_EXITSIZEMOVE:
 | |
|     {
 | |
|       FinishLiveResizing(NOT_RESIZING);
 | |
| 
 | |
|       if (!sIsInMouseCapture) {
 | |
|         NotifySizeMoveDone();
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_DISPLAYCHANGE:
 | |
|     {
 | |
|       ScreenHelperWin::RefreshScreens();
 | |
|       if (mWidgetListener) {
 | |
|         mWidgetListener->UIResolutionChanged();
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     case WM_NCLBUTTONDBLCLK:
 | |
|       DispatchMouseEvent(eMouseDoubleClick, 0, lParamToClient(lParam),
 | |
|                          false, WidgetMouseEvent::eLeftButton,
 | |
|                          MOUSE_INPUT_SOURCE());
 | |
|       result = 
 | |
|         DispatchMouseEvent(eMouseUp, 0, lParamToClient(lParam),
 | |
|                            false, WidgetMouseEvent::eLeftButton,
 | |
|                            MOUSE_INPUT_SOURCE());
 | |
|       DispatchPendingEvents();
 | |
|       break;
 | |
| 
 | |
|     case WM_APPCOMMAND:
 | |
|     {
 | |
|       MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam, mWnd);
 | |
|       result = HandleAppCommandMsg(nativeMsg, aRetValue);
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // The WM_ACTIVATE event is fired when a window is raised or lowered,
 | |
|     // and the loword of wParam specifies which. But we don't want to tell
 | |
|     // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS
 | |
|     // events are fired. Instead, set either the sJustGotActivate or
 | |
|     // gJustGotDeactivate flags and activate/deactivate once the focus
 | |
|     // events arrive.
 | |
|     case WM_ACTIVATE:
 | |
|       if (mWidgetListener) {
 | |
|         int32_t fActive = LOWORD(wParam);
 | |
| 
 | |
|         if (WA_INACTIVE == fActive) {
 | |
|           // when minimizing a window, the deactivation and focus events will
 | |
|           // be fired in the reverse order. Instead, just deactivate right away.
 | |
|           if (HIWORD(wParam))
 | |
|             DispatchFocusToTopLevelWindow(false);
 | |
|           else
 | |
|             sJustGotDeactivate = true;
 | |
| 
 | |
|           if (mIsTopWidgetWindow)
 | |
|             mLastKeyboardLayout = KeyboardLayout::GetInstance()->GetLayout();
 | |
| 
 | |
|         } else {
 | |
|           StopFlashing();
 | |
| 
 | |
|           sJustGotActivate = true;
 | |
|           WidgetMouseEvent event(true, eMouseActivate, this,
 | |
|                                  WidgetMouseEvent::eReal);
 | |
|           InitEvent(event);
 | |
|           ModifierKeyState modifierKeyState;
 | |
|           modifierKeyState.InitInputEvent(event);
 | |
|           DispatchInputEvent(&event);
 | |
|           if (sSwitchKeyboardLayout && mLastKeyboardLayout)
 | |
|             ActivateKeyboardLayout(mLastKeyboardLayout, 0);
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_MOUSEACTIVATE:
 | |
|       // A popup with a parent owner should not be activated when clicked but
 | |
|       // should still allow the mouse event to be fired, so the return value
 | |
|       // is set to MA_NOACTIVATE. But if the owner isn't the frontmost window,
 | |
|       // just use default processing so that the window is activated.
 | |
|       if (IsPopup() && IsOwnerForegroundWindow()) {
 | |
|         *aRetValue = MA_NOACTIVATE;
 | |
|         result = true;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_WINDOWPOSCHANGING:
 | |
|     {
 | |
|       LPWINDOWPOS info = (LPWINDOWPOS)lParam;
 | |
|       OnWindowPosChanging(info);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_GETMINMAXINFO:
 | |
|     {
 | |
|       MINMAXINFO* mmi = (MINMAXINFO*)lParam;
 | |
|       // Set the constraints. The minimum size should also be constrained to the
 | |
|       // default window maximum size so that it fits on screen.
 | |
|       mmi->ptMinTrackSize.x =
 | |
|         std::min((int32_t)mmi->ptMaxTrackSize.x,
 | |
|                std::max((int32_t)mmi->ptMinTrackSize.x, mSizeConstraints.mMinSize.width));
 | |
|       mmi->ptMinTrackSize.y =
 | |
|         std::min((int32_t)mmi->ptMaxTrackSize.y,
 | |
|         std::max((int32_t)mmi->ptMinTrackSize.y, mSizeConstraints.mMinSize.height));
 | |
|       mmi->ptMaxTrackSize.x = std::min((int32_t)mmi->ptMaxTrackSize.x, mSizeConstraints.mMaxSize.width);
 | |
|       mmi->ptMaxTrackSize.y = std::min((int32_t)mmi->ptMaxTrackSize.y, mSizeConstraints.mMaxSize.height);
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_SETFOCUS:
 | |
|       // If previous focused window isn't ours, it must have received the
 | |
|       // redirected message.  So, we should forget it.
 | |
|       if (!WinUtils::IsOurProcessWindow(HWND(wParam))) {
 | |
|         RedirectedKeyDownMessageManager::Forget();
 | |
|       }
 | |
|       if (sJustGotActivate) {
 | |
|         DispatchFocusToTopLevelWindow(true);
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_KILLFOCUS:
 | |
|       if (sJustGotDeactivate) {
 | |
|         DispatchFocusToTopLevelWindow(false);
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case WM_WINDOWPOSCHANGED:
 | |
|     {
 | |
|       WINDOWPOS* wp = (LPWINDOWPOS)lParam;
 | |
|       OnWindowPosChanged(wp);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_INPUTLANGCHANGEREQUEST:
 | |
|       *aRetValue = TRUE;
 | |
|       result = false;
 | |
|       break;
 | |
| 
 | |
|     case WM_INPUTLANGCHANGE:
 | |
|       KeyboardLayout::GetInstance()->
 | |
|         OnLayoutChange(reinterpret_cast<HKL>(lParam));
 | |
|       nsBidiKeyboard::OnLayoutChange();
 | |
|       result = false; // always pass to child window
 | |
|       break;
 | |
| 
 | |
|     case WM_DESTROYCLIPBOARD:
 | |
|     {
 | |
|       nsIClipboard* clipboard;
 | |
|       nsresult rv = CallGetService(kCClipboardCID, &clipboard);
 | |
|       if(NS_SUCCEEDED(rv)) {
 | |
|         clipboard->EmptyClipboard(nsIClipboard::kGlobalClipboard);
 | |
|         NS_RELEASE(clipboard);
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
|     case WM_GETOBJECT:
 | |
|     {
 | |
|       *aRetValue = 0;
 | |
|       // Do explicit casting to make it working on 64bit systems (see bug 649236
 | |
|       // for details).
 | |
|       int32_t objId = static_cast<DWORD>(lParam);
 | |
|       if (objId == OBJID_CLIENT) { // oleacc.dll will be loaded dynamically
 | |
|         RefPtr<IAccessible> root(a11y::LazyInstantiator::GetRootAccessible(mWnd));
 | |
|         if (root) {
 | |
|           *aRetValue = LresultFromObject(IID_IAccessible, wParam, root);
 | |
|           a11y::LazyInstantiator::EnableBlindAggregation(mWnd);
 | |
|           result = true;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| #endif
 | |
| 
 | |
|     case WM_SYSCOMMAND:
 | |
|     {
 | |
|       WPARAM filteredWParam = (wParam &0xFFF0);
 | |
|       if (mSizeMode == nsSizeMode_Fullscreen &&
 | |
|           filteredWParam == SC_RESTORE &&
 | |
|           GetCurrentShowCmd(mWnd) != SW_SHOWMINIMIZED) {
 | |
|         MakeFullScreen(false);
 | |
|         result = true;
 | |
|       }
 | |
| 
 | |
|       // Handle the system menu manually when we're in full screen mode
 | |
|       // so we can set the appropriate options.
 | |
|       if (filteredWParam == SC_KEYMENU && lParam == VK_SPACE &&
 | |
|           mSizeMode == nsSizeMode_Fullscreen) {
 | |
|         DisplaySystemMenu(mWnd, mSizeMode, mIsRTL,
 | |
|                           MOZ_SYSCONTEXT_X_POS,
 | |
|                           MOZ_SYSCONTEXT_Y_POS);
 | |
|         result = true;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case WM_DWMCOMPOSITIONCHANGED:
 | |
|     // First, update the compositor state to latest one. All other methods
 | |
|     // should use same state as here for consistency painting.
 | |
|     nsUXThemeData::CheckForCompositor(true);
 | |
| 
 | |
|     UpdateNonClientMargins();
 | |
|     BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
 | |
|     NotifyThemeChanged();
 | |
|     UpdateGlass();
 | |
|     Invalidate(true, true, true);
 | |
|     break;
 | |
| 
 | |
|   case WM_DPICHANGED:
 | |
|   {
 | |
|     LPRECT rect = (LPRECT) lParam;
 | |
|     OnDPIChanged(rect->left, rect->top, rect->right - rect->left,
 | |
|                  rect->bottom - rect->top);
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   case WM_UPDATEUISTATE:
 | |
|   {
 | |
|     // If the UI state has changed, fire an event so the UI updates the
 | |
|     // keyboard cues based on the system setting and how the window was
 | |
|     // opened. For example, a dialog opened via a keyboard press on a button
 | |
|     // should enable cues, whereas the same dialog opened via a mouse click of
 | |
|     // the button should not.
 | |
|     if (mWindowType == eWindowType_toplevel ||
 | |
|         mWindowType == eWindowType_dialog) {
 | |
|       int32_t action = LOWORD(wParam);
 | |
|       if (action == UIS_SET || action == UIS_CLEAR) {
 | |
|         int32_t flags = HIWORD(wParam);
 | |
|         UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
 | |
|         UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
 | |
|         if (flags & UISF_HIDEACCEL)
 | |
|           showAccelerators = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
 | |
|         if (flags & UISF_HIDEFOCUS)
 | |
|           showFocusRings = (action == UIS_SET) ? UIStateChangeType_Clear : UIStateChangeType_Set;
 | |
| 
 | |
|         NotifyUIStateChanged(showAccelerators, showFocusRings);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   /* Gesture support events */
 | |
|   case WM_TABLET_QUERYSYSTEMGESTURESTATUS:
 | |
|     // According to MS samples, this must be handled to enable
 | |
|     // rotational support in multi-touch drivers.
 | |
|     result = true;
 | |
|     *aRetValue = TABLET_ROTATE_GESTURE_ENABLE;
 | |
|     break;
 | |
| 
 | |
|   case WM_TOUCH:
 | |
|     result = OnTouch(wParam, lParam);
 | |
|     if (result) {
 | |
|       *aRetValue = 0;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   case WM_GESTURE:
 | |
|     result = OnGesture(wParam, lParam);
 | |
|     break;
 | |
| 
 | |
|   case WM_GESTURENOTIFY:
 | |
|     {
 | |
|       if (mWindowType != eWindowType_invisible &&
 | |
|           !IsPlugin()) {
 | |
|         // A GestureNotify event is dispatched to decide which single-finger panning
 | |
|         // direction should be active (including none) and if pan feedback should
 | |
|         // be displayed. Java and plugin windows can make their own calls.
 | |
| 
 | |
|         GESTURENOTIFYSTRUCT * gestureinfo = (GESTURENOTIFYSTRUCT*)lParam;
 | |
|         nsPointWin touchPoint;
 | |
|         touchPoint = gestureinfo->ptsLocation;
 | |
|         touchPoint.ScreenToClient(mWnd);
 | |
|         WidgetGestureNotifyEvent gestureNotifyEvent(true, eGestureNotify, this);
 | |
|         gestureNotifyEvent.mRefPoint =
 | |
|           LayoutDeviceIntPoint::FromUnknownPoint(touchPoint);
 | |
|         nsEventStatus status;
 | |
|         DispatchEvent(&gestureNotifyEvent, status);
 | |
|         mDisplayPanFeedback = gestureNotifyEvent.mDisplayPanFeedback;
 | |
|         if (!mTouchWindow)
 | |
|           mGesture.SetWinGestureSupport(mWnd, gestureNotifyEvent.mPanDirection);
 | |
|       }
 | |
|       result = false; //should always bubble to DefWindowProc
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_CLEAR:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandDelete, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_CUT:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandCut, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_COPY:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandCopy, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case WM_PASTE:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandPaste, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case EM_UNDO:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandUndo, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case EM_REDO:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandRedo, this);
 | |
|       DispatchWindowEvent(&command);
 | |
|       *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case EM_CANPASTE:
 | |
|     {
 | |
|       // Support EM_CANPASTE message only when wParam isn't specified or
 | |
|       // is plain text format.
 | |
|       if (wParam == 0 || wParam == CF_TEXT || wParam == CF_UNICODETEXT) {
 | |
|         WidgetContentCommandEvent command(true, eContentCommandPaste,
 | |
|                                           this, true);
 | |
|         DispatchWindowEvent(&command);
 | |
|         *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
 | |
|         result = true;
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case EM_CANUNDO:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandUndo, this, true);
 | |
|       DispatchWindowEvent(&command);
 | |
|       *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case EM_CANREDO:
 | |
|     {
 | |
|       WidgetContentCommandEvent command(true, eContentCommandRedo, this, true);
 | |
|       DispatchWindowEvent(&command);
 | |
|       *aRetValue = (LRESULT)(command.mSucceeded && command.mIsEnabled);
 | |
|       result = true;
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     case MOZ_WM_SKEWFIX:
 | |
|     {
 | |
|       TimeStamp skewStamp;
 | |
|       if (CurrentWindowsTimeGetter::GetAndClearBackwardsSkewStamp(wParam, &skewStamp)) {
 | |
|         TimeConverter().CompensateForBackwardsSkew(::GetMessageTime(), skewStamp);
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|     default:
 | |
|     {
 | |
|       if (msg == nsAppShell::GetTaskbarButtonCreatedMessage()) {
 | |
|         SetHasTaskbarIconBeenCreated();
 | |
|       }
 | |
|     }
 | |
|     break;
 | |
| 
 | |
|   }
 | |
| 
 | |
|   //*aRetValue = result;
 | |
|   if (mWnd) {
 | |
|     return result;
 | |
|   }
 | |
|   else {
 | |
|     //Events which caused mWnd destruction and aren't consumed
 | |
|     //will crash during the Windows default processing.
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::FinishLiveResizing(ResizeState aNewState)
 | |
| {
 | |
|   if (mResizeState == RESIZING) {
 | |
|     NotifyLiveResizeStopped();
 | |
|   }
 | |
|   mResizeState = aNewState;
 | |
|   ForcePresent();
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Broadcast messaging
 | |
|  *
 | |
|  * Broadcast messages to all windows.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| // Enumerate all child windows sending aMsg to each of them
 | |
| BOOL CALLBACK nsWindow::BroadcastMsgToChildren(HWND aWnd, LPARAM aMsg)
 | |
| {
 | |
|   WNDPROC winProc = (WNDPROC)::GetWindowLongPtrW(aWnd, GWLP_WNDPROC);
 | |
|   if (winProc == &nsWindow::WindowProc) {
 | |
|     // it's one of our windows so go ahead and send a message to it
 | |
|     ::CallWindowProcW(winProc, aWnd, aMsg, 0, 0);
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| // Enumerate all top level windows specifying that the children of each
 | |
| // top level window should be enumerated. Do *not* send the message to
 | |
| // each top level window since it is assumed that the toolkit will send
 | |
| // aMsg to them directly.
 | |
| BOOL CALLBACK nsWindow::BroadcastMsg(HWND aTopWindow, LPARAM aMsg)
 | |
| {
 | |
|   // Iterate each of aTopWindows child windows sending the aMsg
 | |
|   // to each of them.
 | |
|   ::EnumChildWindows(aTopWindow, nsWindow::BroadcastMsgToChildren, aMsg);
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: Event processing helpers
 | |
|  *
 | |
|  * Special processing for certain event types and 
 | |
|  * synthesized events.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| int32_t
 | |
| nsWindow::ClientMarginHitTestPoint(int32_t mx, int32_t my)
 | |
| {
 | |
|   if (mSizeMode == nsSizeMode_Minimized ||
 | |
|       mSizeMode == nsSizeMode_Fullscreen) {
 | |
|     return HTCLIENT;
 | |
|   }
 | |
| 
 | |
|   // Calculations are done in screen coords
 | |
|   RECT winRect;
 | |
|   GetWindowRect(mWnd, &winRect);
 | |
| 
 | |
|   // hit return constants:
 | |
|   // HTBORDER                     - non-resizable border
 | |
|   // HTBOTTOM, HTLEFT, HTRIGHT, HTTOP - resizable border
 | |
|   // HTBOTTOMLEFT, HTBOTTOMRIGHT  - resizable corner
 | |
|   // HTTOPLEFT, HTTOPRIGHT        - resizable corner
 | |
|   // HTCAPTION                    - general title bar area
 | |
|   // HTCLIENT                     - area considered the client
 | |
|   // HTCLOSE                      - hovering over the close button
 | |
|   // HTMAXBUTTON                  - maximize button
 | |
|   // HTMINBUTTON                  - minimize button
 | |
| 
 | |
|   int32_t testResult = HTCLIENT;
 | |
| 
 | |
|   bool isResizable = (mBorderStyle & (eBorderStyle_all |
 | |
|                                       eBorderStyle_resizeh |
 | |
|                                       eBorderStyle_default)) > 0 ? true : false;
 | |
|   if (mSizeMode == nsSizeMode_Maximized)
 | |
|     isResizable = false;
 | |
| 
 | |
|   // Ensure being accessible to borders of window.  Even if contents are in
 | |
|   // this area, the area must behave as border.
 | |
|   nsIntMargin nonClientSize(std::max(mCaptionHeight - mNonClientOffset.top,
 | |
|                                      kResizableBorderMinSize),
 | |
|                             std::max(mHorResizeMargin - mNonClientOffset.right,
 | |
|                                      kResizableBorderMinSize),
 | |
|                             std::max(mVertResizeMargin - mNonClientOffset.bottom,
 | |
|                                      kResizableBorderMinSize),
 | |
|                             std::max(mHorResizeMargin - mNonClientOffset.left,
 | |
|                                      kResizableBorderMinSize));
 | |
| 
 | |
|   bool allowContentOverride = mSizeMode == nsSizeMode_Maximized ||
 | |
|                               (mx >= winRect.left + nonClientSize.left &&
 | |
|                                mx <= winRect.right - nonClientSize.right &&
 | |
|                                my >= winRect.top + nonClientSize.top &&
 | |
|                                my <= winRect.bottom - nonClientSize.bottom);
 | |
| 
 | |
|   // The border size.  If there is no content under mouse cursor, the border
 | |
|   // size should be larger than the values in system settings.  Otherwise,
 | |
|   // contents under the mouse cursor should be able to override the behavior.
 | |
|   // E.g., user must expect that Firefox button always opens the popup menu
 | |
|   // even when the user clicks on the above edge of it.
 | |
|   nsIntMargin borderSize(std::max(nonClientSize.top,    mVertResizeMargin),
 | |
|                          std::max(nonClientSize.right,  mHorResizeMargin),
 | |
|                          std::max(nonClientSize.bottom, mVertResizeMargin),
 | |
|                          std::max(nonClientSize.left,   mHorResizeMargin));
 | |
| 
 | |
|   bool top    = false;
 | |
|   bool bottom = false;
 | |
|   bool left   = false;
 | |
|   bool right  = false;
 | |
| 
 | |
|   if (my >= winRect.top && my < winRect.top + borderSize.top) {
 | |
|     top = true;
 | |
|   } else if (my <= winRect.bottom && my > winRect.bottom - borderSize.bottom) {
 | |
|     bottom = true;
 | |
|   }
 | |
| 
 | |
|   // (the 2x case here doubles the resize area for corners)
 | |
|   int multiplier = (top || bottom) ? 2 : 1;
 | |
|   if (mx >= winRect.left &&
 | |
|       mx < winRect.left + (multiplier * borderSize.left)) {
 | |
|     left = true;
 | |
|   } else if (mx <= winRect.right &&
 | |
|              mx > winRect.right - (multiplier * borderSize.right)) {
 | |
|     right = true;
 | |
|   }
 | |
| 
 | |
|   if (isResizable) {
 | |
|     if (top) {
 | |
|       testResult = HTTOP;
 | |
|       if (left)
 | |
|         testResult = HTTOPLEFT;
 | |
|       else if (right)
 | |
|         testResult = HTTOPRIGHT;
 | |
|     } else if (bottom) {
 | |
|       testResult = HTBOTTOM;
 | |
|       if (left)
 | |
|         testResult = HTBOTTOMLEFT;
 | |
|       else if (right)
 | |
|         testResult = HTBOTTOMRIGHT;
 | |
|     } else {
 | |
|       if (left)
 | |
|         testResult = HTLEFT;
 | |
|       if (right)
 | |
|         testResult = HTRIGHT;
 | |
|     }
 | |
|   } else {
 | |
|     if (top)
 | |
|       testResult = HTCAPTION;
 | |
|     else if (bottom || left || right)
 | |
|       testResult = HTBORDER;
 | |
|   }
 | |
| 
 | |
|   if (!sIsInMouseCapture && allowContentOverride) {
 | |
|     POINT pt = { mx, my };
 | |
|     ::ScreenToClient(mWnd, &pt);
 | |
|     if (pt.x == mCachedHitTestPoint.x && pt.y == mCachedHitTestPoint.y &&
 | |
|         TimeStamp::Now() - mCachedHitTestTime < TimeDuration::FromMilliseconds(HITTEST_CACHE_LIFETIME_MS)) {
 | |
|       return mCachedHitTestResult;
 | |
|     }
 | |
|     if (mDraggableRegion.Contains(pt.x, pt.y)) {
 | |
|       testResult = HTCAPTION;
 | |
|     } else {
 | |
|       testResult = HTCLIENT;
 | |
|     }
 | |
|     mCachedHitTestPoint = pt;
 | |
|     mCachedHitTestTime = TimeStamp::Now();
 | |
|     mCachedHitTestResult = testResult;
 | |
|   }
 | |
| 
 | |
|   return testResult;
 | |
| }
 | |
| 
 | |
| TimeStamp
 | |
| nsWindow::GetMessageTimeStamp(LONG aEventTime) const
 | |
| {
 | |
|   CurrentWindowsTimeGetter getCurrentTime(mWnd);
 | |
|   return TimeConverter().GetTimeStampFromSystemTime(aEventTime,
 | |
|                                                     getCurrentTime);
 | |
| }
 | |
| 
 | |
| void nsWindow::PostSleepWakeNotification(const bool aIsSleepMode)
 | |
| {
 | |
|   if (aIsSleepMode == gIsSleepMode)
 | |
|     return;
 | |
| 
 | |
|   gIsSleepMode = aIsSleepMode;
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> observerService =
 | |
|     mozilla::services::GetObserverService();
 | |
|   if (observerService)
 | |
|     observerService->NotifyObservers(nullptr,
 | |
|       aIsSleepMode ? NS_WIDGET_SLEEP_OBSERVER_TOPIC :
 | |
|                      NS_WIDGET_WAKE_OBSERVER_TOPIC, nullptr);
 | |
| }
 | |
| 
 | |
| LRESULT nsWindow::ProcessCharMessage(const MSG &aMsg, bool *aEventDispatched)
 | |
| {
 | |
|   if (IMEHandler::IsComposingOn(this)) {
 | |
|     IMEHandler::NotifyIME(this, REQUEST_TO_COMMIT_COMPOSITION);
 | |
|   }
 | |
|   // These must be checked here too as a lone WM_CHAR could be received
 | |
|   // if a child window didn't handle it (for example Alt+Space in a content
 | |
|   // window)
 | |
|   ModifierKeyState modKeyState;
 | |
|   NativeKey nativeKey(this, aMsg, modKeyState);
 | |
|   return static_cast<LRESULT>(nativeKey.HandleCharMessage(aEventDispatched));
 | |
| }
 | |
| 
 | |
| LRESULT nsWindow::ProcessKeyUpMessage(const MSG &aMsg, bool *aEventDispatched)
 | |
| {
 | |
|   ModifierKeyState modKeyState;
 | |
|   NativeKey nativeKey(this, aMsg, modKeyState);
 | |
|   bool result = nativeKey.HandleKeyUpMessage(aEventDispatched);
 | |
|   if (aMsg.wParam == VK_F10) {
 | |
|     // Bug 1382199: Windows default behavior will trigger the System menu bar
 | |
|     // when F10 is released. Among other things, this causes the System menu bar
 | |
|     // to appear when a web page overrides the contextmenu event. We *never*
 | |
|     // want this default behavior, so eat this key (never pass it to Windows).
 | |
|     return true;
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| LRESULT nsWindow::ProcessKeyDownMessage(const MSG &aMsg,
 | |
|                                         bool *aEventDispatched)
 | |
| {
 | |
|   // If this method doesn't call NativeKey::HandleKeyDownMessage(), this method
 | |
|   // must clean up the redirected message information itself.  For more
 | |
|   // information, see above comment of
 | |
|   // RedirectedKeyDownMessageManager::AutoFlusher class definition in
 | |
|   // KeyboardLayout.h.
 | |
|   RedirectedKeyDownMessageManager::AutoFlusher redirectedMsgFlusher(this, aMsg);
 | |
| 
 | |
|   ModifierKeyState modKeyState;
 | |
| 
 | |
|   NativeKey nativeKey(this, aMsg, modKeyState);
 | |
|   LRESULT result =
 | |
|     static_cast<LRESULT>(nativeKey.HandleKeyDownMessage(aEventDispatched));
 | |
|   // HandleKeyDownMessage cleaned up the redirected message information
 | |
|   // itself, so, we should do nothing.
 | |
|   redirectedMsgFlusher.Cancel();
 | |
| 
 | |
|   if (aMsg.wParam == VK_MENU ||
 | |
|       (aMsg.wParam == VK_F10 && !modKeyState.IsShift())) {
 | |
|     // We need to let Windows handle this keypress,
 | |
|     // by returning false, if there's a native menu
 | |
|     // bar somewhere in our containing window hierarchy.
 | |
|     // Otherwise we handle the keypress and don't pass
 | |
|     // it on to Windows, by returning true.
 | |
|     bool hasNativeMenu = false;
 | |
|     HWND hWnd = mWnd;
 | |
|     while (hWnd) {
 | |
|       if (::GetMenu(hWnd)) {
 | |
|         hasNativeMenu = true;
 | |
|         break;
 | |
|       }
 | |
|       hWnd = ::GetParent(hWnd);
 | |
|     }
 | |
|     result = !hasNativeMenu;
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
 | |
|                                    int32_t aNativeKeyCode,
 | |
|                                    uint32_t aModifierFlags,
 | |
|                                    const nsAString& aCharacters,
 | |
|                                    const nsAString& aUnmodifiedCharacters,
 | |
|                                    nsIObserver* aObserver)
 | |
| {
 | |
|   AutoObserverNotifier notifier(aObserver, "keyevent");
 | |
| 
 | |
|   KeyboardLayout* keyboardLayout = KeyboardLayout::GetInstance();
 | |
|   return keyboardLayout->SynthesizeNativeKeyEvent(
 | |
|            this, aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags,
 | |
|            aCharacters, aUnmodifiedCharacters);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
 | |
|                                      uint32_t aNativeMessage,
 | |
|                                      uint32_t aModifierFlags,
 | |
|                                      nsIObserver* aObserver)
 | |
| {
 | |
|   AutoObserverNotifier notifier(aObserver, "mouseevent");
 | |
| 
 | |
|   if (aNativeMessage == MOUSEEVENTF_MOVE) {
 | |
|     // Reset sLastMouseMovePoint so that even if we're moving the mouse
 | |
|     // to the position it's already at, we still dispatch a mousemove
 | |
|     // event, because the callers of this function expect that.
 | |
|     sLastMouseMovePoint = {0};
 | |
|   }
 | |
|   ::SetCursorPos(aPoint.x, aPoint.y);
 | |
| 
 | |
|   INPUT input;
 | |
|   memset(&input, 0, sizeof(input));
 | |
| 
 | |
|   input.type = INPUT_MOUSE;
 | |
|   input.mi.dwFlags = aNativeMessage;
 | |
|   ::SendInput(1, &input, sizeof(INPUT));
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
 | |
|                                            uint32_t aNativeMessage,
 | |
|                                            double aDeltaX,
 | |
|                                            double aDeltaY,
 | |
|                                            double aDeltaZ,
 | |
|                                            uint32_t aModifierFlags,
 | |
|                                            uint32_t aAdditionalFlags,
 | |
|                                            nsIObserver* aObserver)
 | |
| {
 | |
|   AutoObserverNotifier notifier(aObserver, "mousescrollevent");
 | |
|   return MouseScrollHandler::SynthesizeNativeMouseScrollEvent(
 | |
|            this, aPoint, aNativeMessage,
 | |
|            (aNativeMessage == WM_MOUSEWHEEL || aNativeMessage == WM_VSCROLL) ?
 | |
|              static_cast<int32_t>(aDeltaY) : static_cast<int32_t>(aDeltaX),
 | |
|            aModifierFlags, aAdditionalFlags);
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  *
 | |
|  * SECTION: OnXXX message handlers
 | |
|  *
 | |
|  * For message handlers that need to be broken out or
 | |
|  * implemented in specific platform code.
 | |
|  *
 | |
|  **************************************************************/
 | |
| 
 | |
| void nsWindow::OnWindowPosChanged(WINDOWPOS* wp)
 | |
| {
 | |
|   if (wp == nullptr)
 | |
|     return;
 | |
| 
 | |
| #ifdef WINSTATE_DEBUG_OUTPUT
 | |
|   if (mWnd == WinUtils::GetTopLevelHWND(mWnd)) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [  top] "));
 | |
|   } else {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** OnWindowPosChanged: [child] "));
 | |
|   }
 | |
|   MOZ_LOG(gWindowsLog, LogLevel::Info, ("WINDOWPOS flags:"));
 | |
|   if (wp->flags & SWP_FRAMECHANGED) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_FRAMECHANGED "));
 | |
|   }
 | |
|   if (wp->flags & SWP_SHOWWINDOW) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_SHOWWINDOW "));
 | |
|   }
 | |
|   if (wp->flags & SWP_NOSIZE) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOSIZE "));
 | |
|   }
 | |
|   if (wp->flags & SWP_HIDEWINDOW) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_HIDEWINDOW "));
 | |
|   }
 | |
|   if (wp->flags & SWP_NOZORDER) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOZORDER "));
 | |
|   }
 | |
|   if (wp->flags & SWP_NOACTIVATE) {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, ("SWP_NOACTIVATE "));
 | |
|   }
 | |
|   MOZ_LOG(gWindowsLog, LogLevel::Info, ("\n"));
 | |
| #endif
 | |
| 
 | |
|   // Handle window size mode changes
 | |
|   if (wp->flags & SWP_FRAMECHANGED && mSizeMode != nsSizeMode_Fullscreen) {
 | |
| 
 | |
|     // Bug 566135 - Windows theme code calls show window on SW_SHOWMINIMIZED
 | |
|     // windows when fullscreen games disable desktop composition. If we're
 | |
|     // minimized and not being activated, ignore the event and let windows
 | |
|     // handle it.
 | |
|     if (mSizeMode == nsSizeMode_Minimized && (wp->flags & SWP_NOACTIVATE))
 | |
|       return;
 | |
| 
 | |
|     WINDOWPLACEMENT pl;
 | |
|     pl.length = sizeof(pl);
 | |
|     ::GetWindowPlacement(mWnd, &pl);
 | |
| 
 | |
|     nsSizeMode previousSizeMode = mSizeMode;
 | |
| 
 | |
|     // Windows has just changed the size mode of this window. The call to
 | |
|     // SizeModeChanged will trigger a call into SetSizeMode where we will
 | |
|     // set the min/max window state again or for nsSizeMode_Normal, call
 | |
|     // SetWindow with a parameter of SW_RESTORE. There's no need however as
 | |
|     // this window's mode has already changed. Updating mSizeMode here
 | |
|     // insures the SetSizeMode call is a no-op. Addresses a bug on Win7 related
 | |
|     // to window docking. (bug 489258)
 | |
|     if (pl.showCmd == SW_SHOWMAXIMIZED)
 | |
|       mSizeMode = (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
 | |
|     else if (pl.showCmd == SW_SHOWMINIMIZED)
 | |
|       mSizeMode = nsSizeMode_Minimized;
 | |
|     else if (mFullscreenMode)
 | |
|       mSizeMode = nsSizeMode_Fullscreen;
 | |
|     else
 | |
|       mSizeMode = nsSizeMode_Normal;
 | |
| 
 | |
| #ifdef WINSTATE_DEBUG_OUTPUT
 | |
|     switch (mSizeMode) {
 | |
|       case nsSizeMode_Normal:
 | |
|           MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|                  ("*** mSizeMode: nsSizeMode_Normal\n"));
 | |
|         break;
 | |
|       case nsSizeMode_Minimized:
 | |
|         MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|                ("*** mSizeMode: nsSizeMode_Minimized\n"));
 | |
|         break;
 | |
|       case nsSizeMode_Maximized:
 | |
|           MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|                  ("*** mSizeMode: nsSizeMode_Maximized\n"));
 | |
|         break;
 | |
|       default:
 | |
|           MOZ_LOG(gWindowsLog, LogLevel::Info, ("*** mSizeMode: ??????\n"));
 | |
|         break;
 | |
|     }
 | |
| #endif
 | |
| 
 | |
|     if (mWidgetListener && mSizeMode != previousSizeMode)
 | |
|       mWidgetListener->SizeModeChanged(mSizeMode);
 | |
| 
 | |
|     // If window was restored, window activation was bypassed during the 
 | |
|     // SetSizeMode call originating from OnWindowPosChanging to avoid saving
 | |
|     // pre-restore attributes. Force activation now to get correct attributes.
 | |
|     if (mLastSizeMode != nsSizeMode_Normal && mSizeMode == nsSizeMode_Normal)
 | |
|       DispatchFocusToTopLevelWindow(true);
 | |
| 
 | |
|     mLastSizeMode = mSizeMode;
 | |
| 
 | |
|     // Skip window size change events below on minimization.
 | |
|     if (mSizeMode == nsSizeMode_Minimized)
 | |
|       return;
 | |
|   }
 | |
| 
 | |
|   // Handle window position changes
 | |
|   if (!(wp->flags & SWP_NOMOVE)) {
 | |
|     mBounds.MoveTo(wp->x, wp->y);
 | |
|     NotifyWindowMoved(wp->x, wp->y);
 | |
|   }
 | |
| 
 | |
|   // Handle window size changes
 | |
|   if (!(wp->flags & SWP_NOSIZE)) {
 | |
|     RECT r;
 | |
|     int32_t newWidth, newHeight;
 | |
| 
 | |
|     ::GetWindowRect(mWnd, &r);
 | |
| 
 | |
|     newWidth  = r.right - r.left;
 | |
|     newHeight = r.bottom - r.top;
 | |
| 
 | |
|     if (newWidth > mLastSize.width)
 | |
|     {
 | |
|       RECT drect;
 | |
| 
 | |
|       // getting wider
 | |
|       drect.left   = wp->x + mLastSize.width;
 | |
|       drect.top    = wp->y;
 | |
|       drect.right  = drect.left + (newWidth - mLastSize.width);
 | |
|       drect.bottom = drect.top + newHeight;
 | |
| 
 | |
|       ::RedrawWindow(mWnd, &drect, nullptr,
 | |
|                      RDW_INVALIDATE |
 | |
|                      RDW_NOERASE |
 | |
|                      RDW_NOINTERNALPAINT |
 | |
|                      RDW_ERASENOW |
 | |
|                      RDW_ALLCHILDREN);
 | |
|     }
 | |
|     if (newHeight > mLastSize.height)
 | |
|     {
 | |
|       RECT drect;
 | |
| 
 | |
|       // getting taller
 | |
|       drect.left   = wp->x;
 | |
|       drect.top    = wp->y + mLastSize.height;
 | |
|       drect.right  = drect.left + newWidth;
 | |
|       drect.bottom = drect.top + (newHeight - mLastSize.height);
 | |
| 
 | |
|       ::RedrawWindow(mWnd, &drect, nullptr,
 | |
|                      RDW_INVALIDATE |
 | |
|                      RDW_NOERASE |
 | |
|                      RDW_NOINTERNALPAINT |
 | |
|                      RDW_ERASENOW |
 | |
|                      RDW_ALLCHILDREN);
 | |
|     }
 | |
| 
 | |
|     mBounds.SizeTo(newWidth, newHeight);
 | |
|     mLastSize.width  = newWidth;
 | |
|     mLastSize.height = newHeight;
 | |
| 
 | |
| #ifdef WINSTATE_DEBUG_OUTPUT
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|            ("*** Resize window: %d x %d x %d x %d\n", wp->x, wp->y, 
 | |
|             newWidth, newHeight));
 | |
| #endif
 | |
| 
 | |
|     // If a maximized window is resized, recalculate the non-client margins.
 | |
|     if (mSizeMode == nsSizeMode_Maximized) {
 | |
|       if (UpdateNonClientMargins(nsSizeMode_Maximized, true)) {
 | |
|         // gecko resize event already sent by UpdateNonClientMargins.
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Notify the widget listener for size change of client area for gecko
 | |
|   // events. This needs to be done when either window size is changed,
 | |
|   // or window frame is changed. They may not happen together.
 | |
|   // However, we don't invoke that for popup when window frame changes,
 | |
|   // because popups may trigger frame change before size change via
 | |
|   // {Set,Clear}ThemeRegion they invoke in Resize. That would make the
 | |
|   // code below call OnResize with a wrong client size first, which can
 | |
|   // lead to flickerling for some popups.
 | |
|   if (!(wp->flags & SWP_NOSIZE) ||
 | |
|       ((wp->flags & SWP_FRAMECHANGED) && !IsPopup())) {
 | |
|     RECT r;
 | |
|     LayoutDeviceIntSize clientSize;
 | |
|     if (::GetClientRect(mWnd, &r)) {
 | |
|       clientSize = WinUtils::ToIntRect(r).Size();
 | |
|     } else {
 | |
|       clientSize = mBounds.Size();
 | |
|     }
 | |
|     // Send a gecko resize event
 | |
|     OnResize(clientSize);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void nsWindow::OnWindowPosChanging(LPWINDOWPOS& info)
 | |
| {
 | |
|   // Update non-client margins if the frame size is changing, and let the
 | |
|   // browser know we are changing size modes, so alternative css can kick in.
 | |
|   // If we're going into fullscreen mode, ignore this, since it'll reset
 | |
|   // margins to normal mode. 
 | |
|   if ((info->flags & SWP_FRAMECHANGED && !(info->flags & SWP_NOSIZE)) &&
 | |
|       mSizeMode != nsSizeMode_Fullscreen) {
 | |
|     WINDOWPLACEMENT pl;
 | |
|     pl.length = sizeof(pl);
 | |
|     ::GetWindowPlacement(mWnd, &pl);
 | |
|     nsSizeMode sizeMode;
 | |
|     if (pl.showCmd == SW_SHOWMAXIMIZED)
 | |
|       sizeMode = (mFullscreenMode ? nsSizeMode_Fullscreen : nsSizeMode_Maximized);
 | |
|     else if (pl.showCmd == SW_SHOWMINIMIZED)
 | |
|       sizeMode = nsSizeMode_Minimized;
 | |
|     else if (mFullscreenMode)
 | |
|       sizeMode = nsSizeMode_Fullscreen;
 | |
|     else
 | |
|       sizeMode = nsSizeMode_Normal;
 | |
| 
 | |
|     if (mWidgetListener)
 | |
|       mWidgetListener->SizeModeChanged(sizeMode);
 | |
| 
 | |
|     UpdateNonClientMargins(sizeMode, false);
 | |
|   }
 | |
| 
 | |
|   // enforce local z-order rules
 | |
|   if (!(info->flags & SWP_NOZORDER)) {
 | |
|     HWND hwndAfter = info->hwndInsertAfter;
 | |
| 
 | |
|     nsWindow *aboveWindow = 0;
 | |
|     nsWindowZ placement;
 | |
| 
 | |
|     if (hwndAfter == HWND_BOTTOM)
 | |
|       placement = nsWindowZBottom;
 | |
|     else if (hwndAfter == HWND_TOP || hwndAfter == HWND_TOPMOST || hwndAfter == HWND_NOTOPMOST)
 | |
|       placement = nsWindowZTop;
 | |
|     else {
 | |
|       placement = nsWindowZRelative;
 | |
|       aboveWindow = WinUtils::GetNSWindowPtr(hwndAfter);
 | |
|     }
 | |
| 
 | |
|     if (mWidgetListener) {
 | |
|       nsCOMPtr<nsIWidget> actualBelow = nullptr;
 | |
|       if (mWidgetListener->ZLevelChanged(false, &placement,
 | |
|                                          aboveWindow, getter_AddRefs(actualBelow))) {
 | |
|         if (placement == nsWindowZBottom)
 | |
|           info->hwndInsertAfter = HWND_BOTTOM;
 | |
|         else if (placement == nsWindowZTop)
 | |
|           info->hwndInsertAfter = HWND_TOP;
 | |
|         else {
 | |
|           info->hwndInsertAfter = (HWND)actualBelow->GetNativeData(NS_NATIVE_WINDOW);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   // prevent rude external programs from making hidden window visible
 | |
|   if (mWindowType == eWindowType_invisible)
 | |
|     info->flags &= ~SWP_SHOWWINDOW;
 | |
| }
 | |
| 
 | |
| void nsWindow::UserActivity()
 | |
| {
 | |
|   // Check if we have the idle service, if not we try to get it.
 | |
|   if (!mIdleService) {
 | |
|     mIdleService = do_GetService("@mozilla.org/widget/idleservice;1");
 | |
|   }
 | |
| 
 | |
|   // Check that we now have the idle service.
 | |
|   if (mIdleService) {
 | |
|     mIdleService->ResetIdleTimeOut(0);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsIntPoint nsWindow::GetTouchCoordinates(WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   nsIntPoint ret;
 | |
|   uint32_t cInputs = LOWORD(wParam);
 | |
|   if (cInputs != 1) {
 | |
|     // Just return 0,0 if there isn't exactly one touch point active
 | |
|     return ret;
 | |
|   }
 | |
|   PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
 | |
|   if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
 | |
|                         sizeof(TOUCHINPUT))) {
 | |
|     ret.x = TOUCH_COORD_TO_PIXEL(pInputs[0].x);
 | |
|     ret.y = TOUCH_COORD_TO_PIXEL(pInputs[0].y);
 | |
|   }
 | |
|   delete[] pInputs;
 | |
|   // Note that we don't call CloseTouchInputHandle here because we need
 | |
|   // to read the touch input info again in OnTouch later.
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| bool nsWindow::OnTouch(WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   uint32_t cInputs = LOWORD(wParam);
 | |
|   PTOUCHINPUT pInputs = new TOUCHINPUT[cInputs];
 | |
| 
 | |
|   if (GetTouchInputInfo((HTOUCHINPUT)lParam, cInputs, pInputs,
 | |
|                         sizeof(TOUCHINPUT))) {
 | |
|     MultiTouchInput touchInput, touchEndInput;
 | |
| 
 | |
|     // Walk across the touch point array processing each contact point.
 | |
|     for (uint32_t i = 0; i < cInputs; i++) {
 | |
|       bool addToEvent = false, addToEndEvent = false;
 | |
| 
 | |
|       // N.B.: According with MS documentation
 | |
|       // https://msdn.microsoft.com/en-us/library/windows/desktop/dd317334(v=vs.85).aspx
 | |
|       // TOUCHEVENTF_DOWN cannot be combined with TOUCHEVENTF_MOVE or TOUCHEVENTF_UP.
 | |
|       // Possibly, it means that TOUCHEVENTF_MOVE and TOUCHEVENTF_UP can be combined together.
 | |
| 
 | |
|       if (pInputs[i].dwFlags & (TOUCHEVENTF_DOWN | TOUCHEVENTF_MOVE)) {
 | |
|         if (touchInput.mTimeStamp.IsNull()) {
 | |
|           // Initialize a touch event to send.
 | |
|           touchInput.mType = MultiTouchInput::MULTITOUCH_MOVE;
 | |
|           touchInput.mTime = ::GetMessageTime();
 | |
|           touchInput.mTimeStamp = GetMessageTimeStamp(touchInput.mTime);
 | |
|           ModifierKeyState modifierKeyState;
 | |
|           touchInput.modifiers = modifierKeyState.GetModifiers();
 | |
|         }
 | |
|         // Pres shell expects this event to be a eTouchStart
 | |
|         // if any new contact points have been added since the last event sent.
 | |
|         if (pInputs[i].dwFlags & TOUCHEVENTF_DOWN) {
 | |
|           touchInput.mType = MultiTouchInput::MULTITOUCH_START;
 | |
|         }
 | |
|         addToEvent = true;
 | |
|       }
 | |
|       if (pInputs[i].dwFlags & TOUCHEVENTF_UP) {
 | |
|         // Pres shell expects removed contacts points to be delivered in a separate
 | |
|         // eTouchEnd event containing only the contact points that were removed.
 | |
|         if (touchEndInput.mTimeStamp.IsNull()) {
 | |
|           // Initialize a touch event to send.
 | |
|           touchEndInput.mType = MultiTouchInput::MULTITOUCH_END;
 | |
|           touchEndInput.mTime = ::GetMessageTime();
 | |
|           touchEndInput.mTimeStamp = GetMessageTimeStamp(touchEndInput.mTime);
 | |
|           ModifierKeyState modifierKeyState;
 | |
|           touchEndInput.modifiers = modifierKeyState.GetModifiers();
 | |
|         }
 | |
|         addToEndEvent = true;
 | |
|       }
 | |
|       if (!addToEvent && !addToEndEvent) {
 | |
|         // Filter out spurious Windows events we don't understand, like palm contact.
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       // Setup the touch point we'll append to the touch event array.
 | |
|       nsPointWin touchPoint;
 | |
|       touchPoint.x = TOUCH_COORD_TO_PIXEL(pInputs[i].x);
 | |
|       touchPoint.y = TOUCH_COORD_TO_PIXEL(pInputs[i].y);
 | |
|       touchPoint.ScreenToClient(mWnd);
 | |
| 
 | |
|       // Initialize the touch data.
 | |
|       SingleTouchData touchData(pInputs[i].dwID,                                      // aIdentifier
 | |
|                                 ScreenIntPoint::FromUnknownPoint(touchPoint),         // aScreenPoint
 | |
|                                 /* radius, if known */
 | |
|                                 pInputs[i].dwFlags & TOUCHINPUTMASKF_CONTACTAREA
 | |
|                                   ? ScreenSize(
 | |
|                                       TOUCH_COORD_TO_PIXEL(pInputs[i].cxContact) / 2,
 | |
|                                       TOUCH_COORD_TO_PIXEL(pInputs[i].cyContact) / 2)
 | |
|                                   : ScreenSize(1, 1),                                 // aRadius
 | |
|                                 0.0f,                                                 // aRotationAngle
 | |
|                                 0.0f);                                                // aForce
 | |
| 
 | |
|       // Append touch data to the appropriate event.
 | |
|       if (addToEvent) {
 | |
|         touchInput.mTouches.AppendElement(touchData);
 | |
|       }
 | |
|       if (addToEndEvent) {
 | |
|         touchEndInput.mTouches.AppendElement(touchData);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Dispatch touch start and touch move event if we have one.
 | |
|     if (!touchInput.mTimeStamp.IsNull()) {
 | |
|       DispatchTouchInput(touchInput);
 | |
|     }
 | |
|     // Dispatch touch end event if we have one.
 | |
|     if (!touchEndInput.mTimeStamp.IsNull()) {
 | |
|       DispatchTouchInput(touchEndInput);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   delete [] pInputs;
 | |
|   CloseTouchInputHandle((HTOUCHINPUT)lParam);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // Gesture event processing. Handles WM_GESTURE events.
 | |
| bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   // Treatment for pan events which translate into scroll events:
 | |
|   if (mGesture.IsPanEvent(lParam)) {
 | |
|     if ( !mGesture.ProcessPanMessage(mWnd, wParam, lParam) )
 | |
|       return false; // ignore
 | |
| 
 | |
|     nsEventStatus status;
 | |
| 
 | |
|     WidgetWheelEvent wheelEvent(true, eWheel, this);
 | |
| 
 | |
|     ModifierKeyState modifierKeyState;
 | |
|     modifierKeyState.InitInputEvent(wheelEvent);
 | |
| 
 | |
|     wheelEvent.button      = 0;
 | |
|     wheelEvent.mTime       = ::GetMessageTime();
 | |
|     wheelEvent.mTimeStamp  = GetMessageTimeStamp(wheelEvent.mTime);
 | |
|     wheelEvent.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 | |
| 
 | |
|     bool endFeedback = true;
 | |
| 
 | |
|     if (mGesture.PanDeltaToPixelScroll(wheelEvent)) {
 | |
|       DispatchEvent(&wheelEvent, status);
 | |
|     }
 | |
| 
 | |
|     if (mDisplayPanFeedback) {
 | |
|       mGesture.UpdatePanFeedbackX(
 | |
|                  mWnd,
 | |
|                  DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaX)),
 | |
|                  endFeedback);
 | |
|       mGesture.UpdatePanFeedbackY(
 | |
|                  mWnd,
 | |
|                  DeprecatedAbs(RoundDown(wheelEvent.mOverflowDeltaY)),
 | |
|                  endFeedback);
 | |
|       mGesture.PanFeedbackFinalize(mWnd, endFeedback);
 | |
|     }
 | |
| 
 | |
|     CloseGestureInfoHandle((HGESTUREINFO)lParam);
 | |
| 
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Other gestures translate into simple gesture events:
 | |
|   WidgetSimpleGestureEvent event(true, eVoidEvent, this);
 | |
|   if ( !mGesture.ProcessGestureMessage(mWnd, wParam, lParam, event) ) {
 | |
|     return false; // fall through to DefWndProc
 | |
|   }
 | |
|   
 | |
|   // Polish up and send off the new event
 | |
|   ModifierKeyState modifierKeyState;
 | |
|   modifierKeyState.InitInputEvent(event);
 | |
|   event.button    = 0;
 | |
|   event.mTime     = ::GetMessageTime();
 | |
|   event.mTimeStamp = GetMessageTimeStamp(event.mTime);
 | |
|   event.inputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
 | |
| 
 | |
|   nsEventStatus status;
 | |
|   DispatchEvent(&event, status);
 | |
|   if (status == nsEventStatus_eIgnore) {
 | |
|     return false; // Ignored, fall through
 | |
|   }
 | |
| 
 | |
|   // Only close this if we process and return true.
 | |
|   CloseGestureInfoHandle((HGESTUREINFO)lParam);
 | |
| 
 | |
|   return true; // Handled
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
 | |
| {
 | |
|   // If this is a remotely updated widget we receive clipping, position, and
 | |
|   // size information from a source other than our owner. Don't let our parent
 | |
|   // update this information.
 | |
|   if (mWindowType == eWindowType_plugin_ipc_chrome) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // XXXroc we could use BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos
 | |
|   // here, if that helps in some situations. So far I haven't seen a
 | |
|   // need.
 | |
|   for (uint32_t i = 0; i < aConfigurations.Length(); ++i) {
 | |
|     const Configuration& configuration = aConfigurations[i];
 | |
|     nsWindow* w = static_cast<nsWindow*>(configuration.mChild.get());
 | |
|     NS_ASSERTION(w->GetParent() == this,
 | |
|                  "Configured widget is not a child");
 | |
|     nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion, true);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|     LayoutDeviceIntRect bounds = w->GetBounds();
 | |
|     if (bounds.Size() != configuration.mBounds.Size()) {
 | |
|       w->Resize(configuration.mBounds.X(), configuration.mBounds.Y(),
 | |
|                 configuration.mBounds.Width(), configuration.mBounds.Height(),
 | |
|                 true);
 | |
|     } else if (bounds.TopLeft() != configuration.mBounds.TopLeft()) {
 | |
|       w->Move(configuration.mBounds.X(), configuration.mBounds.Y());
 | |
| 
 | |
| 
 | |
|       if (gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ||
 | |
|           GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_BASIC) {
 | |
|         // XXX - Workaround for Bug 587508. This will invalidate the part of the
 | |
|         // plugin window that might be touched by moving content somehow. The
 | |
|         // underlying problem should be found and fixed!
 | |
|         LayoutDeviceIntRegion r;
 | |
|         r.Sub(bounds, configuration.mBounds);
 | |
|         r.MoveBy(-bounds.X(),
 | |
|                  -bounds.Y());
 | |
|         LayoutDeviceIntRect toInvalidate = r.GetBounds();
 | |
| 
 | |
|         WinUtils::InvalidatePluginAsWorkaround(w, toInvalidate);
 | |
|       }
 | |
|     }
 | |
|     rv = w->SetWindowClipRegion(configuration.mClipRegion, false);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static HRGN
 | |
| CreateHRGNFromArray(const nsTArray<LayoutDeviceIntRect>& aRects)
 | |
| {
 | |
|   int32_t size = sizeof(RGNDATAHEADER) + sizeof(RECT)*aRects.Length();
 | |
|   AutoTArray<uint8_t,100> buf;
 | |
|   buf.SetLength(size);
 | |
|   RGNDATA* data = reinterpret_cast<RGNDATA*>(buf.Elements());
 | |
|   RECT* rects = reinterpret_cast<RECT*>(data->Buffer);
 | |
|   data->rdh.dwSize = sizeof(data->rdh);
 | |
|   data->rdh.iType = RDH_RECTANGLES;
 | |
|   data->rdh.nCount = aRects.Length();
 | |
|   LayoutDeviceIntRect bounds;
 | |
|   for (uint32_t i = 0; i < aRects.Length(); ++i) {
 | |
|     const LayoutDeviceIntRect& r = aRects[i];
 | |
|     bounds.UnionRect(bounds, r);
 | |
|     ::SetRect(&rects[i], r.X(), r.Y(), r.XMost(), r.YMost());
 | |
|   }
 | |
|   ::SetRect(&data->rdh.rcBound, bounds.X(), bounds.Y(), bounds.XMost(), bounds.YMost());
 | |
|   return ::ExtCreateRegion(nullptr, buf.Length(), data);
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::SetWindowClipRegion(const nsTArray<LayoutDeviceIntRect>& aRects,
 | |
|                               bool aIntersectWithExisting)
 | |
| {
 | |
|   if (IsWindowClipRegionEqual(aRects)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   nsBaseWidget::SetWindowClipRegion(aRects, aIntersectWithExisting);
 | |
| 
 | |
|   HRGN dest = CreateHRGNFromArray(aRects);
 | |
|   if (!dest)
 | |
|     return NS_ERROR_OUT_OF_MEMORY;
 | |
| 
 | |
|   if (aIntersectWithExisting) {
 | |
|     HRGN current = ::CreateRectRgn(0, 0, 0, 0);
 | |
|     if (current) {
 | |
|       if (::GetWindowRgn(mWnd, current) != 0 /*ERROR*/) {
 | |
|         ::CombineRgn(dest, dest, current, RGN_AND);
 | |
|       }
 | |
|       ::DeleteObject(current);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If a plugin is not visible, especially if it is in a background tab,
 | |
|   // it should not be able to steal keyboard focus.  This code checks whether
 | |
|   // the region that the plugin is being clipped to is NULLREGION.  If it is,
 | |
|   // the plugin window gets disabled.
 | |
|   if (IsPlugin()) {
 | |
|     if (NULLREGION == ::CombineRgn(dest, dest, dest, RGN_OR)) {
 | |
|       ::ShowWindow(mWnd, SW_HIDE);
 | |
|       ::EnableWindow(mWnd, FALSE);
 | |
|     } else {
 | |
|       ::EnableWindow(mWnd, TRUE);
 | |
|       ::ShowWindow(mWnd, SW_SHOW);
 | |
|     }
 | |
|   }
 | |
|   if (!::SetWindowRgn(mWnd, dest, TRUE)) {
 | |
|     ::DeleteObject(dest);
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // WM_DESTROY event handler
 | |
| void nsWindow::OnDestroy()
 | |
| {
 | |
|   mOnDestroyCalled = true;
 | |
| 
 | |
|   // Make sure we don't get destroyed in the process of tearing down.
 | |
|   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
 | |
|   
 | |
|   // Dispatch the destroy notification.
 | |
|   if (!mInDtor)
 | |
|     NotifyWindowDestroyed();
 | |
| 
 | |
|   // Prevent the widget from sending additional events.
 | |
|   mWidgetListener = nullptr;
 | |
|   mAttachedWidgetListener = nullptr;
 | |
| 
 | |
|   // Unregister notifications from terminal services
 | |
|   ::WTSUnRegisterSessionNotification(mWnd);
 | |
| 
 | |
|   // Free our subclass and clear |this| stored in the window props. We will no longer
 | |
|   // receive events from Windows after this point.
 | |
|   SubclassWindow(FALSE);
 | |
| 
 | |
|   // Once mWidgetListener is cleared and the subclass is reset, sCurrentWindow can be
 | |
|   // cleared. (It's used in tracking windows for mouse events.)
 | |
|   if (sCurrentWindow == this)
 | |
|     sCurrentWindow = nullptr;
 | |
| 
 | |
|   // Disconnects us from our parent, will call our GetParent().
 | |
|   nsBaseWidget::Destroy();
 | |
| 
 | |
|   // Release references to children, device context, toolkit, and app shell.
 | |
|   nsBaseWidget::OnDestroy();
 | |
|   
 | |
|   // Clear our native parent handle.
 | |
|   // XXX Windows will take care of this in the proper order, and SetParent(nullptr)'s
 | |
|   // remove child on the parent already took place in nsBaseWidget's Destroy call above.
 | |
|   //SetParent(nullptr);
 | |
|   mParent = nullptr;
 | |
| 
 | |
|   // We have to destroy the native drag target before we null out our window pointer.
 | |
|   EnableDragDrop(false);
 | |
| 
 | |
|   // If we're going away and for some reason we're still the rollup widget, rollup and
 | |
|   // turn off capture.
 | |
|   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
 | |
|   nsCOMPtr<nsIWidget> rollupWidget;
 | |
|   if (rollupListener) {
 | |
|     rollupWidget = rollupListener->GetRollupWidget();
 | |
|   }
 | |
|   if (this == rollupWidget) {
 | |
|     if ( rollupListener )
 | |
|       rollupListener->Rollup(0, false, nullptr, nullptr);
 | |
|     CaptureRollupEvents(nullptr, false);
 | |
|   }
 | |
| 
 | |
|   IMEHandler::OnDestroyWindow(this);
 | |
| 
 | |
|   // Free GDI window class objects
 | |
|   if (mBrush) {
 | |
|     VERIFY(::DeleteObject(mBrush));
 | |
|     mBrush = nullptr;
 | |
|   }
 | |
| 
 | |
|   // Destroy any custom cursor resources.
 | |
|   if (mCursor == eCursorInvalid)
 | |
|     SetCursor(eCursor_standard);
 | |
| 
 | |
|   if (mCompositorWidgetDelegate) {
 | |
|     mCompositorWidgetDelegate->OnDestroyWindow();
 | |
|   }
 | |
|   mBasicLayersSurface = nullptr;
 | |
| 
 | |
|   // Finalize panning feedback to possibly restore window displacement
 | |
|   mGesture.PanFeedbackFinalize(mWnd, true);
 | |
| 
 | |
|   // Clear the main HWND.
 | |
|   mWnd = nullptr;
 | |
| }
 | |
| 
 | |
| // Send a resize message to the listener
 | |
| bool
 | |
| nsWindow::OnResize(const LayoutDeviceIntSize& aSize)
 | |
| {
 | |
|   bool result = false;
 | |
|   if (mWidgetListener) {
 | |
|     result = mWidgetListener->
 | |
|       WindowResized(this, aSize.width, aSize.height);
 | |
|   }
 | |
| 
 | |
|   // If there is an attached view, inform it as well as the normal widget listener.
 | |
|   if (mAttachedWidgetListener) {
 | |
|     return mAttachedWidgetListener->
 | |
|       WindowResized(this, aSize.width, aSize.height);
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // Can be overriden. Controls auto-erase of background.
 | |
| bool nsWindow::AutoErase(HDC dc)
 | |
| {
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::IsPopup()
 | |
| {
 | |
|   return mWindowType == eWindowType_popup;
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::ShouldUseOffMainThreadCompositing()
 | |
| {
 | |
|   if (IsSmallPopup()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return nsBaseWidget::ShouldUseOffMainThreadCompositing();
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::WindowUsesOMTC()
 | |
| {
 | |
|   ULONG_PTR style = ::GetClassLongPtr(mWnd, GCL_STYLE);
 | |
|   if (!style) {
 | |
|     NS_WARNING("Could not get window class style");
 | |
|     return;
 | |
|   }
 | |
|   style |= CS_HREDRAW | CS_VREDRAW;
 | |
|   DebugOnly<ULONG_PTR> result = ::SetClassLongPtr(mWnd, GCL_STYLE, style);
 | |
|   NS_WARNING_ASSERTION(result, "Could not reset window class style");
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::HasBogusPopupsDropShadowOnMultiMonitor() {
 | |
|   if (sHasBogusPopupsDropShadowOnMultiMonitor == TRI_UNKNOWN) {
 | |
|     // Since any change in the preferences requires a restart, this can be
 | |
|     // done just once.
 | |
|     // Check for Direct2D first.
 | |
|     sHasBogusPopupsDropShadowOnMultiMonitor =
 | |
|       gfxWindowsPlatform::GetPlatform()->IsDirect2DBackend() ? TRI_TRUE : TRI_FALSE;
 | |
|     if (!sHasBogusPopupsDropShadowOnMultiMonitor) {
 | |
|       // Otherwise check if Direct3D 9 may be used.
 | |
|       if (gfxConfig::IsEnabled(Feature::HW_COMPOSITING) &&
 | |
|           !gfxConfig::IsEnabled(Feature::OPENGL_COMPOSITING))
 | |
|       {
 | |
|         nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
 | |
|         if (gfxInfo) {
 | |
|           int32_t status;
 | |
|           nsCString discardFailureId;
 | |
|           if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
 | |
|                                                      discardFailureId, &status))) {
 | |
|             if (status == nsIGfxInfo::FEATURE_STATUS_OK ||
 | |
|                 gfxConfig::IsForcedOnByUser(Feature::HW_COMPOSITING))
 | |
|             {
 | |
|               sHasBogusPopupsDropShadowOnMultiMonitor = TRI_TRUE;
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return !!sHasBogusPopupsDropShadowOnMultiMonitor;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::OnSysColorChanged()
 | |
| {
 | |
|   if (mWindowType == eWindowType_invisible) {
 | |
|     ::EnumThreadWindows(GetCurrentThreadId(), nsWindow::BroadcastMsg, WM_SYSCOLORCHANGE);
 | |
|   }
 | |
|   else {
 | |
|     // Note: This is sent for child windows as well as top-level windows.
 | |
|     // The Win32 toolkit normally only sends these events to top-level windows.
 | |
|     // But we cycle through all of the childwindows and send it to them as well
 | |
|     // so all presentations get notified properly.
 | |
|     // See nsWindow::GlobalMsgWindowProc.
 | |
|     NotifySysColorChanged();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::OnDPIChanged(int32_t x, int32_t y, int32_t width, int32_t height)
 | |
| {
 | |
|   // Don't try to handle WM_DPICHANGED for popup windows (see bug 1239353);
 | |
|   // they remain tied to their original parent's resolution.
 | |
|   if (mWindowType == eWindowType_popup) {
 | |
|     return;
 | |
|   }
 | |
|   if (DefaultScaleOverride() > 0.0) {
 | |
|     return;
 | |
|   }
 | |
|   mDefaultScale = -1.0; // force recomputation of scale factor
 | |
| 
 | |
|   if (mResizeState != RESIZING && mSizeMode == nsSizeMode_Normal) {
 | |
|     // Limit the position (if not in the middle of a drag-move) & size,
 | |
|     // if it would overflow the destination screen
 | |
|     nsCOMPtr<nsIScreenManager> sm = do_GetService(sScreenManagerContractID);
 | |
|     if (sm) {
 | |
|       nsCOMPtr<nsIScreen> screen;
 | |
|       sm->ScreenForRect(x, y, width, height, getter_AddRefs(screen));
 | |
|       if (screen) {
 | |
|         int32_t availLeft, availTop, availWidth, availHeight;
 | |
|         screen->GetAvailRect(&availLeft, &availTop, &availWidth, &availHeight);
 | |
|         if (mResizeState != MOVING) {
 | |
|           x = std::max(x, availLeft);
 | |
|           y = std::max(y, availTop);
 | |
|         }
 | |
|         width = std::min(width, availWidth);
 | |
|         height = std::min(height, availHeight);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     Resize(x, y, width, height, true);
 | |
|   }
 | |
|   ChangedDPI();
 | |
|   ResetLayout();
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: IME management and accessibility
 | |
|  **
 | |
|  ** Handles managing IME input and accessibility.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| void
 | |
| nsWindow::SetInputContext(const InputContext& aContext,
 | |
|                           const InputContextAction& aAction)
 | |
| {
 | |
|   InputContext newInputContext = aContext;
 | |
|   IMEHandler::SetInputContext(this, newInputContext, aAction);
 | |
|   mInputContext = newInputContext;
 | |
| }
 | |
| 
 | |
| InputContext
 | |
| nsWindow::GetInputContext()
 | |
| {
 | |
|   mInputContext.mIMEState.mOpen = IMEState::CLOSED;
 | |
|   if (WinUtils::IsIMEEnabled(mInputContext) && IMEHandler::GetOpenState(this)) {
 | |
|     mInputContext.mIMEState.mOpen = IMEState::OPEN;
 | |
|   } else {
 | |
|     mInputContext.mIMEState.mOpen = IMEState::CLOSED;
 | |
|   }
 | |
|   return mInputContext;
 | |
| }
 | |
| 
 | |
| TextEventDispatcherListener*
 | |
| nsWindow::GetNativeTextEventDispatcherListener()
 | |
| {
 | |
|   return IMEHandler::GetNativeTextEventDispatcherListener();
 | |
| }
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
| #ifdef DEBUG
 | |
| #define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)                                  \
 | |
|   if (a11y::logging::IsEnabled(a11y::logging::ePlatforms)) {                   \
 | |
|     printf("Get the window:\n  {\n     HWND: %p, parent HWND: %p, wndobj: %p,\n",\
 | |
|            aHwnd, ::GetParent(aHwnd), aWnd);                                   \
 | |
|     printf("     acc: %p", aAcc);                                              \
 | |
|     if (aAcc) {                                                                \
 | |
|       nsAutoString name;                                                       \
 | |
|       aAcc->Name(name);                                                        \
 | |
|       printf(", accname: %s", NS_ConvertUTF16toUTF8(name).get());              \
 | |
|     }                                                                          \
 | |
|     printf("\n }\n");                                                          \
 | |
|   }
 | |
| 
 | |
| #else
 | |
| #define NS_LOG_WMGETOBJECT(aWnd, aHwnd, aAcc)
 | |
| #endif
 | |
| 
 | |
| a11y::Accessible*
 | |
| nsWindow::GetAccessible()
 | |
| {
 | |
|   // If the pref was ePlatformIsDisabled, return null here, disabling a11y.
 | |
|   if (a11y::PlatformDisabledState() == a11y::ePlatformIsDisabled)
 | |
|     return nullptr;
 | |
| 
 | |
|   if (mInDtor || mOnDestroyCalled || mWindowType == eWindowType_invisible) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // In case of popup window return a popup accessible.
 | |
|   nsView* view = nsView::GetViewFor(this);
 | |
|   if (view) {
 | |
|     nsIFrame* frame = view->GetFrame();
 | |
|     if (frame && nsLayoutUtils::IsPopup(frame)) {
 | |
|       nsAccessibilityService* accService = GetOrCreateAccService();
 | |
|       if (accService) {
 | |
|         a11y::DocAccessible* docAcc =
 | |
|           GetAccService()->GetDocAccessible(frame->PresShell());
 | |
|         if (docAcc) {
 | |
|           NS_LOG_WMGETOBJECT(this, mWnd,
 | |
|                              docAcc->GetAccessibleOrDescendant(frame->GetContent()));
 | |
|           return docAcc->GetAccessibleOrDescendant(frame->GetContent());
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // otherwise root document accessible.
 | |
|   NS_LOG_WMGETOBJECT(this, mWnd, GetRootAccessible());
 | |
|   return GetRootAccessible();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Transparency
 | |
|  **
 | |
|  ** Window transparency helpers.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| #ifdef MOZ_XUL
 | |
| 
 | |
| void nsWindow::SetWindowTranslucencyInner(nsTransparencyMode aMode)
 | |
| {
 | |
|   if (aMode == mTransparencyMode)
 | |
|     return;
 | |
| 
 | |
|   // stop on dialogs and popups!
 | |
|   HWND hWnd = WinUtils::GetTopLevelHWND(mWnd, true);
 | |
|   nsWindow* parent = WinUtils::GetNSWindowPtr(hWnd);
 | |
| 
 | |
|   if (!parent)
 | |
|   {
 | |
|     NS_WARNING("Trying to use transparent chrome in an embedded context");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (parent != this) {
 | |
|     NS_WARNING("Setting SetWindowTranslucencyInner on a parent this is not us!");
 | |
|   }
 | |
| 
 | |
|   if (aMode == eTransparencyTransparent) {
 | |
|     // If we're switching to the use of a transparent window, hide the chrome
 | |
|     // on our parent.
 | |
|     HideWindowChrome(true);
 | |
|   } else if (mHideChrome && mTransparencyMode == eTransparencyTransparent) {
 | |
|     // if we're switching out of transparent, re-enable our parent's chrome.
 | |
|     HideWindowChrome(false);
 | |
|   }
 | |
| 
 | |
|   LONG_PTR style = ::GetWindowLongPtrW(hWnd, GWL_STYLE),
 | |
|     exStyle = ::GetWindowLongPtr(hWnd, GWL_EXSTYLE);
 | |
| 
 | |
|   if (parent->mIsVisible) {
 | |
|     style |= WS_VISIBLE;
 | |
|     if (parent->mSizeMode == nsSizeMode_Maximized) {
 | |
|       style |= WS_MAXIMIZE;
 | |
|     } else if (parent->mSizeMode == nsSizeMode_Minimized) {
 | |
|       style |= WS_MINIMIZE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (aMode == eTransparencyTransparent)
 | |
|     exStyle |= WS_EX_LAYERED;
 | |
|   else
 | |
|     exStyle &= ~WS_EX_LAYERED;
 | |
| 
 | |
|   VERIFY_WINDOW_STYLE(style);
 | |
|   ::SetWindowLongPtrW(hWnd, GWL_STYLE, style);
 | |
|   ::SetWindowLongPtrW(hWnd, GWL_EXSTYLE, exStyle);
 | |
| 
 | |
|   if (HasGlass())
 | |
|     memset(&mGlassMargins, 0, sizeof mGlassMargins);
 | |
|   mTransparencyMode = aMode;
 | |
| 
 | |
|   if (mCompositorWidgetDelegate) {
 | |
|     mCompositorWidgetDelegate->UpdateTransparency(aMode);
 | |
|   }
 | |
|   UpdateGlass();
 | |
| }
 | |
| 
 | |
| #endif //MOZ_XUL
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Popup rollup hooks
 | |
|  **
 | |
|  ** Deals with CaptureRollup on popup windows.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| // Schedules a timer for a window, so we can rollup after processing the hook event
 | |
| void nsWindow::ScheduleHookTimer(HWND aWnd, UINT aMsgId)
 | |
| {
 | |
|   // In some cases multiple hooks may be scheduled
 | |
|   // so ignore any other requests once one timer is scheduled
 | |
|   if (sHookTimerId == 0) {
 | |
|     // Remember the window handle and the message ID to be used later
 | |
|     sRollupMsgId = aMsgId;
 | |
|     sRollupMsgWnd = aWnd;
 | |
|     // Schedule native timer for doing the rollup after
 | |
|     // this event is done being processed
 | |
|     sHookTimerId = ::SetTimer(nullptr, 0, 0, (TIMERPROC)HookTimerForPopups);
 | |
|     NS_ASSERTION(sHookTimerId, "Timer couldn't be created.");
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
| int gLastMsgCode = 0;
 | |
| extern MSGFEventMsgInfo gMSGFEvents[];
 | |
| #endif
 | |
| 
 | |
| // Process Menu messages, rollup when popup is clicked.
 | |
| LRESULT CALLBACK nsWindow::MozSpecialMsgFilter(int code, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
|   if (sProcessHook) {
 | |
|     MSG* pMsg = (MSG*)lParam;
 | |
| 
 | |
|     int inx = 0;
 | |
|     while (gMSGFEvents[inx].mId != code && gMSGFEvents[inx].mStr != nullptr) {
 | |
|       inx++;
 | |
|     }
 | |
|     if (code != gLastMsgCode) {
 | |
|       if (gMSGFEvents[inx].mId == code) {
 | |
| #ifdef DEBUG
 | |
|         MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|                ("MozSpecialMessageProc - code: 0x%X  - %s  hw: %p\n", 
 | |
|                 code, gMSGFEvents[inx].mStr, pMsg->hwnd));
 | |
| #endif
 | |
|       } else {
 | |
| #ifdef DEBUG
 | |
|         MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|                ("MozSpecialMessageProc - code: 0x%X  - %d  hw: %p\n", 
 | |
|                 code, gMSGFEvents[inx].mId, pMsg->hwnd));
 | |
| #endif
 | |
|       }
 | |
|       gLastMsgCode = code;
 | |
|     }
 | |
|     PrintEvent(pMsg->message, FALSE, FALSE);
 | |
|   }
 | |
| #endif // #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
| 
 | |
|   if (sProcessHook && code == MSGF_MENU) {
 | |
|     MSG* pMsg = (MSG*)lParam;
 | |
|     ScheduleHookTimer( pMsg->hwnd, pMsg->message);
 | |
|   }
 | |
| 
 | |
|   return ::CallNextHookEx(sMsgFilterHook, code, wParam, lParam);
 | |
| }
 | |
| 
 | |
| // Process all mouse messages. Roll up when a click is in a native window
 | |
| // that doesn't have an nsIWidget.
 | |
| LRESULT CALLBACK nsWindow::MozSpecialMouseProc(int code, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
|   if (sProcessHook) {
 | |
|     switch (WinUtils::GetNativeMessage(wParam)) {
 | |
|       case WM_LBUTTONDOWN:
 | |
|       case WM_RBUTTONDOWN:
 | |
|       case WM_MBUTTONDOWN:
 | |
|       case WM_MOUSEWHEEL:
 | |
|       case WM_MOUSEHWHEEL:
 | |
|       {
 | |
|         MOUSEHOOKSTRUCT* ms = (MOUSEHOOKSTRUCT*)lParam;
 | |
|         nsIWidget* mozWin = WinUtils::GetNSWindowPtr(ms->hwnd);
 | |
|         if (mozWin) {
 | |
|           // If this window is windowed plugin window, the mouse events are not
 | |
|           // sent to us.
 | |
|           if (static_cast<nsWindow*>(mozWin)->IsPlugin())
 | |
|             ScheduleHookTimer(ms->hwnd, (UINT)wParam);
 | |
|         } else {
 | |
|           ScheduleHookTimer(ms->hwnd, (UINT)wParam);
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return ::CallNextHookEx(sCallMouseHook, code, wParam, lParam);
 | |
| }
 | |
| 
 | |
| // Process all messages. Roll up when the window is moving, or
 | |
| // is resizing or when maximized or mininized.
 | |
| LRESULT CALLBACK nsWindow::MozSpecialWndProc(int code, WPARAM wParam, LPARAM lParam)
 | |
| {
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
|   if (sProcessHook) {
 | |
|     CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
 | |
|     PrintEvent(cwpt->message, FALSE, FALSE);
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (sProcessHook) {
 | |
|     CWPSTRUCT* cwpt = (CWPSTRUCT*)lParam;
 | |
|     if (cwpt->message == WM_MOVING ||
 | |
|         cwpt->message == WM_SIZING ||
 | |
|         cwpt->message == WM_GETMINMAXINFO) {
 | |
|       ScheduleHookTimer(cwpt->hwnd, (UINT)cwpt->message);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return ::CallNextHookEx(sCallProcHook, code, wParam, lParam);
 | |
| }
 | |
| 
 | |
| // Register the special "hooks" for dropdown processing.
 | |
| void nsWindow::RegisterSpecialDropdownHooks()
 | |
| {
 | |
|   NS_ASSERTION(!sMsgFilterHook, "sMsgFilterHook must be NULL!");
 | |
|   NS_ASSERTION(!sCallProcHook,  "sCallProcHook must be NULL!");
 | |
| 
 | |
|   DISPLAY_NMM_PRT("***************** Installing Msg Hooks ***************\n");
 | |
| 
 | |
|   // Install msg hook for moving the window and resizing
 | |
|   if (!sMsgFilterHook) {
 | |
|     DISPLAY_NMM_PRT("***** Hooking sMsgFilterHook!\n");
 | |
|     sMsgFilterHook = SetWindowsHookEx(WH_MSGFILTER, MozSpecialMsgFilter,
 | |
|                                       nullptr, GetCurrentThreadId());
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
|     if (!sMsgFilterHook) {
 | |
|       MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|              ("***** SetWindowsHookEx is NOT installed for WH_MSGFILTER!\n"));
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   // Install msg hook for menus
 | |
|   if (!sCallProcHook) {
 | |
|     DISPLAY_NMM_PRT("***** Hooking sCallProcHook!\n");
 | |
|     sCallProcHook  = SetWindowsHookEx(WH_CALLWNDPROC, MozSpecialWndProc,
 | |
|                                       nullptr, GetCurrentThreadId());
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
|     if (!sCallProcHook) {
 | |
|       MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|              ("***** SetWindowsHookEx is NOT installed for WH_CALLWNDPROC!\n"));
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   // Install msg hook for the mouse
 | |
|   if (!sCallMouseHook) {
 | |
|     DISPLAY_NMM_PRT("***** Hooking sCallMouseHook!\n");
 | |
|     sCallMouseHook  = SetWindowsHookEx(WH_MOUSE, MozSpecialMouseProc,
 | |
|                                        nullptr, GetCurrentThreadId());
 | |
| #ifdef POPUP_ROLLUP_DEBUG_OUTPUT
 | |
|     if (!sCallMouseHook) {
 | |
|       MOZ_LOG(gWindowsLog, LogLevel::Info, 
 | |
|              ("***** SetWindowsHookEx is NOT installed for WH_MOUSE!\n"));
 | |
|     }
 | |
| #endif
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Unhook special message hooks for dropdowns.
 | |
| void nsWindow::UnregisterSpecialDropdownHooks()
 | |
| {
 | |
|   DISPLAY_NMM_PRT("***************** De-installing Msg Hooks ***************\n");
 | |
| 
 | |
|   if (sCallProcHook) {
 | |
|     DISPLAY_NMM_PRT("***** Unhooking sCallProcHook!\n");
 | |
|     if (!::UnhookWindowsHookEx(sCallProcHook)) {
 | |
|       DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallProcHook!\n");
 | |
|     }
 | |
|     sCallProcHook = nullptr;
 | |
|   }
 | |
| 
 | |
|   if (sMsgFilterHook) {
 | |
|     DISPLAY_NMM_PRT("***** Unhooking sMsgFilterHook!\n");
 | |
|     if (!::UnhookWindowsHookEx(sMsgFilterHook)) {
 | |
|       DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sMsgFilterHook!\n");
 | |
|     }
 | |
|     sMsgFilterHook = nullptr;
 | |
|   }
 | |
| 
 | |
|   if (sCallMouseHook) {
 | |
|     DISPLAY_NMM_PRT("***** Unhooking sCallMouseHook!\n");
 | |
|     if (!::UnhookWindowsHookEx(sCallMouseHook)) {
 | |
|       DISPLAY_NMM_PRT("***** UnhookWindowsHookEx failed for sCallMouseHook!\n");
 | |
|     }
 | |
|     sCallMouseHook = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // This timer is designed to only fire one time at most each time a "hook" function
 | |
| // is used to rollup the dropdown. In some cases, the timer may be scheduled from the
 | |
| // hook, but that hook event or a subsequent event may roll up the dropdown before
 | |
| // this timer function is executed.
 | |
| //
 | |
| // For example, if an MFC control takes focus, the combobox will lose focus and rollup
 | |
| // before this function fires.
 | |
| VOID CALLBACK nsWindow::HookTimerForPopups(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime)
 | |
| {
 | |
|   if (sHookTimerId != 0) {
 | |
|     // if the window is nullptr then we need to use the ID to kill the timer
 | |
|     DebugOnly<BOOL> status = ::KillTimer(nullptr, sHookTimerId);
 | |
|     NS_ASSERTION(status, "Hook Timer was not killed.");
 | |
|     sHookTimerId = 0;
 | |
|   }
 | |
| 
 | |
|   if (sRollupMsgId != 0) {
 | |
|     // Note: DealWithPopups does the check to make sure that the rollup widget is set.
 | |
|     LRESULT popupHandlingResult;
 | |
|     nsAutoRollup autoRollup;
 | |
|     DealWithPopups(sRollupMsgWnd, sRollupMsgId, 0, 0, &popupHandlingResult);
 | |
|     sRollupMsgId = 0;
 | |
|     sRollupMsgWnd = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| BOOL CALLBACK nsWindow::ClearResourcesCallback(HWND aWnd, LPARAM aMsg)
 | |
| {
 | |
|     nsWindow *window = WinUtils::GetNSWindowPtr(aWnd);
 | |
|     if (window) {
 | |
|         window->ClearCachedResources();
 | |
|     }  
 | |
|     return TRUE;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::ClearCachedResources()
 | |
| {
 | |
|     if (mLayerManager &&
 | |
|         mLayerManager->GetBackendType() == LayersBackend::LAYERS_BASIC) {
 | |
|       mLayerManager->ClearCachedResources();
 | |
|     }
 | |
|     ::EnumChildWindows(mWnd, nsWindow::ClearResourcesCallback, 0);
 | |
| }
 | |
| 
 | |
| static bool IsDifferentThreadWindow(HWND aWnd)
 | |
| {
 | |
|   return ::GetCurrentThreadId() != ::GetWindowThreadProcessId(aWnd, nullptr);
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| nsWindow::EventIsInsideWindow(nsWindow* aWindow)
 | |
| {
 | |
|   RECT r;
 | |
|   ::GetWindowRect(aWindow->mWnd, &r);
 | |
|   DWORD pos = ::GetMessagePos();
 | |
|   POINT mp;
 | |
|   mp.x = GET_X_LPARAM(pos);
 | |
|   mp.y = GET_Y_LPARAM(pos);
 | |
| 
 | |
|   // was the event inside this window?
 | |
|   return static_cast<bool>(::PtInRect(&r, mp));
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| nsWindow::GetPopupsToRollup(nsIRollupListener* aRollupListener,
 | |
|                             uint32_t* aPopupsToRollup)
 | |
| {
 | |
|   // If we're dealing with menus, we probably have submenus and we don't want
 | |
|   // to rollup some of them if the click is in a parent menu of the current
 | |
|   // submenu.
 | |
|   *aPopupsToRollup = UINT32_MAX;
 | |
|   AutoTArray<nsIWidget*, 5> widgetChain;
 | |
|   uint32_t sameTypeCount =
 | |
|     aRollupListener->GetSubmenuWidgetChain(&widgetChain);
 | |
|   for (uint32_t i = 0; i < widgetChain.Length(); ++i) {
 | |
|     nsIWidget* widget = widgetChain[i];
 | |
|     if (EventIsInsideWindow(static_cast<nsWindow*>(widget))) {
 | |
|       // Don't roll up if the mouse event occurred within a menu of the
 | |
|       // same type. If the mouse event occurred in a menu higher than that,
 | |
|       // roll up, but pass the number of popups to Rollup so that only those
 | |
|       // of the same type close up.
 | |
|       if (i < sameTypeCount) {
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       *aPopupsToRollup = sameTypeCount;
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| nsWindow::NeedsToHandleNCActivateDelayed(HWND aWnd)
 | |
| {
 | |
|   // While popup is open, popup window might be activated by other application.
 | |
|   // At this time, we need to take back focus to the previous window but it
 | |
|   // causes flickering its nonclient area because WM_NCACTIVATE comes before
 | |
|   // WM_ACTIVATE and we cannot know which window will take focus at receiving
 | |
|   // WM_NCACTIVATE. Therefore, we need a hack for preventing the flickerling.
 | |
|   //
 | |
|   // If non-popup window receives WM_NCACTIVATE at deactivating, default
 | |
|   // wndproc shouldn't handle it as deactivating. Instead, at receiving
 | |
|   // WM_ACTIVIATE after that, WM_NCACTIVATE should be sent again manually.
 | |
|   // This returns true if the window needs to handle WM_NCACTIVATE later.
 | |
| 
 | |
|   nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
 | |
|   return window && !window->IsPopup();
 | |
| }
 | |
| 
 | |
| static bool
 | |
| IsTouchSupportEnabled(HWND aWnd)
 | |
| {
 | |
|   nsWindow* topWindow = WinUtils::GetNSWindowPtr(WinUtils::GetTopLevelHWND(aWnd, true));
 | |
|   return topWindow ? topWindow->IsTouchWindow() : false;
 | |
| }
 | |
| 
 | |
| // static
 | |
| bool
 | |
| nsWindow::DealWithPopups(HWND aWnd, UINT aMessage,
 | |
|                          WPARAM aWParam, LPARAM aLParam, LRESULT* aResult)
 | |
| {
 | |
|   NS_ASSERTION(aResult, "Bad outResult");
 | |
| 
 | |
|   // XXX Why do we use the return value of WM_MOUSEACTIVATE for all messages?
 | |
|   *aResult = MA_NOACTIVATE;
 | |
| 
 | |
|   if (!::IsWindowVisible(aWnd)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
 | |
|   NS_ENSURE_TRUE(rollupListener, false);
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget();
 | |
|   if (!popup) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   static bool sSendingNCACTIVATE = false;
 | |
|   static bool sPendingNCACTIVATE = false;
 | |
|   uint32_t popupsToRollup = UINT32_MAX;
 | |
| 
 | |
|   bool consumeRollupEvent = false;
 | |
| 
 | |
|   nsWindow* popupWindow = static_cast<nsWindow*>(popup.get());
 | |
|   UINT nativeMessage = WinUtils::GetNativeMessage(aMessage);
 | |
|   switch (nativeMessage) {
 | |
|     case WM_TOUCH:
 | |
|       if (!IsTouchSupportEnabled(aWnd)) {
 | |
|         // If APZ is disabled, don't allow touch inputs to dismiss popups. The
 | |
|         // compatibility mouse events will do it instead.
 | |
|         return false;
 | |
|       }
 | |
|       MOZ_FALLTHROUGH;
 | |
|     case WM_LBUTTONDOWN:
 | |
|     case WM_RBUTTONDOWN:
 | |
|     case WM_MBUTTONDOWN:
 | |
|     case WM_NCLBUTTONDOWN:
 | |
|     case WM_NCRBUTTONDOWN:
 | |
|     case WM_NCMBUTTONDOWN:
 | |
|       if (nativeMessage != WM_TOUCH &&
 | |
|           IsTouchSupportEnabled(aWnd) &&
 | |
|           MOUSE_INPUT_SOURCE() == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
 | |
|         // If any of these mouse events are really compatibility events that
 | |
|         // Windows is sending for touch inputs, then don't allow them to dismiss
 | |
|         // popups when APZ is enabled (instead we do the dismissing as part of
 | |
|         // WM_TOUCH handling which is more correct).
 | |
|         // If we don't do this, then when the user lifts their finger after a
 | |
|         // long-press, the WM_RBUTTONDOWN compatibility event that Windows sends
 | |
|         // us will dismiss the contextmenu popup that we displayed as part of
 | |
|         // handling the long-tap-up.
 | |
|         return false;
 | |
|       }
 | |
|       if (!EventIsInsideWindow(popupWindow) &&
 | |
|           GetPopupsToRollup(rollupListener, &popupsToRollup)) {
 | |
|         break;
 | |
|       }
 | |
|       return false;
 | |
|     case WM_POINTERDOWN:
 | |
|       {
 | |
|         WinPointerEvents pointerEvents;
 | |
|         if (!pointerEvents.ShouldRollupOnPointerEvent(nativeMessage, aWParam)) {
 | |
|           return false;
 | |
|         }
 | |
|         if (!GetPopupsToRollup(rollupListener, &popupsToRollup)) {
 | |
|           return false;
 | |
|         }
 | |
|         // Can't use EventIsInsideWindow to check whether the event is inside
 | |
|         // the popup window. It's because EventIsInsideWindow gets message
 | |
|         // coordinates by GetMessagePos, which returns physical screen
 | |
|         // coordinates at WM_POINTERDOWN.
 | |
|         POINT pt;
 | |
|         pt.x = GET_X_LPARAM(aLParam);
 | |
|         pt.y = GET_Y_LPARAM(aLParam);
 | |
|         RECT r;
 | |
|         ::GetWindowRect(popupWindow->mWnd, &r);
 | |
|         if (::PtInRect(&r, pt) != 0) {
 | |
|           // Don't roll up if the event is inside the popup window.
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
|     case WM_MOUSEWHEEL:
 | |
|     case WM_MOUSEHWHEEL:
 | |
|       // We need to check if the popup thinks that it should cause closing
 | |
|       // itself when mouse wheel events are fired outside the rollup widget.
 | |
|       if (!EventIsInsideWindow(popupWindow)) {
 | |
|         // Check if we should consume this event even if we don't roll-up:
 | |
|         consumeRollupEvent =
 | |
|           rollupListener->ShouldConsumeOnMouseWheelEvent();
 | |
|         *aResult = MA_ACTIVATE;
 | |
|         if (rollupListener->ShouldRollupOnMouseWheelEvent() &&
 | |
|             GetPopupsToRollup(rollupListener, &popupsToRollup)) {
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|       return consumeRollupEvent;
 | |
| 
 | |
|     case WM_ACTIVATEAPP:
 | |
|       break;
 | |
| 
 | |
|     case WM_ACTIVATE:
 | |
|       // NOTE: Don't handle WA_INACTIVE for preventing popup taking focus
 | |
|       // because we cannot distinguish it's caused by mouse or not.
 | |
|       if (LOWORD(aWParam) == WA_ACTIVE && aLParam) {
 | |
|         nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
 | |
|         if (window && window->IsPopup()) {
 | |
|           // Cancel notifying widget listeners of deactivating the previous
 | |
|           // active window (see WM_KILLFOCUS case in ProcessMessage()).
 | |
|           sJustGotDeactivate = false;
 | |
|           // Reactivate the window later.
 | |
|           ::PostMessageW(aWnd, MOZ_WM_REACTIVATE, aWParam, aLParam);
 | |
|           return true;
 | |
|         }
 | |
|         // Don't rollup the popup when focus moves back to the parent window
 | |
|         // from a popup because such case is caused by strange mouse drivers.
 | |
|         nsWindow* prevWindow =
 | |
|           WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
 | |
|         if (prevWindow && prevWindow->IsPopup()) {
 | |
|           return false;
 | |
|         }
 | |
|       } else if (LOWORD(aWParam) == WA_INACTIVE) {
 | |
|         nsWindow* activeWindow =
 | |
|           WinUtils::GetNSWindowPtr(reinterpret_cast<HWND>(aLParam));
 | |
|         if (sPendingNCACTIVATE && NeedsToHandleNCActivateDelayed(aWnd)) {
 | |
|           // If focus moves to non-popup widget or focusable popup, the window
 | |
|           // needs to update its nonclient area.
 | |
|           if (!activeWindow || !activeWindow->IsPopup()) {
 | |
|             sSendingNCACTIVATE = true;
 | |
|             ::SendMessageW(aWnd, WM_NCACTIVATE, false, 0);
 | |
|             sSendingNCACTIVATE = false;
 | |
|           }
 | |
|           sPendingNCACTIVATE = false;
 | |
|         }
 | |
|         // If focus moves from/to popup, we don't need to rollup the popup
 | |
|         // because such case is caused by strange mouse drivers.
 | |
|         if (activeWindow) {
 | |
|           if (activeWindow->IsPopup()) {
 | |
|             return false;
 | |
|           }
 | |
|           nsWindow* deactiveWindow = WinUtils::GetNSWindowPtr(aWnd);
 | |
|           if (deactiveWindow && deactiveWindow->IsPopup()) {
 | |
|             return false;
 | |
|           }
 | |
|         }
 | |
|       } else if (LOWORD(aWParam) == WA_CLICKACTIVE) {
 | |
|         // If the WM_ACTIVATE message is caused by a click in a popup,
 | |
|         // we should not rollup any popups.
 | |
|         nsWindow* window = WinUtils::GetNSWindowPtr(aWnd);
 | |
|         if ((window && window->IsPopup()) ||
 | |
|             !GetPopupsToRollup(rollupListener, &popupsToRollup)) {
 | |
|           return false;
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case MOZ_WM_REACTIVATE:
 | |
|       // The previous active window should take back focus.
 | |
|       if (::IsWindow(reinterpret_cast<HWND>(aLParam))) {
 | |
|         ::SetForegroundWindow(reinterpret_cast<HWND>(aLParam));
 | |
|       }
 | |
|       return true;
 | |
| 
 | |
|     case WM_NCACTIVATE:
 | |
|       if (!aWParam && !sSendingNCACTIVATE &&
 | |
|           NeedsToHandleNCActivateDelayed(aWnd)) {
 | |
|         // Don't just consume WM_NCACTIVATE. It doesn't handle only the
 | |
|         // nonclient area state change.
 | |
|         ::DefWindowProcW(aWnd, aMessage, TRUE, aLParam);
 | |
|         // Accept the deactivating because it's necessary to receive following
 | |
|         // WM_ACTIVATE.
 | |
|         *aResult = TRUE;
 | |
|         sPendingNCACTIVATE = true;
 | |
|         return true;
 | |
|       }
 | |
|       return false;
 | |
| 
 | |
|     case WM_MOUSEACTIVATE:
 | |
|       if (!EventIsInsideWindow(popupWindow) &&
 | |
|           GetPopupsToRollup(rollupListener, &popupsToRollup)) {
 | |
|         // WM_MOUSEACTIVATE may be caused by moving the mouse (e.g., X-mouse
 | |
|         // of TweakUI is enabled. Then, check if the popup should be rolled up
 | |
|         // with rollup listener. If not, just consume the message.
 | |
|         if (HIWORD(aLParam) == WM_MOUSEMOVE &&
 | |
|             !rollupListener->ShouldRollupOnMouseActivate()) {
 | |
|           return true;
 | |
|         }
 | |
|         // Otherwise, it should be handled by wndproc.
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       // Prevent the click inside the popup from causing a change in window
 | |
|       // activation. Since the popup is shown non-activated, we need to eat any
 | |
|       // requests to activate the window while it is displayed. Windows will
 | |
|       // automatically activate the popup on the mousedown otherwise.
 | |
|       return true;
 | |
| 
 | |
|     case WM_SHOWWINDOW:
 | |
|       // If the window is being minimized, close popups.
 | |
|       if (aLParam == SW_PARENTCLOSING) {
 | |
|         break;
 | |
|       }
 | |
|       return false;
 | |
| 
 | |
|     case WM_KILLFOCUS:
 | |
|       // If focus moves to other window created in different process/thread,
 | |
|       // e.g., a plugin window, popups should be rolled up.
 | |
|       if (IsDifferentThreadWindow(reinterpret_cast<HWND>(aWParam))) {
 | |
|         break;
 | |
|       }
 | |
|       return false;
 | |
| 
 | |
|     case WM_MOVING:
 | |
|     case WM_MENUSELECT:
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| 
 | |
|   // Only need to deal with the last rollup for left mouse down events.
 | |
|   NS_ASSERTION(!nsAutoRollup::GetLastRollup(), "last rollup is null");
 | |
| 
 | |
|   if (nativeMessage == WM_TOUCH || nativeMessage == WM_LBUTTONDOWN || nativeMessage == WM_POINTERDOWN) {
 | |
|     nsIntPoint pos;
 | |
|     if (nativeMessage == WM_TOUCH) {
 | |
|       if (nsWindow* win = WinUtils::GetNSWindowPtr(aWnd)) {
 | |
|         pos = win->GetTouchCoordinates(aWParam, aLParam);
 | |
|       }
 | |
|     } else {
 | |
|       POINT pt;
 | |
|       pt.x = GET_X_LPARAM(aLParam);
 | |
|       pt.y = GET_Y_LPARAM(aLParam);
 | |
|       ::ClientToScreen(aWnd, &pt);
 | |
|       pos = nsIntPoint(pt.x, pt.y);
 | |
|     }
 | |
| 
 | |
|     nsIContent* lastRollup;
 | |
|     consumeRollupEvent =
 | |
|       rollupListener->Rollup(popupsToRollup, true, &pos, &lastRollup);
 | |
|     nsAutoRollup::SetLastRollup(lastRollup);
 | |
|   } else {
 | |
|     consumeRollupEvent =
 | |
|       rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
 | |
|   }
 | |
| 
 | |
|   // Tell hook to stop processing messages
 | |
|   sProcessHook = false;
 | |
|   sRollupMsgId = 0;
 | |
|   sRollupMsgWnd = nullptr;
 | |
| 
 | |
|   // If we are NOT supposed to be consuming events, let it go through
 | |
|   if (consumeRollupEvent && nativeMessage != WM_RBUTTONDOWN) {
 | |
|     *aResult = MA_ACTIVATE;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /**************************************************************
 | |
|  **************************************************************
 | |
|  **
 | |
|  ** BLOCK: Misc. utility methods and functions.
 | |
|  **
 | |
|  ** General use.
 | |
|  **
 | |
|  **************************************************************
 | |
|  **************************************************************/
 | |
| 
 | |
| // Note that the result of GetTopLevelWindow method can be different from the
 | |
| // result of WinUtils::GetTopLevelHWND().  The result can be non-floating
 | |
| // window.  Because our top level window may be contained in another window
 | |
| // which is not managed by us.
 | |
| nsWindow* nsWindow::GetTopLevelWindow(bool aStopOnDialogOrPopup)
 | |
| {
 | |
|   nsWindow* curWindow = this;
 | |
| 
 | |
|   while (true) {
 | |
|     if (aStopOnDialogOrPopup) {
 | |
|       switch (curWindow->mWindowType) {
 | |
|         case eWindowType_dialog:
 | |
|         case eWindowType_popup:
 | |
|           return curWindow;
 | |
|         default:
 | |
|           break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Retrieve the top level parent or owner window
 | |
|     nsWindow* parentWindow = curWindow->GetParentWindow(true);
 | |
| 
 | |
|     if (!parentWindow)
 | |
|       return curWindow;
 | |
| 
 | |
|     curWindow = parentWindow;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static BOOL CALLBACK gEnumWindowsProc(HWND hwnd, LPARAM lParam)
 | |
| {
 | |
|   DWORD pid;
 | |
|   ::GetWindowThreadProcessId(hwnd, &pid);
 | |
|   if (pid == GetCurrentProcessId() && ::IsWindowVisible(hwnd))
 | |
|   {
 | |
|     gWindowsVisible = true;
 | |
|     return FALSE;
 | |
|   }
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| bool nsWindow::CanTakeFocus()
 | |
| {
 | |
|   gWindowsVisible = false;
 | |
|   EnumWindows(gEnumWindowsProc, 0);
 | |
|   if (!gWindowsVisible) {
 | |
|     return true;
 | |
|   } else {
 | |
|     HWND fgWnd = ::GetForegroundWindow();
 | |
|     if (!fgWnd) {
 | |
|       return true;
 | |
|     }
 | |
|     DWORD pid;
 | |
|     GetWindowThreadProcessId(fgWnd, &pid);
 | |
|     if (pid == GetCurrentProcessId()) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /* static */ const wchar_t*
 | |
| nsWindow::GetMainWindowClass()
 | |
| {
 | |
|   static const wchar_t* sMainWindowClass = nullptr;
 | |
|   if (!sMainWindowClass) {
 | |
|     nsAutoString className;
 | |
|     Preferences::GetString("ui.window_class_override", className);
 | |
|     if (!className.IsEmpty()) {
 | |
|       sMainWindowClass = wcsdup(className.get());
 | |
|     } else {
 | |
|       sMainWindowClass = kClassNameGeneral;
 | |
|     }
 | |
|   }
 | |
|   return sMainWindowClass;
 | |
| }
 | |
| 
 | |
| LPARAM nsWindow::lParamToScreen(LPARAM lParam)
 | |
| {
 | |
|   POINT pt;
 | |
|   pt.x = GET_X_LPARAM(lParam);
 | |
|   pt.y = GET_Y_LPARAM(lParam);
 | |
|   ::ClientToScreen(mWnd, &pt);
 | |
|   return MAKELPARAM(pt.x, pt.y);
 | |
| }
 | |
| 
 | |
| LPARAM nsWindow::lParamToClient(LPARAM lParam)
 | |
| {
 | |
|   POINT pt;
 | |
|   pt.x = GET_X_LPARAM(lParam);
 | |
|   pt.y = GET_Y_LPARAM(lParam);
 | |
|   ::ScreenToClient(mWnd, &pt);
 | |
|   return MAKELPARAM(pt.x, pt.y);
 | |
| }
 | |
| 
 | |
| void nsWindow::PickerOpen()
 | |
| {
 | |
|   mPickerDisplayCount++;
 | |
| }
 | |
| 
 | |
| void nsWindow::PickerClosed()
 | |
| {
 | |
|   NS_ASSERTION(mPickerDisplayCount > 0, "mPickerDisplayCount out of sync!");
 | |
|   if (!mPickerDisplayCount)
 | |
|     return;
 | |
|   mPickerDisplayCount--;
 | |
|   if (!mPickerDisplayCount && mDestroyCalled) {
 | |
|     Destroy();
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::WidgetTypeSupportsAcceleration()
 | |
| {
 | |
|   // We don't currently support using an accelerated layer manager with
 | |
|   // transparent windows so don't even try. I'm also not sure if we even
 | |
|   // want to support this case. See bug 593471.
 | |
|   //
 | |
|   // Also see bug 1150376, D3D11 composition can cause issues on some devices
 | |
|   // on Windows 7 where presentation fails randomly for windows with drop
 | |
|   // shadows.
 | |
|   return mTransparencyMode != eTransparencyTransparent &&
 | |
|          !(IsPopup() && DeviceManagerDx::Get()->IsWARP());
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::SetCandidateWindowForPlugin(const CandidateWindowPosition& aPosition)
 | |
| {
 | |
|   CANDIDATEFORM form;
 | |
|   form.dwIndex = 0;
 | |
|   if (aPosition.mExcludeRect) {
 | |
|     form.dwStyle = CFS_EXCLUDE;
 | |
|     form.rcArea.left = aPosition.mRect.X();
 | |
|     form.rcArea.top = aPosition.mRect.Y();
 | |
|     form.rcArea.right = aPosition.mRect.XMost();
 | |
|     form.rcArea.bottom = aPosition.mRect.YMost();
 | |
|   } else {
 | |
|     form.dwStyle = CFS_CANDIDATEPOS;
 | |
|   }
 | |
|   form.ptCurrentPos.x = aPosition.mPoint.x;
 | |
|   form.ptCurrentPos.y = aPosition.mPoint.y;
 | |
| 
 | |
|   IMEHandler::SetCandidateWindow(this, &form);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::DefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
 | |
| {
 | |
|   const NPEvent* pPluginEvent =
 | |
|    static_cast<const NPEvent*>(aEvent.mPluginEvent);
 | |
| 
 | |
|   if (NS_WARN_IF(!pPluginEvent)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!mWnd) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // For WM_IME_*COMPOSITION
 | |
|   IMEHandler::DefaultProcOfPluginEvent(this, pPluginEvent);
 | |
| 
 | |
|   CallWindowProcW(GetPrevWindowProc(), mWnd, pPluginEvent->event,
 | |
|                   pPluginEvent->wParam, pPluginEvent->lParam);
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::EnableIMEForPlugin(bool aEnable)
 | |
| {
 | |
|   // Current IME state isn't plugin, ignore this call
 | |
|   if (NS_WARN_IF(mInputContext.mIMEState.mEnabled != IMEState::PLUGIN)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   InputContext inputContext = GetInputContext();
 | |
|   if (aEnable) {
 | |
|     inputContext.mHTMLInputType.AssignLiteral("text");
 | |
|   } else {
 | |
|     inputContext.mHTMLInputType.AssignLiteral("password");
 | |
|   }
 | |
|   SetInputContext(inputContext, InputContextAction());
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsWindow::OnWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
 | |
|                                    nsIKeyEventInPluginCallback* aCallback)
 | |
| {
 | |
|   if (NS_WARN_IF(!mWnd)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
|   const WinNativeKeyEventData* eventData =
 | |
|     static_cast<const WinNativeKeyEventData*>(aKeyEventData);
 | |
|   switch (eventData->mMessage) {
 | |
|     case WM_KEYDOWN:
 | |
|     case WM_SYSKEYDOWN: {
 | |
|       MSG mozMsg =
 | |
|         WinUtils::InitMSG(MOZ_WM_KEYDOWN, eventData->mWParam,
 | |
|                           eventData->mLParam, mWnd);
 | |
|       ModifierKeyState modifierKeyState(eventData->mModifiers);
 | |
|       NativeKey nativeKey(this, mozMsg, modifierKeyState,
 | |
|                           eventData->GetKeyboardLayout());
 | |
|       return nativeKey.HandleKeyDownMessage() ? NS_SUCCESS_EVENT_CONSUMED :
 | |
|                                                 NS_OK;
 | |
|     }
 | |
|     case WM_KEYUP:
 | |
|     case WM_SYSKEYUP: {
 | |
|       MSG mozMsg =
 | |
|         WinUtils::InitMSG(MOZ_WM_KEYUP, eventData->mWParam,
 | |
|                           eventData->mLParam, mWnd);
 | |
|       ModifierKeyState modifierKeyState(eventData->mModifiers);
 | |
|       NativeKey nativeKey(this, mozMsg, modifierKeyState,
 | |
|                           eventData->GetKeyboardLayout());
 | |
|       return nativeKey.HandleKeyUpMessage() ? NS_SUCCESS_EVENT_CONSUMED : NS_OK;
 | |
|     }
 | |
|     default:
 | |
|       // We shouldn't consume WM_*CHAR messages here even if the preceding
 | |
|       // keydown or keyup event on the plugin is consumed.  It should be
 | |
|       // managed in each plugin window rather than top level window.
 | |
|       return NS_OK;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsWindow::OnPointerEvents(UINT msg, WPARAM aWParam, LPARAM aLParam)
 | |
| {
 | |
|   if (!mPointerEvents.ShouldHandleWinPointerMessages(msg, aWParam)) {
 | |
|     return false;
 | |
|   }
 | |
|   if (!mPointerEvents.ShouldFirePointerEventByWinPointerMessages()) {
 | |
|     // We have to handle WM_POINTER* to fetch and cache pen related information
 | |
|     // and fire WidgetMouseEvent with the cached information the WM_*BUTTONDOWN
 | |
|     // handler. This is because Windows doesn't support ::DoDragDrop in the touch
 | |
|     // or pen message handlers.
 | |
|     mPointerEvents.ConvertAndCachePointerInfo(msg, aWParam);
 | |
|     // Don't consume the Windows WM_POINTER* messages
 | |
|     return false;
 | |
|   }
 | |
|   // When dispatching mouse events with pen, there may be some
 | |
|   // WM_POINTERUPDATE messages between WM_POINTERDOWN and WM_POINTERUP with
 | |
|   // small movements. Those events will reset sLastMousePoint and reset
 | |
|   // sLastClickCount. To prevent that, we keep the last pen down position
 | |
|   // and compare it with the subsequent WM_POINTERUPDATE. If the movement is
 | |
|   // smaller than GetSystemMetrics(SM_CXDRAG), then we suppress firing
 | |
|   // eMouseMove for WM_POINTERUPDATE.
 | |
|   static POINT sLastPointerDownPoint = {0};
 | |
| 
 | |
|   // We don't support chorded buttons for pen. Keep the button at
 | |
|   // WM_POINTERDOWN.
 | |
|   static WidgetMouseEvent::buttonType sLastPenDownButton =
 | |
|     WidgetMouseEvent::eLeftButton;
 | |
|   static bool sPointerDown = false;
 | |
| 
 | |
|   EventMessage message;
 | |
|   WidgetMouseEvent::buttonType button = WidgetMouseEvent::eLeftButton;
 | |
|   switch (msg) {
 | |
|   case WM_POINTERDOWN:
 | |
|     {
 | |
|       LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
 | |
|                                       GET_Y_LPARAM(aLParam));
 | |
|       sLastPointerDownPoint.x = eventPoint.x;
 | |
|       sLastPointerDownPoint.y = eventPoint.y;
 | |
|       message = eMouseDown;
 | |
|       button = IS_POINTER_SECONDBUTTON_WPARAM(aWParam) ?
 | |
|                  WidgetMouseEvent::eRightButton : WidgetMouseEvent::eLeftButton;
 | |
|       sLastPenDownButton = button;
 | |
|       sPointerDown = true;
 | |
|     }
 | |
|     break;
 | |
|   case WM_POINTERUP:
 | |
|     message = eMouseUp;
 | |
|     MOZ_ASSERT(sPointerDown, "receive WM_POINTERUP w/o WM_POINTERDOWN");
 | |
|     button = sPointerDown ? sLastPenDownButton : WidgetMouseEvent::eLeftButton;
 | |
|     sPointerDown = false;
 | |
|     break;
 | |
|   case WM_POINTERUPDATE:
 | |
|     message = eMouseMove;
 | |
|     if (sPointerDown) {
 | |
|       LayoutDeviceIntPoint eventPoint(GET_X_LPARAM(aLParam),
 | |
|                                       GET_Y_LPARAM(aLParam));
 | |
|       int32_t movementX = sLastPointerDownPoint.x > eventPoint.x ?
 | |
|                             sLastPointerDownPoint.x - eventPoint.x :
 | |
|                             eventPoint.x - sLastPointerDownPoint.x;
 | |
|       int32_t movementY = sLastPointerDownPoint.y > eventPoint.y ?
 | |
|                             sLastPointerDownPoint.y - eventPoint.y :
 | |
|                             eventPoint.y - sLastPointerDownPoint.y;
 | |
|       bool insideMovementThreshold =
 | |
|         movementX < (int32_t)::GetSystemMetrics(SM_CXDRAG) &&
 | |
|         movementY < (int32_t)::GetSystemMetrics(SM_CYDRAG);
 | |
| 
 | |
|       if (insideMovementThreshold) {
 | |
|         // Suppress firing eMouseMove for WM_POINTERUPDATE if the movement
 | |
|         // from last WM_POINTERDOWN is smaller than SM_CXDRAG / SM_CYDRAG
 | |
|         return false;
 | |
|       }
 | |
|       button = sLastPenDownButton;
 | |
|     }
 | |
|     break;
 | |
|   case WM_POINTERLEAVE:
 | |
|     message = eMouseExitFromWidget;
 | |
|     break;
 | |
|   default:
 | |
|     return false;
 | |
|   }
 | |
|   uint32_t pointerId = mPointerEvents.GetPointerId(aWParam);
 | |
|   POINTER_PEN_INFO penInfo;
 | |
|   mPointerEvents.GetPointerPenInfo(pointerId, &penInfo);
 | |
| 
 | |
|   // Windows defines the pen pressure is normalized to a range between 0 and
 | |
|   // 1024. Convert it to float.
 | |
|   float pressure = penInfo.pressure ? (float)penInfo.pressure / 1024 : 0;
 | |
|   int16_t buttons =
 | |
|     sPointerDown ? button == WidgetMouseEvent::eLeftButton ?
 | |
|                      WidgetMouseEvent::eLeftButtonFlag :
 | |
|                      WidgetMouseEvent::eRightButtonFlag :
 | |
|                    WidgetMouseEvent::eNoButtonFlag;
 | |
|   WinPointerInfo pointerInfo(pointerId, penInfo.tiltX, penInfo.tiltY, pressure,
 | |
|                              buttons);
 | |
| 
 | |
|   // The aLParam of WM_POINTER* is the screen location. Convert it to client
 | |
|   // location
 | |
|   LPARAM newLParam = lParamToClient(aLParam);
 | |
|   DispatchMouseEvent(message, aWParam, newLParam, false, button,
 | |
|                      MouseEvent_Binding::MOZ_SOURCE_PEN, &pointerInfo);
 | |
|   // Consume WM_POINTER* to stop Windows fires WM_*BUTTONDOWN / WM_*BUTTONUP
 | |
|   // WM_MOUSEMOVE.
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| void
 | |
| nsWindow::GetCompositorWidgetInitData(mozilla::widget::CompositorWidgetInitData* aInitData)
 | |
| {
 | |
|   *aInitData = WinCompositorWidgetInitData(
 | |
|       reinterpret_cast<uintptr_t>(mWnd),
 | |
|       reinterpret_cast<uintptr_t>(static_cast<nsIWidget*>(this)),
 | |
|       mTransparencyMode);
 | |
| }
 | |
| 
 | |
| bool
 | |
| nsWindow::SynchronouslyRepaintOnResize()
 | |
| {
 | |
|   return !gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
 | |
| }
 |