Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  nsWindow.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sts=2 sw=2 et cin: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


/*
 * nsWindow - Native window management and event handling.
 *
 * nsWindow is organized into a set of major blocks and
 * block subsections. The layout is as follows:
 *
 *  Includes
 *  Variables
 *  nsIWidget impl.
 *     nsIWidget methods and utilities
 *  nsSwitchToUIThread impl.
 *     nsSwitchToUIThread methods and utilities
 *  Moz events
 *     Event initialization
 *     Event dispatching
 *  Native events
 *     Wndproc(s)
 *     Event processing
 *     OnEvent event handlers
 *  IME management and accessibility
 *  Transparency
 *  Popup hook handling
 *  Misc. utilities
 *  Child window impl.
 *
 * Search for "BLOCK:" to find major blocks.
 * Search for "SECTION:" to find specific sections.
 *
 * Blocks should be split out into separate files if they
 * become unmanageable.
 *
 * Notable related sources:
 *
 *  nsWindowDefs.h     - Definitions, macros, structs, enums
 *                       and general setup.
 *  nsWindowDbg.h/.cpp - Debug related code and directives.
 *  nsWindowGfx.h/.cpp - Graphics and painting.
 *
 */


/**************************************************************
 **************************************************************
 **
 ** BLOCK: Includes
 **
 ** Include headers.
 **
 **************************************************************
 **************************************************************/


#include "gfx2DGlue.h"
#include "gfxEnv.h"
#include "gfxPlatform.h"

#include "mozilla/AppShutdown.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Likely.h"
#include "mozilla/PreXULSkeletonUI.h"
#include "mozilla/Logging.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/SwipeTracker.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/TimeStamp.h"

#include "mozilla/ipc/MessageChannel.h"
#include <algorithm>
#include <limits>

#include "mozilla/widget/WinEventObserver.h"
#include "mozilla/widget/WinMessages.h"
#include "nsLookAndFeel.h"
#include "nsWindow.h"
#include "nsWindowTaskbarConcealer.h"
#include "nsAppRunner.h"

#include <appmodel.h>
#include <shellapi.h>
#include <windows.h>
#include <wtsapi32.h>
#include <process.h>
#include <commctrl.h>
#include <unknwn.h>
#include <psapi.h>
#include <rpc.h>
#include <propvarutil.h>
#include <propkey.h>

#include "mozilla/Logging.h"
#include "prtime.h"
#include "prenv.h"

#include "nsContentUtils.h"
#include "nsISupportsPrimitives.h"
#include "nsITheme.h"
#include "nsIObserverService.h"
#include "nsIScreenManager.h"
#include "imgIContainer.h"
#include "nsIFile.h"
#include "nsIRollupListener.h"
#include "nsIClipboard.h"
#include "WinMouseScrollHandler.h"
#include "nsFontMetrics.h"
#include "nsIFontEnumerator.h"
#include "nsFont.h"
#include "nsRect.h"
#include "nsThreadUtils.h"
#include "nsNativeCharsetUtils.h"
#include "nsGkAtoms.h"
#include "nsCRT.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsWidgetsCID.h"
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsString.h"
#include "mozilla/Components.h"
#include "nsNativeThemeWin.h"
#include "nsXULPopupManager.h"
#include "nsWindowsDllInterceptor.h"
#include "nsLayoutUtils.h"
#include "nsView.h"
#include "nsWindowGfx.h"
#include "gfxWindowsPlatform.h"
#include "gfxDWriteFonts.h"
#include "nsPrintfCString.h"
#include "mozilla/Preferences.h"
#include "SystemTimeConverter.h"
#include "WinTaskbar.h"
#include "WidgetUtils.h"
#include "WinWindowOcclusionTracker.h"
#include "nsIWidgetListener.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/Touch.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/layers/WebRenderLayerManager.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/TextEvents.h"  // For WidgetKeyboardEvent
#include "mozilla/TextEventDispatcherListener.h"
#include "mozilla/widget/nsAutoRollup.h"
#include "mozilla/widget/PlatformWidgetTypes.h"
#include "mozilla/widget/Screen.h"
#include "nsStyleConsts.h"
#include "nsBidiKeyboard.h"
#include "nsStyleConsts.h"
#include "gfxConfig.h"
#include "InProcessWinCompositorWidget.h"
#include "InputDeviceUtils.h"
#include "ScreenHelperWin.h"
#include "mozilla/StaticPrefs_apz.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_widget.h"
#include "nsNativeAppSupportWin.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>

#ifdef ACCESSIBILITY
#  ifdef DEBUG
#    include "mozilla/a11y/Logging.h"
#  endif
#  include "mozilla/a11y/Compatibility.h"
#  include "oleidl.h"
#  include <uiautomation.h>
#  include <winuser.h>
#  include "nsAccessibilityService.h"
#  include "mozilla/a11y/DocAccessible.h"
#  include "mozilla/a11y/LazyInstantiator.h"
#  include "mozilla/a11y/Platform.h"
#  if !defined(WINABLEAPI)
#    include <winable.h>
#  endif  // !defined(WINABLEAPI)
#endif

#include "WindowsUIUtils.h"

#include "nsWindowDefs.h"

#include "nsCrashOnException.h"

#include "nsIContent.h"

#include "mozilla/BackgroundHangMonitor.h"
#include "WinIMEHandler.h"

#include "npapi.h"

#include <d3d11.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

#include "mozilla/gfx/DeviceManagerDx.h"
#include "mozilla/layers/APZInputBridge.h"
#include "mozilla/layers/InputAPZContext.h"
#include "mozilla/layers/KnowsCompositor.h"
#include "InputData.h"

#include "mozilla/TaskController.h"
#include "mozilla/Telemetry.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "mozilla/layers/IAPZCTreeManager.h"

#include "DirectManipulationOwner.h"

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::gfx;
using namespace mozilla::layers;
using namespace mozilla::widget;
using namespace mozilla::plugins;

/**************************************************************
 **************************************************************
 **
 ** BLOCK: Variables
 **
 ** nsWindow Class static initializations and global variables.
 **
 **************************************************************
 **************************************************************/


/**************************************************************
 *
 * SECTION: nsWindow statics
 *
 **************************************************************/

static const wchar_t kUser32LibName[] = L"user32.dll";

uint32_t nsWindow::sInstanceCount = 0;
bool nsWindow::sIsOleInitialized = false;
MOZ_RUNINIT nsIWidget::Cursor nsWindow::sCurrentCursor = {};
nsWindow* nsWindow::sCurrentWindow = nullptr;
bool nsWindow::sJustGotDeactivate = false;
bool nsWindow::sJustGotActivate = false;
bool nsWindow::sIsInMouseCapture = false;

