Source code

Revision control

Other Tools

/* -*- 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 "mozilla/AppShutdown.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/TimeStamp.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 <dbt.h>
#include <unknwn.h>
#include <psapi.h>
#include <rpc.h>
#include "mozilla/Logging.h"
#include "prtime.h"
#include "prenv.h"
#include "mozilla/WidgetTraceEvent.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 "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 "nsWindowGfx.h"
#include "gfxWindowsPlatform.h"
#include "gfxDWriteFonts.h"
#include "Layers.h"
#include "nsPrintfCString.h"
#include "mozilla/Preferences.h"
#include "SystemTimeConverter.h"
#include "WinTaskbar.h"
#include "WidgetUtils.h"
#include "WinContentSystemParameters.h"
#include "nsIWidgetListener.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/GPUProcessManager.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 "nsStyleConsts.h"
#include "nsBidiKeyboard.h"
#include "nsStyleConsts.h"
#include "gfxConfig.h"
#include "InProcessWinCompositorWidget.h"
#include "InputDeviceUtils.h"
#include "ScreenHelperWin.h"
#include "mozilla/StaticPrefs_layout.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/PresShell.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 "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"
#include "mozilla/layers/IAPZCTreeManager.h"
#include "DirectManipulationOwner.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;
bool nsWindow::sIsRestoringSession = 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;
// 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;
// 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;
mLastKillFocusWindow = nullptr;
mTransitionWnd = nullptr;
mPaintDC = nullptr;
mPrevWndProc = nullptr;
mNativeDragTarget = nullptr;
mDeviceNotifyHandle = nullptr;
mInDtor = false;
mIsVisible = false;
mIsTopWidgetWindow = false;
mUnicodeWidget = true;
mDisplayPanFeedback = false;
mTouchWindow = false;
mFutureMarginsToUse = false;
mCustomNonClient = false;
mHideChrome = false;
mFullscreenMode = false;
mMousePresent = false;
mMouseInDraggableArea = false;
mDestroyCalled = false;
mIsEarlyBlankWindow = false;
mResizable = 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;
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
mAspectRatio = 0.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();
}
} // !sInstanceCount
mIdleService = nullptr;
mSizeConstraintsScale = GetDefaultScale().scale;
mMaxTextureSize = -1; // Will be calculated when layer manager is created.
mRequestFxrOutputPending = false;
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);
}
void nsWindow::SendAnAPZEvent(InputData& aEvent) {
RefPtr<nsWindow> strongThis(this);
if (::IsWindowVisible(mWnd)) {
nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener();
if (rollupListener) {
nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget();
if (popup) {
uint32_t popupsToRollup = UINT32_MAX;
rollupListener->Rollup(popupsToRollup, true, nullptr, nullptr);
}
}
}
APZEventResult result;
if (mAPZC) {
result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
}
if (result.mStatus == nsEventStatus_eConsumeNoDefault) {
return;
}
MOZ_ASSERT(aEvent.mInputType == PANGESTURE_INPUT ||
aEvent.mInputType == PINCHGESTURE_INPUT);
if (aEvent.mInputType == PANGESTURE_INPUT) {
PanGestureInput& panInput = aEvent.AsPanGestureInput();
WidgetWheelEvent event = panInput.ToWidgetWheelEvent(this);
ProcessUntransformedAPZEvent(&event, result);
return;
}
PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
WidgetWheelEvent event = pinchInput.ToWidgetWheelEvent(this);
ProcessUntransformedAPZEvent(&event, result);
}
void nsWindow::RecreateDirectManipulationIfNeeded() {
DestroyDirectManipulation();
if (mWindowType != eWindowType_toplevel && mWindowType != eWindowType_popup) {
return;
}
if (!StaticPrefs::apz_windows_use_direct_manipulation() ||
StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
return;
}
if (!IsWin10OrLater()) {
// Chrome source said the Windows Direct Manipulation implementation had
// important bugs until Windows 10 (although IE on Windows 8.1 seems to use
// Direct Manipulation).
return;
}
mDmOwner = MakeUnique<DirectManipulationOwner>(this);
LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
GetHeight(mBounds.Height()));
mDmOwner->Init(bounds);
}
void nsWindow::ResizeDirectManipulationViewport() {
if (mDmOwner) {
LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
GetHeight(mBounds.Height()));
mDmOwner->ResizeViewport(bounds);
}
}
void nsWindow::DestroyDirectManipulation() {
if (mDmOwner) {
mDmOwner->Destroy();
mDmOwner.reset();
}
}
// 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;
mAlwaysOnTop = aInitData->mAlwaysOnTop;
mResizable = aInitData->mResizable;
DWORD style = WindowStyle();
DWORD extendedStyle = WindowExStyle();
// When window is PiP window on Windows7, WS_EX_COMPOSITED is set to suppress
// flickering during resizing with hardware acceleration.
bool isPIPWindow = aInitData && aInitData->mPIPWindow;
if (isPIPWindow && !IsWin8OrLater() &&
gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) &&
WidgetTypeSupportsAcceleration()) {
extendedStyle |= WS_EX_COMPOSITED;
}
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;
}
mDeviceNotifyHandle = InputDeviceUtils::RegisterNotification(mWnd);
// 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 (mAlwaysOnTop) {
::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
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);
}
}
RecreateDirectManipulationIfNeeded();
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);
DestroyDirectManipulation();
/**
* 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();
InputDeviceUtils::UnregisterNotification(mDeviceNotifyHandle);
mDeviceNotifyHandle = nullptr;
// 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));
RecreateDirectManipulationIfNeeded();
}
}
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);
RecreateDirectManipulationIfNeeded();
}
}
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 &&
!gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled()) {
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, nullptr, 0, 0);
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() && !mAlwaysOnTop) {
::ShowWindow(mWnd, SW_SHOWNORMAL);
} else {
::ShowWindow(mWnd, SW_SHOWNOACTIVATE);
// Don't flicker the window if we're restoring session
if (!sIsRestoringSession) {
Unused << GetAttention(2);
}
}
break;
}
} else {
DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
if (wasVisible) flags |= SWP_NOZORDER;
if (mAlwaysOnTop) flags |= SWP_NOACTIVATE;
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;
}
void nsWindow::LockAspectRatio(bool aShouldLock) {
if (aShouldLock) {
mAspectRatio = (float)mBounds.Height() / (float)mBounds.Width();
} else {
mAspectRatio = 0.0;
}
}
/**************************************************************
*
* SECTION: nsIWidget::SetWindowMouseTransparent
*
* Sets whether the window should ignore mouse events.
*
**************************************************************/
void nsWindow::SetWindowMouseTransparent(bool aIsTransparent) {
if (!mWnd) {
return;
}
LONG_PTR oldStyle = ::GetWindowLongPtrW(mWnd, GWL_EXSTYLE);
LONG_PTR newStyle = aIsTransparent ? (oldStyle | WS_EX_TRANSPARENT)
: (oldStyle & ~WS_EX_TRANSPARENT);
::SetWindowLongPtrW(mWnd, GWL_EXSTYLE, newStyle);
mMouseTransparent = aIsTransparent;
}
/**************************************************************
*
* 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 && mResizable) {
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);
}
if (mMaxTextureSize > 0) {
// 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, mMaxTextureSize);
c.mMaxSize.height = std::min(c.mMaxSize.height, mMaxTextureSize);
}
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();
ResizeDirectManipulationViewport();
}
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;
mResizeState = RESIZING;
VERIFY(
::SetWindowPos(mWnd, nullptr, 0, 0, width, GetHeight(height), flags));
mResizeState = NOT_RESIZING;
if (WinUtils::LogToPhysFactor(mWnd) != oldScale) {
ChangedDPI();
}
SetThemeRegion();
ResizeDirectManipulationViewport();
}
if (aRepaint) Invalidate();
NotifyRollupGeometryChange();
}