widget/windows/nsWindow.cpp
author Kartikaya Gupta <kgupta@mozilla.com>
Fri, 24 Feb 2017 08:32:00 -0500
changeset 345170 174aaab139dc326fed3f584a3d427508c7801c40
parent 344784 2b2b65a1b476e98f610299100e97cd401e9b46c0
child 346513 f3b22a31ea36bbf4d620c0dcdf7f6b831ea7b24b
permissions -rw-r--r--
Bug 1341691 - Improve handling of touch events when dismissing popups. r=jimm When user input is used to dismiss popups, the user input may sometimes be consumed (for example, clicking on a combobox element will dismiss the combobox popup, but consume the event to avoid reopening the popup right away). This handling was present for some types of input events but missing for touch events. This patch adds it for touch events as well. MozReview-Commit-ID: 5wsuTdMbkX2

/* -*- 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/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 <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 "prmem.h"
#include "prenv.h"

#include "mozilla/WidgetTraceEvent.h"
#include "nsIAppShell.h"
#include "nsISupportsPrimitives.h"
#include "nsIDOMMouseEvent.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 "nsXPIDLString.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/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/WinNativeEventData.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "nsThemeConstants.h"
#include "nsBidiKeyboard.h"
#include "nsThemeConstants.h"
#include "gfxConfig.h"
#include "InProcessWinCompositorWidget.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/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/HangMonitor.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/APZCTreeManager.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/KnowsCompositor.h"
#include "mozilla/layers/ScrollInputMethods.h"
#include "InputData.h"

#include "mozilla/Telemetry.h"
#include "mozilla/plugins/PluginProcessParent.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;

WPARAM nsWindow::sMouseExitwParam = 0;
LPARAM nsWindow::sMouseExitlParamScreen = 0;

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 = sTipTsfInterceptor.AddHook("ProcessCaretEvents",
          reinterpret_cast<intptr_t>(&ProcessCaretEventsHook),
          (void**) &sProcessCaretEventsStub);
      MOZ_ASSERT(ok);
    }

    if (!sSendMessageTimeoutWStub) {
      sUser32Intercept.Init("user32.dll");
      DebugOnly<bool> hooked = sUser32Intercept.AddHook("SendMessageTimeoutW",
          reinterpret_cast<intptr_t>(&SendMessageTimeoutWHook),
          (void**) &sSendMessageTimeoutWStub);
      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 WINEVENTPROC sProcessCaretEventsStub;
  static decltype(&SendMessageTimeoutW) sSendMessageTimeoutWStub;
  static StaticAutoPtr<TIPMessageHandler> sInstance;

  HHOOK                 mHook;
  UINT                  mMessages[7];
  uint32_t              mA11yBlockCount;
};

WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
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()
  : nsWindowBase()
  , mResizeState(NOT_RESIZING)
{
  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;
  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;

  // 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)
    IMEHandler::Initialize();
    if (SUCCEEDED(::OleInitialize(nullptr))) {
      sIsOleInitialized = TRUE;
    }
    NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
    MouseScrollHandler::Initialize();
    // Init titlebar button info for custom frames.
    nsUXThemeData::InitTitlebarInfo();
    // 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);
}

NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)

/**************************************************************
 *
 * 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()) {
      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 (mIsRTL) {
    DWORD dwAttribute = TRUE;    
    DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute, sizeof dwAttribute);
  }

  if (mOpeningAnimationSuppressed) {
    DWORD dwAttribute = TRUE;
    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
                          &dwAttribute, sizeof dwAttribute);
  }

  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);
  }

  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;
      }
    }
  }

  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)
{
  mParent = aNewParent;

  nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
  nsIWidget* parent = GetParent();
  if (parent) {
    parent->RemoveChild(this);
  }
  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)
{
  NS_PRECONDITION(aNewParent, "");

  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()
{
  HDC dc = ::GetDC(mWnd);
  if (!dc)
    return 96.0f;

  double heightInches = ::GetDeviceCaps(dc, VERTSIZE)/MM_PER_INCH_FLOAT;
  int heightPx = ::GetDeviceCaps(dc, VERTRES);
  ::ReleaseDC(mWnd, dc);
  if (heightInches < 0.25) {
    // Something's broken
    return 96.0f;
  }
  return float(heightPx/heightInches);
}

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;
        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) {
    DWORD dwAttribute = FALSE;
    DwmSetWindowAttribute(mWnd, DWMWA_TRANSITIONS_FORCEDISABLED,
                          &dwAttribute, sizeof dwAttribute);
  }
}

/**************************************************************
 *
 * 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;
  mGesture.RegisterTouchWindow(mWnd);
  ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
}

BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
  nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
  if (win)
    win->mGesture.RegisterTouchWindow(aWnd);
  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.x == x) && (mBounds.y == y))
  {
    // Nothing to do, since it is already positioned correctly.
    return;
  }

  mBounds.x = x;
  mBounds.y = 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 || mLayerManager->GetBackendType() == LayersBackend::LAYERS_D3D9) &&
        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.width == width && mBounds.height == height) {
    if (aRepaint) {
      Invalidate();
    }
    return;
  }

  // Set cached value for lightweight and printing
  mBounds.width  = width;
  mBounds.height = 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.x == x && mBounds.y == y &&
      mBounds.width == width && mBounds.height == height) {
    if (aRepaint) {
      Invalidate();
    }
    return;
  }

  // Set cached value for lightweight and printing
  mBounds.x      = x;
  mBounds.y      = y;
  mBounds.width  = width;
  mBounds.height = 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);
      // Every transition window is only used once.
      mTransitionWnd = nullptr;
    }
    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);
  }
}

// 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.width  = r.right - r.left;
  rect.height = r.bottom - r.top;

  // popup window bounds' are in screen coordinates, not relative to parent
  // window
  if (mWindowType == eWindowType_popup) {
    rect.x = r.left;
    rect.y = 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.x = r.left;
  rect.y = 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.width  = r.right - r.left;
  rect.height = 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));

  LayoutDeviceIntRect rect;
  rect.x = r.left;
  rect.y = r.top;
  rect.width  = r.right - r.left;
  rect.height = r.bottom - r.top;
  return rect;
}

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);
  nsIntRect evRect(WinUtils::ToIntRect(clientRc));
  OnResize(evRect);

  // 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 GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;

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;

  if (!sGetWindowInfoPtrStub) {
    sUser32Intercept.Init("user32.dll");
    if (!sUser32Intercept.AddHook("GetWindowInfo", reinterpret_cast<intptr_t>(GetWindowInfoHook),
                                  (void**) &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 = nsCursor(-1);
  ::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 top-level window
 * containing this widget.
 *
 **************************************************************/