// Urgent-message reentrancy depth for the static `WindowProc` callback.
//
// Three unfortunate facts collide:
//
// ��) Some messages must be processed promptly. If not, Windows will leave the
//    receiving window in an intermediate, and potentially unusable, state until
//    the WindowProc invocation that is handling it returns.
//
// ��) Some messages have indefinitely long processing time. These are mostly
//    messages which may cause us to enter a nested modal loop (via
//    `SpinEventLoopUntil` or similar).
//
// ��) Sometimes, messages skip the queue entirely. Our `WindowProc` may be
//    reentrantly reinvoked from the kernel while we're blocking _on_ the
//    kernel, even briefly, during processing of other messages. (Relevant
//    search term: `KeUserModeCallback`.)
//
// The nightmare scenario, then, is that during processing of an ��-message, we
// briefly become blocked (e.g., by calling `::SendMessageW()`), and the kernel
// takes that opportunity to use �� to hand us a ��-message. (Concretely, see
// bug 1842170.)
//
// There is little we can do to prevent the first half of this scenario. ��) and
// ��) are effectively immutable facts of Windows, and we sometimes legitimately
// need to make blocking calls to process ��-messages. (We may not even be aware
// that we're making such calls, if they're undocumented implementation details
// of another API.)
//
// In an ideal world, WindowProc would always return promptly (or at least in
// bounded time), and ��-messages would not _per se_ exist; long-running modal
// states would instead be implemented in async fashion. In practice, that's far
// easier said than done -- replacing existing uses of `SpinEventLoopUntil` _et
// al._ with asynchronous mechanisms is a collection of mostly-unrelated cross-
// cutting architectural tasks, each of potentially unbounded scope. For now,
// and for the foreseeable future, we're stuck with them.
//
// We therefore simply punt. More specifically: if a known ��-message jumps the
// queue to come in while we're in the middle of processing a known ��-message,
// we:
//  * properly queue the message for processing later;
//  * respond to the ��-message as though we actually had processed it; and
//  * just hope that it can wait until we get around to it.
//
// The word "known" requires a bit of justification. There is no canonical set
// of ��-messages, nor is the set of ��-messages fixed (or even demarcable). We
// can't safely assume that all messages are ��-messages, as that could cause
// ��-messages to be arbitrarily and surprisingly delayed whenever any nested
// event loop is active. We also can't assume all messages are ��-messages,
// since one ��-message jumping the queue while processing another ��-message is
// part of normal and required operation for windowed Windows applications.
//
// So we simply add messages to those sets as we identify them. (Or, preferably,
// rework the ��-message's handling to make it no longer ��. But see above.)
//
// ---
//
// The actual value of `sDepth` is the number of active invocations of
// `WindowProc` that are processing known ��-messages.
size_t nsWindow::WndProcUrgentInvocation::sDepth = 0;

// Hook Data Members 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;

// Used to prevent dispatching mouse events that do not originate from user
// input.
POINT nsWindow::sLastMouseMovePoint = {0};

bool nsWindow::sIsRestoringSession = false;

bool nsWindow::sTouchInjectInitialized = false;
InjectTouchInputPtr nsWindow::sInjectTouchFuncPtr;

static SystemTimeConverter<DWORD>& TimeConverter() {
  static SystemTimeConverter<DWORD> timeConverterSingleton;
  return timeConverterSingleton;
}

static const wchar_t* GetMainWindowClass();
static const wchar_t* ChooseWindowClass(mozilla::widget::WindowType);
// This method registers the given window class, and returns the class name.
static void RegisterWindowClass(const wchar_t* aClassName, UINT aExtraStyle,
                                LPWSTR aIconID);

// Global event hook for window cloaking. Never deregistered.
//  - `Nothing` if not yet set.
//  - `Some(nullptr)` if no attempt should be made to set it.
static mozilla::Maybe<HWINEVENTHOOK> sWinCloakEventHook = Nothing();
static mozilla::LazyLogModule sCloakingLog("DWMCloaking");

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;

static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);

// General purpose user32.dll hook object
MOZ_RUNINIT static WindowsDllInterceptor sUser32Intercept;

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

// Getting this object from the window server can be expensive. Keep it
// around, also get it off the main thread. (See bug 1640852)
StaticRefPtr<IVirtualDesktopManager> gVirtualDesktopManager;
static bool gInitializedVirtualDesktopManager = 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 (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);

    if (!sSendMessageTimeoutWStub) {
      sUser32Intercept.Init("user32.dll");
      DebugOnly<bool> hooked = sSendMessageTimeoutWStub.Set(
          sUser32Intercept, "SendMessageTimeoutW", &SendMessageTimeoutWHook);
      MOZ_ASSERT(hooked);
    }
  }

  class MOZ_RAII A11yInstantiationBlocker {
   public:
    A11yInstantiationBlocker() {
      if (!TIPMessageHandler::sInstance) {
        return;
      }
      ++TIPMessageHandler::sInstance->mA11yBlockCount;
    }  // namespace mozilla

    ~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 < std::size(sInstance->mMessages); ++i) {
      if (msgCode == sInstance->mMessages[i]) {
        A11yInstantiationBlocker block;
        return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
      }
    }

    return ::CallNextHookEx(nullptr, aCode, aWParam, aLParam);
  }

  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
    // (i.e., main) thread.
    if (!aMsgResult || aMsgCode != WM_GETOBJECT ||
        static_cast<LONG>(aLParam) != OBJID_CLIENT || !::NS_IsMainThread() ||
        !WinUtils::GetNSWindowPtr(aHwnd) || !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::FuncHookType<decltype(&SendMessageTimeoutW)>
      sSendMessageTimeoutWStub;
  static StaticAutoPtr<TIPMessageHandler> sInstance;

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

WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
    TIPMessageHandler::sSendMessageTimeoutWStub;
StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;

}  // namespace mozilla

#endif  // defined(ACCESSIBILITY)

namespace mozilla {

// This task will get the VirtualDesktopManager from the generic thread pool
// since doing this on the main thread on startup causes performance issues.
//
// See bug 1640852.
//
// This should be fine and should not require any locking, as when the main
// thread will access it, if it races with this function it will either find
// it to be null or to have a valid value.
class InitializeVirtualDesktopManagerTask : public Task {
 public:
  InitializeVirtualDesktopManagerTask()
      : Task(Kind::OffMainThreadOnly, kDefaultPriorityValue) {}

#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
  bool GetName(nsACString& aName) override {
    aName.AssignLiteral("InitializeVirtualDesktopManagerTask");
    return true;
  }
#endif

  virtual TaskResult Run() override {
    RefPtr<IVirtualDesktopManager> desktopManager;
    HRESULT hr = ::CoCreateInstance(
        CLSID_VirtualDesktopManager, NULL, CLSCTX_INPROC_SERVER,
        __uuidof(IVirtualDesktopManager), getter_AddRefs(desktopManager));
    if (FAILED(hr)) {
      return TaskResult::Complete;
    }

    gVirtualDesktopManager = desktopManager;
    return TaskResult::Complete;
  }
};

// Ground-truth query: does Windows claim the window is cloaked right now?
static bool IsCloaked(HWND hwnd) {
  DWORD cloakedState;
  HRESULT hr = ::DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloakedState,
                                       sizeof(cloakedState));

  if (FAILED(hr)) {
    MOZ_LOG(sCloakingLog, LogLevel::Warning,
            ("failed (%08lX) to query cloaking state for HWND %p", hr, hwnd));
    return false;
  }

  return cloakedState != 0;
}

}  // namespace mozilla

/**************************************************************
 **************************************************************
 **
 ** BLOCK: nsIWidget impl.
 **
 ** nsIWidget interface implementation, broken down into
 ** sections.
 **
 **************************************************************
 **************************************************************/


/**************************************************************
 *
 * SECTION: nsWindow construction and destruction
 *
 **************************************************************/


nsWindow::nsWindow(bool aIsChildWindow)
    : nsBaseWidget(BorderStyle::Default),
      mFrameState(std::in_place, this),
      mIsChildWindow(aIsChildWindow),
      mPIPWindow(false),
      mLastPaintEndTime(TimeStamp::Now()),
      mCachedHitTestTime(TimeStamp::Now()),
      mSizeConstraintsScale(GetDefaultScale().scale),
      mDesktopId("DesktopIdMutex") {
  MOZ_ASSERT(mWindowType == WindowType::Child);

  if (!gInitializedVirtualDesktopManager) {
    TaskController::Get()->AddTask(
        MakeAndAddRef<InitializeVirtualDesktopManagerTask>());
    gInitializedVirtualDesktopManager = true;
  }

  // Global initialization
  if (!sInstanceCount) {
    // Global app registration id for Win7 and up. See
    // WinTaskbar.cpp for details.
    // MSIX packages explicitly do not support setting the appid from within
    // the app, as it is set in the package manifest instead.
    if (!WinUtils::HasPackageIdentity()) {
      mozilla::widget::WinTaskbar::RegisterAppUserModelID();
    }
    if (!StaticPrefs::ui_key_layout_load_when_first_needed()) {
      KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
    }
#if defined(ACCESSIBILITY)
    mozilla::TIPMessageHandler::Initialize();
#endif  // defined(ACCESSIBILITY)
    if (SUCCEEDED(::OleInitialize(nullptr))) {
      sIsOleInitialized = true;
    }
    NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
    MouseScrollHandler::Initialize();
    // Init theme data
    nsUXThemeData::UpdateNativeThemeInfo();
    RedirectedKeyDownMessageManager::Forget();
  }  // !sInstanceCount

  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) {
    IMEHandler::Terminate();
    sCurrentCursor = {};
    if (sIsOleInitialized) {
      ::OleFlushClipboard();
      ::OleUninitialize();
      sIsOleInitialized = false;
    }
  }

  NS_IF_RELEASE(mNativeDragTarget);
}

/**************************************************************
 *
 * SECTION: nsIWidget::Create, nsIWidget::Destroy
 *
 * Creating and destroying windows for this widget.
 *
 **************************************************************/


void nsWindow::SendAnAPZEvent(InputData& aEvent) {
  LRESULT popupHandlingResult;
  if (DealWithPopups(mWnd, MOZ_WM_DMANIP, 0, 0, &popupHandlingResult)) {
    // We need to consume the event after using it to roll up the popup(s).
    return;
  }

  if (mSwipeTracker && aEvent.mInputType == PANGESTURE_INPUT) {
    // Give the swipe tracker a first pass at the event. If a new pan gesture
    // has been started since the beginning of the swipe, the swipe tracker
    // will know to ignore the event.
    nsEventStatus status =
        mSwipeTracker->ProcessEvent(aEvent.AsPanGestureInput());
    if (status == nsEventStatus_eConsumeNoDefault) {
      return;
    }
  }

  APZEventResult result;
  if (mAPZC) {
    result = mAPZC->InputBridge()->ReceiveInputEvent(aEvent);
  }
  if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) {
    return;
  }

  MOZ_ASSERT(aEvent.mInputType == PANGESTURE_INPUT ||
             aEvent.mInputType == PINCHGESTURE_INPUT);

  if (aEvent.mInputType == PANGESTURE_INPUT) {
    PanGestureInput& panInput = aEvent.AsPanGestureInput();
    WidgetWheelEvent event = panInput.ToWidgetEvent(this);
    if (!mAPZC) {
      if (MayStartSwipeForNonAPZ(panInput)) {
        return;
      }
    } else {
      event = MayStartSwipeForAPZ(panInput, result);
    }

    ProcessUntransformedAPZEvent(&event, result);

    return;
  }

  PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
  WidgetWheelEvent event = pinchInput.ToWidgetEvent(this);
  ProcessUntransformedAPZEvent(&event, result);
}

void nsWindow::RecreateDirectManipulationIfNeeded() {
  DestroyDirectManipulation();

  if (mWindowType != WindowType::TopLevel && mWindowType != WindowType::Popup) {
    return;
  }

  if (!(StaticPrefs::apz_allow_zooming() ||
        StaticPrefs::apz_windows_use_direct_manipulation()) ||
      StaticPrefs::apz_windows_force_disable_direct_manipulation()) {
    return;
  }

  mDmOwner = MakeUnique<DirectManipulationOwner>(this);

  LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
                             mBounds.Height());
  mDmOwner->Init(bounds);
}

void nsWindow::ResizeDirectManipulationViewport() {
  if (mDmOwner) {
    LayoutDeviceIntRect bounds(mBounds.X(), mBounds.Y(), mBounds.Width(),
                               mBounds.Height());
    mDmOwner->ResizeViewport(bounds);
  }
}

void nsWindow::DestroyDirectManipulation() {
  if (mDmOwner) {
    mDmOwner->Destroy();
    mDmOwner.reset();
  }
}

namespace mozilla::widget {

// A mask specifying the window-styles associated with window-chrome.
constexpr static const WindowStyles kChromeStylesMask{
    .style = WS_CAPTION | WS_THICKFRAME,
    .ex = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE |
          WS_EX_STATICEDGE,
};

WindowStyles WindowStyles::FromHWND(HWND aWnd) {
  return {.style = ::GetWindowLongPtrW(aWnd, GWL_STYLE),
          .ex = ::GetWindowLongPtrW(aWnd, GWL_EXSTYLE)};
}

void SetWindowStyles(HWND aWnd, const WindowStyles& aStyles) {
  VERIFY_WINDOW_STYLE(aStyles.style);
  ::SetWindowLongPtrW(aWnd, GWL_STYLE, aStyles.style);
  ::SetWindowLongPtrW(aWnd, GWL_EXSTYLE, aStyles.ex);
}

}  // namespace mozilla::widget