#ifdef MOZ_XUL
nsTransparencyMode nsWindow::GetTransparencyMode()
{
  return GetTopLevelWindow(true)->GetWindowTranslucencyInner();
}

void nsWindow::SetTransparencyMode(nsTransparencyMode aMode)
{
  GetTopLevelWindow(true)->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.y = std::max<uint32_t>(largest.y,
                         nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].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.x + aRect.width;
    rect.bottom = aRect.y + aRect.height;

    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.x = NSToIntRound(x * scale);
  initData.mBounds.y = NSToIntRound(y * scale);
  initData.mBounds.width = NSToIntRound(width * scale);
  initData.mBounds.height = 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);
}

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);
  }

  // Send a eMouseEnterIntoWidget event since Windows has already sent
  // a WM_MOUSELEAVE that caused us to send a eMouseExitFromWidget event.
  if (aFullScreen && !sCurrentWindow) {
    sCurrentWindow = this;
    LPARAM pos = sCurrentWindow->lParamToClient(sMouseExitlParamScreen);
    sCurrentWindow->DispatchMouseEvent(eMouseEnterIntoWidget,
                                       sMouseExitwParam, pos, false,
                                       WidgetMouseEvent::eLeftButton,
                                       MOUSE_INPUT_SOURCE());
  }

  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)
{
  NS_ASSERTION(mWnd, "nsWindow::EnableDragDrop() called after Destroy()");

  nsresult rv = NS_ERROR_FAILURE;
  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.
    CompositorWidgetInitData 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: 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));
  }

  if (!IsWin10OrLater()) {
    for (size_t i = 0; i < aThemeGeometries.Length(); i++) {
      if (aThemeGeometries[i].mType == nsNativeThemeWin::eThemeGeometryTypeWindowButtons) {
        LayoutDeviceIntRect bounds = aThemeGeometries[i].mRect;
        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() - 1.0, 1.0));
        clearRegion.Or(clearRegion, gfx::IntRect::Truncate(bounds.X() + 2.0, bounds.YMost() - 1.0, bounds.Width() - 3.0, 1.0));
      }
    }
  }

  layerManager->SetRegionToClear(clearRegion);
}

uint32_t
nsWindow::GetMaxTouchPoints() const
{
  return WinUtils::GetMaxTouchPoints();
}