// Create the proper widget
nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect,
                          widget::InitData* aInitData) {
  // Historical note: there was once some belief and/or intent that nsWindows
  // could be created on arbitrary threads, and this may still be reflected in
  // some comments.
  MOZ_ASSERT(NS_IsMainThread());

  // Ensure that the hidden window exists, so that broadcast Windows messages
  // (WM_FONTCHANGE et al.) are received and processed.
  WinEventWindow::Ensure();

  widget::InitData defaultInitData;
  if (!aInitData) aInitData = &defaultInitData;

  MOZ_DIAGNOSTIC_ASSERT(aInitData->mWindowType != WindowType::Invisible);

  mBounds = aRect;

  // Ensure that the toolkit is created.
  nsToolkit::GetToolkit();

  BaseCreate(aParent, aInitData);

  HWND parent =
      aParent ? (HWND)aParent->GetNativeData(NS_NATIVE_WINDOW) : nullptr;

  mIsRTL = aInitData->mRTL;
  mPIPWindow = aInitData->mPIPWindow;
  mOpeningAnimationSuppressed = aInitData->mIsAnimationSuppressed;
  mAlwaysOnTop = aInitData->mAlwaysOnTop;
  mIsAlert = aInitData->mIsAlert;
  mResizable = aInitData->mResizable;

  Styles desiredStyles{
      .style = static_cast<LONG_PTR>(WindowStyle()),
      .ex = static_cast<LONG_PTR>(WindowExStyle()),
  };

  if (mWindowType != WindowType::Popup) {
    // See if the caller wants to explicitly set clip children and clip siblings
    if (aInitData->mClipChildren) {
      desiredStyles.style |= WS_CLIPCHILDREN;
    } else {
      desiredStyles.style &= ~WS_CLIPCHILDREN;
    }
    if (aInitData->mClipSiblings) {
      desiredStyles.style |= WS_CLIPSIBLINGS;
    }
  }

  const wchar_t* className = ChooseWindowClass(mWindowType);

  // Take specific actions when creating the first top-level window
  static bool sFirstTopLevelWindowCreated = false;
  if (aInitData->mWindowType == WindowType::TopLevel && !aParent &&
      !sFirstTopLevelWindowCreated) {
    sFirstTopLevelWindowCreated = true;
    mWnd = ConsumePreXULSkeletonUIHandle();
    if (mWnd) {
      MOZ_ASSERT(desiredStyles.style == kPreXULSkeletonUIWindowStyle,
                 "The skeleton UI window style should match the expected "
                 "style for the first window created");
      MOZ_ASSERT(desiredStyles.ex == kPreXULSkeletonUIWindowStyleEx,
                 "The skeleton UI window extended style should match the "
                 "expected extended style for the first window created");
      MOZ_ASSERT(
          ::GetWindowThreadProcessId(mWnd, nullptr) == ::GetCurrentThreadId(),
          "The skeleton UI window should be created on the same thread as "
          "other windows");
      mIsShowingPreXULSkeletonUI = true;

      // If we successfully consumed the pre-XUL skeleton UI, just update
      // our internal state to match what is currently being displayed.
      mIsVisible = true;
      mIsCloaked = mozilla::IsCloaked(mWnd);
      mFrameState->ConsumePreXULSkeletonState(WasPreXULSkeletonUIMaximized());

      MOZ_ASSERT(BoundsUseDesktopPixels());
      auto scale = GetDesktopToDeviceScale();
      mBounds = mLastPaintBounds = LayoutDeviceIntRect::FromUnknownRect(
          DesktopIntRect::Round(LayoutDeviceRect(GetBounds()) / scale)
              .ToUnknownRect());

      // Skeleton ui is disabled when custom titlebar is off, see bug 1673092.
      SetCustomTitlebar(true);
      // The skeleton UI already painted over the NC area, so there's no need
      // to do that again; the effective non-client margins haven't changed.
      mNeedsNCAreaClear = false;

      // Reset the WNDPROC for this window and its whole class, as we had
      // to use our own WNDPROC when creating the the skeleton UI window.
      ::SetWindowLongPtrW(mWnd, GWLP_WNDPROC,
                          reinterpret_cast<LONG_PTR>(
                              WinUtils::NonClientDpiScalingDefWindowProcW));
      ::SetClassLongPtrW(mWnd, GCLP_WNDPROC,
                         reinterpret_cast<LONG_PTR>(
                             WinUtils::NonClientDpiScalingDefWindowProcW));
    }
  }

  if (!mWnd) {
    mWnd =
        ::CreateWindowExW(desiredStyles.ex, className, L"", desiredStyles.style,
                          aRect.X(), aRect.Y(), aRect.Width(), aRect.Height(),
                          parent, nullptr, nsToolkit::mDllInstance, nullptr);
    if (!mWnd) {
      NS_WARNING("nsWindow CreateWindowEx failed.");
      return NS_ERROR_FAILURE;
    }
  }

  {
    // Some of the chrome mask window styles can be added implicitly by
    // CreateWindowEx, but we really don't want that.
    // To be safe, only deal with those bits for now, instead of just
    // overriding with extendedStyle or style.
    // This can happen with non-native alert windows for example.
    const auto actualStyles = Styles::FromHWND(mWnd);
    auto newStyles = (actualStyles & ~kChromeStylesMask) |
                     (desiredStyles & kChromeStylesMask);
    if (newStyles != actualStyles) {
      SetWindowStyles(mWnd, newStyles);
    }
  }

  if (!sWinCloakEventHook) {
    MOZ_LOG(sCloakingLog, LogLevel::Info, ("Registering cloaking event hook"));

    // C++03 lambda approximation until P2173R1 is available (-std=c++2b)
    struct StdcallLambda {
      static void CALLBACK OnCloakUncloakHook(HWINEVENTHOOK hWinEventHook,
                                              DWORD event, HWND hwnd,
                                              LONG idObject, LONG idChild,
                                              DWORD idEventThread,
                                              DWORD dwmsEventTime) {
        const bool isCloaked = event == EVENT_OBJECT_CLOAKED ? true : false;
        nsWindow::OnCloakEvent(hwnd, isCloaked);
      }
    };

    const HWINEVENTHOOK hook = ::SetWinEventHook(
        EVENT_OBJECT_CLOAKED, EVENT_OBJECT_UNCLOAKED, HMODULE(nullptr),
        &StdcallLambda::OnCloakUncloakHook, ::GetCurrentProcessId(),
        ::GetCurrentThreadId(), WINEVENT_OUTOFCONTEXT);
    sWinCloakEventHook = Some(hook);

    if (!hook) {
      const DWORD err = ::GetLastError();
      MOZ_LOG(sCloakingLog, LogLevel::Error,
              ("Failed to register cloaking event hook! GLE = %lu (0x%lX)", err,
               err));
    }
  }

  {
    // Although permanent Private Browsing mode is indeed Private Browsing,
    // we choose to make it look like regular Firefox in terms of the icon
    // it uses (which also means we shouldn't use the Private Browsing
    // AUMID).
    bool usePrivateAumid =
        Preferences::GetBool("browser.privateWindowSeparation.enabled"true) &&
        (aInitData->mIsPrivate) &&
        !StaticPrefs::browser_privatebrowsing_autostart();
    RefPtr<IPropertyStore> pPropStore;
    if (!FAILED(SHGetPropertyStoreForWindow(mWnd, IID_IPropertyStore,
                                            getter_AddRefs(pPropStore)))) {
      PROPVARIANT pv;
      nsAutoString aumid;
      // Make sure we're using the correct AUMID so that taskbar
      // grouping works properly
      Unused << NS_WARN_IF(!mozilla::widget::WinTaskbar::GenerateAppUserModelID(
          aumid, usePrivateAumid));
      if (!usePrivateAumid && widget::WinUtils::HasPackageIdentity()) {
        // On MSIX we should always have a provided process AUMID
        // that we can explicitly assign to a regular window.
        UINT32 maxLength = MAX_PATH;
        aumid.SetLength(maxLength);
        Unused << NS_WARN_IF(
            GetCurrentApplicationUserModelId(&maxLength, aumid.get()));
      }
      if (!FAILED(InitPropVariantFromString(aumid.get(), &pv))) {
        if (!FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv))) {
          pPropStore->Commit();
        }

        PropVariantClear(&pv);
      }
    }
    HICON icon = ::LoadIconW(
        ::GetModuleHandleW(nullptr),
        MAKEINTRESOURCEW(usePrivateAumid ? IDI_PBMODE : IDI_APPICON));
    SetBigIcon(icon);
    SetSmallIcon(icon);
  }

  // If mDefaultScale is set before mWnd has been set, it will have the scale of
  // the primary monitor, rather than the monitor that the window is actually
  // on. For non-popup windows this gets corrected by the WM_DPICHANGED message
  // which resets mDefaultScale, but for popup windows we don't reset
  // mDefaultScale on that message. In order to ensure that popup windows
  // spawned on a non-primary monitor end up with the correct scale, we reset
  // mDefaultScale here so that it gets recomputed using the correct monitor now
  // that we have a mWnd.
  mDefaultScale = -1.0;

  if (mIsRTL) {
    DWORD dwAttribute = TRUE;
    DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute,
                          sizeof dwAttribute);
  }

  // Default to the system color scheme unless getting told otherwise.
  SetColorScheme(Nothing());

  if (mOpeningAnimationSuppressed) {
    SuppressAnimation(true);
  }

  if (mAlwaysOnTop) {
    ::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0,
                   SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  }

  if (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 = (WNDPROC)::SetWindowLongPtrW(
        scrollableWnd, GWLP_WNDPROC, (LONG_PTR)nsWindow::WindowProc);
    ::SetWindowLongPtrW(scrollableWnd, GWLP_USERDATA, (LONG_PTR)oldWndProc);
  }

  // We will start receiving native events after associating with our native
  // window. We will also become the output of WinUtils::GetNSWindowPtr for that
  // window.
  if (!AssociateWithNativeWindow()) {
    return NS_ERROR_FAILURE;
  }

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

  static bool a11yPrimed = false;
  if (!a11yPrimed && mWindowType == WindowType::TopLevel) {
    a11yPrimed = true;
    if (Preferences::GetInt("accessibility.force_disabled", 0) == -1) {
      ::PostMessage(mWnd, MOZ_WM_STARTA11Y, 0, 0);
    }
  }

  RecreateDirectManipulationIfNeeded();

  return NS_OK;
}

void nsWindow::LocalesChanged() {
  bool isRTL = intl::LocaleService::GetInstance()->IsAppLocaleRTL();
  if (mIsRTL != isRTL) {
    DWORD dwAttribute = isRTL;
    DwmSetWindowAttribute(mWnd, DWMWA_NONCLIENT_RTL_LAYOUT, &dwAttribute,
                          sizeof dwAttribute);
    mIsRTL = isRTL;
  }
}

// Close this nsWindow
void nsWindow::Destroy() {
  // WM_DESTROY has already fired, avoid calling it twice
  if (mOnDestroyCalled) return;

  // Don't destroy windows that have file pickers open, we'll tear these down
  // later once the picker is closed.
  mDestroyCalled = true;
  if (mPickerDisplayCount) return;

  // During the destruction of all of our children, make sure we don't get
  // deleted.
  nsCOMPtr<nsIWidget> kungFuDeathGrip(this);

  DestroyDirectManipulation();

  /**
   * On windows the LayerManagerOGL destructor wants the widget to be around for
   * cleanup. It also would like to have the HWND intact, so we nullptr it here.
   */

  DestroyLayerManager();

  // 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 (!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.
 *
 **************************************************************/


static void RegisterWindowClass(const wchar_t* aClassName, UINT aExtraStyle,
                                LPWSTR aIconID) {
  WNDCLASSW wc = {};
  if (::GetClassInfoW(nsToolkit::mDllInstance, aClassName, &wc)) {
    // already registered
    return;
  }

  wc.style = CS_DBLCLKS | aExtraStyle;
  wc.lpfnWndProc = WinUtils::NonClientDpiScalingDefWindowProcW;
  wc.hInstance = nsToolkit::mDllInstance;
  wc.hIcon =
      aIconID ? ::LoadIconW(::GetModuleHandleW(nullptr), aIconID) : nullptr;
  wc.lpszClassName = aClassName;

  // Since we discard WM_ERASEBKGND events, the window-class background brush is
  // mostly not used -- it shows up when resizing, but scarcely ever otherwise.
  //
  // In theory we could listen for theme changes and set this brush to an
  // appropriate background color as needed; but given the hoops Win32 makes us
  // jump through to change class data, it's probably not worth the trouble.
  // (See bug 1901875.) Instead, we just make it dark grey, which is probably
  // acceptable in either light or dark mode.
  wc.hbrBackground = (HBRUSH)::GetStockObject(DKGRAY_BRUSH);

  // Failures are ignored as they are handled when ::CreateWindow fails
  ::RegisterClassW(&wc);
}

static LPWSTR const gStockApplicationIcon = MAKEINTRESOURCEW(32512);

static const wchar_t* ChooseWindowClass(WindowType aWindowType) {
  const wchar_t* className = [aWindowType] {
    switch (aWindowType) {
      case WindowType::Dialog:
        return kClassNameDialog;
      case WindowType::Popup:
        return kClassNameDropShadow;
      default:
        return GetMainWindowClass();
    }
  }();
  RegisterWindowClass(className, 0, gStockApplicationIcon);
  return className;
}

/**************************************************************
 *
 * SECTION: Window styles utilities
 *
 * Return the proper windows styles and extended styles.
 *
 **************************************************************/


const DWORD kTitlebarItemsWindowStyles =
    WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const DWORD kAllBorderStyles =
    kTitlebarItemsWindowStyles | WS_THICKFRAME | WS_DLGFRAME;

static DWORD WindowStylesRemovedForBorderStyle(BorderStyle aStyle) {
  if (aStyle == BorderStyle::Default || aStyle == BorderStyle::All) {
    return 0;
  }
  if (aStyle == BorderStyle::None) {
    return kAllBorderStyles;
  }
  DWORD toRemove = 0;
  if (!(aStyle & BorderStyle::Border)) {
    toRemove |= WS_BORDER;
  }
  if (!(aStyle & BorderStyle::Title)) {
    toRemove |= WS_DLGFRAME;
  }
  if (!(aStyle & (BorderStyle::Menu | BorderStyle::Close))) {
    // Looks like getting rid of the system menu also does away with the close
    // box. So, we only get rid of the system menu and the close box if you
    // want neither. How does the Windows "Dialog" window class get just
    // closebox and no sysmenu? Who knows.
    toRemove |= WS_SYSMENU;
  }
  if (!(aStyle & BorderStyle::ResizeH)) {
    toRemove |= WS_THICKFRAME;
  }
  if (!(aStyle & BorderStyle::Minimize)) {
    toRemove |= WS_MINIMIZEBOX;
  }
  if (!(aStyle & BorderStyle::Maximize)) {
    toRemove |= WS_MAXIMIZEBOX;
  }
  return toRemove;
}

// Return nsWindow styles
DWORD nsWindow::WindowStyle() {
  DWORD style;
  switch (mWindowType) {
    case WindowType::Child:
      style = WS_OVERLAPPED;
      break;

    case WindowType::Dialog:
      style = WS_OVERLAPPED | WS_BORDER | WS_DLGFRAME | WS_SYSMENU |
              DS_MODALFRAME | WS_CLIPCHILDREN;
      if (mBorderStyle != BorderStyle::Default) {
        style |= WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
      }
      break;

    case WindowType::Popup:
      style = WS_OVERLAPPED | WS_POPUP;
      break;

    default:
      NS_ERROR("unknown border style");
      [[fallthrough]];

    case WindowType::TopLevel:
      style = WS_OVERLAPPED | WS_CLIPCHILDREN | WS_DLGFRAME | WS_BORDER |
              WS_THICKFRAME | kTitlebarItemsWindowStyles;
      break;
  }

  style &= ~WindowStylesRemovedForBorderStyle(mBorderStyle);

  if (mIsChildWindow) {
    style |= WS_CLIPCHILDREN;
    if (!(style & WS_POPUP)) {
      style |= WS_CHILD;  // WS_POPUP and WS_CHILD are mutually exclusive.
    }
  }

  VERIFY_WINDOW_STYLE(style);
  return style;
}

// Return nsWindow extended styles
DWORD nsWindow::WindowExStyle() {
  switch (mWindowType) {
    case WindowType::Child:
      return 0;
    case WindowType::Popup: {
      DWORD extendedStyle = WS_EX_TOOLWINDOW;
      if (mPopupLevel == PopupLevel::Top) {
        extendedStyle |= WS_EX_TOPMOST;
      }
      return extendedStyle;
    }
    case WindowType::Dialog:
    case WindowType::TopLevel:
    case WindowType::Invisible:
      break;
  }
  if (mIsAlert) {
    MOZ_ASSERT(mWindowType == WindowType::Dialog,
               "Expect alert windows to have type=dialog");
    return WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
  }
  return WS_EX_WINDOWEDGE;
}

/**************************************************************
 *
 * SECTION: Native window association utilities
 *
 * Used in Create and Destroy. A nsWindow can associate with its
 * underlying native window mWnd. Once a native window is
 * associated with a nsWindow, its native events will be handled
 * by the static member function nsWindow::WindowProc. Moreover,
 * the association will be registered in the WinUtils association
 * list, that is, calling WinUtils::GetNSWindowPtr on the native
 * window will return the associated nsWindow. This is used in
 * nsWindow::WindowProc to correctly dispatch native events to
 * the handler methods defined in nsWindow, even though it is a
 * static member function.
 *
 * After dissociation, the native events of the native window will
 * no longer be handled by nsWindow::WindowProc, and will thus not
 * be dispatched to the nsWindow native event handler methods.
 * Moreover, the association will no longer be registered in the
 * WinUtils association list, so calling WinUtils::GetNSWindowPtr
 * on the native window will return nullptr.
 *
 **************************************************************/


bool nsWindow::AssociateWithNativeWindow() {
  if (!mWnd || !IsWindow(mWnd)) {
    NS_ERROR("Invalid window handle");
    return false;
  }

  // Connect the this pointer to the native window handle.
  // This should be done before SetWindowLongPtrW, because nsWindow::WindowProc
  // uses WinUtils::GetNSWindowPtr internally.
  WinUtils::SetNSWindowPtr(mWnd, this);

  ::SetLastError(ERROR_SUCCESS);
  const auto prevWndProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
      mWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(nsWindow::WindowProc)));
  if (!prevWndProc && GetLastError() != ERROR_SUCCESS) {
    NS_ERROR("Failure in SetWindowLongPtrW");
    WinUtils::SetNSWindowPtr(mWnd, nullptr);
    return false;
  }

  mPrevWndProc.emplace(prevWndProc);
  return true;
}