/**************************************************************
 **************************************************************
 **
 ** 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;
}

// 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)
{
  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);
    }

    if (mTouchWindow) {
      // If mTouchWindow is true, then we must have APZ enabled and be
      // feeding it raw touch events. In that case we don't need to
      // send touch-generated mouse events to content. The only exception is
      // the touch-generated mouse double-click, which is used to start off the
      // touch-based drag-and-drop gesture.
      MOZ_ASSERT(mAPZC);
      if (aEventMessage == eMouseDoubleClick) {
        aEventMessage = eMouseTouchDrag;
      } else {
        return result;
      }
    }
  }

  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 (nsIDOMMouseEvent::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);
  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.x = 0;
      rect.y = 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) {
      sMouseExitwParam = wParam;
      sMouseExitlParamScreen = lParamToScreen(lParam);
      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();
        }
      }
    }
  }
}

bool nsWindow::IsTopLevelMouseExit(HWND aWnd)
{
  DWORD pos = ::GetMessagePos();
  POINT mp;
  mp.x = GET_X_LPARAM(pos);
  mp.y = GET_Y_LPARAM(pos);
  HWND mouseWnd = ::WindowFromPoint(mp);

  // 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;
}

inline static mozilla::HangMonitor::ActivityType ActivityTypeForMessage(UINT msg)
{
  if ((msg >= WM_KEYFIRST && msg <= WM_IME_KEYLAST) ||
      (msg >= WM_MOUSEFIRST && msg <= WM_MOUSELAST) ||
      (msg >= MOZ_WM_MOUSEWHEEL_FIRST && msg <= MOZ_WM_MOUSEWHEEL_LAST) ||
      (msg >= NS_WM_IMEFIRST && msg <= NS_WM_IMELAST)) {
    return mozilla::HangMonitor::kUIActivity;
  }

  // This may not actually be right, but we don't want to reset the timer if
  // we're not actually processing a UI message.
  return mozilla::HangMonitor::kActivityUIAVail;
}

// 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();

  HangMonitor::NotifyActivity(ActivityTypeForMessage(msg));

  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;
}

// 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 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);
        obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);

        bool abortQuit;
        cancelQuit->GetData(&abortQuit);
        sCanQuit = abortQuit ? TRI_FALSE : TRI_TRUE;
      }
      *aRetValue = sCanQuit ? TRUE : FALSE;
      result = true;
      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();
        NS_NAMED_LITERAL_STRING(context, "shutdown-persist");
        NS_NAMED_LITERAL_STRING(syncShutdown, "syncShutdown");
        obsServ->NotifyObservers(nullptr, "quit-application-granted", syncShutdown.get());
        obsServ->NotifyObservers(nullptr, "quit-application-forced", nullptr);
        obsServ->NotifyObservers(nullptr, "quit-application", nullptr);
        obsServ->NotifyObservers(nullptr, "profile-change-net-teardown", context.get());
        obsServ->NotifyObservers(nullptr, "profile-change-teardown", context.get());
        obsServ->NotifyObservers(nullptr, "profile-before-change", context.get());
        obsServ->NotifyObservers(nullptr, "profile-before-change-qm", context.get());
        obsServ->NotifyObservers(nullptr, "profile-before-change-telemetry", context.get());
        // Then a controlled but very quick exit.
        _exit(0);
      }
      sCanQuit = TRI_UNKNOWN;
      result = true;
      break;

    case WM_SYSCOLORCHANGE:
      OnSysColorChanged();
      break;

    case WM_THEMECHANGED:
    {
      // Update non-client margin offsets 
      UpdateNonClientMargins();
      nsUXThemeData::InitTitlebarInfo();
      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 (IsWin10OrLater() && mWindowType == eWindowType_invisible && lParam) {
        auto lParamString = reinterpret_cast<const wchar_t*>(lParam);
        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);

        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(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.
      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());
      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());
      DispatchPendingEvents();
    }
    break;

    case WM_LBUTTONUP:
    {
      result = DispatchMouseEvent(eMouseUp, wParam, lParam,
                                  false, WidgetMouseEvent::eLeftButton,
                                  MOUSE_INPUT_SOURCE());
      DispatchPendingEvents();
    }
    break;

    case WM_MOUSELEAVE:
    {
      if (!mMousePresent)
        break;
      mMousePresent = false;

      // 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,
                           nsIDOMMouseEvent::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() == nsIDOMMouseEvent::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());
      DispatchPendingEvents();
      break;

    case WM_RBUTTONUP:
      result = DispatchMouseEvent(eMouseUp, wParam,
                                  lParam, false,
                                  WidgetMouseEvent::eRightButton,
                                  MOUSE_INPUT_SOURCE());
      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_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
        a11y::Accessible* rootAccessible = GetAccessible(); // Held by a11y cache
        if (rootAccessible) {
          IAccessible *msaaAccessible = nullptr;
          rootAccessible->GetNativeInterface((void**)&msaaAccessible); // does an addref
          if (msaaAccessible) {
            *aRetValue = LresultFromObject(IID_IAccessible, wParam, msaaAccessible); // does an addref
            msaaAccessible->Release(); // release extra addref
            result = true;  // We handled the WM_GETOBJECT message
          }
        }
      }
    }
    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);
  return static_cast<LRESULT>(nativeKey.HandleKeyUpMessage(aEventDispatched));
}

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"));
<