void nsWindow::DissociateFromNativeWindow() {
  if (!mWnd || !IsWindow(mWnd) || mPrevWndProc.isNothing()) {
    return;
  }

  DebugOnly<WNDPROC> wndProcBeforeDissociate =
      reinterpret_cast<WNDPROC>(::SetWindowLongPtrW(
          mWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(*mPrevWndProc)));
  NS_ASSERTION(wndProcBeforeDissociate == nsWindow::WindowProc,
               "Unstacked an unexpected native window procedure");

  WinUtils::SetNSWindowPtr(mWnd, nullptr);
  mPrevWndProc.reset();
}

void nsWindow::DidClearParent(nsIWidget*) {
  if (mWindowType == WindowType::Popup || !mWnd) {
    return;
  }
  ::SetParent(mWnd, nullptr);
  RecreateDirectManipulationIfNeeded();
}

static int32_t RoundDown(double aDouble) {
  return aDouble > 0 ? static_cast<int32_t>(floor(aDouble))
                     : static_cast<int32_t>(ceil(aDouble));
}

float nsWindow::GetDPI() {
  float dpi = 96.0f;
  nsCOMPtr<nsIScreen> screen = GetWidgetScreen();
  if (screen) {
    screen->GetDpi(&dpi);
  }
  return dpi;
}

double nsWindow::GetDefaultScaleInternal() {
  if (mDefaultScale <= 0.0) {
    mDefaultScale = WinUtils::LogToPhysFactor(mWnd);
  }
  return mDefaultScale;
}

int32_t nsWindow::LogToPhys(double aValue) {
  return WinUtils::LogToPhys(
      ::MonitorFromWindow(mWnd, MONITOR_DEFAULTTOPRIMARY), aValue);
}

nsWindow* nsWindow::GetParentWindow(bool aIncludeOwner) {
  return static_cast<nsWindow*>(GetParentWindowBase(aIncludeOwner));
}

nsWindow* nsWindow::GetParentWindowBase(bool aIncludeOwner) {
  if (IsTopLevelWidget()) {
    // 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 widget;
}

/**************************************************************
 *
 * SECTION: nsIWidget::Show
 *
 * Hide or show this component.
 *
 **************************************************************/


void nsWindow::Show(bool aState) {
  if (aState && mIsShowingPreXULSkeletonUI) {
    // The first time we decide to actually show the window is when we decide
    // that we've taken over the window from the skeleton UI, and we should
    // no longer treat resizes / moves specially.
    //
    // NOTE(emilio): mIsShowingPreXULSkeletonUI feels a bit odd, or at least
    // misnamed. During regular startup we create the skeleton UI, then the
    // early blank window consumes it, and at that point we set
    // mIsShowingPreXULSkeletonUI to false, but in fact, we're still showing
    // the skeleton UI (because the blank window is, well, blank). We should
    // consider guarding this with !mIsEarlyBlankWindow...
    mIsShowingPreXULSkeletonUI = false;
    // Concomitantly, this is also when we change the cursor away from the
    // default "wait" cursor.
    SetCursor(Cursor{eCursor_standard});
#if defined(ACCESSIBILITY)
    // If our HWND has focus and the a11y engine hasn't started yet, fire a
    // focus win event. Windows already did this when the skeleton UI appeared,
    // but a11y wouldn't have been able to start at that point even if a client
    // responded. Firing this now gives clients the chance to respond with
    // WM_GETOBJECT, which will trigger the a11y engine. We don't want to do
    // this if the a11y engine has already started because it has probably
    // already fired focus on a descendant.
    if (::GetFocus() == mWnd && !GetAccService()) {
      ::NotifyWinEvent(EVENT_OBJECT_FOCUS, mWnd, OBJID_CLIENT, CHILDID_SELF);
    }
#endif  // defined(ACCESSIBILITY)
  }

  if (mWindowType == WindowType::Popup) {
    MOZ_ASSERT(ChooseWindowClass(mWindowType) == kClassNameDropShadow);
    // 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 = aState;

  if (mWnd) {
    if (aState) {
      if (!wasVisible && mWindowType == WindowType::TopLevel) {
        // speed up the initial paint after show for
        // top level windows:
        syncInvalidate = true;

        // Cloak (or uncloak) the window.
        //
        // (DWMWA_CLOAK is effectively orthogonal to any cloaking done by the
        // shell to implement virtual desktops; we don't have to worry about
        // accidentally forcing something on another desktop to become visible.)
        constexpr static const auto CloakWindow = [](HWND hwnd, BOOL state) {
          ::DwmSetWindowAttribute(hwnd, DWMWA_CLOAK, &state, sizeof(state));
        };

        // Clear the window using a theme-appropriate color.
        constexpr static const auto ClearWindow = [](HWND hwnd) {
          // default background color from current theme
          auto const bgcolor = LookAndFeel::Color(
              StyleSystemColor::Window, PreferenceSheet::ColorSchemeForChrome(),
              LookAndFeel::UseStandins::No, NS_RGB(0, 0, 0));

          HBRUSH brush = ::CreateSolidBrush(NSRGB_2_COLOREF(bgcolor));
          if (NS_WARN_IF(!brush)) {
            // GDI object cap hit, possibly?
            return;
          }
          auto const _releaseBrush =
              MakeScopeExit([&] { ::DeleteObject(brush); });

          HDC hdc = ::GetWindowDC(hwnd);
          MOZ_ASSERT(hdc);
          auto const _cleanupDC =
              MakeScopeExit([&] { ::ReleaseDC(hwnd, hdc); });

          RECT rect;
          ::GetWindowRect(hwnd, &rect);  // includes non-client area

          // Convert from screen- to client-coordinates, accounting for the
          // desktop (or, in theory, us) possibly being WS_EX_LAYOUTRTL...
          ::MapWindowPoints(HWND_DESKTOP, hwnd, (LPPOINT)&rect, 2);
          // ... then convert from client- to window- coordinates, with no
          // separate RTL-handling needed.
          ::OffsetRect(&rect, -rect.left, -rect.top);

          ::FillRect(hdc, &rect, brush);
        };

        if (!mHasBeenShown) {
          // On creation, the window's content is not specified; in practice,
          // it's observed to usually be full of bright white, regardless of any
          // window-class options. DWM will happily render that unspecified
          // content to the screen before we get a chance to process a
          // WM_ERASEBKGND event (or, indeed, anything else). To avoid dark-mode
          // users being assaulted with a bright white flash, we need to draw
          // something on top of that at least once before showing the window.
          //
          // Unfortunately, there's a bit of a catch-22 here: until the window
          // has been set "visible" at least once, it doesn't have a backing
          // surface, so we can't draw anything to it! To work around this, we
          // cloak the window before "showing" it.
          CloakWindow(mWnd, TRUE);
        }

        // Set the cursor before showing the window to avoid the default wait
        // cursor.
        SetCursor(Cursor{eCursor_standard});

        switch (mFrameState->GetSizeMode()) {
          case nsSizeMode_Fullscreen:
            ::ShowWindow(mWnd, SW_SHOW);
            break;
          case nsSizeMode_Maximized:
            ::ShowWindow(mWnd, SW_SHOWMAXIMIZED);
            break;
          case nsSizeMode_Minimized:
            ::ShowWindow(mWnd, SW_SHOWMINIMIZED);
            break;
          default:
            if (CanTakeFocus() && !mAlwaysOnTop) {
              ::ShowWindow(mWnd, SW_SHOWNORMAL);
            } else {
              ::ShowWindow(mWnd, SW_SHOWNOACTIVATE);
              // Don't flicker the window if we're restoring session
              if (!sIsRestoringSession) {
                Unused << GetAttention(2);
              }
            }
            break;
        }

        if (!mHasBeenShown) {
          // Now that ::ShowWindow() has been called once, the window surface
          // actually exists, so we can draw to it. Fill it with the theme's
          // background color before uncloaking it to complete the Show().
          ClearWindow(mWnd);
          CloakWindow(mWnd, FALSE);
          mHasBeenShown = true;
        }

      } else {
        DWORD flags = SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW;
        if (wasVisible) {
          flags |= SWP_NOZORDER;
        }
        if (mAlwaysOnTop || mIsAlert) {
          flags |= SWP_NOACTIVATE;
        }

        if (mWindowType == WindowType::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 | SWP_NOOWNERZORDER;
          HWND owner = ::GetWindow(mWnd, GW_OWNER);
          if (owner) {
            // PopupLevel::Top popups should be above all else.  All other
            // types should be placed in front of their owner, without
            // changing the owner's z-level relative to other windows.
            if (mPopupLevel != PopupLevel::Top) {
              ::SetWindowPos(mWnd, owner, 0, 0, 0, 0, flags);
              ::SetWindowPos(
                  owner, mWnd, 0, 0, 0, 0,
                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
            } else {
              ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
            }
          } else {
            ::SetWindowPos(mWnd, HWND_TOPMOST, 0, 0, 0, 0, flags);
          }
        } else {
          if (mWindowType == WindowType::Dialog && !CanTakeFocus())
            flags |= SWP_NOACTIVATE;

          ::SetWindowPos(mWnd, HWND_TOP, 0, 0, 0, 0, flags);
        }
      }
    } else {
      // Clear contents to avoid ghosting of old content if we display
      // this window again.
      if (wasVisible && mTransparencyMode == TransparencyMode::Transparent) {
        if (mCompositorWidgetDelegate) {
          mCompositorWidgetDelegate->ClearTransparentWindow();
        }
      }
      if (mWindowType != WindowType::Dialog) {
        ::ShowWindow(mWnd, SW_HIDE);
      } else {
        ::SetWindowPos(mWnd, 0, 0, 0, 0, 0,
                       SWP_HIDEWINDOW | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER |
                           SWP_NOACTIVATE);
      }
    }
  }

  if (!wasVisible && aState) {
    Invalidate();
    if (syncInvalidate && !mInDtor && !mOnDestroyCalled) {
      ::UpdateWindow(mWnd);
    }
  }

  if (mOpeningAnimationSuppressed) {
    SuppressAnimation(false);
  }
}

/**************************************************************
 *
 * SECTION: nsIWidget::IsVisible
 *
 * Returns the visibility state.
 *
 **************************************************************/


// Return true if the component is visible, false otherwise.
//
// This does not take cloaking into account.
bool nsWindow::IsVisible() const { return mIsVisible; }

/**************************************************************
 *
 * SECTION: Touch and APZ-related functions
 *
 **************************************************************/


void nsWindow::RegisterTouchWindow() {
  mTouchWindow = true;
  ::RegisterTouchWindow(mWnd, TWF_WANTPALM);
  ::EnumChildWindows(mWnd, nsWindow::RegisterTouchForDescendants, 0);
}

BOOL CALLBACK nsWindow::RegisterTouchForDescendants(HWND aWnd, LPARAM aMsg) {
  nsWindow* win = WinUtils::GetNSWindowPtr(aWnd);
  if (win) {
    ::RegisterTouchWindow(aWnd, TWF_WANTPALM);
  }
  return TRUE;
}

void nsWindow::LockAspectRatio(bool aShouldLock) {
  if (aShouldLock) {
    mAspectRatio = (float)mBounds.Width() / (float)mBounds.Height();
  } else {
    mAspectRatio = 0.0;
  }
}

/**************************************************************
 *
 * SECTION: nsIWidget::SetInputRegion
 *
 * Sets whether the window should ignore mouse events.
 *
 **************************************************************/

void nsWindow::SetInputRegion(const InputRegion& aInputRegion) {
  mInputRegion = aInputRegion;
}

/**************************************************************
 *
 * SECTION: nsIWidget::Move, nsIWidget::Resize, nsIWidget::Size
 *
 * Repositioning and sizing a window.
 *
 **************************************************************/


void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) {
  SizeConstraints c = aConstraints;

  if (mWindowType != WindowType::Popup && mResizable) {
    c.mMinSize.width =
        std::max(int32_t(::GetSystemMetrics(SM_CXMINTRACK)), c.mMinSize.width);
    c.mMinSize.height =
        std::max(int32_t(::GetSystemMetrics(SM_CYMINTRACK)), c.mMinSize.height);
  }

  if (mMaxTextureSize > 0) {
    // We can't make ThebesLayers bigger than this anyway.. no point it letting
    // a window grow bigger as we won't be able to draw content there in
    // general.
    c.mMaxSize.width = std::min(c.mMaxSize.width, mMaxTextureSize);
    c.mMaxSize.height = std::min(c.mMaxSize.height, mMaxTextureSize);
  }

  mSizeConstraintsScale = GetDefaultScale().scale;

  nsBaseWidget::SetSizeConstraints(c);
}

const SizeConstraints nsWindow::GetSizeConstraints() {
  double scale = GetDefaultScale().scale;
  if (mSizeConstraintsScale == scale || mSizeConstraintsScale == 0.0) {
    return mSizeConstraints;
  }
  scale /= mSizeConstraintsScale;
  SizeConstraints c = mSizeConstraints;
  if (c.mMinSize.width != NS_MAXSIZE) {
    c.mMinSize.width = NSToIntRound(c.mMinSize.width * scale);
  }
  if (c.mMinSize.height != NS_MAXSIZE) {
    c.mMinSize.height = NSToIntRound(c.mMinSize.height * scale);
  }
  if (c.mMaxSize.width != NS_MAXSIZE) {
    c.mMaxSize.width = NSToIntRound(c.mMaxSize.width * scale);
  }
  if (c.mMaxSize.height != NS_MAXSIZE) {
    c.mMaxSize.height = NSToIntRound(c.mMaxSize.height * scale);
  }
  return c;
}

// Move this component
void nsWindow::Move(double aX, double aY) {
  if (mWindowType == WindowType::TopLevel ||
      mWindowType == WindowType::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
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=88 H=95 G=91

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge