tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit cc2759f7e5df4b6eba74aea3f651bb0157ab45ce
parent 929d7115f3bd2096a69552fd91aca0f9745645eb
Author: Emilio Cobos Álvarez <emilio@crisal.io>
Date:   Tue, 14 Oct 2025 15:13:18 +0000

Bug 1994157 - Remove nsBaseWidget. r=stransky,win-reviewers,gstoll

The distinction between nsBaseWidget and nsIWidget is useless as all our
implementations use the former.

So just merge both.

Differential Revision: https://phabricator.services.mozilla.com/D268518

Diffstat:
Mdom/base/nsDOMWindowUtils.cpp | 1+
Mdom/base/nsFrameLoader.cpp | 4++--
Mgfx/docs/Silk.rst | 32++++++++++++++++----------------
Mgfx/ipc/CompositorSession.cpp | 2+-
Mgfx/ipc/CompositorSession.h | 6+++---
Mgfx/ipc/GPUProcessManager.cpp | 8++++----
Mgfx/ipc/GPUProcessManager.h | 8++++----
Mgfx/ipc/InProcessCompositorSession.cpp | 8++++----
Mgfx/ipc/InProcessCompositorSession.h | 4++--
Mgfx/ipc/RemoteCompositorSession.cpp | 6+++---
Mgfx/ipc/RemoteCompositorSession.h | 2+-
Mgfx/layers/apz/src/APZCTreeManager.cpp | 2+-
Mgfx/layers/apz/util/InputAPZContext.h | 2+-
Mgfx/layers/ipc/APZCTreeManagerParent.cpp | 4++--
Mgfx/layers/ipc/CompositorVsyncScheduler.cpp | 2+-
Mgfx/layers/ipc/UiCompositorControllerChild.cpp | 8++++----
Mgfx/layers/ipc/UiCompositorControllerChild.h | 11+++++------
Mgfx/layers/opengl/TextureHostOGL.h | 4++--
Mgfx/tests/gtest/MockWidget.cpp | 2+-
Mgfx/tests/gtest/MockWidget.h | 4++--
Mgfx/vr/FxRWindowManager.cpp | 1+
Mlayout/base/PresShell.cpp | 2+-
Mwidget/CompositorWidget.cpp | 2+-
Mwidget/CompositorWidget.h | 2+-
Mwidget/IMEData.h | 4++--
Mwidget/InProcessCompositorWidget.cpp | 6+++---
Mwidget/InProcessCompositorWidget.h | 8++++----
Mwidget/InputData.h | 14++++++++++++++
Mwidget/PuppetWidget.cpp | 2+-
Mwidget/PuppetWidget.h | 14++++++++------
Mwidget/VsyncDispatcher.cpp | 4++--
Mwidget/WidgetMessageUtils.h | 6+++---
Mwidget/WidgetUtils.h | 2+-
Mwidget/WindowOcclusionState.h | 4+++-
Mwidget/android/nsWindow.cpp | 14+++++++-------
Mwidget/android/nsWindow.h | 11++++++-----
Mwidget/cocoa/nsCocoaWindow.h | 6+++---
Mwidget/cocoa/nsCocoaWindow.mm | 28++++++++++++++--------------
Mwidget/cocoa/nsMenuX.mm | 2+-
Mwidget/cocoa/nsToolkit.mm | 4++--
Mwidget/gtk/WaylandVsyncSource.cpp | 1+
Mwidget/gtk/nsWidgetFactory.cpp | 2+-
Mwidget/gtk/nsWindow.cpp | 19++++++++++---------
Mwidget/gtk/nsWindow.h | 10+++++-----
Mwidget/headless/HeadlessWidget.cpp | 10++++++----
Mwidget/headless/HeadlessWidget.h | 8++++----
Mwidget/moz.build | 5++---
Dwidget/nsBaseWidget.cpp | 3507-------------------------------------------------------------------------------
Mwidget/nsBaseWidget.h | 750-------------------------------------------------------------------------------
Awidget/nsIWidget.cpp | 3541+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mwidget/nsIWidget.h | 720++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mwidget/tests/gtest/MockWinWidget.cpp | 2+-
Mwidget/tests/gtest/MockWinWidget.h | 5+++--
Mwidget/uikit/nsAppShell.mm | 1+
Mwidget/uikit/nsWindow.h | 4++--
Mwidget/uikit/nsWindow.mm | 10+++++-----
Mwidget/windows/CompositorWidgetChild.cpp | 2+-
Mwidget/windows/DirectManipulationOwner.cpp | 1+
Mwidget/windows/WinWindowOcclusionTracker.cpp | 12++++++------
Mwidget/windows/WinWindowOcclusionTracker.h | 8++++----
Mwidget/windows/nsWindow.cpp | 45+++++++++++++++------------------------------
Mwidget/windows/nsWindow.h | 10+++++-----
Mwidget/windows/nsWindowDefs.h | 2+-
Mwidget/windows/nsWindowGfx.cpp | 4++--
64 files changed, 4325 insertions(+), 4610 deletions(-)

diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp @@ -42,6 +42,7 @@ #include "mozilla/dom/File.h" #include "mozilla/dom/FileBinding.h" #include "mozilla/dom/FunctionBinding.h" +#include "mozilla/dom/MouseEventBinding.h" #include "mozilla/dom/Touch.h" #include "mozilla/dom/UserActivation.h" #include "mozilla/layers/APZCCallbackHelper.h" diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp @@ -74,7 +74,6 @@ #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/toolkit/library/buildid_reader_ffi.h" #include "nsAppRunner.h" -#include "nsBaseWidget.h" #include "nsContentUtils.h" #include "nsDirectoryService.h" #include "nsDirectoryServiceDefs.h" @@ -107,6 +106,7 @@ #include "nsIURI.h" #include "nsIWebNavigation.h" #include "nsIWebProgress.h" +#include "nsIWidget.h" #include "nsIXULRuntime.h" #include "nsLayoutUtils.h" #include "nsNameSpaceManager.h" @@ -1107,7 +1107,7 @@ bool nsFrameLoader::ShowRemoteFrame(nsSubDocumentFrame* aFrame) { // We never want to host remote frameloaders in simple popups, like menus. nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent); - if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) { + if (!widget || widget->IsSmallPopup()) { return false; } diff --git a/gfx/docs/Silk.rst b/gfx/docs/Silk.rst @@ -105,9 +105,9 @@ scheduled or required. Every ``CompositorBridgeParent`` is associated and tied to one ``CompositorVsyncScheduler::Observer``, which is associated with the ``CompositorVsyncDispatcher``. Each ``CompositorBridgeParent`` is associated with one widget and is created -when a new platform window or ``nsBaseWidget`` is created. The +when a new platform window or ``nsIWidget`` is created. The ``CompositorBridgeParent``, ``CompositorVsyncDispatcher``, -``CompositorVsyncScheduler::Observer``, and ``nsBaseWidget`` all have +``CompositorVsyncScheduler::Observer``, and ``nsIWidget`` all have the same lifetimes, which are created and destroyed together. Out-of-process Compositors @@ -142,8 +142,8 @@ CompositorVsyncDispatcher ------------------------- The ``CompositorVsyncDispatcher`` executes on the *Hardware Vsync -Thread*. It contains references to the ``nsBaseWidget`` it is associated -with and has a lifetime equal to the ``nsBaseWidget``. The +Thread*. It contains references to the ``nsIWidget`` it is associated +with and has a lifetime equal to the ``nsIWidget``. The ``CompositorVsyncDispatcher`` is responsible for notifying the ``CompositorBridgeParent`` that a vsync event has occurred. There can be multiple ``CompositorVsyncDispatchers`` per ``Display``, one @@ -241,10 +241,10 @@ occurring. Widget, Compositor, CompositorVsyncDispatcher, GeckoTouchDispatcher Shutdown Procedure -------------------------------------------------------------------------------------- -When the `nsBaseWidget shuts -down <https://hg.mozilla.org/mozilla-central/file/0df249a0e4d3/widget/nsBaseWidget.cpp#l182>`__ -- It calls nsBaseWidget::DestroyCompositor on the *Gecko Main Thread*. -During nsBaseWidget::DestroyCompositor, it first destroys the +When the `nsIWidget shuts +down <https://hg.mozilla.org/mozilla-central/file/0df249a0e4d3/widget/nsIWidget.cpp#l182>`__ +- It calls nsIWidget::DestroyCompositor on the *Gecko Main Thread*. +During nsIWidget::DestroyCompositor, it first destroys the CompositorBridgeChild. CompositorBridgeChild sends a sync IPC call to CompositorBridgeParent::RecvStop, which calls `CompositorBridgeParent::Destroy <https://hg.mozilla.org/mozilla-central/file/ab0490972e1e/gfx/layers/ipc/CompositorParent.cpp#l509>`__. @@ -259,11 +259,11 @@ all ipdl code can finish executing. The ``CompositorVsyncScheduler::Observer`` also unobserves from vsync and cancels any pending composite tasks. Once CompositorBridgeParent::RecvStop finishes, the *main thread* in the -parent process continues shutting down the nsBaseWidget. +parent process continues shutting down the nsIWidget. At the same time, the *Compositor thread* is executing tasks until CompositorBridgeParent::DeferredDestroy runs, which flushes the -compositor message loop. Now we have two tasks as both the nsBaseWidget +compositor message loop. Now we have two tasks as both the nsIWidget releases a reference to the Compositor on the *main thread* during destruction and the CompositorBridgeParent::DeferredDestroy releases a reference to the CompositorBridgeParent on the *Compositor Thread*. @@ -272,9 +272,9 @@ thread* once both references are gone due to explicit `main thread destruction <https://hg.mozilla.org/mozilla-central/file/50b95032152c/gfx/layers/ipc/CompositorParent.h#l148>`__. With the ``CompositorVsyncScheduler::Observer``, any accesses to the -widget after nsBaseWidget::DestroyCompositor executes are invalid. Any +widget after nsIWidget::DestroyCompositor executes are invalid. Any accesses to the compositor between the time the -nsBaseWidget::DestroyCompositor runs and the +nsIWidget::DestroyCompositor runs and the CompositorVsyncScheduler::Observer’s destructor runs aren’t safe yet a hardware vsync event could occur between these times. Since any tasks posted on the Compositor loop after @@ -290,14 +290,14 @@ notification executes on the *hardware vsync thread*, it would post a task to the Compositor loop and may execute after CompositorBridgeParent::DeferredDestroy. Thus, we explicitly shut down vsync events in the ``CompositorVsyncDispatcher`` and -``CompositorVsyncScheduler::Observer`` during nsBaseWidget::Shutdown to +``CompositorVsyncScheduler::Observer`` during nsIWidget::Shutdown to prevent any vsync tasks from executing after CompositorBridgeParent::DeferredDestroy. The ``CompositorVsyncDispatcher`` may be destroyed on either the *main -thread* or *Compositor Thread*, since both the nsBaseWidget and +thread* or *Compositor Thread*, since both the nsIWidget and ``CompositorVsyncScheduler::Observer`` race to destroy on different -threads. nsBaseWidget is destroyed on the *main thread* and releases a +threads. nsIWidget is destroyed on the *main thread* and releases a reference to the ``CompositorVsyncDispatcher`` during destruction. The ``CompositorVsyncScheduler::Observer`` has a race to be destroyed either during CompositorBridgeParent shutdown or from the @@ -440,7 +440,7 @@ to the correct ``RefreshTimer``. Object Lifetime --------------- -1. CompositorVsyncDispatcher - Lives as long as the nsBaseWidget +1. CompositorVsyncDispatcher - Lives as long as the nsIWidget associated with the VsyncDispatcher 2. CompositorVsyncScheduler::Observer - Lives and dies the same time as the CompositorBridgeParent. diff --git a/gfx/ipc/CompositorSession.cpp b/gfx/ipc/CompositorSession.cpp @@ -16,7 +16,7 @@ namespace layers { using namespace gfx; -CompositorSession::CompositorSession(nsBaseWidget* aWidget, +CompositorSession::CompositorSession(nsIWidget* aWidget, CompositorWidgetDelegate* aDelegate, CompositorBridgeChild* aChild, const LayersId& aRootLayerTreeId) diff --git a/gfx/ipc/CompositorSession.h b/gfx/ipc/CompositorSession.h @@ -14,7 +14,7 @@ # include "mozilla/layers/UiCompositorControllerChild.h" #endif // defined(MOZ_WIDGET_ANDROID) -class nsBaseWidget; +class nsIWidget; namespace mozilla { namespace widget { @@ -82,13 +82,13 @@ class CompositorSession { } #endif // defined(MOZ_WIDGET_ANDROID) protected: - CompositorSession(nsBaseWidget* aWidget, CompositorWidgetDelegate* aDelegate, + CompositorSession(nsIWidget* aWidget, CompositorWidgetDelegate* aDelegate, CompositorBridgeChild* aChild, const LayersId& aRootLayerTreeId); virtual ~CompositorSession(); protected: - nsBaseWidget* mWidget; + nsIWidget* mWidget; CompositorWidgetDelegate* mCompositorWidgetDelegate; RefPtr<CompositorBridgeChild> mCompositorBridgeChild; LayersId mRootLayerTreeId; diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp @@ -46,7 +46,7 @@ #ifdef MOZ_WIDGET_SUPPORTS_OOP_COMPOSITING # include "mozilla/widget/CompositorWidgetChild.h" #endif -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsContentUtils.h" #include "VRManagerChild.h" #include "VRManagerParent.h" @@ -558,7 +558,7 @@ bool GPUProcessManager::EnsureVRManager() { #if defined(MOZ_WIDGET_ANDROID) RefPtr<UiCompositorControllerChild> -GPUProcessManager::CreateUiCompositorController(nsBaseWidget* aWidget, +GPUProcessManager::CreateUiCompositorController(nsIWidget* aWidget, const LayersId aId) { MOZ_ASSERT(IsGPUReady()); @@ -1221,7 +1221,7 @@ void GPUProcessManager::StopObserving() { } already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor( - nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* aWidget, WebRenderLayerManager* aLayerManager, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId, bool* aRetryOut) { @@ -1287,7 +1287,7 @@ already_AddRefed<CompositorSession> GPUProcessManager::CreateTopLevelCompositor( } RefPtr<CompositorSession> GPUProcessManager::CreateRemoteSession( - nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* aWidget, WebRenderLayerManager* aLayerManager, const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId) { diff --git a/gfx/ipc/GPUProcessManager.h b/gfx/ipc/GPUProcessManager.h @@ -21,7 +21,7 @@ #include "mozilla/webrender/WebRenderTypes.h" #include "nsIObserver.h" #include "nsThreadUtils.h" -class nsBaseWidget; +class nsIWidget; enum class DeviceResetReason; namespace mozilla { @@ -109,7 +109,7 @@ class GPUProcessManager final : public GPUProcessHost::Listener { nsresult EnsureGPUReady(bool aRetryAfterFallback = true); already_AddRefed<CompositorSession> CreateTopLevelCompositor( - nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* aWidget, WebRenderLayerManager* aLayerManager, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId, bool* aRetry); @@ -331,11 +331,11 @@ class GPUProcessManager final : public GPUProcessHost::Listener { #if defined(MOZ_WIDGET_ANDROID) RefPtr<UiCompositorControllerChild> CreateUiCompositorController( - nsBaseWidget* aWidget, const LayersId aId); + nsIWidget* aWidget, const LayersId aId); #endif // defined(MOZ_WIDGET_ANDROID) RefPtr<CompositorSession> CreateRemoteSession( - nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* aWidget, WebRenderLayerManager* aLayerManager, const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint64_t aInnerWindowId); diff --git a/gfx/ipc/InProcessCompositorSession.cpp b/gfx/ipc/InProcessCompositorSession.cpp @@ -14,13 +14,13 @@ #include "mozilla/layers/IAPZCTreeManager.h" #include "mozilla/widget/CompositorWidget.h" #include "mozilla/widget/PlatformWidgetTypes.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" namespace mozilla { namespace layers { InProcessCompositorSession::InProcessCompositorSession( - nsBaseWidget* aWidget, widget::CompositorWidget* aCompositorWidget, + nsIWidget* aWidget, widget::CompositorWidget* aCompositorWidget, CompositorBridgeChild* aChild, CompositorBridgeParent* aParent) : CompositorSession(aWidget, aCompositorWidget->AsDelegate(), aChild, aParent->RootLayerTreeId()), @@ -31,7 +31,7 @@ InProcessCompositorSession::InProcessCompositorSession( /* static */ RefPtr<InProcessCompositorSession> InProcessCompositorSession::Create( - nsBaseWidget* aWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* aWidget, WebRenderLayerManager* aLayerManager, const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint32_t aNamespace, @@ -63,7 +63,7 @@ RefPtr<InProcessCompositorSession> InProcessCompositorSession::Create( void InProcessCompositorSession::NotifySessionLost() { // Hold a reference to mWidget since NotifyCompositorSessionLost may // release the last reference mid-execution. - RefPtr<nsBaseWidget> widget(mWidget); + RefPtr<nsIWidget> widget(mWidget); widget->NotifyCompositorSessionLost(this); } diff --git a/gfx/ipc/InProcessCompositorSession.h b/gfx/ipc/InProcessCompositorSession.h @@ -23,7 +23,7 @@ class WebRenderLayerManager; class InProcessCompositorSession final : public CompositorSession { public: static RefPtr<InProcessCompositorSession> Create( - nsBaseWidget* baseWidget, WebRenderLayerManager* aLayerManager, + nsIWidget* baseWidget, WebRenderLayerManager* aLayerManager, const LayersId& aRootLayerTreeId, CSSToLayoutDeviceScale aScale, const CompositorOptions& aOptions, bool aUseExternalSurfaceSize, const gfx::IntSize& aSurfaceSize, uint32_t aNamespace, @@ -38,7 +38,7 @@ class InProcessCompositorSession final : public CompositorSession { void NotifySessionLost(); private: - InProcessCompositorSession(nsBaseWidget* aWidget, + InProcessCompositorSession(nsIWidget* aWidget, widget::CompositorWidget* aCompositorWidget, CompositorBridgeChild* aChild, CompositorBridgeParent* aParent); diff --git a/gfx/ipc/RemoteCompositorSession.cpp b/gfx/ipc/RemoteCompositorSession.cpp @@ -13,7 +13,7 @@ #include "mozilla/layers/CompositorBridgeChild.h" #include "mozilla/layers/GeckoContentController.h" #include "mozilla/Unused.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #if defined(MOZ_WIDGET_ANDROID) # include "mozilla/layers/UiCompositorControllerChild.h" #endif // defined(MOZ_WIDGET_ANDROID) @@ -25,7 +25,7 @@ using namespace gfx; using namespace widget; RemoteCompositorSession::RemoteCompositorSession( - nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + nsIWidget* aWidget, CompositorBridgeChild* aChild, CompositorWidgetDelegate* aWidgetDelegate, APZCTreeManagerChild* aAPZ, const LayersId& aRootLayerTreeId) : CompositorSession(aWidget, aWidgetDelegate, aChild, aRootLayerTreeId), @@ -48,7 +48,7 @@ RemoteCompositorSession::~RemoteCompositorSession() { void RemoteCompositorSession::NotifySessionLost() { // Hold a reference to mWidget since NotifyCompositorSessionLost may // release the last reference mid-execution. - RefPtr<nsBaseWidget> widget(mWidget); + RefPtr<nsIWidget> widget(mWidget); // Re-entrancy should be impossible: when we are being notified of a lost // session, we have by definition not shut down yet. We will shutdown, but // then will be removed from the notification list. diff --git a/gfx/ipc/RemoteCompositorSession.h b/gfx/ipc/RemoteCompositorSession.h @@ -19,7 +19,7 @@ class APZCTreeManagerChild; class RemoteCompositorSession final : public CompositorSession { public: - RemoteCompositorSession(nsBaseWidget* aWidget, CompositorBridgeChild* aChild, + RemoteCompositorSession(nsIWidget* aWidget, CompositorBridgeChild* aChild, CompositorWidgetDelegate* aWidgetDelegate, APZCTreeManagerChild* aAPZ, const LayersId& aRootLayerTreeId); diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -2638,7 +2638,7 @@ void APZCTreeManager::UpdateZoomConstraints( const Maybe<ZoomConstraints>& aConstraints) { if (!GetUpdater()->IsUpdaterThread()) { // This can happen if we're in the UI process and got a call directly from - // nsBaseWidget or from a content process over PAPZCTreeManager. In that + // nsIWidget or from a content process over PAPZCTreeManager. In that // case we get this call on the compositor thread, which may be different // from the updater thread. It can also happen in the GPU process if that is // enabled, since the call will go over PAPZCTreeManager and arrive on the diff --git a/gfx/layers/apz/util/InputAPZContext.h b/gfx/layers/apz/util/InputAPZContext.h @@ -19,7 +19,7 @@ namespace layers { // relevant to APZ. // // There are two types of information bits propagated using this class. One -// type is propagated "downwards" (from a process entry point like nsBaseWidget +// type is propagated "downwards" (from a process entry point like nsIWidget // or BrowserChild) into deeper code that is run during complicated operations // like event dispatch. The other type is information that is propagated // "upwards", from the deeper code back to the entry point. diff --git a/gfx/layers/ipc/APZCTreeManagerParent.cpp b/gfx/layers/ipc/APZCTreeManagerParent.cpp @@ -149,10 +149,10 @@ mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag( mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll( const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) { // Unlike RecvStartScrollbarDrag(), this message comes from the parent - // process (via nsBaseWidget::mAPZC) rather than from the child process + // process (via nsIWidget::mAPZC) rather than from the child process // (via BrowserChild::mApzcTreeManager), so there is no need to check the // layers id against mLayersId (and in any case, it wouldn't match, because - // mLayersId stores the parent process's layers id, while nsBaseWidget is + // mLayersId stores the parent process's layers id, while nsIWidget is // sending the child process's layers id). mUpdater->RunOnControllerThread( diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp @@ -90,7 +90,7 @@ CompositorVsyncScheduler::~CompositorVsyncScheduler() { MOZ_ASSERT(!mIsObservingVsync); MOZ_ASSERT(!mVsyncObserver); // The CompositorVsyncDispatcher is cleaned up before this in the - // nsBaseWidget, which stops vsync listeners + // nsIWidget, which stops vsync listeners mVsyncSchedulerOwner = nullptr; } diff --git a/gfx/layers/ipc/UiCompositorControllerChild.cpp b/gfx/layers/ipc/UiCompositorControllerChild.cpp @@ -15,7 +15,7 @@ #include "mozilla/ipc/Endpoint.h" #include "mozilla/StaticPrefs_layers.h" #include "mozilla/StaticPtr.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsProxyRelease.h" #include "nsThreadUtils.h" @@ -37,7 +37,7 @@ namespace layers { /* static */ RefPtr<UiCompositorControllerChild> UiCompositorControllerChild::CreateForSameProcess( - const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget) { + const LayersId& aRootLayerTreeId, nsIWidget* aWidget) { RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(0, aWidget); child->mParent = new UiCompositorControllerParent(aRootLayerTreeId); @@ -53,7 +53,7 @@ UiCompositorControllerChild::CreateForSameProcess( RefPtr<UiCompositorControllerChild> UiCompositorControllerChild::CreateForGPUProcess( const uint64_t& aProcessToken, - Endpoint<PUiCompositorControllerChild>&& aEndpoint, nsBaseWidget* aWidget) { + Endpoint<PUiCompositorControllerChild>&& aEndpoint, nsIWidget* aWidget) { RefPtr<UiCompositorControllerChild> child = new UiCompositorControllerChild(aProcessToken, aWidget); @@ -250,7 +250,7 @@ mozilla::ipc::IPCResult UiCompositorControllerChild::RecvScreenPixels( // private: UiCompositorControllerChild::UiCompositorControllerChild( - const uint64_t& aProcessToken, nsBaseWidget* aWidget) + const uint64_t& aProcessToken, nsIWidget* aWidget) : mIsOpen(false), mProcessToken(aProcessToken), mWidget(aWidget) {} UiCompositorControllerChild::~UiCompositorControllerChild() = default; diff --git a/gfx/layers/ipc/UiCompositorControllerChild.h b/gfx/layers/ipc/UiCompositorControllerChild.h @@ -19,7 +19,7 @@ # include "mozilla/java/CompositorSurfaceManagerWrappers.h" #endif -class nsBaseWidget; +class nsIWidget; namespace mozilla { namespace layers { @@ -32,11 +32,10 @@ class UiCompositorControllerChild final NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UiCompositorControllerChild, final) static RefPtr<UiCompositorControllerChild> CreateForSameProcess( - const LayersId& aRootLayerTreeId, nsBaseWidget* aWidget); + const LayersId& aRootLayerTreeId, nsIWidget* aWidget); static RefPtr<UiCompositorControllerChild> CreateForGPUProcess( const uint64_t& aProcessToken, - Endpoint<PUiCompositorControllerChild>&& aEndpoint, - nsBaseWidget* aWidget); + Endpoint<PUiCompositorControllerChild>&& aEndpoint, nsIWidget* aWidget); bool Pause(); bool Resume(); @@ -88,7 +87,7 @@ class UiCompositorControllerChild final private: explicit UiCompositorControllerChild(const uint64_t& aProcessToken, - nsBaseWidget* aWidget); + nsIWidget* aWidget); virtual ~UiCompositorControllerChild(); void OpenForSameProcess(); void OpenForGPUProcess(Endpoint<PUiCompositorControllerChild>&& aEndpoint); @@ -103,7 +102,7 @@ class UiCompositorControllerChild final Maybe<int32_t> mMaxToolbarHeight; Maybe<uint32_t> mDefaultClearColor; Maybe<bool> mLayerUpdateEnabled; - RefPtr<nsBaseWidget> mWidget; + RefPtr<nsIWidget> mWidget; // Should only be set when compositor is in process. RefPtr<UiCompositorControllerParent> mParent; diff --git a/gfx/layers/opengl/TextureHostOGL.h b/gfx/layers/opengl/TextureHostOGL.h @@ -64,10 +64,10 @@ already_AddRefed<TextureHost> CreateTextureHostOGL( * * Note that it is important to be careful about the ownership model with * the OpenGL backend, due to some widget limitation on Linux: before - * the nsBaseWidget associated with our OpenGL context has been completely + * the nsIWidget associated with our OpenGL context has been completely * deleted, every resource belonging to the OpenGL context MUST have been * released. At the moment the teardown sequence happens in the middle of - * the nsBaseWidget's destructor, meaning that at a given moment we must be + * the nsIWidget's destructor, meaning that at a given moment we must be * able to easily find and release all the GL resources. * The point is: be careful about the ownership model and limit the number * of objects sharing references to GL resources to make the tear down diff --git a/gfx/tests/gtest/MockWidget.cpp b/gfx/tests/gtest/MockWidget.cpp @@ -5,4 +5,4 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "MockWidget.h" -NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget) +NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsIWidget) diff --git a/gfx/tests/gtest/MockWidget.h b/gfx/tests/gtest/MockWidget.h @@ -9,7 +9,7 @@ #include "mozilla/gfx/Point.h" #include "mozilla/widget/InProcessCompositorWidget.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "GLContext.h" #include "GLContextProvider.h" @@ -19,7 +19,7 @@ using mozilla::gl::GLContextProvider; using mozilla::gfx::IntSize; -class MockWidget : public nsBaseWidget { +class MockWidget : public nsIWidget { public: MockWidget() : mCompWidth(0), mCompHeight(0) {} MockWidget(int aWidth, int aHeight) diff --git a/gfx/vr/FxRWindowManager.cpp b/gfx/vr/FxRWindowManager.cpp @@ -7,6 +7,7 @@ #include "mozilla/Assertions.h" #include "nsPIDOMWindow.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/WidgetUtils.h" #include "nsWindow.h" diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp @@ -6218,7 +6218,7 @@ void PresShell::ProcessSynthMouseOrPointerMoveEvent( if (pointShell) { // Since this gets run in a refresh tick there isn't an InputAPZContext on - // the stack from the nsBaseWidget. We need to simulate one with at least + // the stack from the nsIWidget. We need to simulate one with at least // the correct target guid, so that the correct callback transform gets // applied if this event goes to a child process. The input block id is set // to 0 because this is a synthetic event which doesn't really belong to any diff --git a/widget/CompositorWidget.cpp b/widget/CompositorWidget.cpp @@ -4,7 +4,7 @@ #include "CompositorWidget.h" #include "GLConsts.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "VsyncDispatcher.h" namespace mozilla { diff --git a/widget/CompositorWidget.h b/widget/CompositorWidget.h @@ -17,7 +17,7 @@ #endif class nsIWidget; -class nsBaseWidget; +class nsIWidget; namespace mozilla { class VsyncObserver; diff --git a/widget/IMEData.h b/widget/IMEData.h @@ -529,7 +529,7 @@ struct InputContext final { } }; -// FYI: Implemented in nsBaseWidget.cpp +// FYI: Implemented in nsIWidget.cpp const char* ToChar(InputContext::Origin aOrigin); struct InputContextAction final { @@ -678,7 +678,7 @@ enum IMEMessage : IMEMessageType { REQUEST_TO_CANCEL_COMPOSITION }; -// FYI: Implemented in nsBaseWidget.cpp +// FYI: Implemented in nsIWidget.cpp const char* ToChar(IMEMessage aIMEMessage); struct IMENotification final { diff --git a/widget/InProcessCompositorWidget.cpp b/widget/InProcessCompositorWidget.cpp @@ -5,7 +5,7 @@ #include "InProcessCompositorWidget.h" #include "mozilla/VsyncDispatcher.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" namespace mozilla { namespace widget { @@ -24,14 +24,14 @@ RefPtr<CompositorWidget> CompositorWidget::CreateLocal( // only remaining explanation that doesn't involve memory corruption, // so placing a release assert here. For even more sanity-checking, we // do it after the static_cast. - nsBaseWidget* widget = static_cast<nsBaseWidget*>(aWidget); + nsIWidget* widget = static_cast<nsIWidget*>(aWidget); MOZ_RELEASE_ASSERT(widget); return new InProcessCompositorWidget(aOptions, widget); } #endif InProcessCompositorWidget::InProcessCompositorWidget( - const layers::CompositorOptions& aOptions, nsBaseWidget* aWidget) + const layers::CompositorOptions& aOptions, nsIWidget* aWidget) : CompositorWidget(aOptions), mWidget(aWidget), mCanary(CANARY_VALUE), diff --git a/widget/InProcessCompositorWidget.h b/widget/InProcessCompositorWidget.h @@ -11,11 +11,11 @@ namespace mozilla { namespace widget { // This version of CompositorWidget implements a wrapper around -// nsBaseWidget. +// nsIWidget. class InProcessCompositorWidget : public CompositorWidget { public: InProcessCompositorWidget(const layers::CompositorOptions& aOptions, - nsBaseWidget* aWidget); + nsIWidget* aWidget); bool PreRender(WidgetRenderingContext* aManager) override; void PostRender(WidgetRenderingContext* aManager) override; @@ -39,12 +39,12 @@ class InProcessCompositorWidget : public CompositorWidget { nsIWidget* RealWidget() override; protected: - nsBaseWidget* mWidget; + nsIWidget* mWidget; // Bug 1679368: Maintain an additional widget pointer, constant, and // function for sanity checking while we chase a crash. static const char* CANARY_VALUE; const char* mCanary; - nsBaseWidget* mWidgetSanity; + nsIWidget* mWidgetSanity; void CheckWidgetSanity(); }; diff --git a/widget/InputData.h b/widget/InputData.h @@ -25,6 +25,8 @@ template <class E> struct already_AddRefed; class nsIWidget; +enum TouchPointerState : uint8_t; + namespace mozilla { namespace layers { @@ -837,6 +839,18 @@ class KeyboardInput : public InputData { KeyboardInput(); }; +/** + * For widgets that support synthesizing native touch events, this function + * can be used to manage the current state of synthetic pointers. Each widget + * must maintain its own MultiTouchInput instance and pass it in as the state, + * along with the desired parameters for the changes. This function returns + * a new MultiTouchInput object that is ready to be dispatched. + */ +MultiTouchInput UpdateSynthesizedTouchState( + MultiTouchInput* aState, TimeStamp aTimeStamp, uint32_t aPointerId, + TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, + double aPointerPressure, uint32_t aPointerOrientation); + } // namespace mozilla #endif // InputData_h__ diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp @@ -75,7 +75,7 @@ static bool MightNeedIMEFocus(const widget::InitData* aInitData) { #endif } -NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget, nsBaseWidget, +NS_IMPL_ISUPPORTS_INHERITED(PuppetWidget, nsIWidget, TextEventDispatcherListener) PuppetWidget::PuppetWidget(BrowserChild* aBrowserChild) diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h @@ -17,7 +17,7 @@ #include "mozilla/gfx/2D.h" #include "mozilla/RefPtr.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsCOMArray.h" #include "nsThreadUtils.h" #include "mozilla/Attributes.h" @@ -26,6 +26,8 @@ #include "mozilla/TextEventDispatcherListener.h" #include "mozilla/layers/MemoryPressureObserver.h" +class nsRefreshDriver; + namespace mozilla { enum class NativeKeyBindingsType : uint8_t; @@ -41,7 +43,7 @@ namespace widget { struct AutoCacheNativeKeyCommands; -class PuppetWidget final : public nsBaseWidget, +class PuppetWidget final : public nsIWidget, public TextEventDispatcherListener, public layers::MemoryPressureListener { typedef mozilla::CSSRect CSSRect; @@ -54,7 +56,7 @@ class PuppetWidget final : public nsBaseWidget, typedef mozilla::widget::TextEventDispatcherListener TextEventDispatcherListener; - typedef nsBaseWidget Base; + typedef nsIWidget Base; // The width and height of the "widget" are clamped to this. public: @@ -68,7 +70,7 @@ class PuppetWidget final : public nsBaseWidget, // PuppetWidget creation is infallible, hence InfallibleCreate(), which // Create() calls. - using nsBaseWidget::Create; // for Create signature not overridden here + using nsIWidget::Create; // for Create signature not overridden here nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, widget::InitData* aInitData = nullptr) override; void InfallibleCreate(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, @@ -148,7 +150,7 @@ class PuppetWidget final : public nsBaseWidget, friend struct AutoCacheNativeKeyCommands; // - // nsBaseWidget methods we override + // nsIWidget methods we override // // Documents loaded in child processes are always subdocuments of @@ -284,7 +286,7 @@ class PuppetWidget final : public nsBaseWidget, nsresult GetSystemFont(nsCString& aFontName) override; // TextEventDispatcherListener - using nsBaseWidget::NotifyIME; + using nsIWidget::NotifyIME; NS_IMETHOD NotifyIME(TextEventDispatcher* aTextEventDispatcher, const IMENotification& aNotification) override; NS_IMETHOD_(IMENotificationRequests) GetIMENotificationRequests() override; diff --git a/widget/VsyncDispatcher.cpp b/widget/VsyncDispatcher.cpp @@ -74,9 +74,9 @@ void CompositorVsyncDispatcher::SetCompositorVsyncObserver( } void CompositorVsyncDispatcher::Shutdown() { - // Need to explicitly remove CompositorVsyncDispatcher when the nsBaseWidget + // Need to explicitly remove CompositorVsyncDispatcher when the nsIWidget // shuts down. Otherwise, we would get dead vsync notifications between when - // the nsBaseWidget shuts down and the CompositorBridgeParent shuts down. + // the nsIWidget shuts down and the CompositorBridgeParent shuts down. MOZ_ASSERT(XRE_IsParentProcess()); MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mDidShutdown); diff --git a/widget/WidgetMessageUtils.h b/widget/WidgetMessageUtils.h @@ -58,9 +58,9 @@ struct ParamTraits<nsIWidget::TouchpadGesturePhase> nsIWidget::TouchpadGesturePhase::PHASE_END> {}; template <> -struct ParamTraits<nsIWidget::TouchPointerState> - : public BitFlagsEnumSerializer<nsIWidget::TouchPointerState, - nsIWidget::TouchPointerState::ALL_BITS> {}; +struct ParamTraits<TouchPointerState> + : public BitFlagsEnumSerializer<TouchPointerState, + TouchPointerState::ALL_BITS> {}; template <> struct ParamTraits<mozilla::DimensionKind> diff --git a/widget/WidgetUtils.h b/widget/WidgetUtils.h @@ -18,7 +18,7 @@ class nsPIDOMWindowOuter; namespace mozilla { // NB: these must match up with pseudo-enum in nsIScreen.idl. -enum ScreenRotation { +enum ScreenRotation : uint8_t { ROTATION_0 = 0, ROTATION_90, ROTATION_180, diff --git a/widget/WindowOcclusionState.h b/widget/WindowOcclusionState.h @@ -7,12 +7,14 @@ #ifndef widget_WindowOcclusionState_h #define widget_WindowOcclusionState_h +#include <cstdint> + namespace mozilla { namespace widget { // nsWindow's window occlusion state. On Windows, it is tracked by // WinWindowOcclusionTracker. -enum class OcclusionState { +enum class OcclusionState : uint8_t { // The window's occlusion state isn't tracked (NotifyOcclusionState()) or // hasn't been computed yet. UNKNOWN = 0, diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp @@ -2243,7 +2243,7 @@ nsWindow::~nsWindow() { ALOG("nsWindow %p destructor", (void*)this); // The mCompositorSession should have been cleaned up in nsWindow::Destroy() // DestroyLayerManager() will call DestroyCompositor() which will crash if - // called from nsBaseWidget destructor. See Bug 1392705 + // called from nsIWidget destructor. See Bug 1392705 MOZ_ASSERT(!mCompositorSession); } @@ -2290,7 +2290,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, void nsWindow::Destroy() { MutexAutoLock lock(mDestroyMutex); - nsBaseWidget::mOnDestroyCalled = true; + nsIWidget::mOnDestroyCalled = true; // Disassociate our native object from GeckoView. mGeckoViewSupport.Detach(); @@ -2300,15 +2300,15 @@ void nsWindow::Destroy() { // Ensure the compositor has been shutdown before this nsWindow is potentially // deleted - nsBaseWidget::DestroyCompositor(); + nsIWidget::DestroyCompositor(); - nsBaseWidget::Destroy(); + nsIWidget::Destroy(); if (IsTopLevel()) { gTopLevelWindows.RemoveElement(this); } - nsBaseWidget::OnDestroy(); + nsIWidget::OnDestroy(); #ifdef DEBUG_ANDROID_WIDGET DumpWindows(); @@ -2709,7 +2709,7 @@ void nsWindow::CreateLayerManager() { void nsWindow::NotifyCompositorSessionLost( mozilla::layers::CompositorSession* aSession) { - nsBaseWidget::NotifyCompositorSessionLost(aSession); + nsIWidget::NotifyCompositorSessionLost(aSession); DispatchToUiThread("nsWindow::NotifyCompositorSessionLost", [lvs = mLayerViewSupport] { @@ -3257,7 +3257,7 @@ uint32_t nsWindow::GetMaxTouchPoints() const { void nsWindow::UpdateZoomConstraints( const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, const mozilla::Maybe<ZoomConstraints>& aConstraints) { - nsBaseWidget::UpdateZoomConstraints(aPresShellId, aViewId, aConstraints); + nsIWidget::UpdateZoomConstraints(aPresShellId, aViewId, aConstraints); } CompositorBridgeChild* nsWindow::GetCompositorBridgeChild() const { diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h @@ -9,7 +9,7 @@ #include "AndroidGraphics.h" #include "mozilla/layers/CompositorScrollUpdate.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "gfxPoint.h" #include "nsIUserIdleServiceInternal.h" #include "nsTArray.h" @@ -27,6 +27,7 @@ struct ANPEvent; namespace mozilla { class WidgetTouchEvent; +class MouseInput; namespace layers { class CompositorBridgeChild; @@ -53,16 +54,16 @@ class SessionAccessibility; } // namespace a11y } // namespace mozilla -class nsWindow final : public nsBaseWidget { +class nsWindow final : public nsIWidget { private: virtual ~nsWindow(); public: - using nsBaseWidget::GetWindowRenderer; + using nsIWidget::GetWindowRenderer; nsWindow(); - NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsBaseWidget) + NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsIWidget) static void InitNatives(); void OnGeckoViewReady(); @@ -154,7 +155,7 @@ class nsWindow final : public nsBaseWidget { // nsIWidget // - using nsBaseWidget::Create; // for Create signature not overridden here + using nsIWidget::Create; // for Create signature not overridden here [[nodiscard]] nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, InitData* aInitData) override; diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h @@ -13,7 +13,7 @@ #include "mozilla/RefPtr.h" #include "mozilla/layers/NativeLayerRootRemoteMacChild.h" #include "mozilla/layers/NativeLayerRootRemoteMacParent.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsCocoaUtils.h" #include "nsTouchBar.h" #include "ViewRegion.h" @@ -195,10 +195,10 @@ class TextInputHandler; - (void)windowMainStateChanged; @end -class nsCocoaWindow final : public nsBaseWidget { +class nsCocoaWindow final : public nsIWidget { private: friend class nsChildView; - typedef nsBaseWidget Inherited; + typedef nsIWidget Inherited; public: nsCocoaWindow(); diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm @@ -145,7 +145,7 @@ static void RollUpPopups(nsIRollupListener::AllowAnimations aAllowAnimations = pm->RollupTooltips(); } - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); if (!rollupListener) { return; } @@ -311,7 +311,7 @@ void nsCocoaWindow::SetCursor(const Cursor& aCursor) { return; // Don't change the cursor during dragging. } - nsBaseWidget::SetCursor(aCursor); + nsIWidget::SetCursor(aCursor); bool forceUpdate = mUpdateCursor; mUpdateCursor = false; @@ -803,7 +803,7 @@ bool nsCocoaWindow::PaintWindowInDrawTarget( nsAutoRetainCocoaObject kungFuDeathGrip(mChildView); if (GetWindowRenderer()->GetBackendType() == LayersBackend::LAYERS_NONE) { - nsBaseWidget::AutoLayerManagerSetup setupLayerManager(this, &targetContext); + nsIWidget::AutoLayerManagerSetup setupLayerManager(this, &targetContext); return PaintWindow(aRegion); } return false; @@ -904,7 +904,7 @@ void nsCocoaWindow::CreateCompositor(int aWidth, int aHeight) { pm->EnsureGPUReady(); // Do the rest of the compositor setup. - nsBaseWidget::CreateCompositor(aWidth, aHeight); + nsIWidget::CreateCompositor(aWidth, aHeight); } void nsCocoaWindow::GetCompositorWidgetInitData( @@ -992,7 +992,7 @@ void nsCocoaWindow::DestroyCompositor() { &NativeLayerRootRemoteMacParent::Close)); } - nsBaseWidget::DestroyCompositor(); + nsIWidget::DestroyCompositor(); } void nsCocoaWindow::NotifyCompositorSessionLost( @@ -1016,7 +1016,7 @@ void nsCocoaWindow::NotifyCompositorSessionLost( withObject:nil afterDelay:kTriggerPaintDelayAfterGpuProcessCrash]; - nsBaseWidget::NotifyCompositorSessionLost(aSession); + nsIWidget::NotifyCompositorSessionLost(aSession); } void nsCocoaWindow::SetCompositorWidgetDelegate( @@ -1889,7 +1889,7 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong] return; } - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); NS_ENSURE_TRUE_VOID(rollupListener); nsCOMPtr<nsIWidget> widget = rollupListener->GetRollupWidget(); NS_ENSURE_TRUE_VOID(widget); @@ -1912,7 +1912,7 @@ NSEvent* gLastDragMouseDownEvent = nil; // [strong] BOOL consumeEvent = NO; - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); NS_ENSURE_TRUE(rollupListener, false); BOOL isWheelTypeEvent = [theEvent type] == NSEventTypeScrollWheel || @@ -5086,8 +5086,8 @@ void nsCocoaWindow::Destroy() { DestroyNativeWindow(); } - nsBaseWidget::OnDestroy(); - nsBaseWidget::Destroy(); + nsIWidget::OnDestroy(); + nsIWidget::Destroy(); } void* nsCocoaWindow::GetNativeData(uint32_t aDataType) { @@ -5396,7 +5396,7 @@ bool nsCocoaWindow::ShouldUseOffMainThreadCompositing() { // Use main-thread BasicLayerManager for drawing menus. return false; } - return nsBaseWidget::ShouldUseOffMainThreadCompositing(); + return nsIWidget::ShouldUseOffMainThreadCompositing(); } TransparencyMode nsCocoaWindow::GetTransparencyMode() { @@ -5508,7 +5508,7 @@ void nsCocoaWindow::SetSizeConstraints(const SizeConstraints& aConstraints) { : nsCocoaUtils::DevPixelsToCocoaPoints( c.mMaxSize.height, c.mScale.scale)}; mWindow.maxSize = maxSize; - nsBaseWidget::SetSizeConstraints(c); + nsIWidget::SetSizeConstraints(c); NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -6116,7 +6116,7 @@ void nsCocoaWindow::ProcessTransitions() { // show the Dock first, otherwise the newly-created window won't have // its minimize button enabled. See bug 526282. nsCocoaUtils::HideOSChromeOnScreen(true); - nsBaseWidget::InfallibleMakeFullScreen(true); + nsIWidget::InfallibleMakeFullScreen(true); mSuppressSizeModeEvents = false; UpdateFullscreenState(true, false); } @@ -6146,7 +6146,7 @@ void nsCocoaWindow::ProcessTransitions() { // show the Dock first, otherwise the newly-created window won't // have its minimize button enabled. See bug 526282. nsCocoaUtils::HideOSChromeOnScreen(false); - nsBaseWidget::InfallibleMakeFullScreen(false); + nsIWidget::InfallibleMakeFullScreen(false); mSuppressSizeModeEvents = false; UpdateFullscreenState(false, false); } diff --git a/widget/cocoa/nsMenuX.mm b/widget/cocoa/nsMenuX.mm @@ -31,7 +31,7 @@ #include "nsUnicharUtils.h" #include "nsGkAtoms.h" #include "nsCRT.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsIContent.h" #include "nsIDocumentObserver.h" diff --git a/widget/cocoa/nsToolkit.mm b/widget/cocoa/nsToolkit.mm @@ -29,7 +29,7 @@ extern "C" { #include "nsGkAtoms.h" #include "nsIRollupListener.h" #include "nsIWidget.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsIObserverService.h" @@ -160,7 +160,7 @@ void nsToolkit::MonitorAllProcessMouseEvents() { } nsIRollupListener* rollupListener = - nsBaseWidget:: + nsIWidget:: GetActiveRollupListener(); if (!rollupListener) { return; diff --git a/widget/gtk/WaylandVsyncSource.cpp b/widget/gtk/WaylandVsyncSource.cpp @@ -15,6 +15,7 @@ # include "nsGtkUtils.h" # include "mozilla/StaticPrefs_layout.h" # include "mozilla/StaticPrefs_widget.h" +# include "mozilla/widget/WindowOcclusionState.h" # include "nsWindow.h" # include <gdk/gdkwayland.h> diff --git a/widget/gtk/nsWidgetFactory.cpp b/widget/gtk/nsWidgetFactory.cpp @@ -13,7 +13,7 @@ #include "nsWidgetsCID.h" #include "nsAppShell.h" #include "nsAppShellSingleton.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsGtkKeyUtils.h" #include "nsLookAndFeel.h" #include "nsWindow.h" diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp @@ -48,6 +48,7 @@ #include "mozilla/layers/WebRenderLayerManager.h" #include "mozilla/layers/APZInputBridge.h" #include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/widget/WindowOcclusionState.h" #include "mozilla/Likely.h" #include "mozilla/Maybe.h" #include "mozilla/MiscEvents.h" @@ -645,10 +646,10 @@ void nsWindow::OnDestroy(void) { nsCOMPtr<nsIWidget> kungFuDeathGrip = this; // release references to children, device context, toolkit + app shell - nsBaseWidget::OnDestroy(); + nsIWidget::OnDestroy(); // Remove association between this object and its parent and siblings. - nsBaseWidget::Destroy(); + nsIWidget::Destroy(); NotifyWindowDestroyed(); } @@ -692,7 +693,7 @@ void nsWindow::Destroy() { } } - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); if (rollupListener) { nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget(); if (static_cast<nsIWidget*>(this) == rollupWidget) { @@ -931,7 +932,7 @@ void nsWindow::ConstrainSize(int* aWidth, int* aHeight) { // need to also constrain inner sizes as inner, rather than outer. *aWidth -= mClientMargin.LeftRight(); *aHeight -= mClientMargin.TopBottom(); - nsBaseWidget::ConstrainSize(aWidth, aHeight); + nsIWidget::ConstrainSize(aWidth, aHeight); *aWidth += mClientMargin.LeftRight(); *aHeight += mClientMargin.TopBottom(); } @@ -3862,8 +3863,8 @@ void nsWindow::CreateCompositorVsyncDispatcher() { if (!mWaylandVsyncSource) { LOG_VSYNC( " mWaylandVsyncSource is missing, create " - "nsBaseWidget::CompositorVsyncDispatcher()"); - nsBaseWidget::CreateCompositorVsyncDispatcher(); + "nsIWidget::CompositorVsyncDispatcher()"); + nsIWidget::CreateCompositorVsyncDispatcher(); return; } if (!mCompositorVsyncDispatcherLock) { @@ -8828,7 +8829,7 @@ nsIWidget::WindowRenderer* nsWindow::GetWindowRenderer() { return mWindowRenderer; } - return nsBaseWidget::GetWindowRenderer(); + return nsIWidget::GetWindowRenderer(); } void nsWindow::DidGetNonBlankPaint() { @@ -8850,10 +8851,10 @@ void nsWindow::DidGetNonBlankPaint() { * to render into with compositor. * * SetCompositorWidgetDelegate(delegate) is called from - * nsBaseWidget::CreateCompositor(), i.e. nsWindow::GetWindowRenderer(). + * nsIWidget::CreateCompositor(), i.e. nsWindow::GetWindowRenderer(). * * SetCompositorWidgetDelegate(null) is called from - * nsBaseWidget::DestroyCompositor(). + * nsIWidget::DestroyCompositor(). */ void nsWindow::SetCompositorWidgetDelegate(CompositorWidgetDelegate* delegate) { LOG("nsWindow::SetCompositorWidgetDelegate %p mIsMapped %d " diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h @@ -24,7 +24,7 @@ #include "mozilla/gfx/BaseMargin.h" #include "mozilla/widget/WindowSurface.h" #include "mozilla/widget/WindowSurfaceProvider.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsGkAtoms.h" #include "nsIDragService.h" #include "nsRefPtrHashtable.h" @@ -153,7 +153,7 @@ class WaylandSurfaceLock; class gfxImageSurface; -class nsWindow final : public nsBaseWidget { +class nsWindow final : public nsIWidget { public: typedef mozilla::gfx::DrawTarget DrawTarget; typedef mozilla::WidgetEventTime WidgetEventTime; @@ -165,7 +165,7 @@ class nsWindow final : public nsBaseWidget { static void ReleaseGlobals(); - NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsBaseWidget) + NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsIWidget) nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) override; @@ -177,7 +177,7 @@ class nsWindow final : public nsBaseWidget { bool AreBoundsSane(); // nsIWidget - using nsBaseWidget::Create; // for Create signature not overridden here + using nsIWidget::Create; // for Create signature not overridden here [[nodiscard]] nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, InitData* aInitData) override; @@ -837,7 +837,7 @@ class nsWindow final : public nsBaseWidget { bool IsAlwaysUndecoratedWindow() const; - // nsBaseWidget + // nsIWidget WindowRenderer* GetWindowRenderer() override; void DidGetNonBlankPaint() override; diff --git a/widget/headless/HeadlessWidget.cpp b/widget/headless/HeadlessWidget.cpp @@ -2,6 +2,7 @@ * 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/. */ +#include "InputData.h" #include "HeadlessWidget.h" #include "ErrorList.h" #include "HeadlessCompositorWidget.h" @@ -14,6 +15,7 @@ #include "mozilla/Preferences.h" #include "mozilla/TextEventDispatcher.h" #include "mozilla/TextEvents.h" +#include "UnitTransforms.h" #include "mozilla/WritingModes.h" #include "mozilla/widget/HeadlessWidgetTypes.h" #include "mozilla/widget/PlatformWidgetTypes.h" @@ -111,9 +113,9 @@ void HeadlessWidget::Destroy() { } } - nsBaseWidget::OnDestroy(); + nsIWidget::OnDestroy(); - nsBaseWidget::Destroy(); + nsIWidget::Destroy(); } nsresult HeadlessWidget::Create(nsIWidget* aParent, @@ -237,7 +239,7 @@ LayoutDeviceIntPoint HeadlessWidget::WidgetToScreenOffset() { } WindowRenderer* HeadlessWidget::GetWindowRenderer() { - return nsBaseWidget::GetWindowRenderer(); + return nsIWidget::GetWindowRenderer(); } void HeadlessWidget::SetCompositorWidgetDelegate( @@ -335,7 +337,7 @@ void HeadlessWidget::ApplySizeModeSideEffects() { } case nsSizeMode_Fullscreen: // This will take care of resizing the window. - nsBaseWidget::InfallibleMakeFullScreen(true); + nsIWidget::InfallibleMakeFullScreen(true); break; default: break; diff --git a/widget/headless/HeadlessWidget.h b/widget/headless/HeadlessWidget.h @@ -7,7 +7,7 @@ #define HEADLESSWIDGET_H #include "mozilla/widget/InProcessCompositorWidget.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "CompositorWidget.h" #include "mozilla/dom/WheelEventBinding.h" @@ -39,11 +39,11 @@ namespace mozilla { enum class NativeKeyBindingsType : uint8_t; namespace widget { -class HeadlessWidget final : public nsBaseWidget { +class HeadlessWidget final : public nsIWidget { public: HeadlessWidget(); - NS_INLINE_DECL_REFCOUNTING_INHERITED(HeadlessWidget, nsBaseWidget) + NS_INLINE_DECL_REFCOUNTING_INHERITED(HeadlessWidget, nsIWidget) void* GetNativeData(uint32_t aDataType) override { // Headless widgets have no native data. @@ -52,7 +52,7 @@ class HeadlessWidget final : public nsBaseWidget { nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, widget::InitData* aInitData = nullptr) override; - using nsBaseWidget::Create; // for Create signature not overridden here + using nsIWidget::Create; // for Create signature not overridden here void GetCompositorWidgetInitData( mozilla::widget::CompositorWidgetInitData* aInitData) override; diff --git a/widget/moz.build b/widget/moz.build @@ -164,7 +164,6 @@ EXPORTS += [ "nsBaseColorPicker.h", "nsBaseDragService.h", "nsBaseFilePicker.h", - "nsBaseWidget.h", "nsDragServiceProxy.h", "nsIDeviceContextSpec.h", "nsIRollupListener.h", @@ -323,11 +322,11 @@ if CONFIG["NS_PRINTING"]: "nsPrinterListCUPS.cpp", ] -# nsBaseWidget.cpp needs to be built separately because of name clashes in the OS X headers +# nsIWidget.cpp needs to be built separately because of name clashes in the OS X headers # nsBaseDragService.cpp moved out of UNIFIED to fix xgill crash (bug 1259850) after moving widget/ContentHelper -> apz/util/TouchActionHelper SOURCES += [ "nsBaseDragService.cpp", - "nsBaseWidget.cpp", + "nsIWidget.cpp", "ScreenManager.cpp", ] diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp @@ -1,3507 +0,0 @@ - -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* 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/. */ - -#include "nsBaseWidget.h" - -#include <utility> - -#include "GLConsts.h" -#include "InputData.h" -#include "LiveResizeListener.h" -#include "SwipeTracker.h" -#include "TouchEvents.h" -#include "X11UndefineNone.h" -#include "base/thread.h" -#include "mozilla/ArrayUtils.h" -#include "mozilla/Attributes.h" -#include "mozilla/GlobalKeyListener.h" -#include "mozilla/IMEStateManager.h" -#include "mozilla/Logging.h" -#include "mozilla/MouseEvents.h" -#include "mozilla/NativeKeyBindingsType.h" -#include "mozilla/Preferences.h" -#include "mozilla/PresShell.h" -#include "mozilla/ScopeExit.h" -#include "mozilla/Sprintf.h" -#include "mozilla/StaticPrefs_apz.h" -#include "mozilla/StaticPrefs_dom.h" -#include "mozilla/StaticPrefs_gfx.h" -#include "mozilla/StaticPrefs_layers.h" -#include "mozilla/StaticPrefs_layout.h" -#include "mozilla/TextEventDispatcher.h" -#include "mozilla/TextEventDispatcherListener.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/Unused.h" -#include "mozilla/VsyncDispatcher.h" -#include "mozilla/dom/BrowserParent.h" -#include "mozilla/dom/ContentChild.h" -#include "mozilla/dom/Document.h" -#include "mozilla/dom/SimpleGestureEventBinding.h" -#include "mozilla/gfx/2D.h" -#include "mozilla/gfx/GPUProcessManager.h" -#include "mozilla/gfx/gfxVars.h" -#include "mozilla/layers/APZCCallbackHelper.h" -#include "mozilla/layers/AsyncDragMetrics.h" -#include "mozilla/layers/TouchActionHelper.h" -#include "mozilla/layers/APZEventState.h" -#include "mozilla/layers/APZInputBridge.h" -#include "mozilla/layers/APZThreadUtils.h" -#include "mozilla/layers/ChromeProcessController.h" -#include "mozilla/layers/Compositor.h" -#include "mozilla/layers/CompositorBridgeChild.h" -#include "mozilla/layers/CompositorBridgeParent.h" -#include "mozilla/layers/CompositorOptions.h" -#include "mozilla/layers/IAPZCTreeManager.h" -#include "mozilla/layers/ImageBridgeChild.h" -#include "mozilla/layers/InputAPZContext.h" -#include "mozilla/layers/WebRenderLayerManager.h" -#include "mozilla/webrender/WebRenderTypes.h" -#include "mozilla/widget/ScreenManager.h" -#include "nsAppDirectoryServiceDefs.h" -#include "nsBaseDragService.h" -#include "nsCOMPtr.h" -#include "nsContentUtils.h" -#include "nsDeviceContext.h" -#include "nsGfxCIID.h" -#include "nsIAppWindow.h" -#include "nsIBaseWindow.h" -#include "nsIContent.h" -#include "nsIDOMWindowUtils.h" -#include "nsIScreenManager.h" -#include "nsISimpleEnumerator.h" -#include "nsIWidgetListener.h" -#include "nsMenuPopupFrame.h" -#include "nsRefPtrHashtable.h" -#include "nsServiceManagerUtils.h" -#include "nsWidgetsCID.h" -#include "nsXULPopupManager.h" -#include "prdtoa.h" -#include "prenv.h" -#ifdef ACCESSIBILITY -# include "nsAccessibilityService.h" -#endif -#include "gfxConfig.h" -#include "gfxUtils.h" // for ToDeviceColor -#include "mozilla/layers/CompositorSession.h" -#include "VRManagerChild.h" -#include "gfxConfig.h" -#include "nsView.h" -#include "nsViewManager.h" - -static mozilla::LazyLogModule sBaseWidgetLog("BaseWidget"); - -#ifdef DEBUG -# include "nsIObserver.h" - -static void debug_RegisterPrefCallbacks(); - -#endif - -#ifdef NOISY_WIDGET_LEAKS -static int32_t gNumWidgets; -#endif - -using namespace mozilla::dom; -using namespace mozilla::layers; -using namespace mozilla::ipc; -using namespace mozilla::widget; -using namespace mozilla; - -// Async pump timer during injected long touch taps -#define TOUCH_INJECT_PUMP_TIMER_MSEC 50 -#define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500 -int32_t nsIWidget::sPointerIdCounter = 0; - -// Some statics from nsIWidget.h -/*static*/ -uint64_t AutoSynthesizedEventCallbackNotifier::sCallbackId = 0; -MOZ_RUNINIT nsTHashMap<uint64_t, nsCOMPtr<nsISynthesizedEventCallback>> - AutoSynthesizedEventCallbackNotifier::sSavedCallbacks; - -// The maximum amount of time to let the EnableDragDrop runnable wait in the -// idle queue before timing out and moving it to the regular queue. Value is in -// milliseconds. -const uint32_t kAsyncDragDropTimeout = 1000; - -NS_IMPL_ISUPPORTS(nsBaseWidget, nsIWidget, nsISupportsWeakReference) - -//------------------------------------------------------------------------- -// -// nsBaseWidget constructor -// -//------------------------------------------------------------------------- - -nsBaseWidget::nsBaseWidget() : nsBaseWidget(BorderStyle::None) {} - -nsBaseWidget::nsBaseWidget(BorderStyle aBorderStyle) - : mWidgetListener(nullptr), - mAttachedWidgetListener(nullptr), - mPreviouslyAttachedWidgetListener(nullptr), - mCompositorVsyncDispatcher(nullptr), - mBorderStyle(aBorderStyle), - mBounds(0, 0, 0, 0), - mIsTiled(false), - mPopupLevel(PopupLevel::Top), - mPopupType(PopupType::Any), - mHasRemoteContent(false), - mUpdateCursor(true), - mUseAttachedEvents(false), - mIMEHasFocus(false), - mIMEHasQuit(false), - mIsFullyOccluded(false), - mNeedFastSnaphot(false), - mCurrentPanGestureBelongsToSwipe(false), - mIsPIPWindow(false) { -#ifdef NOISY_WIDGET_LEAKS - gNumWidgets++; - printf("WIDGETS+ = %d\n", gNumWidgets); -#endif - -#ifdef DEBUG - debug_RegisterPrefCallbacks(); -#endif - - mShutdownObserver = new WidgetShutdownObserver(this); -} - -NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver) - -WidgetShutdownObserver::WidgetShutdownObserver(nsBaseWidget* aWidget) - : mWidget(aWidget), mRegistered(false) { - Register(); -} - -WidgetShutdownObserver::~WidgetShutdownObserver() { - // No need to call Unregister(), we can't be destroyed until nsBaseWidget - // gets torn down. The observer service and nsBaseWidget have a ref on us - // so nsBaseWidget has to call Unregister and then clear its ref. -} - -NS_IMETHODIMP -WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) { - if (!mWidget) { - return NS_OK; - } - if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { - RefPtr<nsBaseWidget> widget(mWidget); - widget->Shutdown(); - } else if (!strcmp(aTopic, "quit-application")) { - RefPtr<nsBaseWidget> widget(mWidget); - widget->QuitIME(); - } - return NS_OK; -} - -void WidgetShutdownObserver::Register() { - if (!mRegistered) { - mRegistered = true; - nsContentUtils::RegisterShutdownObserver(this); - -#ifndef MOZ_WIDGET_ANDROID - // The primary purpose of observing quit-application is - // to avoid leaking a widget on Windows when nothing else - // breaks the circular reference between the widget and - // TSFTextStore. However, our Android IME code crashes if - // doing this on Android, so let's not do this on Android. - // Doing this on Gtk and Mac just in case. - nsCOMPtr<nsIObserverService> observerService = - mozilla::services::GetObserverService(); - if (observerService) { - observerService->AddObserver(this, "quit-application", false); - } -#endif - } -} - -void WidgetShutdownObserver::Unregister() { - if (mRegistered) { - mWidget = nullptr; - -#ifndef MOZ_WIDGET_ANDROID - nsCOMPtr<nsIObserverService> observerService = - mozilla::services::GetObserverService(); - if (observerService) { - observerService->RemoveObserver(this, "quit-application"); - } -#endif - - nsContentUtils::UnregisterShutdownObserver(this); - mRegistered = false; - } -} - -#define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed" - -NS_IMPL_ISUPPORTS(LocalesChangedObserver, nsIObserver) - -LocalesChangedObserver::LocalesChangedObserver(nsBaseWidget* aWidget) - : mWidget(aWidget), mRegistered(false) { - Register(); -} - -LocalesChangedObserver::~LocalesChangedObserver() { - // No need to call Unregister(), we can't be destroyed until nsBaseWidget - // gets torn down. The observer service and nsBaseWidget have a ref on us - // so nsBaseWidget has to call Unregister and then clear its ref. -} - -NS_IMETHODIMP -LocalesChangedObserver::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) { - if (!mWidget) { - return NS_OK; - } - if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) { - RefPtr<nsBaseWidget> widget(mWidget); - widget->LocalesChanged(); - } - return NS_OK; -} - -void LocalesChangedObserver::Register() { - if (mRegistered) { - return; - } - - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true); - } - - // Locale might be update before registering - RefPtr<nsBaseWidget> widget(mWidget); - widget->LocalesChanged(); - - mRegistered = true; -} - -void LocalesChangedObserver::Unregister() { - if (!mRegistered) { - return; - } - - nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED); - } - - mWidget = nullptr; - mRegistered = false; -} - -void nsBaseWidget::Shutdown() { - NotifyLiveResizeStopped(); - DestroyCompositor(); - FreeLocalesChangedObserver(); - FreeShutdownObserver(); -} - -void nsBaseWidget::QuitIME() { - IMEStateManager::WidgetOnQuit(this); - this->mIMEHasQuit = true; -} - -void nsBaseWidget::DestroyCompositor() { - RevokeTransactionIdAllocator(); - - // We release this before releasing the compositor, since it may hold the - // last reference to our ClientLayerManager. ClientLayerManager's dtor can - // trigger a paint, creating a new compositor, and we don't want to re-use - // the old vsync dispatcher. - if (mCompositorVsyncDispatcher) { - MOZ_ASSERT(mCompositorVsyncDispatcherLock.get()); - - MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); - mCompositorVsyncDispatcher->Shutdown(); - mCompositorVsyncDispatcher = nullptr; - } - - // The compositor shutdown sequence looks like this: - // 1. CompositorSession calls CompositorBridgeChild::Destroy. - // 2. CompositorBridgeChild synchronously sends WillClose. - // 3. CompositorBridgeParent releases some resources (such as the layer - // manager, compositor, and widget). - // 4. CompositorBridgeChild::Destroy returns. - // 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the - // compositor thread when the I/O thread closes the IPC channel. - // 6. Step 5 will schedule DeferredDestroy on the compositor thread, which - // releases the reference CompositorBridgeParent holds to itself. - // - // When CompositorSession::Shutdown returns, we assume the compositor is gone - // or will be gone very soon. - if (mCompositorSession) { - ReleaseContentController(); - mAPZC = nullptr; - SetCompositorWidgetDelegate(nullptr); - mCompositorBridgeChild = nullptr; - mCompositorSession->Shutdown(); - mCompositorSession = nullptr; - } -} - -// This prevents the layer manager from starting a new transaction during -// shutdown. -void nsBaseWidget::RevokeTransactionIdAllocator() { - if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) { - return; - } - mWindowRenderer->AsWebRender()->SetTransactionIdAllocator(nullptr); -} - -void nsBaseWidget::ReleaseContentController() { - if (mRootContentController) { - mRootContentController->Destroy(); - mRootContentController = nullptr; - } -} - -void nsBaseWidget::DestroyLayerManager() { - if (mWindowRenderer) { - mWindowRenderer->Destroy(); - mWindowRenderer = nullptr; - } - DestroyCompositor(); -} - -void nsBaseWidget::OnRenderingDeviceReset() { DestroyLayerManager(); } - -void nsBaseWidget::FreeShutdownObserver() { - if (mShutdownObserver) { - mShutdownObserver->Unregister(); - } - mShutdownObserver = nullptr; -} - -void nsBaseWidget::FreeLocalesChangedObserver() { - if (mLocalesChangedObserver) { - mLocalesChangedObserver->Unregister(); - } - mLocalesChangedObserver = nullptr; -} - -//------------------------------------------------------------------------- -// -// nsBaseWidget destructor -// -//------------------------------------------------------------------------- - -nsBaseWidget::~nsBaseWidget() { - if (mSwipeTracker) { - mSwipeTracker->Destroy(); - mSwipeTracker = nullptr; - } - - IMEStateManager::WidgetDestroyed(this); - - FreeLocalesChangedObserver(); - FreeShutdownObserver(); - DestroyLayerManager(); - -#ifdef NOISY_WIDGET_LEAKS - gNumWidgets--; - printf("WIDGETS- = %d\n", gNumWidgets); -#endif -} - -//------------------------------------------------------------------------- -// -// Basic create. -// -//------------------------------------------------------------------------- -void nsBaseWidget::BaseCreate(nsIWidget* aParent, widget::InitData* aInitData) { - if (aInitData) { - mWindowType = aInitData->mWindowType; - mBorderStyle = aInitData->mBorderStyle; - mPopupLevel = aInitData->mPopupLevel; - mPopupType = aInitData->mPopupHint; - mHasRemoteContent = aInitData->mHasRemoteContent; - mIsPIPWindow = aInitData->mPIPWindow; - } - - mParent = aParent; - if (mParent) { - mParent->AddToChildList(this); - } -} - -void nsIWidget::ClearParent() { - if (!mParent) { - return; - } - nsCOMPtr<nsIWidget> kungFuDeathGrip = this; - nsCOMPtr<nsIWidget> oldParent = mParent; - oldParent->RemoveFromChildList(this); - mParent = nullptr; - DidClearParent(oldParent); -} - -void nsIWidget::RemoveAllChildren() { - while (nsCOMPtr<nsIWidget> kid = mLastChild) { - kid->ClearParent(); - MOZ_ASSERT(kid != mLastChild); - } -} - -nsIFrame* nsIWidget::GetFrame() const { - if (auto* popup = GetPopupFrame()) { - return popup; - } - if (nsView* view = nsView::GetViewFor(this)) { - return view->GetFrame(); - } - return nullptr; -} - -nsMenuPopupFrame* nsIWidget::GetPopupFrame() const { - if (mWindowType != WindowType::Popup) { - return nullptr; - } - MOZ_ASSERT_IF(GetWidgetListener(), - GetWidgetListener()->GetAsMenuPopupFrame()); - return static_cast<nsMenuPopupFrame*>(GetWidgetListener()); -} - -void nsBaseWidget::DynamicToolbarOffsetChanged( - mozilla::ScreenIntCoord aOffset) { - if (mCompositorBridgeChild) { - mCompositorBridgeChild->SendDynamicToolbarOffsetChanged(aOffset); - } -} - -LayoutDeviceIntRect nsIWidget::MaybeRoundToDisplayPixels( - const LayoutDeviceIntRect& aRect, TransparencyMode aTransparency, - int32_t aRound) { - if (aRound == 1) { - return aRect; - } - - // If the widget doesn't support transparency, we prefer truncating to - // ceiling, so that we don't have extra pixels not painted by our frame. - auto size = aTransparency == TransparencyMode::Opaque - ? aRect.Size().TruncatedToMultiple(aRound) - : aRect.Size().CeiledToMultiple(aRound); - Unused << NS_WARN_IF(aTransparency == TransparencyMode::Opaque && - size != aRect.Size()); - return {aRect.TopLeft().RoundedToMultiple(aRound), size}; -} - -//------------------------------------------------------------------------- -// -// Accessor functions to get/set the client data -// -//------------------------------------------------------------------------- - -nsIWidgetListener* nsBaseWidget::GetWidgetListener() const { - return mWidgetListener; -} - -void nsBaseWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener) { - mWidgetListener = aWidgetListener; -} - -already_AddRefed<nsIWidget> nsBaseWidget::CreateChild( - const LayoutDeviceIntRect& aRect, widget::InitData& aInitData) { - nsCOMPtr<nsIWidget> widget; - switch (mWidgetType) { - case WidgetType::Native: { - if (aInitData.mWindowType == WindowType::Popup) { - widget = AllocateChildPopupWidget(); - } else { - widget = nsIWidget::CreateChildWindow(); - } - break; - } - case WidgetType::Headless: - widget = nsIWidget::CreateHeadlessWidget(); - break; - case WidgetType::Puppet: { - // This really only should happen in crashtests that have menupopups. - MOZ_ASSERT(aInitData.mWindowType == WindowType::Popup, - "Creating non-popup puppet widget?"); - widget = nsIWidget::CreatePuppetWidget(nullptr); - break; - } - } - - if (!widget) { - return nullptr; - } - - if (mNeedFastSnaphot) { - widget->SetNeedFastSnaphot(); - } - - if (NS_FAILED(widget->Create(this, aRect, &aInitData))) { - return nullptr; - } - - return widget.forget(); -} - -// Attach a view to our widget which we'll send events to. -void nsBaseWidget::AttachViewToTopLevel(bool aUseAttachedEvents) { - NS_ASSERTION(mWindowType == WindowType::TopLevel || - mWindowType == WindowType::Dialog || - mWindowType == WindowType::Invisible, - "Can't attach to window of that type"); - - mUseAttachedEvents = aUseAttachedEvents; -} - -nsIWidgetListener* nsBaseWidget::GetAttachedWidgetListener() const { - return mAttachedWidgetListener; -} - -nsIWidgetListener* nsBaseWidget::GetPreviouslyAttachedWidgetListener() { - return mPreviouslyAttachedWidgetListener; -} - -void nsBaseWidget::SetPreviouslyAttachedWidgetListener( - nsIWidgetListener* aListener) { - mPreviouslyAttachedWidgetListener = aListener; -} - -void nsBaseWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) { - mAttachedWidgetListener = aListener; -} - -//------------------------------------------------------------------------- -// -// Close this nsBaseWidget -// -//------------------------------------------------------------------------- -void nsBaseWidget::Destroy() { - DestroyCompositor(); - - // Just in case our parent is the only ref to us - nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - // disconnect from the parent - if (mParent) { - mParent->RemoveFromChildList(this); - mParent = nullptr; - } - // disconnect from the children - RemoveAllChildren(); -} - -nsIWidget* nsIWidget::GetTopLevelWidget() { - auto* cur = this; - while (true) { - if (cur->IsTopLevelWidget()) { - break; - } - nsIWidget* parent = cur->GetParent(); - if (!parent) { - break; - } - cur = parent; - } - return cur; -} - -float nsBaseWidget::GetDPI() { return 96.0f; } - -void nsBaseWidget::NotifyAPZOfDPIChange() { - if (mAPZC) { - mAPZC->SetDPI(GetDPI()); - } -} - -CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() { - double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx(); - - if (devPixelsPerCSSPixel <= 0.0) { - devPixelsPerCSSPixel = GetDefaultScaleInternal(); - } - - return CSSToLayoutDeviceScale(devPixelsPerCSSPixel); -} - -nsIntSize nsIWidget::CustomCursorSize(const Cursor& aCursor) { - MOZ_ASSERT(aCursor.IsCustom()); - int32_t width = 0; - int32_t height = 0; - aCursor.mContainer->GetWidth(&width); - aCursor.mContainer->GetHeight(&height); - aCursor.mResolution.ApplyTo(width, height); - return {width, height}; -} - -LayoutDeviceIntSize nsIWidget::NormalSizeModeClientToWindowSizeDifference() { - auto margin = NormalSizeModeClientToWindowMargin(); - MOZ_ASSERT(margin.top >= 0, "Window should be bigger than client area"); - MOZ_ASSERT(margin.left >= 0, "Window should be bigger than client area"); - MOZ_ASSERT(margin.right >= 0, "Window should be bigger than client area"); - MOZ_ASSERT(margin.bottom >= 0, "Window should be bigger than client area"); - return {margin.LeftRight(), margin.TopBottom()}; -} - -RefPtr<mozilla::VsyncDispatcher> nsIWidget::GetVsyncDispatcher() { - return nullptr; -} - -//------------------------------------------------------------------------- -// -// Add a child to the list of children -// -//------------------------------------------------------------------------- -void nsIWidget::AddToChildList(nsIWidget* aChild) { - MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(), - "aChild not properly removed from its old child list"); - - if (!mFirstChild) { - mFirstChild = mLastChild = aChild; - } else { - // append to the list - MOZ_ASSERT(mLastChild); - MOZ_ASSERT(!mLastChild->GetNextSibling()); - mLastChild->SetNextSibling(aChild); - aChild->SetPrevSibling(mLastChild); - mLastChild = aChild; - } -} - -//------------------------------------------------------------------------- -// -// Remove a child from the list of children -// -//------------------------------------------------------------------------- -void nsIWidget::RemoveFromChildList(nsIWidget* aChild) { - MOZ_ASSERT(aChild->GetParent() == this, "Not one of our kids!"); - - if (mLastChild == aChild) { - mLastChild = mLastChild->GetPrevSibling(); - } - if (mFirstChild == aChild) { - mFirstChild = mFirstChild->GetNextSibling(); - } - - // Now remove from the list. Make sure that we pass ownership of the tail - // of the list correctly before we have aChild let go of it. - nsIWidget* prev = aChild->GetPrevSibling(); - nsIWidget* next = aChild->GetNextSibling(); - if (prev) { - prev->SetNextSibling(next); - } - if (next) { - next->SetPrevSibling(prev); - } - - aChild->SetNextSibling(nullptr); - aChild->SetPrevSibling(nullptr); -} - -void nsBaseWidget::GetWorkspaceID(nsAString& workspaceID) { - workspaceID.Truncate(); -} - -void nsBaseWidget::MoveToWorkspace(const nsAString& workspaceID) { - // Noop. -} - -//------------------------------------------------------------------------- -// -// Get this component cursor -// -//------------------------------------------------------------------------- - -void nsBaseWidget::SetCursor(const Cursor& aCursor) { mCursor = aCursor; } - -void nsBaseWidget::SetCustomCursorAllowed(bool aIsAllowed) { - if (aIsAllowed != mCustomCursorAllowed) { - mCustomCursorAllowed = aIsAllowed; - mUpdateCursor = true; - SetCursor(mCursor); - } -} - -//------------------------------------------------------------------------- -// -// Window transparency methods -// -//------------------------------------------------------------------------- - -void nsBaseWidget::SetTransparencyMode(TransparencyMode aMode) {} - -TransparencyMode nsBaseWidget::GetTransparencyMode() { - return TransparencyMode::Opaque; -} - -/* virtual */ -void nsBaseWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage, - uint16_t aDuration, - nsISupports* aData, - nsIRunnable* aCallback) { - MOZ_ASSERT_UNREACHABLE( - "Should never call PerformFullscreenTransition on nsBaseWidget"); -} - -//------------------------------------------------------------------------- -// -// Put the window into full-screen mode -// -//------------------------------------------------------------------------- -void nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen) { -#define MOZ_FORMAT_RECT(fmtstr) "[" fmtstr "," fmtstr " " fmtstr "x" fmtstr "]" -#define MOZ_SPLAT_RECT(rect) \ - (rect).X(), (rect).Y(), (rect).Width(), (rect).Height() - - // Windows which can be made fullscreen are exactly those which are located on - // the desktop, rather than being a child of some other window. - MOZ_DIAGNOSTIC_ASSERT(BoundsUseDesktopPixels(), - "non-desktop windows cannot be made fullscreen"); - - // Ensure that the OS chrome is hidden/shown before we resize and/or exit the - // function. - // - // HideWindowChrome() may (depending on platform, implementation details, and - // OS-level user preferences) alter the reported size of the window. The - // obvious and principled solution is socks-and-shoes: - // - On entering fullscreen mode: hide window chrome, then perform resize. - // - On leaving fullscreen mode: unperform resize, then show window chrome. - // - // ... unfortunately, HideWindowChrome() requires Resize() to be called - // afterwards (see bug 498835), which prevents this from being done in a - // straightforward way. - // - // Instead, we always call HideWindowChrome() just before we call Resize(). - // This at least ensures that our measurements are consistently taken in a - // pre-transition state. - // - // ... unfortunately again, coupling HideWindowChrome() to Resize() means that - // we have to worry about the possibility of control flows that don't call - // Resize() at all. (That shouldn't happen, but it's not trivial to rule out.) - // We therefore set up a fallback to fix up the OS chrome if it hasn't been - // done at exit time. - bool hasAdjustedOSChrome = false; - const auto adjustOSChrome = [&]() { - if (hasAdjustedOSChrome) { - MOZ_ASSERT_UNREACHABLE("window chrome should only be adjusted once"); - return; - } - HideWindowChrome(aFullScreen); - hasAdjustedOSChrome = true; - }; - const auto adjustChromeOnScopeExit = MakeScopeExit([&]() { - if (hasAdjustedOSChrome) { - return; - } - - MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, - ("window was not resized within InfallibleMakeFullScreen()")); - - // Hide chrome and "resize" the window to its current size. - auto rect = GetBounds(); - adjustOSChrome(); - Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(), true); - }); - - // Attempt to resize to `rect`. - // - // Returns the actual rectangle resized to. (This may differ from `rect`, if - // the OS is unhappy with it. See bug 1786226.) - const auto doReposition = [&](auto rect) -> void { - static_assert(std::is_base_of_v<DesktopPixel, - std::remove_reference_t<decltype(rect)>>, - "doReposition requires a rectangle using desktop pixels"); - - if (MOZ_LOG_TEST(sBaseWidgetLog, LogLevel::Debug)) { - const DesktopRect previousSize = - GetScreenBounds() / GetDesktopToDeviceScale(); - MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, - ("before resize: " MOZ_FORMAT_RECT("%f"), - MOZ_SPLAT_RECT(previousSize))); - } - - adjustOSChrome(); - Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(), true); - - if (MOZ_LOG_TEST(sBaseWidgetLog, LogLevel::Warning)) { - // `rect` may have any underlying data type; coerce to float to - // simplify printf-style logging - const gfx::RectTyped<DesktopPixel, float> rectAsFloat{rect}; - - // The OS may have objected to the target position. That's not necessarily - // a problem -- it'll happen regularly on Macs with camera notches in the - // monitor, for instance (see bug 1786226) -- but it probably deserves to - // be called out. - // - // Since there's floating-point math involved, the actual values may be - // off by a few ulps -- as an upper bound, perhaps 8 * FLT_EPSILON * - // max(MOZ_SPLAT_RECT(rect)) -- but 0.01 should be several orders of - // magnitude bigger than that. - - const auto postResizeRectRaw = GetScreenBounds(); - const auto postResizeRect = postResizeRectRaw / GetDesktopToDeviceScale(); - const bool succeeded = postResizeRect.WithinEpsilonOf(rectAsFloat, 0.01); - - if (succeeded) { - MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, - ("resized to: " MOZ_FORMAT_RECT("%f"), - MOZ_SPLAT_RECT(rectAsFloat))); - } else { - MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, - ("attempted to resize to: " MOZ_FORMAT_RECT("%f"), - MOZ_SPLAT_RECT(rectAsFloat))); - MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, - ("... but ended up at: " MOZ_FORMAT_RECT("%f"), - MOZ_SPLAT_RECT(postResizeRect))); - } - - MOZ_LOG( - sBaseWidgetLog, LogLevel::Verbose, - ("(... which, before DPI adjustment, is:" MOZ_FORMAT_RECT("%d") ")", - MOZ_SPLAT_RECT(postResizeRectRaw))); - } - }; - - if (aFullScreen) { - if (!mSavedBounds) { - mSavedBounds = Some(FullscreenSavedState()); - } - // save current position - mSavedBounds->windowRect = GetScreenBounds() / GetDesktopToDeviceScale(); - - nsCOMPtr<nsIScreen> screen = GetWidgetScreen(); - if (!screen) { - return; - } - - // Move to fill the screen. - doReposition(screen->GetRectDisplayPix()); - // Save off the new position. (This may differ from GetRectDisplayPix(), if - // the OS was unhappy with it. See bug 1786226.) - mSavedBounds->screenRect = GetScreenBounds() / GetDesktopToDeviceScale(); - } else { - if (!mSavedBounds) { - // This should never happen, at present, since we don't make windows - // fullscreen at their creation time; but it's not logically impossible. - MOZ_ASSERT(false, "fullscreen window did not have saved position"); - return; - } - - // Figure out where to go from here. - // - // Fortunately, since we're currently fullscreen (and other code should be - // handling _keeping_ us fullscreen even after display-layout changes), - // there's an obvious choice for which display we should attach to; all we - // need to determine is where on that display we should go. - - const DesktopRect currentWinRect = - GetScreenBounds() / GetDesktopToDeviceScale(); - - // Optimization: if where we are is where we were, then where we originally - // came from is where we're going to go. - if (currentWinRect == DesktopRect(mSavedBounds->screenRect)) { - MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, - ("no location change detected; returning to saved location")); - doReposition(mSavedBounds->windowRect); - return; - } - - /* - General case: figure out where we're going to go by dividing where we are - by where we were, and then multiplying by where we originally came from. - - Less abstrusely: resize so that we occupy the same proportional position - on our current display after leaving fullscreen as we occupied on our - previous display before entering fullscreen. - - (N.B.: We do not clamp. If we were only partially on the old display, - we'll be only partially on the new one, too.) - */ - - MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, - ("location change detected; computing new destination")); - - // splat: convert an arbitrary Rect into a tuple, for syntactic convenience. - const auto splat = [](auto rect) { - return std::tuple(rect.X(), rect.Y(), rect.Width(), rect.Height()); - }; - - // remap: find the unique affine mapping which transforms `src` to `dst`, - // and apply it to `val`. - using Range = std::pair<float, float>; - const auto remap = [](Range dst, Range src, float val) { - // linear interpolation and its inverse: lerp(a, b, invlerp(a, b, t)) == t - const auto lerp = [](float lo, float hi, float t) { - return lo + t * (hi - lo); - }; - const auto invlerp = [](float lo, float hi, float mid) { - return (mid - lo) / (hi - lo); - }; - - const auto [dst_a, dst_b] = dst; - const auto [src_a, src_b] = src; - return lerp(dst_a, dst_b, invlerp(src_a, src_b, val)); - }; - - // original position - const auto [px, py, pw, ph] = splat(mSavedBounds->windowRect); - // source desktop rect - const auto [sx, sy, sw, sh] = splat(mSavedBounds->screenRect); - // target desktop rect - const auto [tx, ty, tw, th] = splat(currentWinRect); - - const float nx = remap({tx, tx + tw}, {sx, sx + sw}, px); - const float ny = remap({ty, ty + th}, {sy, sy + sh}, py); - const float nw = remap({0, tw}, {0, sw}, pw); - const float nh = remap({0, th}, {0, sh}, ph); - - doReposition(DesktopRect{nx, ny, nw, nh}); - } - -#undef MOZ_SPLAT_RECT -#undef MOZ_FORMAT_RECT -} - -nsresult nsBaseWidget::MakeFullScreen(bool aFullScreen) { - InfallibleMakeFullScreen(aFullScreen); - return NS_OK; -} - -nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup( - nsBaseWidget* aWidget, gfxContext* aTarget) - : mWidget(aWidget) { - WindowRenderer* renderer = mWidget->GetWindowRenderer(); - if (auto* fallback = renderer->AsFallback()) { - mRenderer = fallback; - mRenderer->SetTarget(aTarget); - } -} - -nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() { - if (mRenderer) { - mRenderer->SetTarget(nullptr); - } -} - -bool nsBaseWidget::IsSmallPopup() const { - return mWindowType == WindowType::Popup && mPopupType != PopupType::Panel; -} - -bool nsBaseWidget::ComputeShouldAccelerate() { - return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) && - (WidgetTypeSupportsAcceleration() || - StaticPrefs::gfx_webrender_unaccelerated_widget_force()); -} - -bool nsBaseWidget::UseAPZ() const { - // APZ disabled globally - if (!gfxPlatform::AsyncPanZoomEnabled()) { - return false; - } - - // Always use APZ for top-level windows. XXX what about Dialog? - if (mWindowType == WindowType::TopLevel) { - return true; - } - - // Never use APZ for tooltips - if (mWindowType == WindowType::Popup && mPopupType == PopupType::Tooltip) { - return false; - } - - if (!StaticPrefs::apz_popups_enabled()) { - return false; - } - - if (HasRemoteContent()) { - return mWindowType == WindowType::Dialog || - mWindowType == WindowType::Popup; - } - - if (StaticPrefs::apz_popups_without_remote_enabled()) { - return mWindowType == WindowType::Popup; - } - - return false; -} - -void nsBaseWidget::CreateCompositor() { - LayoutDeviceIntRect rect = GetBounds(); - CreateCompositor(rect.Width(), rect.Height()); -} - -void nsIWidget::PauseOrResumeCompositor(bool aPause) { - auto* renderer = GetRemoteRenderer(); - if (!renderer) { - return; - } - if (aPause) { - renderer->SendPause(); - } else { - renderer->SendResume(); - } -} - -already_AddRefed<GeckoContentController> -nsBaseWidget::CreateRootContentController() { - RefPtr<GeckoContentController> controller = - new ChromeProcessController(this, mAPZEventState, mAPZC); - return controller.forget(); -} - -void nsBaseWidget::ConfigureAPZCTreeManager() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mAPZC); - - mAPZC->SetDPI(GetDPI()); - - if (StaticPrefs::apz_keyboard_enabled_AtStartup()) { - KeyboardMap map = RootWindowGlobalKeyListener::CollectKeyboardShortcuts(); - mAPZC->SetKeyboardMap(map); - } - - ContentReceivedInputBlockCallback callback( - [treeManager = RefPtr{mAPZC.get()}](uint64_t aInputBlockId, - bool aPreventDefault) { - MOZ_ASSERT(NS_IsMainThread()); - treeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); - }); - mAPZEventState = new APZEventState(this, std::move(callback)); - - mRootContentController = CreateRootContentController(); - if (mRootContentController) { - mCompositorSession->SetContentController(mRootContentController); - } - - // When APZ is enabled, we can actually enable raw touch events because we - // have code that can deal with them properly. If APZ is not enabled, this - // function doesn't get called. - if (StaticPrefs::dom_w3c_touch_events_enabled()) { - RegisterTouchWindow(); - } -} - -void nsBaseWidget::ConfigureAPZControllerThread() { - // By default the controller thread is the main thread. - APZThreadUtils::SetControllerThread(NS_GetCurrentThread()); -} - -void nsBaseWidget::SetConfirmedTargetAPZC( - uint64_t aInputBlockId, - const nsTArray<ScrollableLayerGuid>& aTargets) const { - mAPZC->SetTargetAPZC(aInputBlockId, aTargets); -} - -void nsBaseWidget::UpdateZoomConstraints( - const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, - const Maybe<ZoomConstraints>& aConstraints) { - if (!mCompositorSession || !mAPZC) { - MOZ_ASSERT_IF(mInitialZoomConstraints, - mInitialZoomConstraints->mViewID == aViewId); - if (aConstraints) { - // We have some constraints, but the compositor and APZC aren't - // created yet. Save these so we can use them later. - mInitialZoomConstraints = Some( - InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref())); - } else { - mInitialZoomConstraints.reset(); - } - return; - } - LayersId layersId = mCompositorSession->RootLayerTreeId(); - mAPZC->UpdateZoomConstraints( - ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints); -} - -bool nsBaseWidget::AsyncPanZoomEnabled() const { return !!mAPZC; } - -nsEventStatus nsBaseWidget::ProcessUntransformedAPZEvent( - WidgetInputEvent* aEvent, const APZEventResult& aApzResult) { - MOZ_ASSERT(NS_IsMainThread()); - ScrollableLayerGuid targetGuid = aApzResult.mTargetGuid; - uint64_t inputBlockId = aApzResult.mInputBlockId; - InputAPZContext context(aApzResult.mTargetGuid, inputBlockId, - aApzResult.GetStatus()); - - // Make a copy of the original event for the APZCCallbackHelper helpers that - // we call later, because the event passed to DispatchEvent can get mutated in - // ways that we don't want (i.e. touch points can get stripped out). - nsEventStatus status; - UniquePtr<WidgetEvent> original(aEvent->Duplicate()); - DispatchEvent(aEvent, status); - - if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && - !InputAPZContext::WasDropped() && inputBlockId) { - // EventStateManager did not route the event into the child process and - // the event was dispatched in the parent process. - // It's safe to communicate to APZ that the event has been processed. - // Note that here aGuid.mLayersId might be different from - // mCompositorSession->RootLayerTreeId() because the event might have gotten - // hit-tested by APZ to be targeted at a child process, but a parent process - // event listener called preventDefault on it. In that case aGuid.mLayersId - // would still be the layers id for the child process, but the event would - // not have actually gotten routed to the child process. The main-thread - // hit-test result therefore needs to use the parent process layers id. - LayersId rootLayersId = mCompositorSession->RootLayerTreeId(); - - RefPtr<DisplayportSetListener> postLayerization; - if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) { - nsTArray<TouchBehaviorFlags> allowedTouchBehaviors; - if (touchEvent->mMessage == eTouchStart) { - auto& originalEvent = *original->AsTouchEvent(); - MOZ_ASSERT(NS_IsMainThread()); - allowedTouchBehaviors = TouchActionHelper::GetAllowedTouchBehavior( - this, GetDocument(), originalEvent); - if (!allowedTouchBehaviors.IsEmpty()) { - mAPZC->SetAllowedTouchBehavior(inputBlockId, allowedTouchBehaviors); - } - postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( - this, GetDocument(), originalEvent, rootLayersId, inputBlockId); - } - mAPZEventState->ProcessTouchEvent(*touchEvent, targetGuid, inputBlockId, - aApzResult.GetStatus(), status, - std::move(allowedTouchBehaviors)); - } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) { - MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ); - postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( - this, GetDocument(), *original->AsWheelEvent(), rootLayersId, - inputBlockId); - if (wheelEvent->mCanTriggerSwipe) { - ReportSwipeStarted(inputBlockId, wheelEvent->TriggersSwipe()); - } - mAPZEventState->ProcessWheelEvent(*wheelEvent, inputBlockId); - } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { - MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ); - postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( - this, GetDocument(), *original->AsMouseEvent(), rootLayersId, - inputBlockId); - mAPZEventState->ProcessMouseEvent(*mouseEvent, inputBlockId); - } - if (postLayerization) { - postLayerization->Register(); - } - } - - return status; -} - -template <class InputType, class EventType> -class DispatchEventOnMainThread : public Runnable { - public: - DispatchEventOnMainThread(const InputType& aInput, nsBaseWidget* aWidget, - const APZEventResult& aAPZResult) - : mozilla::Runnable("DispatchEventOnMainThread"), - mInput(aInput), - mWidget(aWidget), - mAPZResult(aAPZResult) {} - - NS_IMETHOD Run() override { - EventType event = mInput.ToWidgetEvent(mWidget); - mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); - return NS_OK; - } - - private: - InputType mInput; - nsBaseWidget* mWidget; - APZEventResult mAPZResult; -}; - -template <> -NS_IMETHODIMP DispatchEventOnMainThread<MouseInput, WidgetMouseEvent>::Run() { - MOZ_ASSERT( - !mInput.IsPointerEventType(), - "Please use DispatchEventOnMainThread<MouseInput, WidgetPointerEvent>"); - WidgetMouseEvent event = mInput.ToWidgetEvent<WidgetMouseEvent>(mWidget); - mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); - return NS_OK; -} - -template <> -NS_IMETHODIMP DispatchEventOnMainThread<MouseInput, WidgetPointerEvent>::Run() { - MOZ_ASSERT( - mInput.IsPointerEventType(), - "Please use DispatchEventOnMainThread<MouseInput, WidgetMouseEvent>"); - WidgetPointerEvent event = mInput.ToWidgetEvent<WidgetPointerEvent>(mWidget); - mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); - return NS_OK; -} - -template <class InputType, class EventType> -class DispatchInputOnControllerThread : public Runnable { - public: - enum class APZOnly { Yes, No }; - DispatchInputOnControllerThread(const EventType& aEvent, - IAPZCTreeManager* aAPZC, - nsBaseWidget* aWidget, - APZOnly aAPZOnly = APZOnly::No) - : mozilla::Runnable("DispatchInputOnControllerThread"), - mMainMessageLoop(MessageLoop::current()), - mInput(aEvent), - mAPZC(aAPZC), - mWidget(aWidget), - mAPZOnly(aAPZOnly) {} - - NS_IMETHOD Run() override { - APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(mInput); - if (mAPZOnly == APZOnly::Yes || - result.GetStatus() == nsEventStatus_eConsumeNoDefault) { - return NS_OK; - } - RefPtr<Runnable> r = new DispatchEventOnMainThread<InputType, EventType>( - mInput, mWidget, result); - mMainMessageLoop->PostTask(r.forget()); - return NS_OK; - } - - private: - MessageLoop* mMainMessageLoop; - InputType mInput; - RefPtr<IAPZCTreeManager> mAPZC; - nsBaseWidget* mWidget; - const APZOnly mAPZOnly; -}; - -void nsBaseWidget::DispatchTouchInput(MultiTouchInput& aInput) { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(aInput.mInputSource == - mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH || - aInput.mInputSource == - mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN); - if (mAPZC) { - MOZ_ASSERT(APZThreadUtils::IsControllerThread()); - - APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); - if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { - return; - } - - WidgetTouchEvent event = aInput.ToWidgetEvent(this); - ProcessUntransformedAPZEvent(&event, result); - } else { - WidgetTouchEvent event = aInput.ToWidgetEvent(this); - - nsEventStatus status; - DispatchEvent(&event, status); - } -} - -void nsBaseWidget::DispatchPanGestureInput(PanGestureInput& aInput) { - MOZ_ASSERT(NS_IsMainThread()); - if (mAPZC) { - MOZ_ASSERT(APZThreadUtils::IsControllerThread()); - - APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); - if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { - return; - } - - WidgetWheelEvent event = aInput.ToWidgetEvent(this); - ProcessUntransformedAPZEvent(&event, result); - } else { - WidgetWheelEvent event = aInput.ToWidgetEvent(this); - nsEventStatus status; - DispatchEvent(&event, status); - } -} - -void nsBaseWidget::DispatchPinchGestureInput(PinchGestureInput& aInput) { - MOZ_ASSERT(NS_IsMainThread()); - if (mAPZC) { - MOZ_ASSERT(APZThreadUtils::IsControllerThread()); - APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); - - if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { - return; - } - WidgetWheelEvent event = aInput.ToWidgetEvent(this); - ProcessUntransformedAPZEvent(&event, result); - } else { - WidgetWheelEvent event = aInput.ToWidgetEvent(this); - nsEventStatus status; - DispatchEvent(&event, status); - } -} - -nsIWidget::ContentAndAPZEventStatus nsBaseWidget::DispatchInputEvent( - WidgetInputEvent* aEvent) { - nsIWidget::ContentAndAPZEventStatus status; - MOZ_ASSERT(NS_IsMainThread()); - - if (mAPZC) { - if (APZThreadUtils::IsControllerThread()) { - APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent); - status.mApzStatus = result.GetStatus(); - if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { - return status; - } - status.mContentStatus = ProcessUntransformedAPZEvent(aEvent, result); - return status; - } - // Most drag events aren't able to converted to MouseEvent except to - // eDragStart and eDragEnd. - const bool canDispatchToApzc = - !aEvent->AsDragEvent() || - aEvent->AsDragEvent()->CanConvertToInputData(); - if (canDispatchToApzc) { - if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) { - RefPtr<Runnable> r = - new DispatchInputOnControllerThread<ScrollWheelInput, - WidgetWheelEvent>(*wheelEvent, - mAPZC, this); - APZThreadUtils::RunOnControllerThread(std::move(r)); - status.mContentStatus = nsEventStatus_eConsumeDoDefault; - return status; - } - if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { - MOZ_ASSERT(aEvent->mMessage == eContextMenu); - RefPtr<Runnable> r = - new DispatchInputOnControllerThread<MouseInput, WidgetPointerEvent>( - *pointerEvent, mAPZC, this); - APZThreadUtils::RunOnControllerThread(std::move(r)); - status.mContentStatus = nsEventStatus_eConsumeDoDefault; - return status; - } - if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { - RefPtr<Runnable> r = - new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>( - *mouseEvent, mAPZC, this); - APZThreadUtils::RunOnControllerThread(std::move(r)); - status.mContentStatus = nsEventStatus_eConsumeDoDefault; - return status; - } - if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) { - RefPtr<Runnable> r = - new DispatchInputOnControllerThread<MultiTouchInput, - WidgetTouchEvent>(*touchEvent, - mAPZC, this); - APZThreadUtils::RunOnControllerThread(std::move(r)); - status.mContentStatus = nsEventStatus_eConsumeDoDefault; - return status; - } - // Allow dispatching keyboard/drag events on Gecko thread - // without sending them to APZ - - // FIXME: APZ can handle keyboard events now, we should - // be sending them to APZ here - MOZ_ASSERT(aEvent->AsKeyboardEvent() || aEvent->AsDragEvent()); - } - } - - DispatchEvent(aEvent, status.mContentStatus); - return status; -} - -void nsBaseWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) { - MOZ_ASSERT(NS_IsMainThread()); - if (mAPZC) { - if (APZThreadUtils::IsControllerThread()) { - mAPZC->InputBridge()->ReceiveInputEvent(*aEvent); - return; - } - - if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { - RefPtr<Runnable> r = - new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>( - *mouseEvent, mAPZC, this, - DispatchInputOnControllerThread<MouseInput, - WidgetMouseEvent>::APZOnly::Yes); - APZThreadUtils::RunOnControllerThread(std::move(r)); - return; - } - - MOZ_ASSERT_UNREACHABLE("Not implemented yet"); - } -} - -bool nsBaseWidget::DispatchWindowEvent(WidgetGUIEvent& event) { - nsEventStatus status; - DispatchEvent(&event, status); - return ConvertStatus(status); -} - -Document* nsBaseWidget::GetDocument() const { - if (mWidgetListener) { - if (PresShell* presShell = mWidgetListener->GetPresShell()) { - return presShell->GetDocument(); - } - } - return nullptr; -} - -void nsBaseWidget::CreateCompositorVsyncDispatcher() { - // Parent directly listens to the vsync source whereas - // child process communicate via IPC - // Should be called AFTER gfxPlatform is initialized - if (XRE_IsParentProcess()) { - if (!mCompositorVsyncDispatcherLock) { - mCompositorVsyncDispatcherLock = - MakeUnique<Mutex>("mCompositorVsyncDispatcherLock"); - } - MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); - if (!mCompositorVsyncDispatcher) { - RefPtr<VsyncDispatcher> vsyncDispatcher = - gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher(); - mCompositorVsyncDispatcher = - new CompositorVsyncDispatcher(std::move(vsyncDispatcher)); - } - } -} - -already_AddRefed<CompositorVsyncDispatcher> -nsBaseWidget::GetCompositorVsyncDispatcher() { - MOZ_ASSERT(mCompositorVsyncDispatcherLock.get()); - - MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); - RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher; - return dispatcher.forget(); -} - -already_AddRefed<WebRenderLayerManager> nsBaseWidget::CreateCompositorSession( - int aWidth, int aHeight, CompositorOptions* aOptionsOut) { - MOZ_ASSERT(aOptionsOut); - - do { - CreateCompositorVsyncDispatcher(); - - // Make sure GPU process is ready for use. - // If it failed to connect to GPU process, GPU process usage is disabled in - // EnsureGPUReady(). It could update gfxVars and gfxConfigs. - gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get(); - if (NS_WARN_IF(!gpm || NS_FAILED(gpm->EnsureGPUReady()))) { - return nullptr; - } - - // If widget type does not supports acceleration, we may be allowed to use - // software WebRender instead. - bool supportsAcceleration = WidgetTypeSupportsAcceleration(); - bool enableSWWR = true; - if (supportsAcceleration || - StaticPrefs::gfx_webrender_unaccelerated_widget_force()) { - enableSWWR = gfx::gfxVars::UseSoftwareWebRender(); - } - bool enableAPZ = UseAPZ(); - CompositorOptions options(enableAPZ, enableSWWR); - -#ifdef XP_WIN - if (supportsAcceleration) { - options.SetAllowSoftwareWebRenderD3D11( - gfx::gfxVars::AllowSoftwareWebRenderD3D11()); - } - if (mNeedFastSnaphot) { - options.SetNeedFastSnaphot(true); - } -#elif defined(MOZ_WIDGET_ANDROID) - MOZ_ASSERT(supportsAcceleration); - options.SetAllowSoftwareWebRenderOGL( - gfx::gfxVars::AllowSoftwareWebRenderOGL()); -#elif defined(MOZ_WIDGET_GTK) - if (supportsAcceleration) { - options.SetAllowSoftwareWebRenderOGL( - gfx::gfxVars::AllowSoftwareWebRenderOGL()); - } - options.SetAllowNativeCompositor(WidgetTypeSupportsNativeCompositing()); -#endif - -#ifdef MOZ_WIDGET_ANDROID - // Unconditionally set the compositor as initially paused, as we have not - // yet had a chance to send the compositor surface to the GPU process. We - // will do so shortly once we have returned to nsWindow::CreateLayerManager, - // where we will also resume the compositor if required. - options.SetInitiallyPaused(true); -#else - options.SetInitiallyPaused(CompositorInitiallyPaused()); -#endif - - RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this); - - uint64_t innerWindowId = 0; - if (Document* doc = GetDocument()) { - innerWindowId = doc->InnerWindowID(); - } - - bool retry = false; - mCompositorSession = gpm->CreateTopLevelCompositor( - this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(), - gfx::IntSize(aWidth, aHeight), innerWindowId, &retry); - - if (mCompositorSession) { - TextureFactoryIdentifier textureFactoryIdentifier; - nsCString error; - lm->Initialize(mCompositorSession->GetCompositorBridgeChild(), - wr::AsPipelineId(mCompositorSession->RootLayerTreeId()), - &textureFactoryIdentifier, error); - if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) { - retry = true; - DestroyCompositor(); - // gfxVars::UseDoubleBufferingWithCompositor() is also disabled. - gfx::GPUProcessManager::Get()->DisableWebRender( - wr::WebRenderError::INITIALIZE, error); - } - } - - // We need to retry in a loop because the act of failing to create the - // compositor can change our state (e.g. disable WebRender). - if (mCompositorSession || !retry) { - *aOptionsOut = options; - return lm.forget(); - } - } while (true); -} - -void nsBaseWidget::CreateCompositor(int aWidth, int aHeight) { - // This makes sure that gfxPlatforms gets initialized if it hasn't by now. - gfxPlatform::GetPlatform(); - - MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(), - "This function assumes OMTC"); - - MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild, - "Should have properly cleaned up the previous PCompositor pair " - "beforehand"); - - if (mCompositorBridgeChild) { - mCompositorBridgeChild->Destroy(); - } - - // Recreating this is tricky, as we may still have an old and we need - // to make sure it's properly destroyed by calling DestroyCompositor! - - // If we've already received a shutdown notification, don't try - // create a new compositor. - if (!mShutdownObserver) { - return; - } - - // The controller thread must be configured before the compositor - // session is created, so that the input bridge runs on the right - // thread. - ConfigureAPZControllerThread(); - - CompositorOptions options; - RefPtr<WebRenderLayerManager> lm = - CreateCompositorSession(aWidth, aHeight, &options); - if (!lm) { - return; - } - - MOZ_ASSERT(mCompositorSession); - mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild(); - SetCompositorWidgetDelegate( - mCompositorSession->GetCompositorWidgetDelegate()); - - if (options.UseAPZ()) { - mAPZC = mCompositorSession->GetAPZCTreeManager(); - ConfigureAPZCTreeManager(); - } else { - mAPZC = nullptr; - } - - if (mInitialZoomConstraints) { - UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID, - mInitialZoomConstraints->mViewID, - Some(mInitialZoomConstraints->mConstraints)); - mInitialZoomConstraints.reset(); - } - - TextureFactoryIdentifier textureFactoryIdentifier = - lm->GetTextureFactoryIdentifier(); - MOZ_ASSERT(textureFactoryIdentifier.mParentBackend == - LayersBackend::LAYERS_WR); - ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); - gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier); - - WindowUsesOMTC(); - - mWindowRenderer = std::move(lm); - - // Only track compositors for top-level windows, since other window types - // may use the basic compositor. Except on the OS X - see bug 1306383 -#if defined(XP_MACOSX) - bool getCompositorFromThisWindow = true; -#else - bool getCompositorFromThisWindow = mWindowType == WindowType::TopLevel; -#endif - - if (getCompositorFromThisWindow) { - gfxPlatform::GetPlatform()->NotifyCompositorCreated( - mWindowRenderer->GetCompositorBackendType()); - } -} - -void nsBaseWidget::NotifyCompositorSessionLost(CompositorSession* aSession) { - MOZ_ASSERT(aSession == mCompositorSession); - DestroyLayerManager(); -} - -bool nsBaseWidget::ShouldUseOffMainThreadCompositing() { - return gfxPlatform::UsesOffMainThreadCompositing(); -} - -WindowRenderer* nsBaseWidget::GetWindowRenderer() { - if (!mWindowRenderer) { - if (!mShutdownObserver) { - // We are shutting down, do not try to re-create a LayerManager - return nullptr; - } - // Try to use an async compositor first, if possible - if (ShouldUseOffMainThreadCompositing()) { - CreateCompositor(); - } - - if (!mWindowRenderer) { - mWindowRenderer = CreateFallbackRenderer(); - } - } - return mWindowRenderer; -} - -WindowRenderer* nsBaseWidget::CreateFallbackRenderer() { - return new FallbackRenderer; -} - -CompositorBridgeChild* nsBaseWidget::GetRemoteRenderer() { - return mCompositorBridgeChild; -} - -void nsBaseWidget::ClearCachedWebrenderResources() { - if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) { - return; - } - mWindowRenderer->AsWebRender()->ClearCachedResources(); -} - -bool nsBaseWidget::SetNeedFastSnaphot() { - MOZ_ASSERT(XRE_IsParentProcess()); - MOZ_ASSERT(!mCompositorSession); - - if (!XRE_IsParentProcess() || mCompositorSession) { - return false; - } - - mNeedFastSnaphot = true; - return true; -} - -already_AddRefed<gfx::DrawTarget> nsBaseWidget::StartRemoteDrawing() { - return nullptr; -} - -uint32_t nsBaseWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA; } - -//------------------------------------------------------------------------- -// -// Destroy the window -// -//------------------------------------------------------------------------- -void nsBaseWidget::OnDestroy() { - if (mTextEventDispatcher) { - mTextEventDispatcher->OnDestroyWidget(); - // Don't release it until this widget actually released because after this - // is called, TextEventDispatcher() may create it again. - } - - // If this widget is being destroyed, let the APZ code know to drop references - // to this widget. Callers of this function all should be holding a deathgrip - // on this widget already. - ReleaseContentController(); -} - -/* static */ -DesktopIntPoint nsBaseWidget::ConstrainPositionToBounds( - const DesktopIntPoint& aPoint, const DesktopIntSize& aSize, - const DesktopIntRect& aScreenRect) { - DesktopIntPoint point = aPoint; - - // The maximum position to which the window can be moved while keeping its - // bottom-right corner within screenRect. - auto const maxX = aScreenRect.XMost() - aSize.Width(); - auto const maxY = aScreenRect.YMost() - aSize.Height(); - - // Note that the conditional-pairs below are not exclusive with each other, - // and cannot be replaced with a simple call to `std::clamp`! If the window - // provided is too large to fit on the screen, they will both fire. Their - // order has been chosen to ensure that the window's top left corner will be - // onscreen. - - if (point.x >= maxX) { - point.x = maxX; - } - if (point.x < aScreenRect.x) { - point.x = aScreenRect.x; - } - - if (point.y >= maxY) { - point.y = maxY; - } - if (point.y < aScreenRect.y) { - point.y = aScreenRect.y; - } - - return point; -} - -void nsBaseWidget::MoveClient(const DesktopPoint& aOffset) { - LayoutDeviceIntPoint clientOffset(GetClientOffset()); - - // GetClientOffset returns device pixels; scale back to desktop pixels - // if that's what this widget uses for the Move/Resize APIs - if (BoundsUseDesktopPixels()) { - DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale(); - Move(aOffset.x - desktopOffset.x, aOffset.y - desktopOffset.y); - } else { - LayoutDevicePoint layoutOffset = aOffset * GetDesktopToDeviceScale(); - Move(layoutOffset.x - LayoutDeviceCoord(clientOffset.x), - layoutOffset.y - LayoutDeviceCoord(clientOffset.y)); - } -} - -void nsBaseWidget::ResizeClient(const DesktopSize& aSize, bool aRepaint) { - NS_ASSERTION((aSize.width >= 0), "Negative width passed to ResizeClient"); - NS_ASSERTION((aSize.height >= 0), "Negative height passed to ResizeClient"); - - LayoutDeviceIntRect clientBounds = GetClientBounds(); - - // GetClientBounds and mBounds are device pixels; scale back to desktop pixels - // if that's what this widget uses for the Move/Resize APIs - if (BoundsUseDesktopPixels()) { - DesktopSize desktopDelta = - (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) - - clientBounds.Size()) / - GetDesktopToDeviceScale(); - Resize(aSize.width + desktopDelta.width, aSize.height + desktopDelta.height, - aRepaint); - } else { - LayoutDeviceSize layoutSize = aSize * GetDesktopToDeviceScale(); - Resize(mBounds.Width() + (layoutSize.width - clientBounds.Width()), - mBounds.Height() + (layoutSize.height - clientBounds.Height()), - aRepaint); - } -} - -void nsBaseWidget::ResizeClient(const DesktopRect& aRect, bool aRepaint) { - NS_ASSERTION((aRect.Width() >= 0), "Negative width passed to ResizeClient"); - NS_ASSERTION((aRect.Height() >= 0), "Negative height passed to ResizeClient"); - - LayoutDeviceIntRect clientBounds = GetClientBounds(); - LayoutDeviceIntPoint clientOffset = GetClientOffset(); - DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale(); - - if (BoundsUseDesktopPixels()) { - DesktopPoint desktopOffset = clientOffset / scale; - DesktopSize desktopDelta = - (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) - - clientBounds.Size()) / - scale; - Resize(aRect.X() - desktopOffset.x, aRect.Y() - desktopOffset.y, - aRect.Width() + desktopDelta.width, - aRect.Height() + desktopDelta.height, aRepaint); - } else { - LayoutDeviceRect layoutRect = aRect * scale; - Resize(layoutRect.X() - clientOffset.x, layoutRect.Y() - clientOffset.y, - layoutRect.Width() + mBounds.Width() - clientBounds.Width(), - layoutRect.Height() + mBounds.Height() - clientBounds.Height(), - aRepaint); - } -} - -//------------------------------------------------------------------------- -// -// Bounds -// -//------------------------------------------------------------------------- - -/** - * If the implementation of nsWindow supports borders this method MUST be - * overridden - * - **/ -LayoutDeviceIntRect nsBaseWidget::GetClientBounds() { return GetBounds(); } - -/** - * If the implementation of nsWindow supports borders this method MUST be - * overridden - * - **/ -LayoutDeviceIntRect nsBaseWidget::GetBounds() { return mBounds; } - -/** - * If the implementation of nsWindow uses a local coordinate system within the - *window, this method must be overridden - * - **/ -LayoutDeviceIntRect nsBaseWidget::GetScreenBounds() { return GetBounds(); } - -nsresult nsBaseWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect) { - if (SizeMode() != nsSizeMode_Normal) { - return NS_ERROR_FAILURE; - } - aRect = GetScreenBounds(); - return NS_OK; -} - -LayoutDeviceIntPoint nsBaseWidget::GetClientOffset() { - return LayoutDeviceIntPoint(0, 0); -} - -uint32_t nsBaseWidget::GetMaxTouchPoints() const { return 0; } - -bool nsBaseWidget::HasPendingInputEvent() { return false; } - -bool nsBaseWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) { - return false; -} - -/** - * Modifies aFile to point at an icon file with the given name and suffix. The - * suffix may correspond to a file extension with leading '.' if appropriate. - * Returns true if the icon file exists and can be read. - */ -static bool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName, - const nsAString& aIconSuffix) { - aFile->Append(u"icons"_ns); - aFile->Append(u"default"_ns); - aFile->Append(aIconName + aIconSuffix); - - bool readable; - return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable; -} - -/** - * Resolve the given icon name into a local file object. This method is - * intended to be called by subclasses of nsBaseWidget. aIconSuffix is a - * platform specific icon file suffix (e.g., ".ico" under Win32). - * - * If no file is found matching the given parameters, then null is returned. - */ -void nsBaseWidget::ResolveIconName(const nsAString& aIconName, - const nsAString& aIconSuffix, - nsIFile** aResult) { - *aResult = nullptr; - - nsCOMPtr<nsIProperties> dirSvc = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); - if (!dirSvc) return; - - // first check auxilary chrome directories - - nsCOMPtr<nsISimpleEnumerator> dirs; - dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), - getter_AddRefs(dirs)); - if (dirs) { - bool hasMore; - while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr<nsISupports> element; - dirs->GetNext(getter_AddRefs(element)); - if (!element) continue; - nsCOMPtr<nsIFile> file = do_QueryInterface(element); - if (!file) continue; - if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) { - NS_ADDREF(*aResult = file); - return; - } - } - } - - // then check the main app chrome directory - - nsCOMPtr<nsIFile> file; - dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(file)); - if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix)) - NS_ADDREF(*aResult = file); -} - -void nsBaseWidget::SetSizeConstraints(const SizeConstraints& aConstraints) { - mSizeConstraints = aConstraints; - - // Popups are constrained during layout, and we don't want to synchronously - // paint from reflow, so bail out... This is not great, but it's no worse than - // what we used to do. - // - // The right fix here is probably making constraint changes go through the - // view manager and such. - if (mWindowType == WindowType::Popup) { - return; - } - - // If the current size doesn't meet the new constraints, trigger a - // resize to apply it. Note that, we don't want to invoke Resize if - // the new constraints don't affect the current size, because Resize - // implementation on some platforms may touch other geometry even if - // the size don't need to change. - LayoutDeviceIntSize curSize = mBounds.Size(); - LayoutDeviceIntSize clampedSize = - Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize)); - if (clampedSize != curSize) { - gfx::Size size; - if (BoundsUseDesktopPixels()) { - DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale(); - size = desktopSize.ToUnknownSize(); - } else { - size = gfx::Size(clampedSize.ToUnknownSize()); - } - Resize(size.width, size.height, true); - } -} - -const widget::SizeConstraints nsBaseWidget::GetSizeConstraints() { - return mSizeConstraints; -} - -// static -nsIRollupListener* nsBaseWidget::GetActiveRollupListener() { - // TODO: Simplify this. - return nsXULPopupManager::GetInstance(); -} - -void nsBaseWidget::NotifyWindowDestroyed() { - if (!mWidgetListener) return; - - nsCOMPtr<nsIAppWindow> window = mWidgetListener->GetAppWindow(); - nsCOMPtr<nsIBaseWindow> appWindow(do_QueryInterface(window)); - if (appWindow) { - appWindow->Destroy(); - } -} - -void nsBaseWidget::NotifyWindowMoved(int32_t aX, int32_t aY, - ByMoveToRect aByMoveToRect) { - if (mWidgetListener) { - mWidgetListener->WindowMoved(this, aX, aY, aByMoveToRect); - } - - if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) { - NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE)); - } -} - -void nsBaseWidget::NotifySizeMoveDone() { - if (!mWidgetListener) { - return; - } - if (PresShell* presShell = mWidgetListener->GetPresShell()) { - presShell->WindowSizeMoveDone(); - } -} - -void nsBaseWidget::NotifyThemeChanged(ThemeChangeKind aKind) { - LookAndFeel::NotifyChangedAllWindows(aKind); -} - -nsresult nsBaseWidget::NotifyIME(const IMENotification& aIMENotification) { - if (mIMEHasQuit) { - return NS_OK; - } - switch (aIMENotification.mMessage) { - case REQUEST_TO_COMMIT_COMPOSITION: - case REQUEST_TO_CANCEL_COMPOSITION: - // We should send request to IME only when there is a TextEventDispatcher - // instance (this means that this widget has dispatched at least one - // composition event or keyboard event) and the it has composition. - // Otherwise, there is nothing to do. - // Note that if current input transaction is for native input events, - // TextEventDispatcher::NotifyIME() will call - // TextEventDispatcherListener::NotifyIME(). - if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) { - return mTextEventDispatcher->NotifyIME(aIMENotification); - } - return NS_OK; - default: { - if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) { - mIMEHasFocus = true; - } - EnsureTextEventDispatcher(); - // TextEventDispatcher::NotifyIME() will always call - // TextEventDispatcherListener::NotifyIME(). I.e., even if current - // input transaction is for synthesized events for automated tests, - // notifications will be sent to native IME. - nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification); - if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) { - mIMEHasFocus = false; - } - return rv; - } - } -} - -void nsBaseWidget::EnsureTextEventDispatcher() { - if (mTextEventDispatcher) { - return; - } - mTextEventDispatcher = new TextEventDispatcher(this); -} - -nsIWidget::NativeIMEContext nsBaseWidget::GetNativeIMEContext() { - if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) { - // If we already have a TextEventDispatcher and it's working with - // a TextInputProcessor, we need to return pseudo IME context since - // TextCompositionArray::IndexOf(nsIWidget*) should return a composition - // on the pseudo IME context in such case. - NativeIMEContext pseudoIMEContext; - pseudoIMEContext.InitWithRawNativeIMEContext( - mTextEventDispatcher->GetPseudoIMEContext()); - return pseudoIMEContext; - } - return NativeIMEContext(this); -} - -nsIWidget::TextEventDispatcher* nsBaseWidget::GetTextEventDispatcher() { - EnsureTextEventDispatcher(); - return mTextEventDispatcher; -} - -void* nsBaseWidget::GetPseudoIMEContext() { - TextEventDispatcher* dispatcher = GetTextEventDispatcher(); - if (!dispatcher) { - return nullptr; - } - return dispatcher->GetPseudoIMEContext(); -} - -TextEventDispatcherListener* -nsBaseWidget::GetNativeTextEventDispatcherListener() { - // TODO: If all platforms supported use of TextEventDispatcher for handling - // native IME and keyboard events, this method should be removed since - // in such case, this is overridden by all the subclasses. - return nullptr; -} - -void nsBaseWidget::ZoomToRect(const uint32_t& aPresShellId, - const ScrollableLayerGuid::ViewID& aViewId, - const CSSRect& aRect, const uint32_t& aFlags) { - if (!mCompositorSession || !mAPZC) { - return; - } - LayersId layerId = mCompositorSession->RootLayerTreeId(); - mAPZC->ZoomToRect(ScrollableLayerGuid(layerId, aPresShellId, aViewId), - ZoomTarget{aRect}, aFlags); -} - -#ifdef ACCESSIBILITY - -a11y::LocalAccessible* nsBaseWidget::GetRootAccessible() { - NS_ENSURE_TRUE(mWidgetListener, nullptr); - - PresShell* presShell = mWidgetListener->GetPresShell(); - NS_ENSURE_TRUE(presShell, nullptr); - - // If container is null then the presshell is not active. This often happens - // when a preshell is being held onto for fastback. - nsPresContext* presContext = presShell->GetPresContext(); - NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr); - - // LocalAccessible creation might be not safe so use IsSafeToRunScript to - // make sure it's not created at unsafe times. - nsAccessibilityService* accService = GetOrCreateAccService(); - if (accService) { - return accService->GetRootDocumentAccessible( - presShell, nsContentUtils::IsSafeToRunScript()); - } - - return nullptr; -} - -#endif // ACCESSIBILITY - -void nsBaseWidget::StartAsyncScrollbarDrag( - const AsyncDragMetrics& aDragMetrics) { - if (!AsyncPanZoomEnabled()) { - return; - } - - MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession); - - LayersId layersId = mCompositorSession->RootLayerTreeId(); - ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, - aDragMetrics.mViewId); - - mAPZC->StartScrollbarDrag(guid, aDragMetrics); -} - -bool nsBaseWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation, - const ScrollableLayerGuid& aGuid) { - MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled()); - - return mAPZC->StartAutoscroll(aGuid, aAnchorLocation); -} - -void nsBaseWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) { - MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled()); - - mAPZC->StopAutoscroll(aGuid); -} - -LayersId nsBaseWidget::GetRootLayerTreeId() { - return mCompositorSession ? mCompositorSession->RootLayerTreeId() - : LayersId{0}; -} - -already_AddRefed<widget::Screen> nsBaseWidget::GetWidgetScreen() { - ScreenManager& screenManager = ScreenManager::GetSingleton(); - LayoutDeviceIntRect bounds = GetScreenBounds(); - DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale()); - return screenManager.ScreenForRect(deskBounds); -} - -nsresult nsIWidget::SynthesizeNativeTouchTap( - LayoutDeviceIntPoint aPoint, bool aLongTap, - nsISynthesizedEventCallback* aCallback) { - AutoSynthesizedEventCallbackNotifier notifier(aCallback); - if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) { - sPointerIdCounter = 0; - } - int pointerId = sPointerIdCounter; - sPointerIdCounter++; - nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, aPoint, - 1.0, 90, nullptr); - if (NS_FAILED(rv)) { - return rv; - } - - if (!aLongTap) { - return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0, - nullptr); - } - - // initiate a long tap - int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay", - TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC); - if (!mLongTapTimer) { - mLongTapTimer = NS_NewTimer(); - if (!mLongTapTimer) { - SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, aPoint, 0, 0, - nullptr); - return NS_ERROR_UNEXPECTED; - } - // Windows requires recuring events, so we set this to a smaller window - // than the pref value. - int timeout = elapse; - if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) { - timeout = TOUCH_INJECT_PUMP_TIMER_MSEC; - } - mLongTapTimer->InitWithNamedFuncCallback( - OnLongTapTimerCallback, this, timeout, nsITimer::TYPE_REPEATING_SLACK, - "nsIWidget::SynthesizeNativeTouchTap"_ns); - } - - // If we already have a long tap pending, cancel it. We only allow one long - // tap to be active at a time. - if (mLongTapTouchPoint) { - SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL, - mLongTapTouchPoint->mPosition, 0, 0, nullptr); - } - - mLongTapTouchPoint = MakeUnique<LongTapInfo>( - pointerId, aPoint, TimeDuration::FromMilliseconds(elapse), aCallback); - notifier.SkipNotification(); // we'll do it in the long-tap callback - return NS_OK; -} - -// static -void nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) { - auto* self = static_cast<nsIWidget*>(aClosure); - - if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) > - TimeStamp::Now()) { -#ifdef XP_WIN - // Windows needs us to keep pumping feedback to the digitizer, so update - // the pointer id with the same position. - self->SynthesizeNativeTouchPoint( - self->mLongTapTouchPoint->mPointerId, TOUCH_CONTACT, - self->mLongTapTouchPoint->mPosition, 1.0, 90, nullptr); -#endif - return; - } - - AutoSynthesizedEventCallbackNotifier notifier( - self->mLongTapTouchPoint->mCallback); - - // finished, remove the touch point - self->mLongTapTimer->Cancel(); - self->mLongTapTimer = nullptr; - self->SynthesizeNativeTouchPoint( - self->mLongTapTouchPoint->mPointerId, TOUCH_REMOVE, - self->mLongTapTouchPoint->mPosition, 0, 0, nullptr); - self->mLongTapTouchPoint = nullptr; -} - -float nsIWidget::GetFallbackDPI() { - RefPtr<const Screen> primaryScreen = - ScreenManager::GetSingleton().GetPrimaryScreen(); - return primaryScreen->GetDPI(); -} - -CSSToLayoutDeviceScale nsIWidget::GetFallbackDefaultScale() { - RefPtr<const Screen> s = ScreenManager::GetSingleton().GetPrimaryScreen(); - return s->GetCSSToLayoutDeviceScale(Screen::IncludeOSZoom::No); -} - -MultiTouchInput nsBaseWidget::UpdateSynthesizedTouchState( - MultiTouchInput* aState, mozilla::TimeStamp aTimeStamp, uint32_t aPointerId, - TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, - double aPointerPressure, uint32_t aPointerOrientation) { - ScreenIntPoint pointerScreenPoint = ViewAs<ScreenPixel>( - aPoint, PixelCastJustification::LayoutDeviceIsScreenForBounds); - - // We can't dispatch *aState directly because (a) dispatching - // it might inadvertently modify it and (b) in the case of touchend or - // touchcancel events aState will hold the touches that are - // still down whereas the input dispatched needs to hold the removed - // touch(es). We use |inputToDispatch| for this purpose. - MultiTouchInput inputToDispatch; - inputToDispatch.mInputType = MULTITOUCH_INPUT; - inputToDispatch.mTimeStamp = aTimeStamp; - - int32_t index = aState->IndexOfTouch((int32_t)aPointerId); - if (aPointerState == TOUCH_CONTACT) { - if (index >= 0) { - // found an existing touch point, update it - SingleTouchData& point = aState->mTouches[index]; - point.mScreenPoint = pointerScreenPoint; - point.mRotationAngle = (float)aPointerOrientation; - point.mForce = (float)aPointerPressure; - inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE; - } else { - // new touch point, add it - aState->mTouches.AppendElement(SingleTouchData( - (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0), - (float)aPointerOrientation, (float)aPointerPressure)); - inputToDispatch.mType = MultiTouchInput::MULTITOUCH_START; - } - inputToDispatch.mTouches = aState->mTouches; - } else { - MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL); - // a touch point is being lifted, so remove it from the stored list - if (index >= 0) { - aState->mTouches.RemoveElementAt(index); - } - inputToDispatch.mType = - (aPointerState == TOUCH_REMOVE ? MultiTouchInput::MULTITOUCH_END - : MultiTouchInput::MULTITOUCH_CANCEL); - inputToDispatch.mTouches.AppendElement(SingleTouchData( - (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0), - (float)aPointerOrientation, (float)aPointerPressure)); - } - - return inputToDispatch; -} - -void nsBaseWidget::NotifyLiveResizeStarted() { - // If we have mLiveResizeListeners already non-empty, we should notify those - // listeners that the resize stopped before starting anew. In theory this - // should never happen because we shouldn't get nested live resize actions. - NotifyLiveResizeStopped(); - MOZ_ASSERT(mLiveResizeListeners.IsEmpty()); - - // If we can get the active remote tab for the current widget, suppress - // the displayport on it during the live resize. - if (!mWidgetListener) { - return; - } - nsCOMPtr<nsIAppWindow> appWindow = mWidgetListener->GetAppWindow(); - if (!appWindow) { - return; - } - mLiveResizeListeners = appWindow->GetLiveResizeListeners(); - for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) { - mLiveResizeListeners[i]->LiveResizeStarted(); - } -} - -void nsBaseWidget::NotifyLiveResizeStopped() { - if (!mLiveResizeListeners.IsEmpty()) { - for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) { - mLiveResizeListeners[i]->LiveResizeStopped(); - } - mLiveResizeListeners.Clear(); - } -} - -nsresult nsBaseWidget::AsyncEnableDragDrop(bool aEnable) { - RefPtr<nsBaseWidget> kungFuDeathGrip = this; - return NS_DispatchToCurrentThreadQueue( - NS_NewRunnableFunction( - "AsyncEnableDragDropFn", - [this, aEnable, kungFuDeathGrip]() { EnableDragDrop(aEnable); }), - kAsyncDragDropTimeout, EventQueuePriority::Idle); -} - -void nsBaseWidget::SwipeFinished() { - if (mSwipeTracker) { - mSwipeTracker->Destroy(); - mSwipeTracker = nullptr; - } -} - -void nsBaseWidget::ReportSwipeStarted(uint64_t aInputBlockId, - bool aStartSwipe) { - if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) { - if (aStartSwipe) { - PanGestureInput& startEvent = mSwipeEventQueue->queuedEvents[0]; - TrackScrollEventAsSwipe(startEvent, mSwipeEventQueue->allowedDirections, - aInputBlockId); - for (size_t i = 1; i < mSwipeEventQueue->queuedEvents.Length(); i++) { - mSwipeTracker->ProcessEvent(mSwipeEventQueue->queuedEvents[i]); - } - } else if (mAPZC) { - // If the event wasn't start swipe, we need to notify it to APZ. - mAPZC->SetBrowserGestureResponse(aInputBlockId, - BrowserGestureResponse::NotConsumed); - } - mSwipeEventQueue = nullptr; - } -} - -void nsBaseWidget::TrackScrollEventAsSwipe( - const mozilla::PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections, uint64_t aInputBlockId) { - // If a swipe is currently being tracked kill it -- it's been interrupted - // by another gesture event. - if (mSwipeTracker) { - mSwipeTracker->CancelSwipe(aSwipeStartEvent.mTimeStamp); - mSwipeTracker->Destroy(); - mSwipeTracker = nullptr; - } - - uint32_t direction = - (aSwipeStartEvent.mPanDisplacement.x > 0.0) - ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT - : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT; - - mSwipeTracker = - new SwipeTracker(*this, aSwipeStartEvent, aAllowedDirections, direction); - - if (!mAPZC) { - mCurrentPanGestureBelongsToSwipe = true; - } else { - // Now SwipeTracker has started consuming pan events, notify it to APZ so - // that APZ can discard queued events. - mAPZC->SetBrowserGestureResponse(aInputBlockId, - BrowserGestureResponse::Consumed); - } -} - -nsBaseWidget::SwipeInfo nsBaseWidget::SendMayStartSwipe( - const mozilla::PanGestureInput& aSwipeStartEvent) { - nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - - uint32_t direction = - (aSwipeStartEvent.mPanDisplacement.x > 0.0) - ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT - : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT; - - // We're ready to start the animation. Tell Gecko about it, and at the same - // time ask it if it really wants to start an animation for this event. - // This event also reports back the directions that we can swipe in. - LayoutDeviceIntPoint position = RoundedToInt(aSwipeStartEvent.mPanStartPoint * - ScreenToLayoutDeviceScale(1)); - WidgetSimpleGestureEvent geckoEvent = SwipeTracker::CreateSwipeGestureEvent( - eSwipeGestureMayStart, this, position, aSwipeStartEvent.mTimeStamp); - geckoEvent.mDirection = direction; - geckoEvent.mDelta = 0.0; - geckoEvent.mAllowedDirections = 0; - bool shouldStartSwipe = - DispatchWindowEvent(geckoEvent); // event cancelled == swipe should start - - SwipeInfo result = {shouldStartSwipe, geckoEvent.mAllowedDirections}; - return result; -} - -WidgetWheelEvent nsBaseWidget::MayStartSwipeForAPZ( - const PanGestureInput& aPanInput, const APZEventResult& aApzResult) { - WidgetWheelEvent event = aPanInput.ToWidgetEvent(this); - - // Ignore swipe-to-navigation in PiP window. - if (mIsPIPWindow) { - return event; - } - - if (aPanInput.mHandledByAPZ && aPanInput.AllowsSwipe()) { - SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput); - event.mCanTriggerSwipe = swipeInfo.wantsSwipe; - if (swipeInfo.wantsSwipe) { - if (aApzResult.GetStatus() == nsEventStatus_eIgnore) { - // APZ has determined and that scrolling horizontally in the - // requested direction is impossible, so it didn't do any - // scrolling for the event. - // We know now that MayStartSwipe wants a swipe, so we can start - // the swipe now. - TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections, - aApzResult.mInputBlockId); - } else if (!aApzResult.GetHandledResult() || - !aApzResult.GetHandledResult()->IsHandledByRoot()) { - // We don't know whether this event can start a swipe, so we need - // to queue up events and wait for a call to ReportSwipeStarted. - // APZ might already have started scrolling in response to the - // event if it knew that it's the right thing to do. In that case - // we'll still get a call to ReportSwipeStarted, and we will - // discard the queued events at that point. - mSwipeEventQueue = MakeUnique<SwipeEventQueue>( - swipeInfo.allowedDirections, aApzResult.mInputBlockId); - } - } else { - // Inform that the browser gesture didn't use the pan event (pan-start - // precisely), so that APZ can now start using the event for - // scrolling/overscrolling. - mAPZC->SetBrowserGestureResponse(aApzResult.mInputBlockId, - BrowserGestureResponse::NotConsumed); - } - } - - if (mSwipeEventQueue && - mSwipeEventQueue->inputBlockId == aApzResult.mInputBlockId) { - mSwipeEventQueue->queuedEvents.AppendElement(aPanInput); - } - - return event; -} - -bool nsBaseWidget::MayStartSwipeForNonAPZ(const PanGestureInput& aPanInput) { - // Ignore swipe-to-navigation in PiP window. - if (mIsPIPWindow) { - return false; - } - - if (aPanInput.mType == PanGestureInput::PANGESTURE_MAYSTART || - aPanInput.mType == PanGestureInput::PANGESTURE_START) { - mCurrentPanGestureBelongsToSwipe = false; - } - if (mCurrentPanGestureBelongsToSwipe) { - // Ignore this event. It's a momentum event from a scroll gesture - // that was processed as a swipe, and the swipe animation has - // already finished (so mSwipeTracker is already null). - MOZ_ASSERT(aPanInput.IsMomentum(), - "If the fingers are still on the touchpad, we should still have " - "a SwipeTracker, " - "and it should have consumed this event."); - return true; - } - - if (!aPanInput.MayTriggerSwipe()) { - return false; - } - - SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput); - - // We're in the non-APZ case here, but we still want to know whether - // the event was routed to a child process, so we use InputAPZContext - // to get that piece of information. - ScrollableLayerGuid guid; - uint64_t blockId = 0; - InputAPZContext context(guid, blockId, nsEventStatus_eIgnore); - - WidgetWheelEvent event = aPanInput.ToWidgetEvent(this); - event.mCanTriggerSwipe = swipeInfo.wantsSwipe; - nsEventStatus status; - DispatchEvent(&event, status); - if (swipeInfo.wantsSwipe) { - if (context.WasRoutedToChildProcess()) { - // We don't know whether this event can start a swipe, so we need - // to queue up events and wait for a call to ReportSwipeStarted. - mSwipeEventQueue = - MakeUnique<SwipeEventQueue>(swipeInfo.allowedDirections, blockId); - } else if (event.TriggersSwipe()) { - TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections, blockId); - } - } - - if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == 0) { - mSwipeEventQueue->queuedEvents.AppendElement(aPanInput); - } - - return true; -} - -LayersId nsBaseWidget::GetLayersId() const { - return mCompositorSession ? mCompositorSession->RootLayerTreeId() - : LayersId{0}; -} - -const IMENotificationRequests& nsIWidget::IMENotificationRequestsRef() { - TextEventDispatcher* dispatcher = GetTextEventDispatcher(); - return dispatcher->IMENotificationRequestsRef(); -} - -void nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {} - -bool nsIWidget::GetEditCommands(NativeKeyBindingsType aType, - const WidgetKeyboardEvent& aEvent, - nsTArray<CommandInt>& aCommands) { - MOZ_ASSERT(aEvent.IsTrusted()); - MOZ_ASSERT(aCommands.IsEmpty()); - return true; -} - -already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboard() { - if (XRE_IsContentProcess()) { - return CreateBidiKeyboardContentProcess(); - } - return CreateBidiKeyboardInner(); -} - -#ifdef ANDROID -already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() { - // no bidi keyboard implementation - return nullptr; -} -#endif - -namespace mozilla::widget { - -const char* ToChar(InputContext::Origin aOrigin) { - switch (aOrigin) { - case InputContext::ORIGIN_MAIN: - return "ORIGIN_MAIN"; - case InputContext::ORIGIN_CONTENT: - return "ORIGIN_CONTENT"; - default: - return "Unexpected value"; - } -} - -const char* ToChar(IMEMessage aIMEMessage) { - switch (aIMEMessage) { - case NOTIFY_IME_OF_NOTHING: - return "NOTIFY_IME_OF_NOTHING"; - case NOTIFY_IME_OF_FOCUS: - return "NOTIFY_IME_OF_FOCUS"; - case NOTIFY_IME_OF_BLUR: - return "NOTIFY_IME_OF_BLUR"; - case NOTIFY_IME_OF_SELECTION_CHANGE: - return "NOTIFY_IME_OF_SELECTION_CHANGE"; - case NOTIFY_IME_OF_TEXT_CHANGE: - return "NOTIFY_IME_OF_TEXT_CHANGE"; - case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: - return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED"; - case NOTIFY_IME_OF_POSITION_CHANGE: - return "NOTIFY_IME_OF_POSITION_CHANGE"; - case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: - return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT"; - case REQUEST_TO_COMMIT_COMPOSITION: - return "REQUEST_TO_COMMIT_COMPOSITION"; - case REQUEST_TO_CANCEL_COMPOSITION: - return "REQUEST_TO_CANCEL_COMPOSITION"; - default: - return "Unexpected value"; - } -} - -void NativeIMEContext::Init(nsIWidget* aWidget) { - if (!aWidget) { - mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); - mOriginProcessID = static_cast<uint64_t>(-1); - return; - } - if (!XRE_IsContentProcess()) { - mRawNativeIMEContext = reinterpret_cast<uintptr_t>( - aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT)); - mOriginProcessID = 0; - return; - } - // If this is created in a child process, aWidget is an instance of - // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT. - // Instead of that PuppetWidget::GetNativeIMEContext() returns cached - // native IME context of the parent process. - *this = aWidget->GetNativeIMEContext(); -} - -void NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext) { - if (NS_WARN_IF(!aRawNativeIMEContext)) { - mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); - mOriginProcessID = static_cast<uint64_t>(-1); - return; - } - mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext); - mOriginProcessID = - XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0; -} - -void IMENotification::TextChangeDataBase::MergeWith( - const IMENotification::TextChangeDataBase& aOther) { - MOZ_ASSERT(aOther.IsValid(), "Merging data must store valid data"); - MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset, - "end of removed text must be same or larger than start"); - MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset, - "end of added text must be same or larger than start"); - - if (!IsValid()) { - *this = aOther; - return; - } - - // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed - // text ranges. I.e., mStartOffset should be the smallest offset of all - // modified text ranges in old text. |mRemovedEndOffset| should be the - // largest end offset in old text of all modified text ranges. - // |mAddedEndOffset| represents the end offset of all inserted text ranges. - // I.e., only this is an offset in new text. - // In other words, between mStartOffset and |mRemovedEndOffset| of the - // premodified text was already removed. And some text whose length is - // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|. I.e., - // this allows IME to mark dirty the modified text range with |mStartOffset| - // and |mRemovedEndOffset| if IME stores all text of the focused editor and - // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|. - // Additionally, IME can retrieve only the text between |mStartOffset| and - // |mAddedEndOffset| for updating stored text. - - // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they - // should be adjusted to be in same text. The |newData.mStartOffset| and - // |newData.mRemovedEndOffset| should be computed as in old text because - // |mStartOffset| and |mRemovedEndOffset| represent the modified text range - // in the old text but even if some text before the values of the newData - // has already been modified, the values don't include the changes. - - // For comparing new and old |mAddedEndOffset| values, they should be - // adjusted to be in same text. The |oldData.mAddedEndOffset| should be - // computed as in the new text because |mAddedEndOffset| indicates the end - // offset of inserted text in the new text but |oldData.mAddedEndOffset| - // doesn't include any changes of the text before |newData.mAddedEndOffset|. - - const TextChangeDataBase& newData = aOther; - const TextChangeDataBase oldData = *this; - - // mCausedOnlyByComposition should be true only when all changes are caused - // by composition. - mCausedOnlyByComposition = - newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition; - - // mIncludingChangesWithoutComposition should be true if at least one of - // merged changes occurred without composition. - mIncludingChangesWithoutComposition = - newData.mIncludingChangesWithoutComposition || - oldData.mIncludingChangesWithoutComposition; - - // mIncludingChangesDuringComposition should be true when at least one of - // the merged non-composition changes occurred during the latest composition. - if (!newData.mCausedOnlyByComposition && - !newData.mIncludingChangesDuringComposition) { - MOZ_ASSERT(newData.mIncludingChangesWithoutComposition); - MOZ_ASSERT(mIncludingChangesWithoutComposition); - // If new change is neither caused by composition nor occurred during - // composition, set mIncludingChangesDuringComposition to false because - // IME doesn't want outdated text changes as text change during current - // composition. - mIncludingChangesDuringComposition = false; - } else { - // Otherwise, set mIncludingChangesDuringComposition to true if either - // oldData or newData includes changes during composition. - mIncludingChangesDuringComposition = - newData.mIncludingChangesDuringComposition || - oldData.mIncludingChangesDuringComposition; - } - - if (newData.mStartOffset >= oldData.mAddedEndOffset) { - // Case 1: - // If new start is after old end offset of added text, it means that text - // after the modified range is modified. Like: - // added range of old change: +----------+ - // removed range of new change: +----------+ - // So, the old start offset is always the smaller offset. - mStartOffset = oldData.mStartOffset; - // The new end offset of removed text is moved by the old change and we - // need to cancel the move of the old change for comparing the offsets in - // same text because it doesn't make sensce to compare offsets in different - // text. - uint32_t newRemovedEndOffsetInOldText = - newData.mRemovedEndOffset - oldData.Difference(); - mRemovedEndOffset = - std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); - // The new end offset of added text is always the larger offset. - mAddedEndOffset = newData.mAddedEndOffset; - return; - } - - if (newData.mStartOffset >= oldData.mStartOffset) { - // If new start is in the modified range, it means that new data changes - // a part or all of the range. - mStartOffset = oldData.mStartOffset; - if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) { - // Case 2: - // If new end of removed text is greater than old end of added text, it - // means that all or a part of modified range modified again and text - // after the modified range is also modified. Like: - // added range of old change: +----------+ - // removed range of new change: +----------+ - // So, the new removed end offset is moved by the old change and we need - // to cancel the move of the old change for comparing the offsets in the - // same text because it doesn't make sense to compare the offsets in - // different text. - uint32_t newRemovedEndOffsetInOldText = - newData.mRemovedEndOffset - oldData.Difference(); - mRemovedEndOffset = - std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); - // The old end of added text is replaced by new change. So, it should be - // same as the new start. On the other hand, the new added end offset is - // always same or larger. Therefore, the merged end offset of added - // text should be the new end offset of added text. - mAddedEndOffset = newData.mAddedEndOffset; - return; - } - - // Case 3: - // If new end of removed text is less than old end of added text, it means - // that only a part of the modified range is modified again. Like: - // added range of old change: +------------+ - // removed range of new change: +-----+ - // So, the new end offset of removed text should be same as the old end - // offset of removed text. Therefore, the merged end offset of removed - // text should be the old text change's |mRemovedEndOffset|. - mRemovedEndOffset = oldData.mRemovedEndOffset; - // The old end of added text is moved by new change. So, we need to cancel - // the move of the new change for comparing the offsets in same text. - uint32_t oldAddedEndOffsetInNewText = - oldData.mAddedEndOffset + newData.Difference(); - mAddedEndOffset = - std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); - return; - } - - if (newData.mRemovedEndOffset >= oldData.mStartOffset) { - // If new end of removed text is greater than old start (and new start is - // less than old start), it means that a part of modified range is modified - // again and some new text before the modified range is also modified. - MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset, - "new start offset should be less than old one here"); - mStartOffset = newData.mStartOffset; - if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) { - // Case 4: - // If new end of removed text is greater than old end of added text, it - // means that all modified text and text after the modified range is - // modified. Like: - // added range of old change: +----------+ - // removed range of new change: +------------------+ - // So, the new end of removed text is moved by the old change. Therefore, - // we need to cancel the move of the old change for comparing the offsets - // in same text because it doesn't make sense to compare the offsets in - // different text. - uint32_t newRemovedEndOffsetInOldText = - newData.mRemovedEndOffset - oldData.Difference(); - mRemovedEndOffset = - std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); - // The old end of added text is replaced by new change. So, the old end - // offset of added text is same as new text change's start offset. Then, - // new change's end offset of added text is always same or larger than - // it. Therefore, merged end offset of added text is always the new end - // offset of added text. - mAddedEndOffset = newData.mAddedEndOffset; - return; - } - - // Case 5: - // If new end of removed text is less than old end of added text, it - // means that only a part of the modified range is modified again. Like: - // added range of old change: +----------+ - // removed range of new change: +----------+ - // So, the new end of removed text should be same as old end of removed - // text for preventing end of removed text to be modified. Therefore, - // merged end offset of removed text is always the old end offset of removed - // text. - mRemovedEndOffset = oldData.mRemovedEndOffset; - // The old end of added text is moved by this change. So, we need to - // cancel the move of the new change for comparing the offsets in same text - // because it doesn't make sense to compare the offsets in different text. - uint32_t oldAddedEndOffsetInNewText = - oldData.mAddedEndOffset + newData.Difference(); - mAddedEndOffset = - std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); - return; - } - - // Case 6: - // Otherwise, i.e., both new end of added text and new start are less than - // old start, text before the modified range is modified. Like: - // added range of old change: +----------+ - // removed range of new change: +----------+ - MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset, - "new start offset should be less than old one here"); - mStartOffset = newData.mStartOffset; - MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset, - "new removed end offset should be less than old one here"); - mRemovedEndOffset = oldData.mRemovedEndOffset; - // The end of added text should be adjusted with the new difference. - uint32_t oldAddedEndOffsetInNewText = - oldData.mAddedEndOffset + newData.Difference(); - mAddedEndOffset = - std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); -} - -#ifdef DEBUG - -// Let's test the code of merging multiple text change data in debug build -// and crash if one of them fails because this feature is very complex but -// cannot be tested with mochitest. -void IMENotification::TextChangeDataBase::Test() { - static bool gTestTextChangeEvent = true; - if (!gTestTextChangeEvent) { - return; - } - gTestTextChangeEvent = false; - - /**************************************************************************** - * Case 1 - ****************************************************************************/ - - // Appending text - MergeWith(TextChangeData(10, 10, 20, false, false)); - MergeWith(TextChangeData(20, 20, 35, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 1-1-1: mStartOffset should be the first offset"); - MOZ_ASSERT( - mRemovedEndOffset == 10, // 20 - (20 - 10) - "Test 1-1-2: mRemovedEndOffset should be the first end of removed text"); - MOZ_ASSERT( - mAddedEndOffset == 35, - "Test 1-1-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Removing text (longer line -> shorter line) - MergeWith(TextChangeData(10, 20, 10, false, false)); - MergeWith(TextChangeData(10, 30, 10, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 1-2-1: mStartOffset should be the first offset"); - MOZ_ASSERT(mRemovedEndOffset == 40, // 30 + (10 - 20) - "Test 1-2-2: mRemovedEndOffset should be the the last end of " - "removed text " - "with already removed length"); - MOZ_ASSERT( - mAddedEndOffset == 10, - "Test 1-2-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Removing text (shorter line -> longer line) - MergeWith(TextChangeData(10, 20, 10, false, false)); - MergeWith(TextChangeData(10, 15, 10, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 1-3-1: mStartOffset should be the first offset"); - MOZ_ASSERT(mRemovedEndOffset == 25, // 15 + (10 - 20) - "Test 1-3-2: mRemovedEndOffset should be the the last end of " - "removed text " - "with already removed length"); - MOZ_ASSERT( - mAddedEndOffset == 10, - "Test 1-3-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Appending text at different point (not sure if actually occurs) - MergeWith(TextChangeData(10, 10, 20, false, false)); - MergeWith(TextChangeData(55, 55, 60, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 1-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 45, // 55 - (10 - 20) - "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed " - "text without already added length"); - MOZ_ASSERT( - mAddedEndOffset == 60, - "Test 1-4-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Removing text at different point (not sure if actually occurs) - MergeWith(TextChangeData(10, 20, 10, false, false)); - MergeWith(TextChangeData(55, 68, 55, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 1-5-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 78, // 68 - (10 - 20) - "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed " - "text with already removed length"); - MOZ_ASSERT( - mAddedEndOffset == 55, - "Test 1-5-3: mAddedEndOffset should be the largest end of added text"); - Clear(); - - // Replacing text and append text (becomes longer) - MergeWith(TextChangeData(30, 35, 32, false, false)); - MergeWith(TextChangeData(32, 32, 40, false, false)); - MOZ_ASSERT(mStartOffset == 30, - "Test 1-6-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 35, // 32 - (32 - 35) - "Test 1-6-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 40, - "Test 1-6-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text and append text (becomes shorter) - MergeWith(TextChangeData(30, 35, 32, false, false)); - MergeWith(TextChangeData(32, 32, 33, false, false)); - MOZ_ASSERT(mStartOffset == 30, - "Test 1-7-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 35, // 32 - (32 - 35) - "Test 1-7-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 33, - "Test 1-7-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Removing text and replacing text after first range (not sure if actually - // occurs) - MergeWith(TextChangeData(30, 35, 30, false, false)); - MergeWith(TextChangeData(32, 34, 48, false, false)); - MOZ_ASSERT(mStartOffset == 30, - "Test 1-8-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 39, // 34 - (30 - 35) - "Test 1-8-2: mRemovedEndOffset should be the the first end of " - "removed text " - "without already removed text"); - MOZ_ASSERT( - mAddedEndOffset == 48, - "Test 1-8-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Removing text and replacing text after first range (not sure if actually - // occurs) - MergeWith(TextChangeData(30, 35, 30, false, false)); - MergeWith(TextChangeData(32, 38, 36, false, false)); - MOZ_ASSERT(mStartOffset == 30, - "Test 1-9-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 43, // 38 - (30 - 35) - "Test 1-9-2: mRemovedEndOffset should be the the first end of " - "removed text " - "without already removed text"); - MOZ_ASSERT( - mAddedEndOffset == 36, - "Test 1-9-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - /**************************************************************************** - * Case 2 - ****************************************************************************/ - - // Replacing text in around end of added text (becomes shorter) (not sure - // if actually occurs) - MergeWith(TextChangeData(50, 50, 55, false, false)); - MergeWith(TextChangeData(53, 60, 54, false, false)); - MOZ_ASSERT(mStartOffset == 50, - "Test 2-1-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 55, // 60 - (55 - 50) - "Test 2-1-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 54, - "Test 2-1-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text around end of added text (becomes longer) (not sure - // if actually occurs) - MergeWith(TextChangeData(50, 50, 55, false, false)); - MergeWith(TextChangeData(54, 62, 68, false, false)); - MOZ_ASSERT(mStartOffset == 50, - "Test 2-2-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 57, // 62 - (55 - 50) - "Test 2-2-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 68, - "Test 2-2-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text around end of replaced text (became shorter) (not sure if - // actually occurs) - MergeWith(TextChangeData(36, 48, 45, false, false)); - MergeWith(TextChangeData(43, 50, 49, false, false)); - MOZ_ASSERT(mStartOffset == 36, - "Test 2-3-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 53, // 50 - (45 - 48) - "Test 2-3-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already removed text length"); - MOZ_ASSERT( - mAddedEndOffset == 49, - "Test 2-3-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text around end of replaced text (became longer) (not sure if - // actually occurs) - MergeWith(TextChangeData(36, 52, 53, false, false)); - MergeWith(TextChangeData(43, 68, 61, false, false)); - MOZ_ASSERT(mStartOffset == 36, - "Test 2-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 67, // 68 - (53 - 52) - "Test 2-4-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 61, - "Test 2-4-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - /**************************************************************************** - * Case 3 - ****************************************************************************/ - - // Appending text in already added text (not sure if actually occurs) - MergeWith(TextChangeData(10, 10, 20, false, false)); - MergeWith(TextChangeData(15, 15, 30, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 3-1-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 10, - "Test 3-1-2: mRemovedEndOffset should be the the first end of " - "removed text"); - MOZ_ASSERT( - mAddedEndOffset == 35, // 20 + (30 - 15) - "Test 3-1-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text in added text (not sure if actually occurs) - MergeWith(TextChangeData(50, 50, 55, false, false)); - MergeWith(TextChangeData(52, 53, 56, false, false)); - MOZ_ASSERT(mStartOffset == 50, - "Test 3-2-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 50, - "Test 3-2-2: mRemovedEndOffset should be the the first end of " - "removed text"); - MOZ_ASSERT( - mAddedEndOffset == 58, // 55 + (56 - 53) - "Test 3-2-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text in replaced text (became shorter) (not sure if actually - // occurs) - MergeWith(TextChangeData(36, 48, 45, false, false)); - MergeWith(TextChangeData(37, 38, 50, false, false)); - MOZ_ASSERT(mStartOffset == 36, - "Test 3-3-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 48, - "Test 3-3-2: mRemovedEndOffset should be the the first end of " - "removed text"); - MOZ_ASSERT( - mAddedEndOffset == 57, // 45 + (50 - 38) - "Test 3-3-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text in replaced text (became longer) (not sure if actually - // occurs) - MergeWith(TextChangeData(32, 48, 53, false, false)); - MergeWith(TextChangeData(43, 50, 52, false, false)); - MOZ_ASSERT(mStartOffset == 32, - "Test 3-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 48, - "Test 3-4-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 55, // 53 + (52 - 50) - "Test 3-4-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text in replaced text (became shorter) (not sure if actually - // occurs) - MergeWith(TextChangeData(36, 48, 50, false, false)); - MergeWith(TextChangeData(37, 49, 47, false, false)); - MOZ_ASSERT(mStartOffset == 36, - "Test 3-5-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 48, - "Test 3-5-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49) - "Test 3-5-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); - - // Replacing text in replaced text (became longer) (not sure if actually - // occurs) - MergeWith(TextChangeData(32, 48, 53, false, false)); - MergeWith(TextChangeData(43, 50, 47, false, false)); - MOZ_ASSERT(mStartOffset == 32, - "Test 3-6-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 48, - "Test 3-6-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT(mAddedEndOffset == 50, // 53 + (47 - 50) - "Test 3-6-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); - - /**************************************************************************** - * Case 4 - ****************************************************************************/ - - // Replacing text all of already append text (not sure if actually occurs) - MergeWith(TextChangeData(50, 50, 55, false, false)); - MergeWith(TextChangeData(44, 66, 68, false, false)); - MOZ_ASSERT(mStartOffset == 44, - "Test 4-1-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 61, // 66 - (55 - 50) - "Test 4-1-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 68, - "Test 4-1-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text around a point in which text was removed (not sure if - // actually occurs) - MergeWith(TextChangeData(50, 62, 50, false, false)); - MergeWith(TextChangeData(44, 66, 68, false, false)); - MOZ_ASSERT(mStartOffset == 44, - "Test 4-2-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 78, // 66 - (50 - 62) - "Test 4-2-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already removed text length"); - MOZ_ASSERT( - mAddedEndOffset == 68, - "Test 4-2-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text all replaced text (became shorter) (not sure if actually - // occurs) - MergeWith(TextChangeData(50, 62, 60, false, false)); - MergeWith(TextChangeData(49, 128, 130, false, false)); - MOZ_ASSERT(mStartOffset == 49, - "Test 4-3-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 130, // 128 - (60 - 62) - "Test 4-3-2: mRemovedEndOffset should be the the last end of " - "removed text " - "without already removed text length"); - MOZ_ASSERT( - mAddedEndOffset == 130, - "Test 4-3-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - // Replacing text all replaced text (became longer) (not sure if actually - // occurs) - MergeWith(TextChangeData(50, 61, 73, false, false)); - MergeWith(TextChangeData(44, 100, 50, false, false)); - MOZ_ASSERT(mStartOffset == 44, - "Test 4-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT(mRemovedEndOffset == 88, // 100 - (73 - 61) - "Test 4-4-2: mRemovedEndOffset should be the the last end of " - "removed text " - "with already added text length"); - MOZ_ASSERT( - mAddedEndOffset == 50, - "Test 4-4-3: mAddedEndOffset should be the last end of added text"); - Clear(); - - /**************************************************************************** - * Case 5 - ****************************************************************************/ - - // Replacing text around start of added text (not sure if actually occurs) - MergeWith(TextChangeData(50, 50, 55, false, false)); - MergeWith(TextChangeData(48, 52, 49, false, false)); - MOZ_ASSERT(mStartOffset == 48, - "Test 5-1-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 50, - "Test 5-1-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 52, // 55 + (52 - 49) - "Test 5-1-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text around start of replaced text (became shorter) (not sure if - // actually occurs) - MergeWith(TextChangeData(50, 60, 58, false, false)); - MergeWith(TextChangeData(43, 50, 48, false, false)); - MOZ_ASSERT(mStartOffset == 43, - "Test 5-2-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 60, - "Test 5-2-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50) - "Test 5-2-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); - - // Replacing text around start of replaced text (became longer) (not sure if - // actually occurs) - MergeWith(TextChangeData(50, 60, 68, false, false)); - MergeWith(TextChangeData(43, 55, 53, false, false)); - MOZ_ASSERT(mStartOffset == 43, - "Test 5-3-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 60, - "Test 5-3-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55) - "Test 5-3-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); - - // Replacing text around start of replaced text (became shorter) (not sure if - // actually occurs) - MergeWith(TextChangeData(50, 60, 58, false, false)); - MergeWith(TextChangeData(43, 50, 128, false, false)); - MOZ_ASSERT(mStartOffset == 43, - "Test 5-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 60, - "Test 5-4-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 136, // 58 + (128 - 50) - "Test 5-4-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Replacing text around start of replaced text (became longer) (not sure if - // actually occurs) - MergeWith(TextChangeData(50, 60, 68, false, false)); - MergeWith(TextChangeData(43, 55, 65, false, false)); - MOZ_ASSERT(mStartOffset == 43, - "Test 5-5-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 60, - "Test 5-5-2: mRemovedEndOffset should be the the first end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 78, // 68 + (65 - 55) - "Test 5-5-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - /**************************************************************************** - * Case 6 - ****************************************************************************/ - - // Appending text before already added text (not sure if actually occurs) - MergeWith(TextChangeData(30, 30, 45, false, false)); - MergeWith(TextChangeData(10, 10, 20, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 6-1-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 30, - "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 55, // 45 + (20 - 10) - "Test 6-1-3: mAddedEndOffset should be the first end of added text with " - "added text length by the new change"); - Clear(); - - // Removing text before already removed text (not sure if actually occurs) - MergeWith(TextChangeData(30, 35, 30, false, false)); - MergeWith(TextChangeData(10, 25, 10, false, false)); - MOZ_ASSERT(mStartOffset == 10, - "Test 6-2-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 35, - "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed " - "text"); - MOZ_ASSERT( - mAddedEndOffset == 15, // 30 - (25 - 10) - "Test 6-2-3: mAddedEndOffset should be the first end of added text with " - "removed text length by the new change"); - Clear(); - - // Replacing text before already replaced text (not sure if actually occurs) - MergeWith(TextChangeData(50, 65, 70, false, false)); - MergeWith(TextChangeData(13, 24, 15, false, false)); - MOZ_ASSERT(mStartOffset == 13, - "Test 6-3-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 65, - "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed " - "text"); - MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24) - "Test 6-3-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); - - // Replacing text before already replaced text (not sure if actually occurs) - MergeWith(TextChangeData(50, 65, 70, false, false)); - MergeWith(TextChangeData(13, 24, 36, false, false)); - MOZ_ASSERT(mStartOffset == 13, - "Test 6-4-1: mStartOffset should be the smallest offset"); - MOZ_ASSERT( - mRemovedEndOffset == 65, - "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed " - "text"); - MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24) - "Test 6-4-3: mAddedEndOffset should be the first end of added " - "text without " - "removed text length by the new change"); - Clear(); -} - -#endif // #ifdef DEBUG - -} // namespace mozilla::widget - -#ifdef DEBUG -////////////////////////////////////////////////////////////// -// -// Code to deal with paint and event debug prefs. -// -////////////////////////////////////////////////////////////// -struct PrefPair { - const char* name; - bool value; -}; - -static PrefPair debug_PrefValues[] = { - {"nglayout.debug.crossing_event_dumping", false}, - {"nglayout.debug.event_dumping", false}, - {"nglayout.debug.invalidate_dumping", false}, - {"nglayout.debug.motion_event_dumping", false}, - {"nglayout.debug.paint_dumping", false}}; - -////////////////////////////////////////////////////////////// -bool nsBaseWidget::debug_GetCachedBoolPref(const char* aPrefName) { - NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null."); - - for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { - if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) { - return debug_PrefValues[i].value; - } - } - - return false; -} -////////////////////////////////////////////////////////////// -static void debug_SetCachedBoolPref(const char* aPrefName, bool aValue) { - NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null."); - - for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { - if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) { - debug_PrefValues[i].value = aValue; - - return; - } - } - - NS_ASSERTION(false, "cmon, this code is not reached dude."); -} - -////////////////////////////////////////////////////////////// -class Debug_PrefObserver final : public nsIObserver { - ~Debug_PrefObserver() = default; - - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER -}; - -NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver) - -NS_IMETHODIMP -Debug_PrefObserver::Observe(nsISupports* subject, const char* topic, - const char16_t* data) { - NS_ConvertUTF16toUTF8 prefName(data); - - bool value = Preferences::GetBool(prefName.get(), false); - debug_SetCachedBoolPref(prefName.get(), value); - return NS_OK; -} - -////////////////////////////////////////////////////////////// -/* static */ void debug_RegisterPrefCallbacks() { - static bool once = true; - - if (!once) { - return; - } - - once = false; - - nsCOMPtr<nsIObserver> obs(new Debug_PrefObserver()); - for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { - // Initialize the pref values - debug_PrefValues[i].value = - Preferences::GetBool(debug_PrefValues[i].name, false); - - if (obs) { - // Register callbacks for when these change - nsCString name; - name.AssignLiteral(debug_PrefValues[i].name, - strlen(debug_PrefValues[i].name)); - Preferences::AddStrongObserver(obs, name); - } - } -} -////////////////////////////////////////////////////////////// -static int32_t _GetPrintCount() { - static int32_t sCount = 0; - - return ++sCount; -} -////////////////////////////////////////////////////////////// -/* static */ -void nsBaseWidget::debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget, - WidgetGUIEvent* aGuiEvent, - const char* aWidgetName, int32_t aWindowID) { - if (aGuiEvent->mMessage == eMouseMove) { - if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping")) return; - } - - if (aGuiEvent->mMessage == eMouseEnterIntoWidget || - aGuiEvent->mMessage == eMouseExitFromWidget) { - if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping")) - return; - } - - if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping")) return; - - fprintf(aFileOut, "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n", - _GetPrintCount(), ToChar(aGuiEvent->mMessage), (void*)aWidget, - aWidgetName, aWindowID, aGuiEvent->mRefPoint.x.value, - aGuiEvent->mRefPoint.y.value); -} -////////////////////////////////////////////////////////////// -/* static */ -void nsBaseWidget::debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget, - const nsIntRegion& aRegion, - const char* aWidgetName, - int32_t aWindowID) { - NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE"); - NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null"); - - if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping")) return; - - nsIntRect rect = aRegion.GetBounds(); - fprintf(aFileOut, - "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d " - "%3d,%-3d", - _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID, rect.X(), - rect.Y(), rect.Width(), rect.Height()); - - fprintf(aFileOut, "\n"); -} -////////////////////////////////////////////////////////////// -/* static */ -void nsBaseWidget::debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget, - const LayoutDeviceIntRect* aRect, - const char* aWidgetName, - int32_t aWindowID) { - if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping")) return; - - NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE"); - NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null"); - - fprintf(aFileOut, "%4d Invalidate widget=%p name=%-12s id=0x%-6x", - _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID); - - if (aRect) { - fprintf(aFileOut, " rect=%3d,%-3d %3d,%-3d", aRect->X(), aRect->Y(), - aRect->Width(), aRect->Height()); - } else { - fprintf(aFileOut, " rect=%-15s", "none"); - } - - fprintf(aFileOut, "\n"); -} -////////////////////////////////////////////////////////////// - -#endif // DEBUG diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h @@ -1,750 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ -#ifndef nsBaseWidget_h__ -#define nsBaseWidget_h__ - -#include "InputData.h" -#include "mozilla/EventForwards.h" -#include "mozilla/Mutex.h" -#include "mozilla/RefPtr.h" -#include "mozilla/UniquePtr.h" -#include "mozilla/WidgetUtils.h" -#include "mozilla/dom/MouseEventBinding.h" -#include "mozilla/layers/APZCCallbackHelper.h" -#include "mozilla/layers/CompositorOptions.h" -#include "mozilla/layers/NativeLayer.h" -#include "mozilla/widget/ThemeChangeKind.h" -#include "mozilla/widget/WindowOcclusionState.h" -#include "nsRect.h" -#include "nsIWidget.h" -#include "nsWidgetsCID.h" -#include "nsIFile.h" -#include "nsString.h" -#include "nsCOMPtr.h" -#include "nsIRollupListener.h" -#include "nsIObserver.h" -#include "nsIWidgetListener.h" -#include "nsPIDOMWindow.h" -#include "nsWeakReference.h" - -#include <algorithm> - -class nsIContent; -class gfxContext; - -namespace mozilla { -class CompositorVsyncDispatcher; -class LiveResizeListener; -class FallbackRenderer; -class SwipeTracker; -struct SwipeEventQueue; -class WidgetWheelEvent; - -#ifdef ACCESSIBILITY -namespace a11y { -class LocalAccessible; -} -#endif - -namespace gfx { -class DrawTarget; -class SourceSurface; -} // namespace gfx - -namespace layers { -class CompositorBridgeChild; -class CompositorBridgeParent; -class IAPZCTreeManager; -class GeckoContentController; -class APZEventState; -struct APZEventResult; -class CompositorSession; -class ImageContainer; -class WebRenderLayerManager; -struct ScrollableLayerGuid; -class RemoteCompositorSession; -} // namespace layers - -namespace widget { -class CompositorWidgetDelegate; -class InProcessCompositorWidget; -class WidgetRenderingContext; -} // namespace widget - -class CompositorVsyncDispatcher; -} // namespace mozilla - -namespace base { -class Thread; -} // namespace base - -// Windows specific constant indicating the maximum number of touch points the -// inject api will allow. This also sets the maximum numerical value for touch -// ids we can use when injecting touch points on Windows. -#define TOUCH_INJECT_MAX_POINTS 256 - -class nsBaseWidget; - -// Helper class used in shutting down gfx related code. -class WidgetShutdownObserver final : public nsIObserver { - ~WidgetShutdownObserver(); - - public: - explicit WidgetShutdownObserver(nsBaseWidget* aWidget); - - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - void Register(); - void Unregister(); - - nsBaseWidget* mWidget; - bool mRegistered; -}; - -// Helper class used for observing locales change. -class LocalesChangedObserver final : public nsIObserver { - ~LocalesChangedObserver(); - - public: - explicit LocalesChangedObserver(nsBaseWidget* aWidget); - - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - void Register(); - void Unregister(); - - nsBaseWidget* mWidget; - bool mRegistered; -}; - -/** - * Common widget implementation used as base class for native - * or crossplatform implementations of Widgets. - * All cross-platform behavior that all widgets need to implement - * should be placed in this class. - * (Note: widget implementations are not required to use this - * class, but it gives them a head start.) - */ - -class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference { - template <class EventType, class InputType> - friend class DispatchEventOnMainThread; - friend class mozilla::widget::InProcessCompositorWidget; - friend class mozilla::layers::RemoteCompositorSession; - - protected: - typedef base::Thread Thread; - typedef mozilla::gfx::DrawTarget DrawTarget; - typedef mozilla::gfx::SourceSurface SourceSurface; - typedef mozilla::layers::CompositorBridgeChild CompositorBridgeChild; - typedef mozilla::layers::CompositorBridgeParent CompositorBridgeParent; - typedef mozilla::layers::IAPZCTreeManager IAPZCTreeManager; - typedef mozilla::layers::GeckoContentController GeckoContentController; - typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid; - typedef mozilla::layers::APZEventState APZEventState; - typedef mozilla::CSSIntRect CSSIntRect; - typedef mozilla::CSSRect CSSRect; - typedef mozilla::ScreenRotation ScreenRotation; - typedef mozilla::widget::CompositorWidgetDelegate CompositorWidgetDelegate; - typedef mozilla::layers::CompositorSession CompositorSession; - typedef mozilla::layers::ImageContainer ImageContainer; - - virtual ~nsBaseWidget(); - - public: - nsBaseWidget(); - - explicit nsBaseWidget(BorderStyle aBorderStyle); - - NS_DECL_THREADSAFE_ISUPPORTS - - // nsIWidget interface - void CaptureRollupEvents(bool aDoCapture) override {} - nsIWidgetListener* GetWidgetListener() const override; - void SetWidgetListener(nsIWidgetListener* alistener) override; - void Destroy() override; - float GetDPI() override; - - void GetWorkspaceID(nsAString& workspaceID) override; - void MoveToWorkspace(const nsAString& workspaceID) override; - bool IsTiled() const override { return mIsTiled; } - - bool IsFullyOccluded() const override { return mIsFullyOccluded; } - - void SetCursor(const Cursor&) override; - void SetCustomCursorAllowed(bool) override; - void ClearCachedCursor() final { - mCursor = {}; - mUpdateCursor = true; - } - void SetTransparencyMode(TransparencyMode aMode) override; - TransparencyMode GetTransparencyMode() override; - void SetWindowShadowStyle(mozilla::WindowShadow) override {} - void SetShowsToolbarButton(bool aShow) override {} - void SetSupportsNativeFullscreen(bool aSupportsNativeFullscreen) override {} - void SetWindowAnimationType(WindowAnimationType aType) override {} - void HideWindowChrome(bool aShouldHide) override {} - bool PrepareForFullscreenTransition(nsISupports** aData) override { - return false; - } - void PerformFullscreenTransition(FullscreenTransitionStage aStage, - uint16_t aDuration, nsISupports* aData, - nsIRunnable* aCallback) override; - void CleanupFullscreenTransition() override {} - already_AddRefed<Screen> GetWidgetScreen() override; - nsresult MakeFullScreen(bool aFullScreen) override; - void InfallibleMakeFullScreen(bool aFullScreen); - - LayersId GetLayersId() const override; - WindowRenderer* GetWindowRenderer() override; - bool HasWindowRenderer() const final { return !!mWindowRenderer; } - - void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) override; - - // A remote compositor session tied to this window has been lost and IPC - // messages will no longer work. The widget must clean up any lingering - // resources and possibly schedule another paint. - // - // A reference to the session object is held until this function has - // returned. Callers should hold a reference to the widget, since this - // function could deallocate the widget if it is unparented. - virtual void NotifyCompositorSessionLost( - mozilla::layers::CompositorSession* aSession); - - already_AddRefed<mozilla::CompositorVsyncDispatcher> - GetCompositorVsyncDispatcher(); - virtual void CreateCompositorVsyncDispatcher(); - virtual void CreateCompositor(); - virtual void CreateCompositor(int aWidth, int aHeight); - virtual void SetCompositorWidgetDelegate(CompositorWidgetDelegate*) {} - void PrepareWindowEffects() override {} - void UpdateThemeGeometries( - const nsTArray<ThemeGeometry>& aThemeGeometries) override {} - void SetModal(bool aModal) override {} - uint32_t GetMaxTouchPoints() const override; - void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass, - const nsAString& xulWinName) override {} - // Return whether this widget interprets parameters to Move and Resize APIs - // as "desktop pixels" rather than "device pixels", and therefore - // applies its GetDefaultScale() value to them before using them as mBounds - // etc (which are always stored in device pixels). - // Note that APIs that -get- the widget's position/size/bounds, rather than - // -setting- them (i.e. moving or resizing the widget) will always return - // values in the widget's device pixels. - bool BoundsUseDesktopPixels() const { - return mWindowType <= WindowType::Popup; - } - // Default implementation, to be overridden by platforms where desktop coords - // are virtualized and may not correspond to device pixels on the screen. - mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() override { - return mozilla::DesktopToLayoutDeviceScale(1.0); - } - - void ConstrainPosition(DesktopIntPoint&) override {} - // Utility function for derived-class overrides of ConstrainPosition. - static DesktopIntPoint ConstrainPositionToBounds( - const DesktopIntPoint&, const mozilla::DesktopIntSize&, - const DesktopIntRect&); - void MoveClient(const DesktopPoint& aOffset) override; - void ResizeClient(const DesktopSize& aSize, bool aRepaint) override; - void ResizeClient(const DesktopRect& aRect, bool aRepaint) override; - LayoutDeviceIntRect GetBounds() override; - LayoutDeviceIntRect GetClientBounds() override; - LayoutDeviceIntRect GetScreenBounds() override; - [[nodiscard]] nsresult GetRestoredBounds(LayoutDeviceIntRect& aRect) override; - LayoutDeviceIntPoint GetClientOffset() override; - void EnableDragDrop(bool aEnable) override {}; - nsresult AsyncEnableDragDrop(bool aEnable) override; - [[nodiscard]] nsresult GetAttention(int32_t aCycleCount) override { - return NS_OK; - } - bool HasPendingInputEvent() override; - void SetIcon(const nsAString& aIconSpec) override {} - bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) override; - void FreeNativeData(void* data, uint32_t aDataType) override {} - nsresult ActivateNativeMenuItemAt(const nsAString& indexString) override { - return NS_ERROR_NOT_IMPLEMENTED; - } - nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) override { - return NS_ERROR_NOT_IMPLEMENTED; - } - nsresult NotifyIME(const IMENotification& aIMENotification) final; - [[nodiscard]] nsresult AttachNativeKeyEvent( - mozilla::WidgetKeyboardEvent& aEvent) override { - return NS_ERROR_NOT_IMPLEMENTED; - } - bool ComputeShouldAccelerate(); - virtual bool WidgetTypeSupportsAcceleration() { return true; } - virtual bool WidgetTypeSupportsNativeCompositing() { return true; } - [[nodiscard]] nsresult OnDefaultButtonLoaded( - const LayoutDeviceIntRect& aButtonRect) override { - return NS_ERROR_NOT_IMPLEMENTED; - } - already_AddRefed<nsIWidget> CreateChild(const LayoutDeviceIntRect& aRect, - InitData&) final; - void AttachViewToTopLevel(bool aUseAttachedEvents) override; - nsIWidgetListener* GetAttachedWidgetListener() const override; - void SetAttachedWidgetListener(nsIWidgetListener* aListener) override; - nsIWidgetListener* GetPreviouslyAttachedWidgetListener() override; - void SetPreviouslyAttachedWidgetListener(nsIWidgetListener*) override; - NativeIMEContext GetNativeIMEContext() override; - TextEventDispatcher* GetTextEventDispatcher() final; - TextEventDispatcherListener* GetNativeTextEventDispatcherListener() override; - void ZoomToRect(const uint32_t& aPresShellId, - const ScrollableLayerGuid::ViewID& aViewId, - const CSSRect& aRect, const uint32_t& aFlags) override; - - // Dispatch an event that must be first be routed through APZ. - ContentAndAPZEventStatus DispatchInputEvent( - mozilla::WidgetInputEvent* aEvent) override; - void DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) override; - - bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event) override; - - void SetConfirmedTargetAPZC( - uint64_t aInputBlockId, - const nsTArray<ScrollableLayerGuid>& aTargets) const override; - - void UpdateZoomConstraints( - const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, - const mozilla::Maybe<ZoomConstraints>& aConstraints) override; - - bool AsyncPanZoomEnabled() const override; - - void SwipeFinished() override; - void ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe) override; - void TrackScrollEventAsSwipe(const mozilla::PanGestureInput& aSwipeStartEvent, - uint32_t aAllowedDirections, - uint64_t aInputBlockId); - struct SwipeInfo { - bool wantsSwipe; - uint32_t allowedDirections; - }; - SwipeInfo SendMayStartSwipe(const mozilla::PanGestureInput& aSwipeStartEvent); - // Returns a WidgetWheelEvent which needs to be handled by APZ regardless of - // whether |aPanInput| event was used for SwipeTracker or not. - mozilla::WidgetWheelEvent MayStartSwipeForAPZ( - const mozilla::PanGestureInput& aPanInput, - const mozilla::layers::APZEventResult& aApzResult); - - // Returns true if |aPanInput| event was used for SwipeTracker, false - // otherwise. - bool MayStartSwipeForNonAPZ(const mozilla::PanGestureInput& aPanInput); - - void NotifyWindowDestroyed(); - void NotifySizeMoveDone(); - - using ByMoveToRect = nsIWidgetListener::ByMoveToRect; - void NotifyWindowMoved(int32_t aX, int32_t aY, - ByMoveToRect = ByMoveToRect::No); - - // Should be called by derived implementations to notify on system color and - // theme changes. (Only one invocation per change is needed, not one - // invocation per change per window.) - void NotifyThemeChanged(mozilla::widget::ThemeChangeKind); - - void NotifyAPZOfDPIChange(); - -#ifdef ACCESSIBILITY - // Get the accessible for the window. - mozilla::a11y::LocalAccessible* GetRootAccessible(); -#endif - - // Return true if this is a simple widget (that is typically not worth - // accelerating) - bool IsSmallPopup() const; - - PopupLevel GetPopupLevel() { return mPopupLevel; } - - const SizeConstraints GetSizeConstraints() override; - void SetSizeConstraints(const SizeConstraints& aConstraints) override; - - void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override; - - bool StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation, - const ScrollableLayerGuid& aGuid) override; - - void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) override; - - mozilla::layers::LayersId GetRootLayerTreeId() override; - - /** - * Use this when GetLayerManager() returns a BasicLayerManager - * (nsBaseWidget::GetLayerManager() does). This sets up the widget's - * layer manager to temporarily render into aTarget. - * - * |aNaturalWidgetBounds| is the un-rotated bounds of |aWidget|. - * |aRotation| is the "virtual rotation" to apply when rendering to - * the target. When |aRotation| is ROTATION_0, - * |aNaturalWidgetBounds| is not used. - */ - class AutoLayerManagerSetup { - public: - AutoLayerManagerSetup(nsBaseWidget* aWidget, gfxContext* aTarget); - ~AutoLayerManagerSetup(); - - private: - nsBaseWidget* mWidget; - mozilla::FallbackRenderer* mRenderer = nullptr; - }; - friend class AutoLayerManagerSetup; - - virtual bool ShouldUseOffMainThreadCompositing(); - - static nsIRollupListener* GetActiveRollupListener(); - - void Shutdown(); - - void QuitIME(); - - // These functions should be called at the start and end of a "live" widget - // resize (i.e. when the window contents are repainting during the resize, - // such as when the user drags a window border). It will suppress the - // displayport during the live resize to avoid unneccessary overpainting. - void NotifyLiveResizeStarted(); - void NotifyLiveResizeStopped(); - - void NotifyCompositorScrollUpdate( - const mozilla::layers::CompositorScrollUpdate& aUpdate) override {}; - -#if defined(MOZ_WIDGET_ANDROID) - void RecvToolbarAnimatorMessageFromCompositor(int32_t) override {}; - void RecvScreenPixels(mozilla::ipc::Shmem&& aMem, const ScreenIntSize& aSize, - bool aNeedsYFlip) override {}; -#endif - - virtual void LocalesChanged() {} - - virtual void NotifyOcclusionState(mozilla::widget::OcclusionState aState) {} - - protected: - // These are methods for CompositorWidgetWrapper, and should only be - // accessed from that class. Derived widgets can choose which methods to - // implement, or none if supporting out-of-process compositing. - virtual bool PreRender(mozilla::widget::WidgetRenderingContext* aContext) { - return true; - } - virtual void PostRender(mozilla::widget::WidgetRenderingContext* aContext) {} - virtual RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() { - return nullptr; - } - virtual already_AddRefed<DrawTarget> StartRemoteDrawing(); - virtual already_AddRefed<DrawTarget> StartRemoteDrawingInRegion( - const LayoutDeviceIntRegion& aInvalidRegion) { - return StartRemoteDrawing(); - } - virtual void EndRemoteDrawing() {} - virtual void EndRemoteDrawingInRegion( - DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) { - EndRemoteDrawing(); - } - virtual void CleanupRemoteDrawing() {} - virtual void CleanupWindowEffects() {} - virtual bool InitCompositor(mozilla::layers::Compositor* aCompositor) { - return true; - } - virtual uint32_t GetGLFrameBufferFormat(); - virtual bool CompositorInitiallyPaused() { return false; } - - protected: - void ResolveIconName(const nsAString& aIconName, const nsAString& aIconSuffix, - nsIFile** aResult); - virtual void OnDestroy(); - void BaseCreate(nsIWidget* aParent, InitData* aInitData); - - virtual void ConfigureAPZCTreeManager(); - virtual void ConfigureAPZControllerThread(); - virtual already_AddRefed<GeckoContentController> - CreateRootContentController(); - - // Dispatch an event that has already been routed through APZ. - nsEventStatus ProcessUntransformedAPZEvent( - mozilla::WidgetInputEvent* aEvent, - const mozilla::layers::APZEventResult& aApzResult); - - nsresult SynthesizeNativeKeyEvent( - int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, - uint32_t aModifierFlags, const nsAString& aCharacters, - const nsAString& aUnmodifiedCharacters, - nsISynthesizedEventCallback* aCallback) override { - mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeMouseEvent( - LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage, - mozilla::MouseButton aButton, nsIWidget::Modifiers aModifierFlags, - nsISynthesizedEventCallback* aCallback) override { - mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeMouseMove( - LayoutDeviceIntPoint aPoint, - nsISynthesizedEventCallback* aCallback) override { - mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeMouseScrollEvent( - LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX, - double aDeltaY, double aDeltaZ, uint32_t aModifierFlags, - uint32_t aAdditionalFlags, - nsISynthesizedEventCallback* aCallback) override { - mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeTouchPoint( - uint32_t aPointerId, TouchPointerState aPointerState, - LayoutDeviceIntPoint aPoint, double aPointerPressure, - uint32_t aPointerOrientation, - nsISynthesizedEventCallback* aCallback) override { - mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeTouchPadPinch(TouchpadGesturePhase aEventPhase, - float aScale, - LayoutDeviceIntPoint aPoint, - int32_t aModifierFlags) override { - MOZ_RELEASE_ASSERT( - false, "This method is not implemented on the current platform"); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativePenInput( - uint32_t aPointerId, TouchPointerState aPointerState, - LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation, - int32_t aTiltX, int32_t aTiltY, int32_t aButton, - nsISynthesizedEventCallback* aCallback) override { - MOZ_RELEASE_ASSERT( - false, "This method is not implemented on the current platform"); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeTouchpadDoubleTap(LayoutDeviceIntPoint aPoint, - uint32_t aModifierFlags) override { - MOZ_RELEASE_ASSERT( - false, "This method is not implemented on the current platform"); - return NS_ERROR_UNEXPECTED; - } - - nsresult SynthesizeNativeTouchpadPan( - TouchpadGesturePhase aEventPhase, LayoutDeviceIntPoint aPoint, - double aDeltaX, double aDeltaY, int32_t aModifierFlags, - nsISynthesizedEventCallback* aCallback) override { - MOZ_RELEASE_ASSERT( - false, "This method is not implemented on the current platform"); - return NS_ERROR_UNEXPECTED; - } - - /** - * GetPseudoIMEContext() returns pseudo IME context when TextEventDispatcher - * has non-native input transaction. Otherwise, returns nullptr. - */ - void* GetPseudoIMEContext(); - - protected: - virtual already_AddRefed<nsIWidget> AllocateChildPopupWidget() { - return nsIWidget::CreateChildWindow(); - } - - WindowRenderer* CreateFallbackRenderer(); - - PopupType GetPopupType() const { return mPopupType; } - - bool HasRemoteContent() const { return mHasRemoteContent; } - - /** - * Apply the current size constraints to the given size. - * - * @param aWidth width to constrain - * @param aHeight height to constrain - */ - void ConstrainSize(int32_t* aWidth, int32_t* aHeight) override { - SizeConstraints c = GetSizeConstraints(); - *aWidth = std::clamp(*aWidth, c.mMinSize.width, c.mMaxSize.width); - *aHeight = std::clamp(*aHeight, c.mMinSize.height, c.mMaxSize.height); - } - - CompositorBridgeChild* GetRemoteRenderer() override; - - void ClearCachedWebrenderResources() override; - - bool SetNeedFastSnaphot() override; - - /** - * Notify the widget that this window is being used with OMTC. - */ - virtual void WindowUsesOMTC() {} - virtual void RegisterTouchWindow() {} - - mozilla::dom::Document* GetDocument() const; - - void EnsureTextEventDispatcher(); - - // Notify the compositor that a device reset has occurred. - void OnRenderingDeviceReset(); - - bool UseAPZ() const; - - bool AllowWebRenderForThisWindow(); - - /** - * For widgets that support synthesizing native touch events, this function - * can be used to manage the current state of synthetic pointers. Each widget - * must maintain its own MultiTouchInput instance and pass it in as the state, - * along with the desired parameters for the changes. This function returns - * a new MultiTouchInput object that is ready to be dispatched. - */ - mozilla::MultiTouchInput UpdateSynthesizedTouchState( - mozilla::MultiTouchInput* aState, mozilla::TimeStamp aTimeStamp, - uint32_t aPointerId, TouchPointerState aPointerState, - LayoutDeviceIntPoint aPoint, double aPointerPressure, - uint32_t aPointerOrientation); - - /** - * Dispatch the given MultiTouchInput through APZ to Gecko (if APZ is enabled) - * or directly to gecko (if APZ is not enabled). This function must only - * be called from the main thread, and if APZ is enabled, that must also be - * the APZ controller thread. - */ - void DispatchTouchInput(mozilla::MultiTouchInput& aInput); - - /** - * Dispatch the given PanGestureInput through APZ to Gecko (if APZ is enabled) - * or directly to gecko (if APZ is not enabled). This function must only - * be called from the main thread, and if APZ is enabled, that must also be - * the APZ controller thread. - */ - void DispatchPanGestureInput(mozilla::PanGestureInput& aInput); - void DispatchPinchGestureInput(mozilla::PinchGestureInput& aInput); - - static bool ConvertStatus(nsEventStatus aStatus) { - return aStatus == nsEventStatus_eConsumeNoDefault; - } - - protected: - // Returns whether compositing should use an external surface size. - virtual bool UseExternalCompositingSurface() const { return false; } - - /** - * Starts the OMTC compositor destruction sequence. - * - * When this function returns, the compositor should not be - * able to access the opengl context anymore. - * It is safe to call it several times if platform implementations - * require the compositor to be destroyed before ~nsBaseWidget is - * reached (This is the case with gtk2 for instance). - */ - virtual void DestroyCompositor(); - void DestroyLayerManager(); - void ReleaseContentController(); - void RevokeTransactionIdAllocator(); - - void FreeShutdownObserver(); - void FreeLocalesChangedObserver(); - - bool IsPIPWindow() const { return mIsPIPWindow; }; - - nsIWidgetListener* mWidgetListener; - nsIWidgetListener* mAttachedWidgetListener; - nsIWidgetListener* mPreviouslyAttachedWidgetListener; - RefPtr<WindowRenderer> mWindowRenderer; - RefPtr<CompositorSession> mCompositorSession; - RefPtr<CompositorBridgeChild> mCompositorBridgeChild; - - mozilla::UniquePtr<mozilla::Mutex> mCompositorVsyncDispatcherLock; - RefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher; - - RefPtr<IAPZCTreeManager> mAPZC; - RefPtr<GeckoContentController> mRootContentController; - RefPtr<APZEventState> mAPZEventState; - RefPtr<WidgetShutdownObserver> mShutdownObserver; - RefPtr<LocalesChangedObserver> mLocalesChangedObserver; - RefPtr<TextEventDispatcher> mTextEventDispatcher; - RefPtr<mozilla::SwipeTracker> mSwipeTracker; - mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue; - Cursor mCursor; - bool mCustomCursorAllowed = true; - BorderStyle mBorderStyle; - LayoutDeviceIntRect mBounds; - bool mIsTiled; - PopupLevel mPopupLevel; - PopupType mPopupType; - SizeConstraints mSizeConstraints; - bool mHasRemoteContent; - - struct FullscreenSavedState { - DesktopRect windowRect; - DesktopRect screenRect; - }; - mozilla::Maybe<FullscreenSavedState> mSavedBounds; - - bool mUpdateCursor; - bool mUseAttachedEvents; - bool mIMEHasFocus; - bool mIMEHasQuit; - // if the window is fully occluded (rendering may be paused in response) - bool mIsFullyOccluded; - bool mNeedFastSnaphot; - // This flag is only used when APZ is off. It indicates that the current pan - // gesture was processed as a swipe. Sometimes the swipe animation can finish - // before momentum events of the pan gesture have stopped firing, so this - // flag tells us that we shouldn't allow the remaining events to cause - // scrolling. It is reset to false once a new gesture starts (as indicated by - // a PANGESTURE_(MAY)START event). - bool mCurrentPanGestureBelongsToSwipe; - - // It's PictureInPicture window. - bool mIsPIPWindow : 1; - - struct InitialZoomConstraints { - InitialZoomConstraints(const uint32_t& aPresShellID, - const ScrollableLayerGuid::ViewID& aViewID, - const ZoomConstraints& aConstraints) - : mPresShellID(aPresShellID), - mViewID(aViewID), - mConstraints(aConstraints) {} - - uint32_t mPresShellID; - ScrollableLayerGuid::ViewID mViewID; - ZoomConstraints mConstraints; - }; - - mozilla::Maybe<InitialZoomConstraints> mInitialZoomConstraints; - - // This points to the resize listeners who have been notified that a live - // resize is in progress. This should always be empty when a live-resize is - // not in progress. - nsTArray<RefPtr<mozilla::LiveResizeListener>> mLiveResizeListeners; - -#ifdef DEBUG - protected: - static void debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget, - const LayoutDeviceIntRect* aRect, - const char* aWidgetName, int32_t aWindowID); - - static void debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget, - mozilla::WidgetGUIEvent* aGuiEvent, - const char* aWidgetName, int32_t aWindowID); - - static void debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget, - const nsIntRegion& aPaintEvent, - const char* aWidgetName, int32_t aWindowID); - - static bool debug_GetCachedBoolPref(const char* aPrefName); -#endif - - private: - already_AddRefed<mozilla::layers::WebRenderLayerManager> - CreateCompositorSession(int aWidth, int aHeight, - mozilla::layers::CompositorOptions* aOptionsOut); -}; - -#endif // nsBaseWidget_h__ diff --git a/widget/nsIWidget.cpp b/widget/nsIWidget.cpp @@ -0,0 +1,3541 @@ + +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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/. */ + +#include "nsIWidget.h" + +#include <utility> + +#include "GLConsts.h" +#include "InputData.h" +#include "LiveResizeListener.h" +#include "SwipeTracker.h" +#include "TouchEvents.h" +#include "X11UndefineNone.h" +#include "base/thread.h" +#include "mozilla/ArrayUtils.h" +#include "mozilla/Attributes.h" +#include "mozilla/GlobalKeyListener.h" +#include "mozilla/IMEStateManager.h" +#include "mozilla/Logging.h" +#include "mozilla/MouseEvents.h" +#include "mozilla/NativeKeyBindingsType.h" +#include "mozilla/Preferences.h" +#include "mozilla/PresShell.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Sprintf.h" +#include "mozilla/StaticPrefs_apz.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/StaticPrefs_gfx.h" +#include "mozilla/StaticPrefs_layers.h" +#include "mozilla/StaticPrefs_layout.h" +#include "mozilla/TextEventDispatcher.h" +#include "mozilla/TextEventDispatcherListener.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/Unused.h" +#include "mozilla/VsyncDispatcher.h" +#include "mozilla/dom/BrowserParent.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/SimpleGestureEventBinding.h" +#include "mozilla/gfx/2D.h" +#include "mozilla/gfx/GPUProcessManager.h" +#include "mozilla/gfx/gfxVars.h" +#include "mozilla/layers/APZCCallbackHelper.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/TouchActionHelper.h" +#include "mozilla/layers/APZEventState.h" +#include "mozilla/layers/APZInputBridge.h" +#include "mozilla/layers/APZThreadUtils.h" +#include "mozilla/layers/ChromeProcessController.h" +#include "mozilla/layers/Compositor.h" +#include "mozilla/layers/CompositorBridgeChild.h" +#include "mozilla/layers/CompositorBridgeParent.h" +#include "mozilla/layers/CompositorOptions.h" +#include "mozilla/layers/IAPZCTreeManager.h" +#include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/InputAPZContext.h" +#include "mozilla/layers/WebRenderLayerManager.h" +#include "mozilla/webrender/WebRenderTypes.h" +#include "mozilla/widget/ScreenManager.h" +#include "nsAppDirectoryServiceDefs.h" +#include "nsBaseDragService.h" +#include "nsCOMPtr.h" +#include "nsContentUtils.h" +#include "nsDeviceContext.h" +#include "nsGfxCIID.h" +#include "nsIAppWindow.h" +#include "nsIBaseWindow.h" +#include "nsIContent.h" +#include "nsIDOMWindowUtils.h" +#include "nsIScreenManager.h" +#include "nsISimpleEnumerator.h" +#include "nsIWidgetListener.h" +#include "nsMenuPopupFrame.h" +#include "nsRefPtrHashtable.h" +#include "nsServiceManagerUtils.h" +#include "nsWidgetsCID.h" +#include "nsXULPopupManager.h" +#include "prdtoa.h" +#include "prenv.h" +#ifdef ACCESSIBILITY +# include "nsAccessibilityService.h" +#endif +#include "gfxConfig.h" +#include "gfxUtils.h" // for ToDeviceColor +#include "mozilla/layers/CompositorSession.h" +#include "VRManagerChild.h" +#include "gfxConfig.h" +#include "nsView.h" +#include "nsViewManager.h" + +static mozilla::LazyLogModule sBaseWidgetLog("BaseWidget"); + +#ifdef DEBUG +# include "nsIObserver.h" + +static void debug_RegisterPrefCallbacks(); + +#endif + +#ifdef NOISY_WIDGET_LEAKS +static int32_t gNumWidgets; +#endif + +using namespace mozilla::dom; +using namespace mozilla::layers; +using namespace mozilla::ipc; +using namespace mozilla::widget; +using namespace mozilla; + +namespace mozilla::widget { + +// Helper class used in shutting down gfx related code. +class WidgetShutdownObserver final : public nsIObserver { + ~WidgetShutdownObserver(); + + public: + explicit WidgetShutdownObserver(nsIWidget* aWidget); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + void Register(); + void Unregister(); + + nsIWidget* mWidget; + bool mRegistered; +}; + +NS_IMPL_ISUPPORTS(WidgetShutdownObserver, nsIObserver) + +WidgetShutdownObserver::WidgetShutdownObserver(nsIWidget* aWidget) + : mWidget(aWidget), mRegistered(false) { + Register(); +} + +WidgetShutdownObserver::~WidgetShutdownObserver() { + // No need to call Unregister(), we can't be destroyed until nsIWidget + // gets torn down. The observer service and nsIWidget.have a ref on us + // so nsIWidget.has to call Unregister and then clear its ref. +} + +NS_IMETHODIMP +WidgetShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!mWidget) { + return NS_OK; + } + if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) { + RefPtr<nsIWidget> widget(mWidget); + widget->Shutdown(); + } else if (!strcmp(aTopic, "quit-application")) { + RefPtr<nsIWidget> widget(mWidget); + widget->QuitIME(); + } + return NS_OK; +} + +void WidgetShutdownObserver::Register() { + if (!mRegistered) { + mRegistered = true; + nsContentUtils::RegisterShutdownObserver(this); + +#ifndef MOZ_WIDGET_ANDROID + // The primary purpose of observing quit-application is + // to avoid leaking a widget on Windows when nothing else + // breaks the circular reference between the widget and + // TSFTextStore. However, our Android IME code crashes if + // doing this on Android, so let's not do this on Android. + // Doing this on Gtk and Mac just in case. + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->AddObserver(this, "quit-application", false); + } +#endif + } +} + +void WidgetShutdownObserver::Unregister() { + if (mRegistered) { + mWidget = nullptr; + +#ifndef MOZ_WIDGET_ANDROID + nsCOMPtr<nsIObserverService> observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->RemoveObserver(this, "quit-application"); + } +#endif + + nsContentUtils::UnregisterShutdownObserver(this); + mRegistered = false; + } +} + +#define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed" + +// Helper class used for observing locales change. +class LocalesChangedObserver final : public nsIObserver { + ~LocalesChangedObserver(); + + public: + explicit LocalesChangedObserver(nsIWidget* aWidget); + + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + void Register(); + void Unregister(); + + nsIWidget* mWidget; + bool mRegistered; +}; + +NS_IMPL_ISUPPORTS(LocalesChangedObserver, nsIObserver) + +LocalesChangedObserver::LocalesChangedObserver(nsIWidget* aWidget) + : mWidget(aWidget), mRegistered(false) { + Register(); +} + +LocalesChangedObserver::~LocalesChangedObserver() { + // No need to call Unregister(), we can't be destroyed until nsIWidget + // gets torn down. The observer service and nsIWidget.have a ref on us + // so nsIWidget.has to call Unregister and then clear its ref. +} + +NS_IMETHODIMP +LocalesChangedObserver::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!mWidget) { + return NS_OK; + } + if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) { + RefPtr<nsIWidget> widget(mWidget); + widget->LocalesChanged(); + } + return NS_OK; +} + +void LocalesChangedObserver::Register() { + if (mRegistered) { + return; + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true); + } + + // Locale might be update before registering + RefPtr<nsIWidget> widget(mWidget); + widget->LocalesChanged(); + + mRegistered = true; +} + +void LocalesChangedObserver::Unregister() { + if (!mRegistered) { + return; + } + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED); + } + + mWidget = nullptr; + mRegistered = false; +} + +} // namespace mozilla::widget + +// Async pump timer during injected long touch taps +#define TOUCH_INJECT_PUMP_TIMER_MSEC 50 +#define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500 +int32_t nsIWidget::sPointerIdCounter = 0; + +// Some statics from nsIWidget.h +/*static*/ +uint64_t AutoSynthesizedEventCallbackNotifier::sCallbackId = 0; +MOZ_RUNINIT nsTHashMap<uint64_t, nsCOMPtr<nsISynthesizedEventCallback>> + AutoSynthesizedEventCallbackNotifier::sSavedCallbacks; + +// The maximum amount of time to let the EnableDragDrop runnable wait in the +// idle queue before timing out and moving it to the regular queue. Value is in +// milliseconds. +const uint32_t kAsyncDragDropTimeout = 1000; + +NS_IMPL_ISUPPORTS(nsIWidget, nsIWidget, nsISupportsWeakReference) + +//------------------------------------------------------------------------- +// +// nsIWidget constructor +// +//------------------------------------------------------------------------- + +nsIWidget::nsIWidget() : nsIWidget(BorderStyle::None) {} + +nsIWidget::nsIWidget(BorderStyle aBorderStyle) + : mWidgetListener(nullptr), + mAttachedWidgetListener(nullptr), + mPreviouslyAttachedWidgetListener(nullptr), + mCompositorVsyncDispatcher(nullptr), + mBorderStyle(aBorderStyle), + mBounds(0, 0, 0, 0), + mIsTiled(false), + mPopupLevel(PopupLevel::Top), + mPopupType(PopupType::Any), + mHasRemoteContent(false), + mUpdateCursor(true), + mUseAttachedEvents(false), + mIMEHasFocus(false), + mIMEHasQuit(false), + mIsFullyOccluded(false), + mNeedFastSnaphot(false), + mCurrentPanGestureBelongsToSwipe(false), + mIsPIPWindow(false) { +#ifdef NOISY_WIDGET_LEAKS + gNumWidgets++; + printf("WIDGETS+ = %d\n", gNumWidgets); +#endif + +#ifdef DEBUG + debug_RegisterPrefCallbacks(); +#endif + + mShutdownObserver = new WidgetShutdownObserver(this); +} + +void nsIWidget::Shutdown() { + NotifyLiveResizeStopped(); + DestroyCompositor(); + FreeLocalesChangedObserver(); + FreeShutdownObserver(); +} + +void nsIWidget::QuitIME() { + IMEStateManager::WidgetOnQuit(this); + this->mIMEHasQuit = true; +} + +void nsIWidget::DestroyCompositor() { + RevokeTransactionIdAllocator(); + + // We release this before releasing the compositor, since it may hold the + // last reference to our ClientLayerManager. ClientLayerManager's dtor can + // trigger a paint, creating a new compositor, and we don't want to re-use + // the old vsync dispatcher. + if (mCompositorVsyncDispatcher) { + MOZ_ASSERT(mCompositorVsyncDispatcherLock.get()); + + MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); + mCompositorVsyncDispatcher->Shutdown(); + mCompositorVsyncDispatcher = nullptr; + } + + // The compositor shutdown sequence looks like this: + // 1. CompositorSession calls CompositorBridgeChild::Destroy. + // 2. CompositorBridgeChild synchronously sends WillClose. + // 3. CompositorBridgeParent releases some resources (such as the layer + // manager, compositor, and widget). + // 4. CompositorBridgeChild::Destroy returns. + // 5. Asynchronously, CompositorBridgeParent::ActorDestroy will fire on the + // compositor thread when the I/O thread closes the IPC channel. + // 6. Step 5 will schedule DeferredDestroy on the compositor thread, which + // releases the reference CompositorBridgeParent holds to itself. + // + // When CompositorSession::Shutdown returns, we assume the compositor is gone + // or will be gone very soon. + if (mCompositorSession) { + ReleaseContentController(); + mAPZC = nullptr; + SetCompositorWidgetDelegate(nullptr); + mCompositorBridgeChild = nullptr; + mCompositorSession->Shutdown(); + mCompositorSession = nullptr; + } +} + +// This prevents the layer manager from starting a new transaction during +// shutdown. +void nsIWidget::RevokeTransactionIdAllocator() { + if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) { + return; + } + mWindowRenderer->AsWebRender()->SetTransactionIdAllocator(nullptr); +} + +void nsIWidget::ReleaseContentController() { + if (mRootContentController) { + mRootContentController->Destroy(); + mRootContentController = nullptr; + } +} + +void nsIWidget::DestroyLayerManager() { + if (mWindowRenderer) { + mWindowRenderer->Destroy(); + mWindowRenderer = nullptr; + } + DestroyCompositor(); +} + +void nsIWidget::OnRenderingDeviceReset() { DestroyLayerManager(); } + +void nsIWidget::FreeShutdownObserver() { + if (mShutdownObserver) { + mShutdownObserver->Unregister(); + } + mShutdownObserver = nullptr; +} + +void nsIWidget::EnsureLocalesChangedObserver() { + if (!mLocalesChangedObserver) { + mLocalesChangedObserver = new LocalesChangedObserver(this); + } +} + +void nsIWidget::FreeLocalesChangedObserver() { + if (mLocalesChangedObserver) { + mLocalesChangedObserver->Unregister(); + } + mLocalesChangedObserver = nullptr; +} + +//------------------------------------------------------------------------- +// +// nsIWidget destructor +// +//------------------------------------------------------------------------- + +nsIWidget::~nsIWidget() { + if (mSwipeTracker) { + mSwipeTracker->Destroy(); + mSwipeTracker = nullptr; + } + + IMEStateManager::WidgetDestroyed(this); + + FreeLocalesChangedObserver(); + FreeShutdownObserver(); + DestroyLayerManager(); + +#ifdef NOISY_WIDGET_LEAKS + gNumWidgets--; + printf("WIDGETS- = %d\n", gNumWidgets); +#endif +} + +//------------------------------------------------------------------------- +// +// Basic create. +// +//------------------------------------------------------------------------- +void nsIWidget::BaseCreate(nsIWidget* aParent, widget::InitData* aInitData) { + if (aInitData) { + mWindowType = aInitData->mWindowType; + mBorderStyle = aInitData->mBorderStyle; + mPopupLevel = aInitData->mPopupLevel; + mPopupType = aInitData->mPopupHint; + mHasRemoteContent = aInitData->mHasRemoteContent; + mIsPIPWindow = aInitData->mPIPWindow; + } + + mParent = aParent; + if (mParent) { + mParent->AddToChildList(this); + } +} + +void nsIWidget::ClearParent() { + if (!mParent) { + return; + } + nsCOMPtr<nsIWidget> kungFuDeathGrip = this; + nsCOMPtr<nsIWidget> oldParent = mParent; + oldParent->RemoveFromChildList(this); + mParent = nullptr; + DidClearParent(oldParent); +} + +void nsIWidget::RemoveAllChildren() { + while (nsCOMPtr<nsIWidget> kid = mLastChild) { + kid->ClearParent(); + MOZ_ASSERT(kid != mLastChild); + } +} + +nsIFrame* nsIWidget::GetFrame() const { + if (auto* popup = GetPopupFrame()) { + return popup; + } + if (nsView* view = nsView::GetViewFor(this)) { + return view->GetFrame(); + } + return nullptr; +} + +nsMenuPopupFrame* nsIWidget::GetPopupFrame() const { + if (mWindowType != WindowType::Popup) { + return nullptr; + } + MOZ_ASSERT_IF(GetWidgetListener(), + GetWidgetListener()->GetAsMenuPopupFrame()); + return static_cast<nsMenuPopupFrame*>(GetWidgetListener()); +} + +void nsIWidget::DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) { + if (mCompositorBridgeChild) { + mCompositorBridgeChild->SendDynamicToolbarOffsetChanged(aOffset); + } +} + +LayoutDeviceIntRect nsIWidget::MaybeRoundToDisplayPixels( + const LayoutDeviceIntRect& aRect, TransparencyMode aTransparency, + int32_t aRound) { + if (aRound == 1) { + return aRect; + } + + // If the widget doesn't support transparency, we prefer truncating to + // ceiling, so that we don't have extra pixels not painted by our frame. + auto size = aTransparency == TransparencyMode::Opaque + ? aRect.Size().TruncatedToMultiple(aRound) + : aRect.Size().CeiledToMultiple(aRound); + Unused << NS_WARN_IF(aTransparency == TransparencyMode::Opaque && + size != aRect.Size()); + return {aRect.TopLeft().RoundedToMultiple(aRound), size}; +} + +//------------------------------------------------------------------------- +// +// Accessor functions to get/set the client data +// +//------------------------------------------------------------------------- + +nsIWidgetListener* nsIWidget::GetWidgetListener() const { + return mWidgetListener; +} + +void nsIWidget::SetWidgetListener(nsIWidgetListener* aWidgetListener) { + mWidgetListener = aWidgetListener; +} + +already_AddRefed<nsIWidget> nsIWidget::CreateChild( + const LayoutDeviceIntRect& aRect, widget::InitData& aInitData) { + nsCOMPtr<nsIWidget> widget; + switch (mWidgetType) { + case WidgetType::Native: { + if (aInitData.mWindowType == WindowType::Popup) { + widget = AllocateChildPopupWidget(); + } else { + widget = nsIWidget::CreateChildWindow(); + } + break; + } + case WidgetType::Headless: + widget = nsIWidget::CreateHeadlessWidget(); + break; + case WidgetType::Puppet: { + // This really only should happen in crashtests that have menupopups. + MOZ_ASSERT(aInitData.mWindowType == WindowType::Popup, + "Creating non-popup puppet widget?"); + widget = nsIWidget::CreatePuppetWidget(nullptr); + break; + } + } + + if (!widget) { + return nullptr; + } + + if (mNeedFastSnaphot) { + widget->SetNeedFastSnaphot(); + } + + if (NS_FAILED(widget->Create(this, aRect, &aInitData))) { + return nullptr; + } + + return widget.forget(); +} + +// Attach a view to our widget which we'll send events to. +void nsIWidget::AttachViewToTopLevel(bool aUseAttachedEvents) { + NS_ASSERTION(mWindowType == WindowType::TopLevel || + mWindowType == WindowType::Dialog || + mWindowType == WindowType::Invisible, + "Can't attach to window of that type"); + + mUseAttachedEvents = aUseAttachedEvents; +} + +nsIWidgetListener* nsIWidget::GetAttachedWidgetListener() const { + return mAttachedWidgetListener; +} + +nsIWidgetListener* nsIWidget::GetPreviouslyAttachedWidgetListener() { + return mPreviouslyAttachedWidgetListener; +} + +void nsIWidget::SetPreviouslyAttachedWidgetListener( + nsIWidgetListener* aListener) { + mPreviouslyAttachedWidgetListener = aListener; +} + +void nsIWidget::SetAttachedWidgetListener(nsIWidgetListener* aListener) { + mAttachedWidgetListener = aListener; +} + +//------------------------------------------------------------------------- +// +// Close this nsIWidget +// +//------------------------------------------------------------------------- +void nsIWidget::Destroy() { + DestroyCompositor(); + + // Just in case our parent is the only ref to us + nsCOMPtr<nsIWidget> kungFuDeathGrip(this); + // disconnect from the parent + if (mParent) { + mParent->RemoveFromChildList(this); + mParent = nullptr; + } + // disconnect from the children + RemoveAllChildren(); +} + +nsIWidget* nsIWidget::GetTopLevelWidget() { + auto* cur = this; + while (true) { + if (cur->IsTopLevelWidget()) { + break; + } + nsIWidget* parent = cur->GetParent(); + if (!parent) { + break; + } + cur = parent; + } + return cur; +} + +float nsIWidget::GetDPI() { return 96.0f; } + +void nsIWidget::NotifyAPZOfDPIChange() { + if (mAPZC) { + mAPZC->SetDPI(GetDPI()); + } +} + +CSSToLayoutDeviceScale nsIWidget::GetDefaultScale() { + double devPixelsPerCSSPixel = StaticPrefs::layout_css_devPixelsPerPx(); + + if (devPixelsPerCSSPixel <= 0.0) { + devPixelsPerCSSPixel = GetDefaultScaleInternal(); + } + + return CSSToLayoutDeviceScale(devPixelsPerCSSPixel); +} + +nsIntSize nsIWidget::CustomCursorSize(const Cursor& aCursor) { + MOZ_ASSERT(aCursor.IsCustom()); + int32_t width = 0; + int32_t height = 0; + aCursor.mContainer->GetWidth(&width); + aCursor.mContainer->GetHeight(&height); + aCursor.mResolution.ApplyTo(width, height); + return {width, height}; +} + +LayoutDeviceIntSize nsIWidget::NormalSizeModeClientToWindowSizeDifference() { + auto margin = NormalSizeModeClientToWindowMargin(); + MOZ_ASSERT(margin.top >= 0, "Window should be bigger than client area"); + MOZ_ASSERT(margin.left >= 0, "Window should be bigger than client area"); + MOZ_ASSERT(margin.right >= 0, "Window should be bigger than client area"); + MOZ_ASSERT(margin.bottom >= 0, "Window should be bigger than client area"); + return {margin.LeftRight(), margin.TopBottom()}; +} + +RefPtr<mozilla::VsyncDispatcher> nsIWidget::GetVsyncDispatcher() { + return nullptr; +} + +//------------------------------------------------------------------------- +// +// Add a child to the list of children +// +//------------------------------------------------------------------------- +void nsIWidget::AddToChildList(nsIWidget* aChild) { + MOZ_ASSERT(!aChild->GetNextSibling() && !aChild->GetPrevSibling(), + "aChild not properly removed from its old child list"); + + if (!mFirstChild) { + mFirstChild = mLastChild = aChild; + } else { + // append to the list + MOZ_ASSERT(mLastChild); + MOZ_ASSERT(!mLastChild->GetNextSibling()); + mLastChild->SetNextSibling(aChild); + aChild->SetPrevSibling(mLastChild); + mLastChild = aChild; + } +} + +//------------------------------------------------------------------------- +// +// Remove a child from the list of children +// +//------------------------------------------------------------------------- +void nsIWidget::RemoveFromChildList(nsIWidget* aChild) { + MOZ_ASSERT(aChild->GetParent() == this, "Not one of our kids!"); + + if (mLastChild == aChild) { + mLastChild = mLastChild->GetPrevSibling(); + } + if (mFirstChild == aChild) { + mFirstChild = mFirstChild->GetNextSibling(); + } + + // Now remove from the list. Make sure that we pass ownership of the tail + // of the list correctly before we have aChild let go of it. + nsIWidget* prev = aChild->GetPrevSibling(); + nsIWidget* next = aChild->GetNextSibling(); + if (prev) { + prev->SetNextSibling(next); + } + if (next) { + next->SetPrevSibling(prev); + } + + aChild->SetNextSibling(nullptr); + aChild->SetPrevSibling(nullptr); +} + +//------------------------------------------------------------------------- +// +// Get this component cursor +// +//------------------------------------------------------------------------- + +void nsIWidget::SetCursor(const Cursor& aCursor) { mCursor = aCursor; } + +void nsIWidget::SetCustomCursorAllowed(bool aIsAllowed) { + if (aIsAllowed != mCustomCursorAllowed) { + mCustomCursorAllowed = aIsAllowed; + mUpdateCursor = true; + SetCursor(mCursor); + } +} + +//------------------------------------------------------------------------- +// +// Window transparency methods +// +//------------------------------------------------------------------------- + +void nsIWidget::SetTransparencyMode(TransparencyMode aMode) {} + +TransparencyMode nsIWidget::GetTransparencyMode() { + return TransparencyMode::Opaque; +} + +/* virtual */ +void nsIWidget::PerformFullscreenTransition(FullscreenTransitionStage aStage, + uint16_t aDuration, + nsISupports* aData, + nsIRunnable* aCallback) { + MOZ_ASSERT_UNREACHABLE( + "Should never call PerformFullscreenTransition on nsIWidget"); +} + +//------------------------------------------------------------------------- +// +// Put the window into full-screen mode +// +//------------------------------------------------------------------------- +void nsIWidget::InfallibleMakeFullScreen(bool aFullScreen) { +#define MOZ_FORMAT_RECT(fmtstr) "[" fmtstr "," fmtstr " " fmtstr "x" fmtstr "]" +#define MOZ_SPLAT_RECT(rect) \ + (rect).X(), (rect).Y(), (rect).Width(), (rect).Height() + + // Windows which can be made fullscreen are exactly those which are located on + // the desktop, rather than being a child of some other window. + MOZ_DIAGNOSTIC_ASSERT(BoundsUseDesktopPixels(), + "non-desktop windows cannot be made fullscreen"); + + // Ensure that the OS chrome is hidden/shown before we resize and/or exit the + // function. + // + // HideWindowChrome() may (depending on platform, implementation details, and + // OS-level user preferences) alter the reported size of the window. The + // obvious and principled solution is socks-and-shoes: + // - On entering fullscreen mode: hide window chrome, then perform resize. + // - On leaving fullscreen mode: unperform resize, then show window chrome. + // + // ... unfortunately, HideWindowChrome() requires Resize() to be called + // afterwards (see bug 498835), which prevents this from being done in a + // straightforward way. + // + // Instead, we always call HideWindowChrome() just before we call Resize(). + // This at least ensures that our measurements are consistently taken in a + // pre-transition state. + // + // ... unfortunately again, coupling HideWindowChrome() to Resize() means that + // we have to worry about the possibility of control flows that don't call + // Resize() at all. (That shouldn't happen, but it's not trivial to rule out.) + // We therefore set up a fallback to fix up the OS chrome if it hasn't been + // done at exit time. + bool hasAdjustedOSChrome = false; + const auto adjustOSChrome = [&]() { + if (hasAdjustedOSChrome) { + MOZ_ASSERT_UNREACHABLE("window chrome should only be adjusted once"); + return; + } + HideWindowChrome(aFullScreen); + hasAdjustedOSChrome = true; + }; + const auto adjustChromeOnScopeExit = MakeScopeExit([&]() { + if (hasAdjustedOSChrome) { + return; + } + + MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, + ("window was not resized within InfallibleMakeFullScreen()")); + + // Hide chrome and "resize" the window to its current size. + auto rect = GetBounds(); + adjustOSChrome(); + Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(), true); + }); + + // Attempt to resize to `rect`. + // + // Returns the actual rectangle resized to. (This may differ from `rect`, if + // the OS is unhappy with it. See bug 1786226.) + const auto doReposition = [&](auto rect) -> void { + static_assert(std::is_base_of_v<DesktopPixel, + std::remove_reference_t<decltype(rect)>>, + "doReposition requires a rectangle using desktop pixels"); + + if (MOZ_LOG_TEST(sBaseWidgetLog, LogLevel::Debug)) { + const DesktopRect previousSize = + GetScreenBounds() / GetDesktopToDeviceScale(); + MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, + ("before resize: " MOZ_FORMAT_RECT("%f"), + MOZ_SPLAT_RECT(previousSize))); + } + + adjustOSChrome(); + Resize(rect.X(), rect.Y(), rect.Width(), rect.Height(), true); + + if (MOZ_LOG_TEST(sBaseWidgetLog, LogLevel::Warning)) { + // `rect` may have any underlying data type; coerce to float to + // simplify printf-style logging + const gfx::RectTyped<DesktopPixel, float> rectAsFloat{rect}; + + // The OS may have objected to the target position. That's not necessarily + // a problem -- it'll happen regularly on Macs with camera notches in the + // monitor, for instance (see bug 1786226) -- but it probably deserves to + // be called out. + // + // Since there's floating-point math involved, the actual values may be + // off by a few ulps -- as an upper bound, perhaps 8 * FLT_EPSILON * + // max(MOZ_SPLAT_RECT(rect)) -- but 0.01 should be several orders of + // magnitude bigger than that. + + const auto postResizeRectRaw = GetScreenBounds(); + const auto postResizeRect = postResizeRectRaw / GetDesktopToDeviceScale(); + const bool succeeded = postResizeRect.WithinEpsilonOf(rectAsFloat, 0.01); + + if (succeeded) { + MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, + ("resized to: " MOZ_FORMAT_RECT("%f"), + MOZ_SPLAT_RECT(rectAsFloat))); + } else { + MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, + ("attempted to resize to: " MOZ_FORMAT_RECT("%f"), + MOZ_SPLAT_RECT(rectAsFloat))); + MOZ_LOG(sBaseWidgetLog, LogLevel::Warning, + ("... but ended up at: " MOZ_FORMAT_RECT("%f"), + MOZ_SPLAT_RECT(postResizeRect))); + } + + MOZ_LOG( + sBaseWidgetLog, LogLevel::Verbose, + ("(... which, before DPI adjustment, is:" MOZ_FORMAT_RECT("%d") ")", + MOZ_SPLAT_RECT(postResizeRectRaw))); + } + }; + + if (aFullScreen) { + if (!mSavedBounds) { + mSavedBounds = Some(FullscreenSavedState()); + } + // save current position + mSavedBounds->windowRect = GetScreenBounds() / GetDesktopToDeviceScale(); + + nsCOMPtr<nsIScreen> screen = GetWidgetScreen(); + if (!screen) { + return; + } + + // Move to fill the screen. + doReposition(screen->GetRectDisplayPix()); + // Save off the new position. (This may differ from GetRectDisplayPix(), if + // the OS was unhappy with it. See bug 1786226.) + mSavedBounds->screenRect = GetScreenBounds() / GetDesktopToDeviceScale(); + } else { + if (!mSavedBounds) { + // This should never happen, at present, since we don't make windows + // fullscreen at their creation time; but it's not logically impossible. + MOZ_ASSERT(false, "fullscreen window did not have saved position"); + return; + } + + // Figure out where to go from here. + // + // Fortunately, since we're currently fullscreen (and other code should be + // handling _keeping_ us fullscreen even after display-layout changes), + // there's an obvious choice for which display we should attach to; all we + // need to determine is where on that display we should go. + + const DesktopRect currentWinRect = + GetScreenBounds() / GetDesktopToDeviceScale(); + + // Optimization: if where we are is where we were, then where we originally + // came from is where we're going to go. + if (currentWinRect == DesktopRect(mSavedBounds->screenRect)) { + MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, + ("no location change detected; returning to saved location")); + doReposition(mSavedBounds->windowRect); + return; + } + + /* + General case: figure out where we're going to go by dividing where we are + by where we were, and then multiplying by where we originally came from. + + Less abstrusely: resize so that we occupy the same proportional position + on our current display after leaving fullscreen as we occupied on our + previous display before entering fullscreen. + + (N.B.: We do not clamp. If we were only partially on the old display, + we'll be only partially on the new one, too.) + */ + + MOZ_LOG(sBaseWidgetLog, LogLevel::Debug, + ("location change detected; computing new destination")); + + // splat: convert an arbitrary Rect into a tuple, for syntactic convenience. + const auto splat = [](auto rect) { + return std::tuple(rect.X(), rect.Y(), rect.Width(), rect.Height()); + }; + + // remap: find the unique affine mapping which transforms `src` to `dst`, + // and apply it to `val`. + using Range = std::pair<float, float>; + const auto remap = [](Range dst, Range src, float val) { + // linear interpolation and its inverse: lerp(a, b, invlerp(a, b, t)) == t + const auto lerp = [](float lo, float hi, float t) { + return lo + t * (hi - lo); + }; + const auto invlerp = [](float lo, float hi, float mid) { + return (mid - lo) / (hi - lo); + }; + + const auto [dst_a, dst_b] = dst; + const auto [src_a, src_b] = src; + return lerp(dst_a, dst_b, invlerp(src_a, src_b, val)); + }; + + // original position + const auto [px, py, pw, ph] = splat(mSavedBounds->windowRect); + // source desktop rect + const auto [sx, sy, sw, sh] = splat(mSavedBounds->screenRect); + // target desktop rect + const auto [tx, ty, tw, th] = splat(currentWinRect); + + const float nx = remap({tx, tx + tw}, {sx, sx + sw}, px); + const float ny = remap({ty, ty + th}, {sy, sy + sh}, py); + const float nw = remap({0, tw}, {0, sw}, pw); + const float nh = remap({0, th}, {0, sh}, ph); + + doReposition(DesktopRect{nx, ny, nw, nh}); + } + +#undef MOZ_SPLAT_RECT +#undef MOZ_FORMAT_RECT +} + +nsresult nsIWidget::MakeFullScreen(bool aFullScreen) { + InfallibleMakeFullScreen(aFullScreen); + return NS_OK; +} + +nsIWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(nsIWidget* aWidget, + gfxContext* aTarget) + : mWidget(aWidget) { + WindowRenderer* renderer = mWidget->GetWindowRenderer(); + if (auto* fallback = renderer->AsFallback()) { + mRenderer = fallback; + mRenderer->SetTarget(aTarget); + } +} + +nsIWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup() { + if (mRenderer) { + mRenderer->SetTarget(nullptr); + } +} + +bool nsIWidget::IsSmallPopup() const { + return mWindowType == WindowType::Popup && mPopupType != PopupType::Panel; +} + +bool nsIWidget::ComputeShouldAccelerate() { + return gfx::gfxConfig::IsEnabled(gfx::Feature::HW_COMPOSITING) && + (WidgetTypeSupportsAcceleration() || + StaticPrefs::gfx_webrender_unaccelerated_widget_force()); +} + +bool nsIWidget::UseAPZ() const { + // APZ disabled globally + if (!gfxPlatform::AsyncPanZoomEnabled()) { + return false; + } + + // Always use APZ for top-level windows. XXX what about Dialog? + if (mWindowType == WindowType::TopLevel) { + return true; + } + + // Never use APZ for tooltips + if (mWindowType == WindowType::Popup && mPopupType == PopupType::Tooltip) { + return false; + } + + if (!StaticPrefs::apz_popups_enabled()) { + return false; + } + + if (HasRemoteContent()) { + return mWindowType == WindowType::Dialog || + mWindowType == WindowType::Popup; + } + + if (StaticPrefs::apz_popups_without_remote_enabled()) { + return mWindowType == WindowType::Popup; + } + + return false; +} + +void nsIWidget::CreateCompositor() { + LayoutDeviceIntRect rect = GetBounds(); + CreateCompositor(rect.Width(), rect.Height()); +} + +void nsIWidget::PauseOrResumeCompositor(bool aPause) { + auto* renderer = GetRemoteRenderer(); + if (!renderer) { + return; + } + if (aPause) { + renderer->SendPause(); + } else { + renderer->SendResume(); + } +} + +already_AddRefed<GeckoContentController> +nsIWidget::CreateRootContentController() { + RefPtr<GeckoContentController> controller = + new ChromeProcessController(this, mAPZEventState, mAPZC); + return controller.forget(); +} + +void nsIWidget::ConfigureAPZCTreeManager() { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mAPZC); + + mAPZC->SetDPI(GetDPI()); + + if (StaticPrefs::apz_keyboard_enabled_AtStartup()) { + KeyboardMap map = RootWindowGlobalKeyListener::CollectKeyboardShortcuts(); + mAPZC->SetKeyboardMap(map); + } + + ContentReceivedInputBlockCallback callback( + [treeManager = RefPtr{mAPZC.get()}](uint64_t aInputBlockId, + bool aPreventDefault) { + MOZ_ASSERT(NS_IsMainThread()); + treeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault); + }); + mAPZEventState = new APZEventState(this, std::move(callback)); + + mRootContentController = CreateRootContentController(); + if (mRootContentController) { + mCompositorSession->SetContentController(mRootContentController); + } + + // When APZ is enabled, we can actually enable raw touch events because we + // have code that can deal with them properly. If APZ is not enabled, this + // function doesn't get called. + if (StaticPrefs::dom_w3c_touch_events_enabled()) { + RegisterTouchWindow(); + } +} + +void nsIWidget::ConfigureAPZControllerThread() { + // By default the controller thread is the main thread. + APZThreadUtils::SetControllerThread(NS_GetCurrentThread()); +} + +void nsIWidget::SetConfirmedTargetAPZC( + uint64_t aInputBlockId, + const nsTArray<ScrollableLayerGuid>& aTargets) const { + mAPZC->SetTargetAPZC(aInputBlockId, aTargets); +} + +void nsIWidget::UpdateZoomConstraints( + const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, + const Maybe<ZoomConstraints>& aConstraints) { + if (!mCompositorSession || !mAPZC) { + MOZ_ASSERT_IF(mInitialZoomConstraints, + mInitialZoomConstraints->mViewID == aViewId); + if (aConstraints) { + // We have some constraints, but the compositor and APZC aren't + // created yet. Save these so we can use them later. + mInitialZoomConstraints = Some( + InitialZoomConstraints(aPresShellId, aViewId, aConstraints.ref())); + } else { + mInitialZoomConstraints.reset(); + } + return; + } + LayersId layersId = mCompositorSession->RootLayerTreeId(); + mAPZC->UpdateZoomConstraints( + ScrollableLayerGuid(layersId, aPresShellId, aViewId), aConstraints); +} + +bool nsIWidget::AsyncPanZoomEnabled() const { return !!mAPZC; } + +nsEventStatus nsIWidget::ProcessUntransformedAPZEvent( + WidgetInputEvent* aEvent, const APZEventResult& aApzResult) { + MOZ_ASSERT(NS_IsMainThread()); + ScrollableLayerGuid targetGuid = aApzResult.mTargetGuid; + uint64_t inputBlockId = aApzResult.mInputBlockId; + InputAPZContext context(aApzResult.mTargetGuid, inputBlockId, + aApzResult.GetStatus()); + + // Make a copy of the original event for the APZCCallbackHelper helpers that + // we call later, because the event passed to DispatchEvent can get mutated in + // ways that we don't want (i.e. touch points can get stripped out). + nsEventStatus status; + UniquePtr<WidgetEvent> original(aEvent->Duplicate()); + DispatchEvent(aEvent, status); + + if (mAPZC && !InputAPZContext::WasRoutedToChildProcess() && + !InputAPZContext::WasDropped() && inputBlockId) { + // EventStateManager did not route the event into the child process and + // the event was dispatched in the parent process. + // It's safe to communicate to APZ that the event has been processed. + // Note that here aGuid.mLayersId might be different from + // mCompositorSession->RootLayerTreeId() because the event might have gotten + // hit-tested by APZ to be targeted at a child process, but a parent process + // event listener called preventDefault on it. In that case aGuid.mLayersId + // would still be the layers id for the child process, but the event would + // not have actually gotten routed to the child process. The main-thread + // hit-test result therefore needs to use the parent process layers id. + LayersId rootLayersId = mCompositorSession->RootLayerTreeId(); + + RefPtr<DisplayportSetListener> postLayerization; + if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) { + nsTArray<TouchBehaviorFlags> allowedTouchBehaviors; + if (touchEvent->mMessage == eTouchStart) { + auto& originalEvent = *original->AsTouchEvent(); + MOZ_ASSERT(NS_IsMainThread()); + allowedTouchBehaviors = TouchActionHelper::GetAllowedTouchBehavior( + this, GetDocument(), originalEvent); + if (!allowedTouchBehaviors.IsEmpty()) { + mAPZC->SetAllowedTouchBehavior(inputBlockId, allowedTouchBehaviors); + } + postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( + this, GetDocument(), originalEvent, rootLayersId, inputBlockId); + } + mAPZEventState->ProcessTouchEvent(*touchEvent, targetGuid, inputBlockId, + aApzResult.GetStatus(), status, + std::move(allowedTouchBehaviors)); + } else if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) { + MOZ_ASSERT(wheelEvent->mFlags.mHandledByAPZ); + postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( + this, GetDocument(), *original->AsWheelEvent(), rootLayersId, + inputBlockId); + if (wheelEvent->mCanTriggerSwipe) { + ReportSwipeStarted(inputBlockId, wheelEvent->TriggersSwipe()); + } + mAPZEventState->ProcessWheelEvent(*wheelEvent, inputBlockId); + } else if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { + MOZ_ASSERT(mouseEvent->mFlags.mHandledByAPZ); + postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification( + this, GetDocument(), *original->AsMouseEvent(), rootLayersId, + inputBlockId); + mAPZEventState->ProcessMouseEvent(*mouseEvent, inputBlockId); + } + if (postLayerization) { + postLayerization->Register(); + } + } + + return status; +} + +template <class InputType, class EventType> +class DispatchEventOnMainThread : public Runnable { + public: + DispatchEventOnMainThread(const InputType& aInput, nsIWidget* aWidget, + const APZEventResult& aAPZResult) + : mozilla::Runnable("DispatchEventOnMainThread"), + mInput(aInput), + mWidget(aWidget), + mAPZResult(aAPZResult) {} + + NS_IMETHOD Run() override { + EventType event = mInput.ToWidgetEvent(mWidget); + mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); + return NS_OK; + } + + private: + InputType mInput; + nsIWidget* mWidget; + APZEventResult mAPZResult; +}; + +template <> +NS_IMETHODIMP DispatchEventOnMainThread<MouseInput, WidgetMouseEvent>::Run() { + MOZ_ASSERT( + !mInput.IsPointerEventType(), + "Please use DispatchEventOnMainThread<MouseInput, WidgetPointerEvent>"); + WidgetMouseEvent event = mInput.ToWidgetEvent<WidgetMouseEvent>(mWidget); + mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); + return NS_OK; +} + +template <> +NS_IMETHODIMP DispatchEventOnMainThread<MouseInput, WidgetPointerEvent>::Run() { + MOZ_ASSERT( + mInput.IsPointerEventType(), + "Please use DispatchEventOnMainThread<MouseInput, WidgetMouseEvent>"); + WidgetPointerEvent event = mInput.ToWidgetEvent<WidgetPointerEvent>(mWidget); + mWidget->ProcessUntransformedAPZEvent(&event, mAPZResult); + return NS_OK; +} + +template <class InputType, class EventType> +class DispatchInputOnControllerThread : public Runnable { + public: + enum class APZOnly { Yes, No }; + DispatchInputOnControllerThread(const EventType& aEvent, + IAPZCTreeManager* aAPZC, nsIWidget* aWidget, + APZOnly aAPZOnly = APZOnly::No) + : mozilla::Runnable("DispatchInputOnControllerThread"), + mMainMessageLoop(MessageLoop::current()), + mInput(aEvent), + mAPZC(aAPZC), + mWidget(aWidget), + mAPZOnly(aAPZOnly) {} + + NS_IMETHOD Run() override { + APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(mInput); + if (mAPZOnly == APZOnly::Yes || + result.GetStatus() == nsEventStatus_eConsumeNoDefault) { + return NS_OK; + } + RefPtr<Runnable> r = new DispatchEventOnMainThread<InputType, EventType>( + mInput, mWidget, result); + mMainMessageLoop->PostTask(r.forget()); + return NS_OK; + } + + private: + MessageLoop* mMainMessageLoop; + InputType mInput; + RefPtr<IAPZCTreeManager> mAPZC; + nsIWidget* mWidget; + const APZOnly mAPZOnly; +}; + +void nsIWidget::DispatchTouchInput(MultiTouchInput& aInput) { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aInput.mInputSource == + mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_TOUCH || + aInput.mInputSource == + mozilla::dom::MouseEvent_Binding::MOZ_SOURCE_PEN); + if (mAPZC) { + MOZ_ASSERT(APZThreadUtils::IsControllerThread()); + + APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); + if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { + return; + } + + WidgetTouchEvent event = aInput.ToWidgetEvent(this); + ProcessUntransformedAPZEvent(&event, result); + } else { + WidgetTouchEvent event = aInput.ToWidgetEvent(this); + + nsEventStatus status; + DispatchEvent(&event, status); + } +} + +void nsIWidget::DispatchPanGestureInput(PanGestureInput& aInput) { + MOZ_ASSERT(NS_IsMainThread()); + if (mAPZC) { + MOZ_ASSERT(APZThreadUtils::IsControllerThread()); + + APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); + if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { + return; + } + + WidgetWheelEvent event = aInput.ToWidgetEvent(this); + ProcessUntransformedAPZEvent(&event, result); + } else { + WidgetWheelEvent event = aInput.ToWidgetEvent(this); + nsEventStatus status; + DispatchEvent(&event, status); + } +} + +void nsIWidget::DispatchPinchGestureInput(PinchGestureInput& aInput) { + MOZ_ASSERT(NS_IsMainThread()); + if (mAPZC) { + MOZ_ASSERT(APZThreadUtils::IsControllerThread()); + APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(aInput); + + if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { + return; + } + WidgetWheelEvent event = aInput.ToWidgetEvent(this); + ProcessUntransformedAPZEvent(&event, result); + } else { + WidgetWheelEvent event = aInput.ToWidgetEvent(this); + nsEventStatus status; + DispatchEvent(&event, status); + } +} + +nsIWidget::ContentAndAPZEventStatus nsIWidget::DispatchInputEvent( + WidgetInputEvent* aEvent) { + nsIWidget::ContentAndAPZEventStatus status; + MOZ_ASSERT(NS_IsMainThread()); + + if (mAPZC) { + if (APZThreadUtils::IsControllerThread()) { + APZEventResult result = mAPZC->InputBridge()->ReceiveInputEvent(*aEvent); + status.mApzStatus = result.GetStatus(); + if (result.GetStatus() == nsEventStatus_eConsumeNoDefault) { + return status; + } + status.mContentStatus = ProcessUntransformedAPZEvent(aEvent, result); + return status; + } + // Most drag events aren't able to converted to MouseEvent except to + // eDragStart and eDragEnd. + const bool canDispatchToApzc = + !aEvent->AsDragEvent() || + aEvent->AsDragEvent()->CanConvertToInputData(); + if (canDispatchToApzc) { + if (WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent()) { + RefPtr<Runnable> r = + new DispatchInputOnControllerThread<ScrollWheelInput, + WidgetWheelEvent>(*wheelEvent, + mAPZC, this); + APZThreadUtils::RunOnControllerThread(std::move(r)); + status.mContentStatus = nsEventStatus_eConsumeDoDefault; + return status; + } + if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) { + MOZ_ASSERT(aEvent->mMessage == eContextMenu); + RefPtr<Runnable> r = + new DispatchInputOnControllerThread<MouseInput, WidgetPointerEvent>( + *pointerEvent, mAPZC, this); + APZThreadUtils::RunOnControllerThread(std::move(r)); + status.mContentStatus = nsEventStatus_eConsumeDoDefault; + return status; + } + if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { + RefPtr<Runnable> r = + new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>( + *mouseEvent, mAPZC, this); + APZThreadUtils::RunOnControllerThread(std::move(r)); + status.mContentStatus = nsEventStatus_eConsumeDoDefault; + return status; + } + if (WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent()) { + RefPtr<Runnable> r = + new DispatchInputOnControllerThread<MultiTouchInput, + WidgetTouchEvent>(*touchEvent, + mAPZC, this); + APZThreadUtils::RunOnControllerThread(std::move(r)); + status.mContentStatus = nsEventStatus_eConsumeDoDefault; + return status; + } + // Allow dispatching keyboard/drag events on Gecko thread + // without sending them to APZ + + // FIXME: APZ can handle keyboard events now, we should + // be sending them to APZ here + MOZ_ASSERT(aEvent->AsKeyboardEvent() || aEvent->AsDragEvent()); + } + } + + DispatchEvent(aEvent, status.mContentStatus); + return status; +} + +void nsIWidget::DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) { + MOZ_ASSERT(NS_IsMainThread()); + if (mAPZC) { + if (APZThreadUtils::IsControllerThread()) { + mAPZC->InputBridge()->ReceiveInputEvent(*aEvent); + return; + } + + if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) { + RefPtr<Runnable> r = + new DispatchInputOnControllerThread<MouseInput, WidgetMouseEvent>( + *mouseEvent, mAPZC, this, + DispatchInputOnControllerThread<MouseInput, + WidgetMouseEvent>::APZOnly::Yes); + APZThreadUtils::RunOnControllerThread(std::move(r)); + return; + } + + MOZ_ASSERT_UNREACHABLE("Not implemented yet"); + } +} + +bool nsIWidget::DispatchWindowEvent(WidgetGUIEvent& event) { + nsEventStatus status; + DispatchEvent(&event, status); + return ConvertStatus(status); +} + +Document* nsIWidget::GetDocument() const { + if (mWidgetListener) { + if (PresShell* presShell = mWidgetListener->GetPresShell()) { + return presShell->GetDocument(); + } + } + return nullptr; +} + +void nsIWidget::CreateCompositorVsyncDispatcher() { + // Parent directly listens to the vsync source whereas + // child process communicate via IPC + // Should be called AFTER gfxPlatform is initialized + if (XRE_IsParentProcess()) { + if (!mCompositorVsyncDispatcherLock) { + mCompositorVsyncDispatcherLock = + MakeUnique<Mutex>("mCompositorVsyncDispatcherLock"); + } + MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); + if (!mCompositorVsyncDispatcher) { + RefPtr<VsyncDispatcher> vsyncDispatcher = + gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher(); + mCompositorVsyncDispatcher = + new CompositorVsyncDispatcher(std::move(vsyncDispatcher)); + } + } +} + +already_AddRefed<CompositorVsyncDispatcher> +nsIWidget::GetCompositorVsyncDispatcher() { + MOZ_ASSERT(mCompositorVsyncDispatcherLock.get()); + + MutexAutoLock lock(*mCompositorVsyncDispatcherLock.get()); + RefPtr<CompositorVsyncDispatcher> dispatcher = mCompositorVsyncDispatcher; + return dispatcher.forget(); +} + +already_AddRefed<WebRenderLayerManager> nsIWidget::CreateCompositorSession( + int aWidth, int aHeight, CompositorOptions* aOptionsOut) { + MOZ_ASSERT(aOptionsOut); + + do { + CreateCompositorVsyncDispatcher(); + + // Make sure GPU process is ready for use. + // If it failed to connect to GPU process, GPU process usage is disabled in + // EnsureGPUReady(). It could update gfxVars and gfxConfigs. + gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get(); + if (NS_WARN_IF(!gpm || NS_FAILED(gpm->EnsureGPUReady()))) { + return nullptr; + } + + // If widget type does not supports acceleration, we may be allowed to use + // software WebRender instead. + bool supportsAcceleration = WidgetTypeSupportsAcceleration(); + bool enableSWWR = true; + if (supportsAcceleration || + StaticPrefs::gfx_webrender_unaccelerated_widget_force()) { + enableSWWR = gfx::gfxVars::UseSoftwareWebRender(); + } + bool enableAPZ = UseAPZ(); + CompositorOptions options(enableAPZ, enableSWWR); + +#ifdef XP_WIN + if (supportsAcceleration) { + options.SetAllowSoftwareWebRenderD3D11( + gfx::gfxVars::AllowSoftwareWebRenderD3D11()); + } + if (mNeedFastSnaphot) { + options.SetNeedFastSnaphot(true); + } +#elif defined(MOZ_WIDGET_ANDROID) + MOZ_ASSERT(supportsAcceleration); + options.SetAllowSoftwareWebRenderOGL( + gfx::gfxVars::AllowSoftwareWebRenderOGL()); +#elif defined(MOZ_WIDGET_GTK) + if (supportsAcceleration) { + options.SetAllowSoftwareWebRenderOGL( + gfx::gfxVars::AllowSoftwareWebRenderOGL()); + } + options.SetAllowNativeCompositor(WidgetTypeSupportsNativeCompositing()); +#endif + +#ifdef MOZ_WIDGET_ANDROID + // Unconditionally set the compositor as initially paused, as we have not + // yet had a chance to send the compositor surface to the GPU process. We + // will do so shortly once we have returned to nsWindow::CreateLayerManager, + // where we will also resume the compositor if required. + options.SetInitiallyPaused(true); +#else + options.SetInitiallyPaused(CompositorInitiallyPaused()); +#endif + + RefPtr<WebRenderLayerManager> lm = new WebRenderLayerManager(this); + + uint64_t innerWindowId = 0; + if (Document* doc = GetDocument()) { + innerWindowId = doc->InnerWindowID(); + } + + bool retry = false; + mCompositorSession = gpm->CreateTopLevelCompositor( + this, lm, GetDefaultScale(), options, UseExternalCompositingSurface(), + gfx::IntSize(aWidth, aHeight), innerWindowId, &retry); + + if (mCompositorSession) { + TextureFactoryIdentifier textureFactoryIdentifier; + nsCString error; + lm->Initialize(mCompositorSession->GetCompositorBridgeChild(), + wr::AsPipelineId(mCompositorSession->RootLayerTreeId()), + &textureFactoryIdentifier, error); + if (textureFactoryIdentifier.mParentBackend != LayersBackend::LAYERS_WR) { + retry = true; + DestroyCompositor(); + // gfxVars::UseDoubleBufferingWithCompositor() is also disabled. + gfx::GPUProcessManager::Get()->DisableWebRender( + wr::WebRenderError::INITIALIZE, error); + } + } + + // We need to retry in a loop because the act of failing to create the + // compositor can change our state (e.g. disable WebRender). + if (mCompositorSession || !retry) { + *aOptionsOut = options; + return lm.forget(); + } + } while (true); +} + +void nsIWidget::CreateCompositor(int aWidth, int aHeight) { + // This makes sure that gfxPlatforms gets initialized if it hasn't by now. + gfxPlatform::GetPlatform(); + + MOZ_ASSERT(gfxPlatform::UsesOffMainThreadCompositing(), + "This function assumes OMTC"); + + MOZ_ASSERT(!mCompositorSession && !mCompositorBridgeChild, + "Should have properly cleaned up the previous PCompositor pair " + "beforehand"); + + if (mCompositorBridgeChild) { + mCompositorBridgeChild->Destroy(); + } + + // Recreating this is tricky, as we may still have an old and we need + // to make sure it's properly destroyed by calling DestroyCompositor! + + // If we've already received a shutdown notification, don't try + // create a new compositor. + if (!mShutdownObserver) { + return; + } + + // The controller thread must be configured before the compositor + // session is created, so that the input bridge runs on the right + // thread. + ConfigureAPZControllerThread(); + + CompositorOptions options; + RefPtr<WebRenderLayerManager> lm = + CreateCompositorSession(aWidth, aHeight, &options); + if (!lm) { + return; + } + + MOZ_ASSERT(mCompositorSession); + mCompositorBridgeChild = mCompositorSession->GetCompositorBridgeChild(); + SetCompositorWidgetDelegate( + mCompositorSession->GetCompositorWidgetDelegate()); + + if (options.UseAPZ()) { + mAPZC = mCompositorSession->GetAPZCTreeManager(); + ConfigureAPZCTreeManager(); + } else { + mAPZC = nullptr; + } + + if (mInitialZoomConstraints) { + UpdateZoomConstraints(mInitialZoomConstraints->mPresShellID, + mInitialZoomConstraints->mViewID, + Some(mInitialZoomConstraints->mConstraints)); + mInitialZoomConstraints.reset(); + } + + TextureFactoryIdentifier textureFactoryIdentifier = + lm->GetTextureFactoryIdentifier(); + MOZ_ASSERT(textureFactoryIdentifier.mParentBackend == + LayersBackend::LAYERS_WR); + ImageBridgeChild::IdentifyCompositorTextureHost(textureFactoryIdentifier); + gfx::VRManagerChild::IdentifyTextureHost(textureFactoryIdentifier); + + WindowUsesOMTC(); + + mWindowRenderer = std::move(lm); + + // Only track compositors for top-level windows, since other window types + // may use the basic compositor. Except on the OS X - see bug 1306383 +#if defined(XP_MACOSX) + bool getCompositorFromThisWindow = true; +#else + bool getCompositorFromThisWindow = mWindowType == WindowType::TopLevel; +#endif + + if (getCompositorFromThisWindow) { + gfxPlatform::GetPlatform()->NotifyCompositorCreated( + mWindowRenderer->GetCompositorBackendType()); + } +} + +void nsIWidget::NotifyCompositorSessionLost(CompositorSession* aSession) { + MOZ_ASSERT(aSession == mCompositorSession); + DestroyLayerManager(); +} + +bool nsIWidget::ShouldUseOffMainThreadCompositing() { + return gfxPlatform::UsesOffMainThreadCompositing(); +} + +WindowRenderer* nsIWidget::GetWindowRenderer() { + if (!mWindowRenderer) { + if (!mShutdownObserver) { + // We are shutting down, do not try to re-create a LayerManager + return nullptr; + } + // Try to use an async compositor first, if possible + if (ShouldUseOffMainThreadCompositing()) { + CreateCompositor(); + } + + if (!mWindowRenderer) { + mWindowRenderer = CreateFallbackRenderer(); + } + } + return mWindowRenderer; +} + +WindowRenderer* nsIWidget::CreateFallbackRenderer() { + return new FallbackRenderer; +} + +CompositorBridgeChild* nsIWidget::GetRemoteRenderer() { + return mCompositorBridgeChild; +} + +void nsIWidget::ClearCachedWebrenderResources() { + if (!mWindowRenderer || !mWindowRenderer->AsWebRender()) { + return; + } + mWindowRenderer->AsWebRender()->ClearCachedResources(); +} + +bool nsIWidget::SetNeedFastSnaphot() { + MOZ_ASSERT(XRE_IsParentProcess()); + MOZ_ASSERT(!mCompositorSession); + + if (!XRE_IsParentProcess() || mCompositorSession) { + return false; + } + + mNeedFastSnaphot = true; + return true; +} + +already_AddRefed<gfx::DrawTarget> nsIWidget::StartRemoteDrawing() { + return nullptr; +} + +uint32_t nsIWidget::GetGLFrameBufferFormat() { return LOCAL_GL_RGBA; } + +//------------------------------------------------------------------------- +// +// Destroy the window +// +//------------------------------------------------------------------------- +void nsIWidget::OnDestroy() { + if (mTextEventDispatcher) { + mTextEventDispatcher->OnDestroyWidget(); + // Don't release it until this widget actually released because after this + // is called, TextEventDispatcher() may create it again. + } + + // If this widget is being destroyed, let the APZ code know to drop references + // to this widget. Callers of this function all should be holding a deathgrip + // on this widget already. + ReleaseContentController(); +} + +/* static */ +DesktopIntPoint nsIWidget::ConstrainPositionToBounds( + const DesktopIntPoint& aPoint, const DesktopIntSize& aSize, + const DesktopIntRect& aScreenRect) { + DesktopIntPoint point = aPoint; + + // The maximum position to which the window can be moved while keeping its + // bottom-right corner within screenRect. + auto const maxX = aScreenRect.XMost() - aSize.Width(); + auto const maxY = aScreenRect.YMost() - aSize.Height(); + + // Note that the conditional-pairs below are not exclusive with each other, + // and cannot be replaced with a simple call to `std::clamp`! If the window + // provided is too large to fit on the screen, they will both fire. Their + // order has been chosen to ensure that the window's top left corner will be + // onscreen. + + if (point.x >= maxX) { + point.x = maxX; + } + if (point.x < aScreenRect.x) { + point.x = aScreenRect.x; + } + + if (point.y >= maxY) { + point.y = maxY; + } + if (point.y < aScreenRect.y) { + point.y = aScreenRect.y; + } + + return point; +} + +void nsIWidget::MoveClient(const DesktopPoint& aOffset) { + LayoutDeviceIntPoint clientOffset(GetClientOffset()); + + // GetClientOffset returns device pixels; scale back to desktop pixels + // if that's what this widget uses for the Move/Resize APIs + if (BoundsUseDesktopPixels()) { + DesktopPoint desktopOffset = clientOffset / GetDesktopToDeviceScale(); + Move(aOffset.x - desktopOffset.x, aOffset.y - desktopOffset.y); + } else { + LayoutDevicePoint layoutOffset = aOffset * GetDesktopToDeviceScale(); + Move(layoutOffset.x - LayoutDeviceCoord(clientOffset.x), + layoutOffset.y - LayoutDeviceCoord(clientOffset.y)); + } +} + +void nsIWidget::ResizeClient(const DesktopSize& aSize, bool aRepaint) { + NS_ASSERTION((aSize.width >= 0), "Negative width passed to ResizeClient"); + NS_ASSERTION((aSize.height >= 0), "Negative height passed to ResizeClient"); + + LayoutDeviceIntRect clientBounds = GetClientBounds(); + + // GetClientBounds and mBounds are device pixels; scale back to desktop pixels + // if that's what this widget uses for the Move/Resize APIs + if (BoundsUseDesktopPixels()) { + DesktopSize desktopDelta = + (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) - + clientBounds.Size()) / + GetDesktopToDeviceScale(); + Resize(aSize.width + desktopDelta.width, aSize.height + desktopDelta.height, + aRepaint); + } else { + LayoutDeviceSize layoutSize = aSize * GetDesktopToDeviceScale(); + Resize(mBounds.Width() + (layoutSize.width - clientBounds.Width()), + mBounds.Height() + (layoutSize.height - clientBounds.Height()), + aRepaint); + } +} + +void nsIWidget::ResizeClient(const DesktopRect& aRect, bool aRepaint) { + NS_ASSERTION((aRect.Width() >= 0), "Negative width passed to ResizeClient"); + NS_ASSERTION((aRect.Height() >= 0), "Negative height passed to ResizeClient"); + + LayoutDeviceIntRect clientBounds = GetClientBounds(); + LayoutDeviceIntPoint clientOffset = GetClientOffset(); + DesktopToLayoutDeviceScale scale = GetDesktopToDeviceScale(); + + if (BoundsUseDesktopPixels()) { + DesktopPoint desktopOffset = clientOffset / scale; + DesktopSize desktopDelta = + (LayoutDeviceIntSize(mBounds.Width(), mBounds.Height()) - + clientBounds.Size()) / + scale; + Resize(aRect.X() - desktopOffset.x, aRect.Y() - desktopOffset.y, + aRect.Width() + desktopDelta.width, + aRect.Height() + desktopDelta.height, aRepaint); + } else { + LayoutDeviceRect layoutRect = aRect * scale; + Resize(layoutRect.X() - clientOffset.x, layoutRect.Y() - clientOffset.y, + layoutRect.Width() + mBounds.Width() - clientBounds.Width(), + layoutRect.Height() + mBounds.Height() - clientBounds.Height(), + aRepaint); + } +} + +//------------------------------------------------------------------------- +// +// Bounds +// +//------------------------------------------------------------------------- + +/** + * If the implementation of nsWindow supports borders this method MUST be + * overridden + * + **/ +LayoutDeviceIntRect nsIWidget::GetClientBounds() { return GetBounds(); } + +/** + * If the implementation of nsWindow supports borders this method MUST be + * overridden + * + **/ +LayoutDeviceIntRect nsIWidget::GetBounds() { return mBounds; } + +/** + * If the implementation of nsWindow uses a local coordinate system within the + *window, this method must be overridden + * + **/ +LayoutDeviceIntRect nsIWidget::GetScreenBounds() { return GetBounds(); } + +nsresult nsIWidget::GetRestoredBounds(LayoutDeviceIntRect& aRect) { + if (SizeMode() != nsSizeMode_Normal) { + return NS_ERROR_FAILURE; + } + aRect = GetScreenBounds(); + return NS_OK; +} + +LayoutDeviceIntPoint nsIWidget::GetClientOffset() { + return LayoutDeviceIntPoint(0, 0); +} + +uint32_t nsIWidget::GetMaxTouchPoints() const { return 0; } + +bool nsIWidget::HasPendingInputEvent() { return false; } + +bool nsIWidget::ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) { + return false; +} + +/** + * Modifies aFile to point at an icon file with the given name and suffix. The + * suffix may correspond to a file extension with leading '.' if appropriate. + * Returns true if the icon file exists and can be read. + */ +static bool ResolveIconNameHelper(nsIFile* aFile, const nsAString& aIconName, + const nsAString& aIconSuffix) { + aFile->Append(u"icons"_ns); + aFile->Append(u"default"_ns); + aFile->Append(aIconName + aIconSuffix); + + bool readable; + return NS_SUCCEEDED(aFile->IsReadable(&readable)) && readable; +} + +/** + * Resolve the given icon name into a local file object. This method is + * intended to be called by subclasses of nsIWidget. aIconSuffix is a + * platform specific icon file suffix (e.g., ".ico" under Win32). + * + * If no file is found matching the given parameters, then null is returned. + */ +void nsIWidget::ResolveIconName(const nsAString& aIconName, + const nsAString& aIconSuffix, + nsIFile** aResult) { + *aResult = nullptr; + + nsCOMPtr<nsIProperties> dirSvc = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + if (!dirSvc) return; + + // first check auxilary chrome directories + + nsCOMPtr<nsISimpleEnumerator> dirs; + dirSvc->Get(NS_APP_CHROME_DIR_LIST, NS_GET_IID(nsISimpleEnumerator), + getter_AddRefs(dirs)); + if (dirs) { + bool hasMore; + while (NS_SUCCEEDED(dirs->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr<nsISupports> element; + dirs->GetNext(getter_AddRefs(element)); + if (!element) continue; + nsCOMPtr<nsIFile> file = do_QueryInterface(element); + if (!file) continue; + if (ResolveIconNameHelper(file, aIconName, aIconSuffix)) { + NS_ADDREF(*aResult = file); + return; + } + } + } + + // then check the main app chrome directory + + nsCOMPtr<nsIFile> file; + dirSvc->Get(NS_APP_CHROME_DIR, NS_GET_IID(nsIFile), getter_AddRefs(file)); + if (file && ResolveIconNameHelper(file, aIconName, aIconSuffix)) + NS_ADDREF(*aResult = file); +} + +void nsIWidget::SetSizeConstraints(const SizeConstraints& aConstraints) { + mSizeConstraints = aConstraints; + + // Popups are constrained during layout, and we don't want to synchronously + // paint from reflow, so bail out... This is not great, but it's no worse than + // what we used to do. + // + // The right fix here is probably making constraint changes go through the + // view manager and such. + if (mWindowType == WindowType::Popup) { + return; + } + + // If the current size doesn't meet the new constraints, trigger a + // resize to apply it. Note that, we don't want to invoke Resize if + // the new constraints don't affect the current size, because Resize + // implementation on some platforms may touch other geometry even if + // the size don't need to change. + LayoutDeviceIntSize curSize = mBounds.Size(); + LayoutDeviceIntSize clampedSize = + Max(aConstraints.mMinSize, Min(aConstraints.mMaxSize, curSize)); + if (clampedSize != curSize) { + gfx::Size size; + if (BoundsUseDesktopPixels()) { + DesktopSize desktopSize = clampedSize / GetDesktopToDeviceScale(); + size = desktopSize.ToUnknownSize(); + } else { + size = gfx::Size(clampedSize.ToUnknownSize()); + } + Resize(size.width, size.height, true); + } +} + +const widget::SizeConstraints nsIWidget::GetSizeConstraints() { + return mSizeConstraints; +} + +// static +nsIRollupListener* nsIWidget::GetActiveRollupListener() { + // TODO: Simplify this. + return nsXULPopupManager::GetInstance(); +} + +void nsIWidget::NotifyWindowDestroyed() { + if (!mWidgetListener) return; + + nsCOMPtr<nsIAppWindow> window = mWidgetListener->GetAppWindow(); + nsCOMPtr<nsIBaseWindow> appWindow(do_QueryInterface(window)); + if (appWindow) { + appWindow->Destroy(); + } +} + +void nsIWidget::NotifyWindowMoved(int32_t aX, int32_t aY, + ByMoveToRect aByMoveToRect) { + if (mWidgetListener) { + mWidgetListener->WindowMoved(this, aX, aY, aByMoveToRect); + } + + if (mIMEHasFocus && IMENotificationRequestsRef().WantPositionChanged()) { + NotifyIME(IMENotification(IMEMessage::NOTIFY_IME_OF_POSITION_CHANGE)); + } +} + +void nsIWidget::NotifySizeMoveDone() { + if (!mWidgetListener) { + return; + } + if (PresShell* presShell = mWidgetListener->GetPresShell()) { + presShell->WindowSizeMoveDone(); + } +} + +void nsIWidget::NotifyThemeChanged(ThemeChangeKind aKind) { + LookAndFeel::NotifyChangedAllWindows(aKind); +} + +nsresult nsIWidget::NotifyIME(const IMENotification& aIMENotification) { + if (mIMEHasQuit) { + return NS_OK; + } + switch (aIMENotification.mMessage) { + case REQUEST_TO_COMMIT_COMPOSITION: + case REQUEST_TO_CANCEL_COMPOSITION: + // We should send request to IME only when there is a TextEventDispatcher + // instance (this means that this widget has dispatched at least one + // composition event or keyboard event) and the it has composition. + // Otherwise, there is nothing to do. + // Note that if current input transaction is for native input events, + // TextEventDispatcher::NotifyIME() will call + // TextEventDispatcherListener::NotifyIME(). + if (mTextEventDispatcher && mTextEventDispatcher->IsComposing()) { + return mTextEventDispatcher->NotifyIME(aIMENotification); + } + return NS_OK; + default: { + if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) { + mIMEHasFocus = true; + } + EnsureTextEventDispatcher(); + // TextEventDispatcher::NotifyIME() will always call + // TextEventDispatcherListener::NotifyIME(). I.e., even if current + // input transaction is for synthesized events for automated tests, + // notifications will be sent to native IME. + nsresult rv = mTextEventDispatcher->NotifyIME(aIMENotification); + if (aIMENotification.mMessage == NOTIFY_IME_OF_BLUR) { + mIMEHasFocus = false; + } + return rv; + } + } +} + +void nsIWidget::EnsureTextEventDispatcher() { + if (mTextEventDispatcher) { + return; + } + mTextEventDispatcher = new TextEventDispatcher(this); +} + +nsIWidget::NativeIMEContext nsIWidget::GetNativeIMEContext() { + if (mTextEventDispatcher && mTextEventDispatcher->GetPseudoIMEContext()) { + // If we already have a TextEventDispatcher and it's working with + // a TextInputProcessor, we need to return pseudo IME context since + // TextCompositionArray::IndexOf(nsIWidget*) should return a composition + // on the pseudo IME context in such case. + NativeIMEContext pseudoIMEContext; + pseudoIMEContext.InitWithRawNativeIMEContext( + mTextEventDispatcher->GetPseudoIMEContext()); + return pseudoIMEContext; + } + return NativeIMEContext(this); +} + +nsIWidget::TextEventDispatcher* nsIWidget::GetTextEventDispatcher() { + EnsureTextEventDispatcher(); + return mTextEventDispatcher; +} + +void* nsIWidget::GetPseudoIMEContext() { + TextEventDispatcher* dispatcher = GetTextEventDispatcher(); + if (!dispatcher) { + return nullptr; + } + return dispatcher->GetPseudoIMEContext(); +} + +TextEventDispatcherListener* nsIWidget::GetNativeTextEventDispatcherListener() { + // TODO: If all platforms supported use of TextEventDispatcher for handling + // native IME and keyboard events, this method should be removed since + // in such case, this is overridden by all the subclasses. + return nullptr; +} + +void nsIWidget::ZoomToRect(const uint32_t& aPresShellId, + const ScrollableLayerGuid::ViewID& aViewId, + const CSSRect& aRect, const uint32_t& aFlags) { + if (!mCompositorSession || !mAPZC) { + return; + } + LayersId layerId = mCompositorSession->RootLayerTreeId(); + mAPZC->ZoomToRect(ScrollableLayerGuid(layerId, aPresShellId, aViewId), + ZoomTarget{aRect}, aFlags); +} + +#ifdef ACCESSIBILITY + +a11y::LocalAccessible* nsIWidget::GetRootAccessible() { + NS_ENSURE_TRUE(mWidgetListener, nullptr); + + PresShell* presShell = mWidgetListener->GetPresShell(); + NS_ENSURE_TRUE(presShell, nullptr); + + // If container is null then the presshell is not active. This often happens + // when a preshell is being held onto for fastback. + nsPresContext* presContext = presShell->GetPresContext(); + NS_ENSURE_TRUE(presContext->GetContainerWeak(), nullptr); + + // LocalAccessible creation might be not safe so use IsSafeToRunScript to + // make sure it's not created at unsafe times. + nsAccessibilityService* accService = GetOrCreateAccService(); + if (accService) { + return accService->GetRootDocumentAccessible( + presShell, nsContentUtils::IsSafeToRunScript()); + } + + return nullptr; +} + +#endif // ACCESSIBILITY + +void nsIWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) { + if (!AsyncPanZoomEnabled()) { + return; + } + + MOZ_ASSERT(XRE_IsParentProcess() && mCompositorSession); + + LayersId layersId = mCompositorSession->RootLayerTreeId(); + ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, + aDragMetrics.mViewId); + + mAPZC->StartScrollbarDrag(guid, aDragMetrics); +} + +bool nsIWidget::StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation, + const ScrollableLayerGuid& aGuid) { + MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled()); + + return mAPZC->StartAutoscroll(aGuid, aAnchorLocation); +} + +void nsIWidget::StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) { + MOZ_ASSERT(XRE_IsParentProcess() && AsyncPanZoomEnabled()); + + mAPZC->StopAutoscroll(aGuid); +} + +LayersId nsIWidget::GetRootLayerTreeId() { + return mCompositorSession ? mCompositorSession->RootLayerTreeId() + : LayersId{0}; +} + +already_AddRefed<widget::Screen> nsIWidget::GetWidgetScreen() { + ScreenManager& screenManager = ScreenManager::GetSingleton(); + LayoutDeviceIntRect bounds = GetScreenBounds(); + DesktopIntRect deskBounds = RoundedToInt(bounds / GetDesktopToDeviceScale()); + return screenManager.ScreenForRect(deskBounds); +} + +nsresult nsIWidget::SynthesizeNativeTouchTap( + LayoutDeviceIntPoint aPoint, bool aLongTap, + nsISynthesizedEventCallback* aCallback) { + AutoSynthesizedEventCallbackNotifier notifier(aCallback); + if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) { + sPointerIdCounter = 0; + } + int pointerId = sPointerIdCounter; + sPointerIdCounter++; + nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, aPoint, + 1.0, 90, nullptr); + if (NS_FAILED(rv)) { + return rv; + } + + if (!aLongTap) { + return SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, aPoint, 0, 0, + nullptr); + } + + // initiate a long tap + int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay", + TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC); + if (!mLongTapTimer) { + mLongTapTimer = NS_NewTimer(); + if (!mLongTapTimer) { + SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, aPoint, 0, 0, + nullptr); + return NS_ERROR_UNEXPECTED; + } + // Windows requires recuring events, so we set this to a smaller window + // than the pref value. + int timeout = elapse; + if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) { + timeout = TOUCH_INJECT_PUMP_TIMER_MSEC; + } + mLongTapTimer->InitWithNamedFuncCallback( + OnLongTapTimerCallback, this, timeout, nsITimer::TYPE_REPEATING_SLACK, + "nsIWidget::SynthesizeNativeTouchTap"_ns); + } + + // If we already have a long tap pending, cancel it. We only allow one long + // tap to be active at a time. + if (mLongTapTouchPoint) { + SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL, + mLongTapTouchPoint->mPosition, 0, 0, nullptr); + } + + mLongTapTouchPoint = MakeUnique<LongTapInfo>( + pointerId, aPoint, TimeDuration::FromMilliseconds(elapse), aCallback); + notifier.SkipNotification(); // we'll do it in the long-tap callback + return NS_OK; +} + +// static +void nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) { + auto* self = static_cast<nsIWidget*>(aClosure); + + if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) > + TimeStamp::Now()) { +#ifdef XP_WIN + // Windows needs us to keep pumping feedback to the digitizer, so update + // the pointer id with the same position. + self->SynthesizeNativeTouchPoint( + self->mLongTapTouchPoint->mPointerId, TOUCH_CONTACT, + self->mLongTapTouchPoint->mPosition, 1.0, 90, nullptr); +#endif + return; + } + + AutoSynthesizedEventCallbackNotifier notifier( + self->mLongTapTouchPoint->mCallback); + + // finished, remove the touch point + self->mLongTapTimer->Cancel(); + self->mLongTapTimer = nullptr; + self->SynthesizeNativeTouchPoint( + self->mLongTapTouchPoint->mPointerId, TOUCH_REMOVE, + self->mLongTapTouchPoint->mPosition, 0, 0, nullptr); + self->mLongTapTouchPoint = nullptr; +} + +float nsIWidget::GetFallbackDPI() { + RefPtr<const Screen> primaryScreen = + ScreenManager::GetSingleton().GetPrimaryScreen(); + return primaryScreen->GetDPI(); +} + +CSSToLayoutDeviceScale nsIWidget::GetFallbackDefaultScale() { + RefPtr<const Screen> s = ScreenManager::GetSingleton().GetPrimaryScreen(); + return s->GetCSSToLayoutDeviceScale(Screen::IncludeOSZoom::No); +} + +void nsIWidget::NotifyLiveResizeStarted() { + // If we have mLiveResizeListeners already non-empty, we should notify those + // listeners that the resize stopped before starting anew. In theory this + // should never happen because we shouldn't get nested live resize actions. + NotifyLiveResizeStopped(); + MOZ_ASSERT(mLiveResizeListeners.IsEmpty()); + + // If we can get the active remote tab for the current widget, suppress + // the displayport on it during the live resize. + if (!mWidgetListener) { + return; + } + nsCOMPtr<nsIAppWindow> appWindow = mWidgetListener->GetAppWindow(); + if (!appWindow) { + return; + } + mLiveResizeListeners = appWindow->GetLiveResizeListeners(); + for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) { + mLiveResizeListeners[i]->LiveResizeStarted(); + } +} + +void nsIWidget::NotifyLiveResizeStopped() { + if (!mLiveResizeListeners.IsEmpty()) { + for (uint32_t i = 0; i < mLiveResizeListeners.Length(); i++) { + mLiveResizeListeners[i]->LiveResizeStopped(); + } + mLiveResizeListeners.Clear(); + } +} + +nsresult nsIWidget::AsyncEnableDragDrop(bool aEnable) { + RefPtr<nsIWidget> kungFuDeathGrip = this; + return NS_DispatchToCurrentThreadQueue( + NS_NewRunnableFunction( + "AsyncEnableDragDropFn", + [this, aEnable, kungFuDeathGrip]() { EnableDragDrop(aEnable); }), + kAsyncDragDropTimeout, EventQueuePriority::Idle); +} + +void nsIWidget::SwipeFinished() { + if (mSwipeTracker) { + mSwipeTracker->Destroy(); + mSwipeTracker = nullptr; + } +} + +void nsIWidget::ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe) { + if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == aInputBlockId) { + if (aStartSwipe) { + PanGestureInput& startEvent = mSwipeEventQueue->queuedEvents[0]; + TrackScrollEventAsSwipe(startEvent, mSwipeEventQueue->allowedDirections, + aInputBlockId); + for (size_t i = 1; i < mSwipeEventQueue->queuedEvents.Length(); i++) { + mSwipeTracker->ProcessEvent(mSwipeEventQueue->queuedEvents[i]); + } + } else if (mAPZC) { + // If the event wasn't start swipe, we need to notify it to APZ. + mAPZC->SetBrowserGestureResponse(aInputBlockId, + BrowserGestureResponse::NotConsumed); + } + mSwipeEventQueue = nullptr; + } +} + +void nsIWidget::TrackScrollEventAsSwipe( + const mozilla::PanGestureInput& aSwipeStartEvent, + uint32_t aAllowedDirections, uint64_t aInputBlockId) { + // If a swipe is currently being tracked kill it -- it's been interrupted + // by another gesture event. + if (mSwipeTracker) { + mSwipeTracker->CancelSwipe(aSwipeStartEvent.mTimeStamp); + mSwipeTracker->Destroy(); + mSwipeTracker = nullptr; + } + + uint32_t direction = + (aSwipeStartEvent.mPanDisplacement.x > 0.0) + ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT + : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT; + + mSwipeTracker = + new SwipeTracker(*this, aSwipeStartEvent, aAllowedDirections, direction); + + if (!mAPZC) { + mCurrentPanGestureBelongsToSwipe = true; + } else { + // Now SwipeTracker has started consuming pan events, notify it to APZ so + // that APZ can discard queued events. + mAPZC->SetBrowserGestureResponse(aInputBlockId, + BrowserGestureResponse::Consumed); + } +} + +nsIWidget::SwipeInfo nsIWidget::SendMayStartSwipe( + const mozilla::PanGestureInput& aSwipeStartEvent) { + nsCOMPtr<nsIWidget> kungFuDeathGrip(this); + + uint32_t direction = + (aSwipeStartEvent.mPanDisplacement.x > 0.0) + ? (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_RIGHT + : (uint32_t)dom::SimpleGestureEvent_Binding::DIRECTION_LEFT; + + // We're ready to start the animation. Tell Gecko about it, and at the same + // time ask it if it really wants to start an animation for this event. + // This event also reports back the directions that we can swipe in. + LayoutDeviceIntPoint position = RoundedToInt(aSwipeStartEvent.mPanStartPoint * + ScreenToLayoutDeviceScale(1)); + WidgetSimpleGestureEvent geckoEvent = SwipeTracker::CreateSwipeGestureEvent( + eSwipeGestureMayStart, this, position, aSwipeStartEvent.mTimeStamp); + geckoEvent.mDirection = direction; + geckoEvent.mDelta = 0.0; + geckoEvent.mAllowedDirections = 0; + bool shouldStartSwipe = + DispatchWindowEvent(geckoEvent); // event cancelled == swipe should start + + SwipeInfo result = {shouldStartSwipe, geckoEvent.mAllowedDirections}; + return result; +} + +WidgetWheelEvent nsIWidget::MayStartSwipeForAPZ( + const PanGestureInput& aPanInput, const APZEventResult& aApzResult) { + WidgetWheelEvent event = aPanInput.ToWidgetEvent(this); + + // Ignore swipe-to-navigation in PiP window. + if (mIsPIPWindow) { + return event; + } + + if (aPanInput.mHandledByAPZ && aPanInput.AllowsSwipe()) { + SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput); + event.mCanTriggerSwipe = swipeInfo.wantsSwipe; + if (swipeInfo.wantsSwipe) { + if (aApzResult.GetStatus() == nsEventStatus_eIgnore) { + // APZ has determined and that scrolling horizontally in the + // requested direction is impossible, so it didn't do any + // scrolling for the event. + // We know now that MayStartSwipe wants a swipe, so we can start + // the swipe now. + TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections, + aApzResult.mInputBlockId); + } else if (!aApzResult.GetHandledResult() || + !aApzResult.GetHandledResult()->IsHandledByRoot()) { + // We don't know whether this event can start a swipe, so we need + // to queue up events and wait for a call to ReportSwipeStarted. + // APZ might already have started scrolling in response to the + // event if it knew that it's the right thing to do. In that case + // we'll still get a call to ReportSwipeStarted, and we will + // discard the queued events at that point. + mSwipeEventQueue = MakeUnique<SwipeEventQueue>( + swipeInfo.allowedDirections, aApzResult.mInputBlockId); + } + } else { + // Inform that the browser gesture didn't use the pan event (pan-start + // precisely), so that APZ can now start using the event for + // scrolling/overscrolling. + mAPZC->SetBrowserGestureResponse(aApzResult.mInputBlockId, + BrowserGestureResponse::NotConsumed); + } + } + + if (mSwipeEventQueue && + mSwipeEventQueue->inputBlockId == aApzResult.mInputBlockId) { + mSwipeEventQueue->queuedEvents.AppendElement(aPanInput); + } + + return event; +} + +bool nsIWidget::MayStartSwipeForNonAPZ(const PanGestureInput& aPanInput) { + // Ignore swipe-to-navigation in PiP window. + if (mIsPIPWindow) { + return false; + } + + if (aPanInput.mType == PanGestureInput::PANGESTURE_MAYSTART || + aPanInput.mType == PanGestureInput::PANGESTURE_START) { + mCurrentPanGestureBelongsToSwipe = false; + } + if (mCurrentPanGestureBelongsToSwipe) { + // Ignore this event. It's a momentum event from a scroll gesture + // that was processed as a swipe, and the swipe animation has + // already finished (so mSwipeTracker is already null). + MOZ_ASSERT(aPanInput.IsMomentum(), + "If the fingers are still on the touchpad, we should still have " + "a SwipeTracker, " + "and it should have consumed this event."); + return true; + } + + if (!aPanInput.MayTriggerSwipe()) { + return false; + } + + SwipeInfo swipeInfo = SendMayStartSwipe(aPanInput); + + // We're in the non-APZ case here, but we still want to know whether + // the event was routed to a child process, so we use InputAPZContext + // to get that piece of information. + ScrollableLayerGuid guid; + uint64_t blockId = 0; + InputAPZContext context(guid, blockId, nsEventStatus_eIgnore); + + WidgetWheelEvent event = aPanInput.ToWidgetEvent(this); + event.mCanTriggerSwipe = swipeInfo.wantsSwipe; + nsEventStatus status; + DispatchEvent(&event, status); + if (swipeInfo.wantsSwipe) { + if (context.WasRoutedToChildProcess()) { + // We don't know whether this event can start a swipe, so we need + // to queue up events and wait for a call to ReportSwipeStarted. + mSwipeEventQueue = + MakeUnique<SwipeEventQueue>(swipeInfo.allowedDirections, blockId); + } else if (event.TriggersSwipe()) { + TrackScrollEventAsSwipe(aPanInput, swipeInfo.allowedDirections, blockId); + } + } + + if (mSwipeEventQueue && mSwipeEventQueue->inputBlockId == 0) { + mSwipeEventQueue->queuedEvents.AppendElement(aPanInput); + } + + return true; +} + +LayersId nsIWidget::GetLayersId() const { + return mCompositorSession ? mCompositorSession->RootLayerTreeId() + : LayersId{0}; +} + +const IMENotificationRequests& nsIWidget::IMENotificationRequestsRef() { + TextEventDispatcher* dispatcher = GetTextEventDispatcher(); + return dispatcher->IMENotificationRequestsRef(); +} + +void nsIWidget::PostHandleKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) {} + +bool nsIWidget::GetEditCommands(NativeKeyBindingsType aType, + const WidgetKeyboardEvent& aEvent, + nsTArray<CommandInt>& aCommands) { + MOZ_ASSERT(aEvent.IsTrusted()); + MOZ_ASSERT(aCommands.IsEmpty()); + return true; +} + +already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboard() { + if (XRE_IsContentProcess()) { + return CreateBidiKeyboardContentProcess(); + } + return CreateBidiKeyboardInner(); +} + +#ifdef ANDROID +already_AddRefed<nsIBidiKeyboard> nsIWidget::CreateBidiKeyboardInner() { + // no bidi keyboard implementation + return nullptr; +} +#endif + +namespace mozilla { + +MultiTouchInput UpdateSynthesizedTouchState( + MultiTouchInput* aState, TimeStamp aTimeStamp, uint32_t aPointerId, + TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, + double aPointerPressure, uint32_t aPointerOrientation) { + ScreenIntPoint pointerScreenPoint = ViewAs<ScreenPixel>( + aPoint, PixelCastJustification::LayoutDeviceIsScreenForBounds); + + // We can't dispatch *aState directly because (a) dispatching + // it might inadvertently modify it and (b) in the case of touchend or + // touchcancel events aState will hold the touches that are + // still down whereas the input dispatched needs to hold the removed + // touch(es). We use |inputToDispatch| for this purpose. + MultiTouchInput inputToDispatch; + inputToDispatch.mInputType = MULTITOUCH_INPUT; + inputToDispatch.mTimeStamp = aTimeStamp; + + int32_t index = aState->IndexOfTouch((int32_t)aPointerId); + if (aPointerState == TOUCH_CONTACT) { + if (index >= 0) { + // found an existing touch point, update it + SingleTouchData& point = aState->mTouches[index]; + point.mScreenPoint = pointerScreenPoint; + point.mRotationAngle = (float)aPointerOrientation; + point.mForce = (float)aPointerPressure; + inputToDispatch.mType = MultiTouchInput::MULTITOUCH_MOVE; + } else { + // new touch point, add it + aState->mTouches.AppendElement(SingleTouchData( + (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0), + (float)aPointerOrientation, (float)aPointerPressure)); + inputToDispatch.mType = MultiTouchInput::MULTITOUCH_START; + } + inputToDispatch.mTouches = aState->mTouches; + } else { + MOZ_ASSERT(aPointerState == TOUCH_REMOVE || aPointerState == TOUCH_CANCEL); + // a touch point is being lifted, so remove it from the stored list + if (index >= 0) { + aState->mTouches.RemoveElementAt(index); + } + inputToDispatch.mType = + (aPointerState == TOUCH_REMOVE ? MultiTouchInput::MULTITOUCH_END + : MultiTouchInput::MULTITOUCH_CANCEL); + inputToDispatch.mTouches.AppendElement(SingleTouchData( + (int32_t)aPointerId, pointerScreenPoint, ScreenSize(0, 0), + (float)aPointerOrientation, (float)aPointerPressure)); + } + + return inputToDispatch; +} + +namespace widget { + +const char* ToChar(InputContext::Origin aOrigin) { + switch (aOrigin) { + case InputContext::ORIGIN_MAIN: + return "ORIGIN_MAIN"; + case InputContext::ORIGIN_CONTENT: + return "ORIGIN_CONTENT"; + default: + return "Unexpected value"; + } +} + +const char* ToChar(IMEMessage aIMEMessage) { + switch (aIMEMessage) { + case NOTIFY_IME_OF_NOTHING: + return "NOTIFY_IME_OF_NOTHING"; + case NOTIFY_IME_OF_FOCUS: + return "NOTIFY_IME_OF_FOCUS"; + case NOTIFY_IME_OF_BLUR: + return "NOTIFY_IME_OF_BLUR"; + case NOTIFY_IME_OF_SELECTION_CHANGE: + return "NOTIFY_IME_OF_SELECTION_CHANGE"; + case NOTIFY_IME_OF_TEXT_CHANGE: + return "NOTIFY_IME_OF_TEXT_CHANGE"; + case NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED: + return "NOTIFY_IME_OF_COMPOSITION_EVENT_HANDLED"; + case NOTIFY_IME_OF_POSITION_CHANGE: + return "NOTIFY_IME_OF_POSITION_CHANGE"; + case NOTIFY_IME_OF_MOUSE_BUTTON_EVENT: + return "NOTIFY_IME_OF_MOUSE_BUTTON_EVENT"; + case REQUEST_TO_COMMIT_COMPOSITION: + return "REQUEST_TO_COMMIT_COMPOSITION"; + case REQUEST_TO_CANCEL_COMPOSITION: + return "REQUEST_TO_CANCEL_COMPOSITION"; + default: + return "Unexpected value"; + } +} + +void NativeIMEContext::Init(nsIWidget* aWidget) { + if (!aWidget) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); + mOriginProcessID = static_cast<uint64_t>(-1); + return; + } + if (!XRE_IsContentProcess()) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>( + aWidget->GetNativeData(NS_RAW_NATIVE_IME_CONTEXT)); + mOriginProcessID = 0; + return; + } + // If this is created in a child process, aWidget is an instance of + // PuppetWidget which doesn't support NS_RAW_NATIVE_IME_CONTEXT. + // Instead of that PuppetWidget::GetNativeIMEContext() returns cached + // native IME context of the parent process. + *this = aWidget->GetNativeIMEContext(); +} + +void NativeIMEContext::InitWithRawNativeIMEContext(void* aRawNativeIMEContext) { + if (NS_WARN_IF(!aRawNativeIMEContext)) { + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(nullptr); + mOriginProcessID = static_cast<uint64_t>(-1); + return; + } + mRawNativeIMEContext = reinterpret_cast<uintptr_t>(aRawNativeIMEContext); + mOriginProcessID = + XRE_IsContentProcess() ? ContentChild::GetSingleton()->GetID() : 0; +} + +void IMENotification::TextChangeDataBase::MergeWith( + const IMENotification::TextChangeDataBase& aOther) { + MOZ_ASSERT(aOther.IsValid(), "Merging data must store valid data"); + MOZ_ASSERT(aOther.mStartOffset <= aOther.mRemovedEndOffset, + "end of removed text must be same or larger than start"); + MOZ_ASSERT(aOther.mStartOffset <= aOther.mAddedEndOffset, + "end of added text must be same or larger than start"); + + if (!IsValid()) { + *this = aOther; + return; + } + + // |mStartOffset| and |mRemovedEndOffset| represent all replaced or removed + // text ranges. I.e., mStartOffset should be the smallest offset of all + // modified text ranges in old text. |mRemovedEndOffset| should be the + // largest end offset in old text of all modified text ranges. + // |mAddedEndOffset| represents the end offset of all inserted text ranges. + // I.e., only this is an offset in new text. + // In other words, between mStartOffset and |mRemovedEndOffset| of the + // premodified text was already removed. And some text whose length is + // |mAddedEndOffset - mStartOffset| is inserted to |mStartOffset|. I.e., + // this allows IME to mark dirty the modified text range with |mStartOffset| + // and |mRemovedEndOffset| if IME stores all text of the focused editor and + // to compute new text length with |mAddedEndOffset| and |mRemovedEndOffset|. + // Additionally, IME can retrieve only the text between |mStartOffset| and + // |mAddedEndOffset| for updating stored text. + + // For comparing new and old |mStartOffset|/|mRemovedEndOffset| values, they + // should be adjusted to be in same text. The |newData.mStartOffset| and + // |newData.mRemovedEndOffset| should be computed as in old text because + // |mStartOffset| and |mRemovedEndOffset| represent the modified text range + // in the old text but even if some text before the values of the newData + // has already been modified, the values don't include the changes. + + // For comparing new and old |mAddedEndOffset| values, they should be + // adjusted to be in same text. The |oldData.mAddedEndOffset| should be + // computed as in the new text because |mAddedEndOffset| indicates the end + // offset of inserted text in the new text but |oldData.mAddedEndOffset| + // doesn't include any changes of the text before |newData.mAddedEndOffset|. + + const TextChangeDataBase& newData = aOther; + const TextChangeDataBase oldData = *this; + + // mCausedOnlyByComposition should be true only when all changes are caused + // by composition. + mCausedOnlyByComposition = + newData.mCausedOnlyByComposition && oldData.mCausedOnlyByComposition; + + // mIncludingChangesWithoutComposition should be true if at least one of + // merged changes occurred without composition. + mIncludingChangesWithoutComposition = + newData.mIncludingChangesWithoutComposition || + oldData.mIncludingChangesWithoutComposition; + + // mIncludingChangesDuringComposition should be true when at least one of + // the merged non-composition changes occurred during the latest composition. + if (!newData.mCausedOnlyByComposition && + !newData.mIncludingChangesDuringComposition) { + MOZ_ASSERT(newData.mIncludingChangesWithoutComposition); + MOZ_ASSERT(mIncludingChangesWithoutComposition); + // If new change is neither caused by composition nor occurred during + // composition, set mIncludingChangesDuringComposition to false because + // IME doesn't want outdated text changes as text change during current + // composition. + mIncludingChangesDuringComposition = false; + } else { + // Otherwise, set mIncludingChangesDuringComposition to true if either + // oldData or newData includes changes during composition. + mIncludingChangesDuringComposition = + newData.mIncludingChangesDuringComposition || + oldData.mIncludingChangesDuringComposition; + } + + if (newData.mStartOffset >= oldData.mAddedEndOffset) { + // Case 1: + // If new start is after old end offset of added text, it means that text + // after the modified range is modified. Like: + // added range of old change: +----------+ + // removed range of new change: +----------+ + // So, the old start offset is always the smaller offset. + mStartOffset = oldData.mStartOffset; + // The new end offset of removed text is moved by the old change and we + // need to cancel the move of the old change for comparing the offsets in + // same text because it doesn't make sensce to compare offsets in different + // text. + uint32_t newRemovedEndOffsetInOldText = + newData.mRemovedEndOffset - oldData.Difference(); + mRemovedEndOffset = + std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); + // The new end offset of added text is always the larger offset. + mAddedEndOffset = newData.mAddedEndOffset; + return; + } + + if (newData.mStartOffset >= oldData.mStartOffset) { + // If new start is in the modified range, it means that new data changes + // a part or all of the range. + mStartOffset = oldData.mStartOffset; + if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) { + // Case 2: + // If new end of removed text is greater than old end of added text, it + // means that all or a part of modified range modified again and text + // after the modified range is also modified. Like: + // added range of old change: +----------+ + // removed range of new change: +----------+ + // So, the new removed end offset is moved by the old change and we need + // to cancel the move of the old change for comparing the offsets in the + // same text because it doesn't make sense to compare the offsets in + // different text. + uint32_t newRemovedEndOffsetInOldText = + newData.mRemovedEndOffset - oldData.Difference(); + mRemovedEndOffset = + std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); + // The old end of added text is replaced by new change. So, it should be + // same as the new start. On the other hand, the new added end offset is + // always same or larger. Therefore, the merged end offset of added + // text should be the new end offset of added text. + mAddedEndOffset = newData.mAddedEndOffset; + return; + } + + // Case 3: + // If new end of removed text is less than old end of added text, it means + // that only a part of the modified range is modified again. Like: + // added range of old change: +------------+ + // removed range of new change: +-----+ + // So, the new end offset of removed text should be same as the old end + // offset of removed text. Therefore, the merged end offset of removed + // text should be the old text change's |mRemovedEndOffset|. + mRemovedEndOffset = oldData.mRemovedEndOffset; + // The old end of added text is moved by new change. So, we need to cancel + // the move of the new change for comparing the offsets in same text. + uint32_t oldAddedEndOffsetInNewText = + oldData.mAddedEndOffset + newData.Difference(); + mAddedEndOffset = + std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); + return; + } + + if (newData.mRemovedEndOffset >= oldData.mStartOffset) { + // If new end of removed text is greater than old start (and new start is + // less than old start), it means that a part of modified range is modified + // again and some new text before the modified range is also modified. + MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset, + "new start offset should be less than old one here"); + mStartOffset = newData.mStartOffset; + if (newData.mRemovedEndOffset >= oldData.mAddedEndOffset) { + // Case 4: + // If new end of removed text is greater than old end of added text, it + // means that all modified text and text after the modified range is + // modified. Like: + // added range of old change: +----------+ + // removed range of new change: +------------------+ + // So, the new end of removed text is moved by the old change. Therefore, + // we need to cancel the move of the old change for comparing the offsets + // in same text because it doesn't make sense to compare the offsets in + // different text. + uint32_t newRemovedEndOffsetInOldText = + newData.mRemovedEndOffset - oldData.Difference(); + mRemovedEndOffset = + std::max(newRemovedEndOffsetInOldText, oldData.mRemovedEndOffset); + // The old end of added text is replaced by new change. So, the old end + // offset of added text is same as new text change's start offset. Then, + // new change's end offset of added text is always same or larger than + // it. Therefore, merged end offset of added text is always the new end + // offset of added text. + mAddedEndOffset = newData.mAddedEndOffset; + return; + } + + // Case 5: + // If new end of removed text is less than old end of added text, it + // means that only a part of the modified range is modified again. Like: + // added range of old change: +----------+ + // removed range of new change: +----------+ + // So, the new end of removed text should be same as old end of removed + // text for preventing end of removed text to be modified. Therefore, + // merged end offset of removed text is always the old end offset of removed + // text. + mRemovedEndOffset = oldData.mRemovedEndOffset; + // The old end of added text is moved by this change. So, we need to + // cancel the move of the new change for comparing the offsets in same text + // because it doesn't make sense to compare the offsets in different text. + uint32_t oldAddedEndOffsetInNewText = + oldData.mAddedEndOffset + newData.Difference(); + mAddedEndOffset = + std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); + return; + } + + // Case 6: + // Otherwise, i.e., both new end of added text and new start are less than + // old start, text before the modified range is modified. Like: + // added range of old change: +----------+ + // removed range of new change: +----------+ + MOZ_ASSERT(newData.mStartOffset < oldData.mStartOffset, + "new start offset should be less than old one here"); + mStartOffset = newData.mStartOffset; + MOZ_ASSERT(newData.mRemovedEndOffset < oldData.mRemovedEndOffset, + "new removed end offset should be less than old one here"); + mRemovedEndOffset = oldData.mRemovedEndOffset; + // The end of added text should be adjusted with the new difference. + uint32_t oldAddedEndOffsetInNewText = + oldData.mAddedEndOffset + newData.Difference(); + mAddedEndOffset = + std::max(newData.mAddedEndOffset, oldAddedEndOffsetInNewText); +} + +#ifdef DEBUG + +// Let's test the code of merging multiple text change data in debug build +// and crash if one of them fails because this feature is very complex but +// cannot be tested with mochitest. +void IMENotification::TextChangeDataBase::Test() { + static bool gTestTextChangeEvent = true; + if (!gTestTextChangeEvent) { + return; + } + gTestTextChangeEvent = false; + + /**************************************************************************** + * Case 1 + ****************************************************************************/ + + // Appending text + MergeWith(TextChangeData(10, 10, 20, false, false)); + MergeWith(TextChangeData(20, 20, 35, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 1-1-1: mStartOffset should be the first offset"); + MOZ_ASSERT( + mRemovedEndOffset == 10, // 20 - (20 - 10) + "Test 1-1-2: mRemovedEndOffset should be the first end of removed text"); + MOZ_ASSERT( + mAddedEndOffset == 35, + "Test 1-1-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Removing text (longer line -> shorter line) + MergeWith(TextChangeData(10, 20, 10, false, false)); + MergeWith(TextChangeData(10, 30, 10, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 1-2-1: mStartOffset should be the first offset"); + MOZ_ASSERT(mRemovedEndOffset == 40, // 30 + (10 - 20) + "Test 1-2-2: mRemovedEndOffset should be the the last end of " + "removed text " + "with already removed length"); + MOZ_ASSERT( + mAddedEndOffset == 10, + "Test 1-2-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Removing text (shorter line -> longer line) + MergeWith(TextChangeData(10, 20, 10, false, false)); + MergeWith(TextChangeData(10, 15, 10, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 1-3-1: mStartOffset should be the first offset"); + MOZ_ASSERT(mRemovedEndOffset == 25, // 15 + (10 - 20) + "Test 1-3-2: mRemovedEndOffset should be the the last end of " + "removed text " + "with already removed length"); + MOZ_ASSERT( + mAddedEndOffset == 10, + "Test 1-3-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Appending text at different point (not sure if actually occurs) + MergeWith(TextChangeData(10, 10, 20, false, false)); + MergeWith(TextChangeData(55, 55, 60, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 1-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 45, // 55 - (10 - 20) + "Test 1-4-2: mRemovedEndOffset should be the the largest end of removed " + "text without already added length"); + MOZ_ASSERT( + mAddedEndOffset == 60, + "Test 1-4-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Removing text at different point (not sure if actually occurs) + MergeWith(TextChangeData(10, 20, 10, false, false)); + MergeWith(TextChangeData(55, 68, 55, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 1-5-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 78, // 68 - (10 - 20) + "Test 1-5-2: mRemovedEndOffset should be the the largest end of removed " + "text with already removed length"); + MOZ_ASSERT( + mAddedEndOffset == 55, + "Test 1-5-3: mAddedEndOffset should be the largest end of added text"); + Clear(); + + // Replacing text and append text (becomes longer) + MergeWith(TextChangeData(30, 35, 32, false, false)); + MergeWith(TextChangeData(32, 32, 40, false, false)); + MOZ_ASSERT(mStartOffset == 30, + "Test 1-6-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 35, // 32 - (32 - 35) + "Test 1-6-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 40, + "Test 1-6-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text and append text (becomes shorter) + MergeWith(TextChangeData(30, 35, 32, false, false)); + MergeWith(TextChangeData(32, 32, 33, false, false)); + MOZ_ASSERT(mStartOffset == 30, + "Test 1-7-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 35, // 32 - (32 - 35) + "Test 1-7-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 33, + "Test 1-7-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Removing text and replacing text after first range (not sure if actually + // occurs) + MergeWith(TextChangeData(30, 35, 30, false, false)); + MergeWith(TextChangeData(32, 34, 48, false, false)); + MOZ_ASSERT(mStartOffset == 30, + "Test 1-8-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 39, // 34 - (30 - 35) + "Test 1-8-2: mRemovedEndOffset should be the the first end of " + "removed text " + "without already removed text"); + MOZ_ASSERT( + mAddedEndOffset == 48, + "Test 1-8-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Removing text and replacing text after first range (not sure if actually + // occurs) + MergeWith(TextChangeData(30, 35, 30, false, false)); + MergeWith(TextChangeData(32, 38, 36, false, false)); + MOZ_ASSERT(mStartOffset == 30, + "Test 1-9-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 43, // 38 - (30 - 35) + "Test 1-9-2: mRemovedEndOffset should be the the first end of " + "removed text " + "without already removed text"); + MOZ_ASSERT( + mAddedEndOffset == 36, + "Test 1-9-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + /**************************************************************************** + * Case 2 + ****************************************************************************/ + + // Replacing text in around end of added text (becomes shorter) (not sure + // if actually occurs) + MergeWith(TextChangeData(50, 50, 55, false, false)); + MergeWith(TextChangeData(53, 60, 54, false, false)); + MOZ_ASSERT(mStartOffset == 50, + "Test 2-1-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 55, // 60 - (55 - 50) + "Test 2-1-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 54, + "Test 2-1-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text around end of added text (becomes longer) (not sure + // if actually occurs) + MergeWith(TextChangeData(50, 50, 55, false, false)); + MergeWith(TextChangeData(54, 62, 68, false, false)); + MOZ_ASSERT(mStartOffset == 50, + "Test 2-2-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 57, // 62 - (55 - 50) + "Test 2-2-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 68, + "Test 2-2-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text around end of replaced text (became shorter) (not sure if + // actually occurs) + MergeWith(TextChangeData(36, 48, 45, false, false)); + MergeWith(TextChangeData(43, 50, 49, false, false)); + MOZ_ASSERT(mStartOffset == 36, + "Test 2-3-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 53, // 50 - (45 - 48) + "Test 2-3-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already removed text length"); + MOZ_ASSERT( + mAddedEndOffset == 49, + "Test 2-3-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text around end of replaced text (became longer) (not sure if + // actually occurs) + MergeWith(TextChangeData(36, 52, 53, false, false)); + MergeWith(TextChangeData(43, 68, 61, false, false)); + MOZ_ASSERT(mStartOffset == 36, + "Test 2-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 67, // 68 - (53 - 52) + "Test 2-4-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 61, + "Test 2-4-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + /**************************************************************************** + * Case 3 + ****************************************************************************/ + + // Appending text in already added text (not sure if actually occurs) + MergeWith(TextChangeData(10, 10, 20, false, false)); + MergeWith(TextChangeData(15, 15, 30, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 3-1-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 10, + "Test 3-1-2: mRemovedEndOffset should be the the first end of " + "removed text"); + MOZ_ASSERT( + mAddedEndOffset == 35, // 20 + (30 - 15) + "Test 3-1-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text in added text (not sure if actually occurs) + MergeWith(TextChangeData(50, 50, 55, false, false)); + MergeWith(TextChangeData(52, 53, 56, false, false)); + MOZ_ASSERT(mStartOffset == 50, + "Test 3-2-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 50, + "Test 3-2-2: mRemovedEndOffset should be the the first end of " + "removed text"); + MOZ_ASSERT( + mAddedEndOffset == 58, // 55 + (56 - 53) + "Test 3-2-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text in replaced text (became shorter) (not sure if actually + // occurs) + MergeWith(TextChangeData(36, 48, 45, false, false)); + MergeWith(TextChangeData(37, 38, 50, false, false)); + MOZ_ASSERT(mStartOffset == 36, + "Test 3-3-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 48, + "Test 3-3-2: mRemovedEndOffset should be the the first end of " + "removed text"); + MOZ_ASSERT( + mAddedEndOffset == 57, // 45 + (50 - 38) + "Test 3-3-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text in replaced text (became longer) (not sure if actually + // occurs) + MergeWith(TextChangeData(32, 48, 53, false, false)); + MergeWith(TextChangeData(43, 50, 52, false, false)); + MOZ_ASSERT(mStartOffset == 32, + "Test 3-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 48, + "Test 3-4-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 55, // 53 + (52 - 50) + "Test 3-4-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text in replaced text (became shorter) (not sure if actually + // occurs) + MergeWith(TextChangeData(36, 48, 50, false, false)); + MergeWith(TextChangeData(37, 49, 47, false, false)); + MOZ_ASSERT(mStartOffset == 36, + "Test 3-5-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 48, + "Test 3-5-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT(mAddedEndOffset == 48, // 50 + (47 - 49) + "Test 3-5-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); + + // Replacing text in replaced text (became longer) (not sure if actually + // occurs) + MergeWith(TextChangeData(32, 48, 53, false, false)); + MergeWith(TextChangeData(43, 50, 47, false, false)); + MOZ_ASSERT(mStartOffset == 32, + "Test 3-6-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 48, + "Test 3-6-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT(mAddedEndOffset == 50, // 53 + (47 - 50) + "Test 3-6-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); + + /**************************************************************************** + * Case 4 + ****************************************************************************/ + + // Replacing text all of already append text (not sure if actually occurs) + MergeWith(TextChangeData(50, 50, 55, false, false)); + MergeWith(TextChangeData(44, 66, 68, false, false)); + MOZ_ASSERT(mStartOffset == 44, + "Test 4-1-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 61, // 66 - (55 - 50) + "Test 4-1-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 68, + "Test 4-1-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text around a point in which text was removed (not sure if + // actually occurs) + MergeWith(TextChangeData(50, 62, 50, false, false)); + MergeWith(TextChangeData(44, 66, 68, false, false)); + MOZ_ASSERT(mStartOffset == 44, + "Test 4-2-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 78, // 66 - (50 - 62) + "Test 4-2-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already removed text length"); + MOZ_ASSERT( + mAddedEndOffset == 68, + "Test 4-2-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text all replaced text (became shorter) (not sure if actually + // occurs) + MergeWith(TextChangeData(50, 62, 60, false, false)); + MergeWith(TextChangeData(49, 128, 130, false, false)); + MOZ_ASSERT(mStartOffset == 49, + "Test 4-3-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 130, // 128 - (60 - 62) + "Test 4-3-2: mRemovedEndOffset should be the the last end of " + "removed text " + "without already removed text length"); + MOZ_ASSERT( + mAddedEndOffset == 130, + "Test 4-3-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + // Replacing text all replaced text (became longer) (not sure if actually + // occurs) + MergeWith(TextChangeData(50, 61, 73, false, false)); + MergeWith(TextChangeData(44, 100, 50, false, false)); + MOZ_ASSERT(mStartOffset == 44, + "Test 4-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT(mRemovedEndOffset == 88, // 100 - (73 - 61) + "Test 4-4-2: mRemovedEndOffset should be the the last end of " + "removed text " + "with already added text length"); + MOZ_ASSERT( + mAddedEndOffset == 50, + "Test 4-4-3: mAddedEndOffset should be the last end of added text"); + Clear(); + + /**************************************************************************** + * Case 5 + ****************************************************************************/ + + // Replacing text around start of added text (not sure if actually occurs) + MergeWith(TextChangeData(50, 50, 55, false, false)); + MergeWith(TextChangeData(48, 52, 49, false, false)); + MOZ_ASSERT(mStartOffset == 48, + "Test 5-1-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 50, + "Test 5-1-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 52, // 55 + (52 - 49) + "Test 5-1-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text around start of replaced text (became shorter) (not sure if + // actually occurs) + MergeWith(TextChangeData(50, 60, 58, false, false)); + MergeWith(TextChangeData(43, 50, 48, false, false)); + MOZ_ASSERT(mStartOffset == 43, + "Test 5-2-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 60, + "Test 5-2-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT(mAddedEndOffset == 56, // 58 + (48 - 50) + "Test 5-2-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); + + // Replacing text around start of replaced text (became longer) (not sure if + // actually occurs) + MergeWith(TextChangeData(50, 60, 68, false, false)); + MergeWith(TextChangeData(43, 55, 53, false, false)); + MOZ_ASSERT(mStartOffset == 43, + "Test 5-3-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 60, + "Test 5-3-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT(mAddedEndOffset == 66, // 68 + (53 - 55) + "Test 5-3-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); + + // Replacing text around start of replaced text (became shorter) (not sure if + // actually occurs) + MergeWith(TextChangeData(50, 60, 58, false, false)); + MergeWith(TextChangeData(43, 50, 128, false, false)); + MOZ_ASSERT(mStartOffset == 43, + "Test 5-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 60, + "Test 5-4-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 136, // 58 + (128 - 50) + "Test 5-4-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Replacing text around start of replaced text (became longer) (not sure if + // actually occurs) + MergeWith(TextChangeData(50, 60, 68, false, false)); + MergeWith(TextChangeData(43, 55, 65, false, false)); + MOZ_ASSERT(mStartOffset == 43, + "Test 5-5-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 60, + "Test 5-5-2: mRemovedEndOffset should be the the first end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 78, // 68 + (65 - 55) + "Test 5-5-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + /**************************************************************************** + * Case 6 + ****************************************************************************/ + + // Appending text before already added text (not sure if actually occurs) + MergeWith(TextChangeData(30, 30, 45, false, false)); + MergeWith(TextChangeData(10, 10, 20, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 6-1-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 30, + "Test 6-1-2: mRemovedEndOffset should be the the largest end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 55, // 45 + (20 - 10) + "Test 6-1-3: mAddedEndOffset should be the first end of added text with " + "added text length by the new change"); + Clear(); + + // Removing text before already removed text (not sure if actually occurs) + MergeWith(TextChangeData(30, 35, 30, false, false)); + MergeWith(TextChangeData(10, 25, 10, false, false)); + MOZ_ASSERT(mStartOffset == 10, + "Test 6-2-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 35, + "Test 6-2-2: mRemovedEndOffset should be the the largest end of removed " + "text"); + MOZ_ASSERT( + mAddedEndOffset == 15, // 30 - (25 - 10) + "Test 6-2-3: mAddedEndOffset should be the first end of added text with " + "removed text length by the new change"); + Clear(); + + // Replacing text before already replaced text (not sure if actually occurs) + MergeWith(TextChangeData(50, 65, 70, false, false)); + MergeWith(TextChangeData(13, 24, 15, false, false)); + MOZ_ASSERT(mStartOffset == 13, + "Test 6-3-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 65, + "Test 6-3-2: mRemovedEndOffset should be the the largest end of removed " + "text"); + MOZ_ASSERT(mAddedEndOffset == 61, // 70 + (15 - 24) + "Test 6-3-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); + + // Replacing text before already replaced text (not sure if actually occurs) + MergeWith(TextChangeData(50, 65, 70, false, false)); + MergeWith(TextChangeData(13, 24, 36, false, false)); + MOZ_ASSERT(mStartOffset == 13, + "Test 6-4-1: mStartOffset should be the smallest offset"); + MOZ_ASSERT( + mRemovedEndOffset == 65, + "Test 6-4-2: mRemovedEndOffset should be the the largest end of removed " + "text"); + MOZ_ASSERT(mAddedEndOffset == 82, // 70 + (36 - 24) + "Test 6-4-3: mAddedEndOffset should be the first end of added " + "text without " + "removed text length by the new change"); + Clear(); +} + +#endif // #ifdef DEBUG + +} // namespace widget +} // namespace mozilla + +#ifdef DEBUG +////////////////////////////////////////////////////////////// +// +// Code to deal with paint and event debug prefs. +// +////////////////////////////////////////////////////////////// +struct PrefPair { + const char* name; + bool value; +}; + +static PrefPair debug_PrefValues[] = { + {"nglayout.debug.crossing_event_dumping", false}, + {"nglayout.debug.event_dumping", false}, + {"nglayout.debug.invalidate_dumping", false}, + {"nglayout.debug.motion_event_dumping", false}, + {"nglayout.debug.paint_dumping", false}}; + +////////////////////////////////////////////////////////////// +bool nsIWidget::debug_GetCachedBoolPref(const char* aPrefName) { + NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null."); + + for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { + if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) { + return debug_PrefValues[i].value; + } + } + + return false; +} +////////////////////////////////////////////////////////////// +static void debug_SetCachedBoolPref(const char* aPrefName, bool aValue) { + NS_ASSERTION(nullptr != aPrefName, "cmon, pref name is null."); + + for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { + if (strcmp(debug_PrefValues[i].name, aPrefName) == 0) { + debug_PrefValues[i].value = aValue; + + return; + } + } + + NS_ASSERTION(false, "cmon, this code is not reached dude."); +} + +////////////////////////////////////////////////////////////// +class Debug_PrefObserver final : public nsIObserver { + ~Debug_PrefObserver() = default; + + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER +}; + +NS_IMPL_ISUPPORTS(Debug_PrefObserver, nsIObserver) + +NS_IMETHODIMP +Debug_PrefObserver::Observe(nsISupports* subject, const char* topic, + const char16_t* data) { + NS_ConvertUTF16toUTF8 prefName(data); + + bool value = Preferences::GetBool(prefName.get(), false); + debug_SetCachedBoolPref(prefName.get(), value); + return NS_OK; +} + +////////////////////////////////////////////////////////////// +/* static */ void debug_RegisterPrefCallbacks() { + static bool once = true; + + if (!once) { + return; + } + + once = false; + + nsCOMPtr<nsIObserver> obs(new Debug_PrefObserver()); + for (uint32_t i = 0; i < std::size(debug_PrefValues); i++) { + // Initialize the pref values + debug_PrefValues[i].value = + Preferences::GetBool(debug_PrefValues[i].name, false); + + if (obs) { + // Register callbacks for when these change + nsCString name; + name.AssignLiteral(debug_PrefValues[i].name, + strlen(debug_PrefValues[i].name)); + Preferences::AddStrongObserver(obs, name); + } + } +} +////////////////////////////////////////////////////////////// +static int32_t _GetPrintCount() { + static int32_t sCount = 0; + + return ++sCount; +} +////////////////////////////////////////////////////////////// +/* static */ +void nsIWidget::debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget, + WidgetGUIEvent* aGuiEvent, + const char* aWidgetName, int32_t aWindowID) { + if (aGuiEvent->mMessage == eMouseMove) { + if (!debug_GetCachedBoolPref("nglayout.debug.motion_event_dumping")) return; + } + + if (aGuiEvent->mMessage == eMouseEnterIntoWidget || + aGuiEvent->mMessage == eMouseExitFromWidget) { + if (!debug_GetCachedBoolPref("nglayout.debug.crossing_event_dumping")) + return; + } + + if (!debug_GetCachedBoolPref("nglayout.debug.event_dumping")) return; + + fprintf(aFileOut, "%4d %-26s widget=%-8p name=%-12s id=0x%-6x refpt=%d,%d\n", + _GetPrintCount(), ToChar(aGuiEvent->mMessage), (void*)aWidget, + aWidgetName, aWindowID, aGuiEvent->mRefPoint.x.value, + aGuiEvent->mRefPoint.y.value); +} +////////////////////////////////////////////////////////////// +/* static */ +void nsIWidget::debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget, + const nsIntRegion& aRegion, + const char* aWidgetName, + int32_t aWindowID) { + NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE"); + NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null"); + + if (!debug_GetCachedBoolPref("nglayout.debug.paint_dumping")) return; + + nsIntRect rect = aRegion.GetBounds(); + fprintf(aFileOut, + "%4d PAINT widget=%p name=%-12s id=0x%-6x bounds-rect=%3d,%-3d " + "%3d,%-3d", + _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID, rect.X(), + rect.Y(), rect.Width(), rect.Height()); + + fprintf(aFileOut, "\n"); +} +////////////////////////////////////////////////////////////// +/* static */ +void nsIWidget::debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget, + const LayoutDeviceIntRect* aRect, + const char* aWidgetName, + int32_t aWindowID) { + if (!debug_GetCachedBoolPref("nglayout.debug.invalidate_dumping")) return; + + NS_ASSERTION(nullptr != aFileOut, "cmon, null output FILE"); + NS_ASSERTION(nullptr != aWidget, "cmon, the widget is null"); + + fprintf(aFileOut, "%4d Invalidate widget=%p name=%-12s id=0x%-6x", + _GetPrintCount(), (void*)aWidget, aWidgetName, aWindowID); + + if (aRect) { + fprintf(aFileOut, " rect=%3d,%-3d %3d,%-3d", aRect->X(), aRect->Y(), + aRect->Width(), aRect->Height()); + } else { + fprintf(aFileOut, " rect=%-15s", "none"); + } + + fprintf(aFileOut, "\n"); +} +////////////////////////////////////////////////////////////// + +#endif // DEBUG diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h @@ -41,25 +41,42 @@ #include "nsStringFwd.h" #include "nsTArray.h" #include "nsTHashMap.h" +#include "nsWeakReference.h" #include "mozilla/widget/InitData.h" #include "nsXULAppAPI.h" +// Windows specific constant indicating the maximum number of touch points the +// inject api will allow. This also sets the maximum numerical value for touch +// ids we can use when injecting touch points on Windows. +#define TOUCH_INJECT_MAX_POINTS 256 + // forward declarations class nsIBidiKeyboard; class nsIRollupListener; class nsIContent; class nsMenuPopupFrame; class nsIRunnable; +class nsIWidget; namespace mozilla { -enum class NativeKeyBindingsType : uint8_t; +class CompositorVsyncDispatcher; +class FallbackRenderer; +class LiveResizeListener; +class PanGestureInput; +class MultiTouchInput; +class Mutex; +class PinchGestureInput; +class SwipeTracker; class VsyncDispatcher; class WidgetGUIEvent; class WidgetInputEvent; class WidgetKeyboardEvent; -struct FontRange; +enum ScreenRotation : uint8_t; enum class ColorScheme : uint8_t; +enum class NativeKeyBindingsType : uint8_t; enum class WindowButtonType : uint8_t; +struct FontRange; +struct SwipeEventQueue; enum class WindowShadow : uint8_t { None, @@ -78,20 +95,44 @@ class BrowserChild; enum class CallerType : uint32_t; } // namespace dom class WindowRenderer; +namespace gfx { +class DrawTarget; +class SourceSurface; +} // namespace gfx namespace layers { +class APZEventState; class AsyncDragMetrics; class Compositor; class CompositorBridgeChild; -struct CompositorScrollUpdate; -struct FrameMetrics; +class CompositorBridgeParent; +class CompositorOptions; +class CompositorSession; +class GeckoContentController; +class IAPZCTreeManager; +class ImageContainer; class LayerManager; +class NativeLayer; +class NativeLayerRoot; +class RemoteCompositorSession; class WebRenderBridgeChild; +class WebRenderLayerManager; +struct APZEventResult; +struct CompositorScrollUpdate; +struct FrameMetrics; +struct ScrollableLayerGuid; } // namespace layers namespace widget { +enum class ThemeChangeKind : uint8_t; +enum class OcclusionState : uint8_t; class TextEventDispatcher; class TextEventDispatcherListener; +class LocalesChangedObserver; +class WidgetShutdownObserver; class CompositorWidget; class CompositorWidgetInitData; +class CompositorWidgetDelegate; +class InProcessCompositorWidget; +class WidgetRenderingContext; class Screen; } // namespace widget namespace wr { @@ -99,6 +140,11 @@ class DisplayListBuilder; class IpcResourceUpdateQueue; enum class RenderRoot : uint8_t; } // namespace wr +#ifdef ACCESSIBILITY +namespace a11y { +class LocalAccessible; +} +#endif } // namespace mozilla /** @@ -118,6 +164,27 @@ typedef nsEventStatus (*EVENT_CALLBACK)(mozilla::WidgetGUIEvent* aEvent); // to ensure cross-platform code. typedef void* nsNativeWidget; +/* + * TouchPointerState states for SynthesizeNativeTouchPoint. Match + * touch states in nsIDOMWindowUtils.idl. + */ +enum TouchPointerState : uint8_t { + // The pointer is in a hover state above the digitizer + TOUCH_HOVER = (1 << 0), + // The pointer is in contact with the digitizer + TOUCH_CONTACT = (1 << 1), + // The pointer has been removed from the digitizer detection area + TOUCH_REMOVE = (1 << 2), + // The pointer has been canceled. Will cancel any pending os level + // gestures that would triggered as a result of completion of the + // input sequence. This may not cancel moz platform related events + // that might get tirggered by input already delivered. + TOUCH_CANCEL = (1 << 3), + + // ALL_BITS used for validity checking during IPC serialization + ALL_BITS = (1 << 4) - 1 +}; + /** * Values for the GetNativeData function */ @@ -325,20 +392,32 @@ class MOZ_RAII AutoSynthesizedEventCallbackNotifier final { * The base class for all the widgets. It provides the interface for * all basic and necessary functionality. */ -class nsIWidget : public nsISupports { - protected: - friend class nsBaseWidget; - typedef mozilla::dom::BrowserChild BrowserChild; - +class nsIWidget : public nsSupportsWeakReference { public: + template <class EventType, class InputType> + friend class DispatchEventOnMainThread; + friend class mozilla::widget::InProcessCompositorWidget; + friend class mozilla::layers::RemoteCompositorSession; + typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::SourceSurface SourceSurface; typedef mozilla::layers::CompositorBridgeChild CompositorBridgeChild; + typedef mozilla::layers::CompositorBridgeParent CompositorBridgeParent; + typedef mozilla::layers::IAPZCTreeManager IAPZCTreeManager; + typedef mozilla::layers::GeckoContentController GeckoContentController; + typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid; + typedef mozilla::layers::APZEventState APZEventState; + typedef mozilla::CSSIntRect CSSIntRect; + typedef mozilla::ScreenRotation ScreenRotation; + typedef mozilla::widget::CompositorWidgetDelegate CompositorWidgetDelegate; + typedef mozilla::layers::CompositorSession CompositorSession; + typedef mozilla::layers::ImageContainer ImageContainer; + typedef mozilla::dom::BrowserChild BrowserChild; typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::WindowRenderer WindowRenderer; typedef mozilla::layers::LayersBackend LayersBackend; typedef mozilla::layers::LayersId LayersId; - typedef mozilla::layers::ScrollableLayerGuid ScrollableLayerGuid; typedef mozilla::layers::ZoomConstraints ZoomConstraints; typedef mozilla::widget::IMEEnabled IMEEnabled; typedef mozilla::widget::IMEMessage IMEMessage; @@ -372,6 +451,9 @@ class nsIWidget : public nsISupports { typedef mozilla::CSSPoint CSSPoint; typedef mozilla::CSSRect CSSRect; + NS_DECL_THREADSAFE_ISUPPORTS + + using TouchPointerState = ::TouchPointerState; using InitData = mozilla::widget::InitData; using WindowType = mozilla::widget::WindowType; using PopupType = mozilla::widget::PopupType; @@ -395,8 +477,6 @@ class nsIWidget : public nsISupports { NS_INLINE_DECL_STATIC_IID(NS_IWIDGET_IID) - nsIWidget() = default; - /** * Create and initialize a widget. * @@ -457,8 +537,8 @@ class nsIWidget : public nsISupports { * which is entirely non-native. All other params are the same as * for |Create()|. */ - virtual already_AddRefed<nsIWidget> CreateChild( - const LayoutDeviceIntRect& aRect, InitData&) = 0; + already_AddRefed<nsIWidget> CreateChild(const LayoutDeviceIntRect& aRect, + InitData&); /** * Attach to a top level widget. @@ -474,17 +554,17 @@ class nsIWidget : public nsISupports { * aUseAttachedEvents if true, events are sent to the attached listener * instead of the normal listener. */ - virtual void AttachViewToTopLevel(bool aUseAttachedEvents) = 0; + virtual void AttachViewToTopLevel(bool aUseAttachedEvents); /** * Accessor functions to get and set the attached listener. Used by * nsView in connection with AttachViewToTopLevel above. */ - virtual void SetAttachedWidgetListener(nsIWidgetListener* aListener) = 0; - virtual nsIWidgetListener* GetAttachedWidgetListener() const = 0; + virtual void SetAttachedWidgetListener(nsIWidgetListener* aListener); + virtual nsIWidgetListener* GetAttachedWidgetListener() const; virtual void SetPreviouslyAttachedWidgetListener( - nsIWidgetListener* aListener) = 0; - virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener() = 0; + nsIWidgetListener* aListener); + virtual nsIWidgetListener* GetPreviouslyAttachedWidgetListener(); /** * Notifies the root widget of a non-blank paint. @@ -495,17 +575,15 @@ class nsIWidget : public nsISupports { * Accessor functions to get and set the listener which handles various * actions for the widget. */ - //@{ - virtual nsIWidgetListener* GetWidgetListener() const = 0; - virtual void SetWidgetListener(nsIWidgetListener* alistener) = 0; - //@} + virtual nsIWidgetListener* GetWidgetListener() const; + virtual void SetWidgetListener(nsIWidgetListener* alistener); /** * Close and destroy the internal native window. * This method does not delete the widget. */ - virtual void Destroy() = 0; + virtual void Destroy(); /** * Destroyed() returns true if Destroy() has been called already. @@ -544,7 +622,7 @@ class nsIWidget : public nsISupports { * Return the physical DPI of the screen containing the window ... * the number of device pixels per inch. */ - virtual float GetDPI() = 0; + virtual float GetDPI(); /** * Fallback DPI for when there's no widget available. @@ -556,9 +634,16 @@ class nsIWidget : public nsISupports { * dependent "desktop pixels" used to manage window positions on a * potentially multi-screen, mixed-resolution desktop. */ - virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() = 0; + virtual mozilla::DesktopToLayoutDeviceScale GetDesktopToDeviceScale() { + return mozilla::DesktopToLayoutDeviceScale(1.0); + } - virtual void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset) = 0; + // Utility function for derived-class overrides of ConstrainPosition. + static DesktopIntPoint ConstrainPositionToBounds( + const DesktopIntPoint&, const mozilla::DesktopIntSize&, + const DesktopIntRect&); + + void DynamicToolbarOffsetChanged(mozilla::ScreenIntCoord aOffset); /** * Return the default scale factor for the window. This is the @@ -623,7 +708,7 @@ class nsIWidget : public nsISupports { /** * Make the window modal. */ - virtual void SetModal(bool aModal) = 0; + virtual void SetModal(bool aModal) {} /** * Are we app modal. Currently only implemented on Cocoa. @@ -636,7 +721,7 @@ class nsIWidget : public nsISupports { * screens), the value will be the maximum of the set of maximum supported * contacts by each individual digitizer. */ - virtual uint32_t GetMaxTouchPoints() const = 0; + virtual uint32_t GetMaxTouchPoints() const; /** * Returns whether the window is visible @@ -656,7 +741,7 @@ class nsIWidget : public nsISupports { * Perform platform-dependent sanity check on a potential window position. * This is guaranteed to work only for top-level windows. */ - virtual void ConstrainPosition(DesktopIntPoint&) = 0; + virtual void ConstrainPosition(DesktopIntPoint&) {} /** * NOTE: @@ -689,6 +774,17 @@ class nsIWidget : public nsISupports { **/ virtual void Move(double aX, double aY) = 0; + // Return whether this widget interprets parameters to Move and Resize APIs + // as "desktop pixels" rather than "device pixels", and therefore + // applies its GetDefaultScale() value to them before using them as mBounds + // etc (which are always stored in device pixels). + // Note that APIs that -get- the widget's position/size/bounds, rather than + // -setting- them (i.e. moving or resizing the widget) will always return + // values in the widget's device pixels. + bool BoundsUseDesktopPixels() const { + return mWindowType <= WindowType::Popup; + } + /** * Reposition this widget so that the client area has the given offset. * @@ -697,7 +793,7 @@ class nsIWidget : public nsISupports { * widget (for root widgets and popup widgets it is in * screen coordinates) **/ - virtual void MoveClient(const DesktopPoint& aOffset) = 0; + virtual void MoveClient(const DesktopPoint& aOffset); /** * Resize this widget. Any size constraints set for the window by a @@ -742,7 +838,7 @@ class nsIWidget : public nsISupports { * @param aSize the new size of the client area. * @param aRepaint whether the widget should be repainted */ - virtual void ResizeClient(const DesktopSize& aSize, bool aRepaint) = 0; + virtual void ResizeClient(const DesktopSize& aSize, bool aRepaint); /** * Resize and reposition the widget so tht inner client area has the given @@ -754,7 +850,7 @@ class nsIWidget : public nsISupports { * is in screen coordinates). * @param aRepaint whether the widget should be repainted */ - virtual void ResizeClient(const DesktopRect& aRect, bool aRepaint) = 0; + virtual void ResizeClient(const DesktopRect& aRect, bool aRepaint); /** * Minimize, maximize or normalize the window size. @@ -762,9 +858,11 @@ class nsIWidget : public nsISupports { */ virtual void SetSizeMode(nsSizeMode aMode) = 0; - virtual void GetWorkspaceID(nsAString& workspaceID) = 0; + virtual void GetWorkspaceID(nsAString& aWorkspaceID) { + aWorkspaceID.Truncate(); + } - virtual void MoveToWorkspace(const nsAString& workspaceID) = 0; + virtual void MoveToWorkspace(const nsAString& aWorkspaceID) {} // Assume that it is not, since most widgets are not cloaked. virtual bool IsCloaked() const { return false; } @@ -783,15 +881,10 @@ class nsIWidget : public nsISupports { */ virtual nsSizeMode SizeMode() = 0; - /** - * Ask whether the window is tiled. - */ - virtual bool IsTiled() const = 0; - - /** - * Ask wether the widget is fully occluded - */ - virtual bool IsFullyOccluded() const = 0; + /** Ask whether the window is tiled. */ + bool IsTiled() const { return mIsTiled; } + /** Ask whether the widget is fully occluded */ + bool IsFullyOccluded() const { return mIsFullyOccluded; } /** * Enable or disable this Widget @@ -825,7 +918,7 @@ class nsIWidget : public nsISupports { * * @return the x, y, width and height of this widget. */ - virtual LayoutDeviceIntRect GetBounds() = 0; + virtual LayoutDeviceIntRect GetBounds(); /** * Get this widget's outside dimensions in device coordinates. This @@ -833,7 +926,7 @@ class nsIWidget : public nsISupports { * * @return the x, y, width and height of this widget. */ - virtual LayoutDeviceIntRect GetScreenBounds() = 0; + virtual LayoutDeviceIntRect GetScreenBounds(); /** * Similar to GetScreenBounds except that this function will always @@ -850,8 +943,7 @@ class nsIWidget : public nsISupports { * @param aRect On return it holds the x, y, width and height of * this widget. */ - [[nodiscard]] virtual nsresult GetRestoredBounds( - LayoutDeviceIntRect& aRect) = 0; + [[nodiscard]] virtual nsresult GetRestoredBounds(LayoutDeviceIntRect& aRect); /** * On some platforms (namely, GTK), we can't know the bounds of the client @@ -868,7 +960,7 @@ class nsIWidget : public nsISupports { * * @return the x, y, width and height of the client area of this widget. */ - virtual LayoutDeviceIntRect GetClientBounds() = 0; + virtual LayoutDeviceIntRect GetClientBounds(); /** Whether to extend the client area into the titlebar. */ virtual void SetCustomTitlebar(bool) {} @@ -884,7 +976,7 @@ class nsIWidget : public nsISupports { * * @return the x and y of the offset. */ - virtual LayoutDeviceIntPoint GetClientOffset() = 0; + virtual LayoutDeviceIntPoint GetClientOffset(); /** * Returns the slop from the screen edges in device pixels. @@ -911,13 +1003,6 @@ class nsIWidget : public nsISupports { virtual void SetBackgroundColor(const nscolor& aColor) {} - /** - * If a cursor type is currently cached locally for this widget, clear the - * cached cursor to force an update on the next SetCursor call. - */ - - virtual void ClearCachedCursor() = 0; - struct Cursor { // The system cursor chosen by the page. This is used if there's no custom // cursor, or if we fail to use the custom cursor in some way (if the image @@ -944,9 +1029,16 @@ class nsIWidget : public nsISupports { /** * Sets the cursor for this widget. */ - virtual void SetCursor(const Cursor&) = 0; - - virtual void SetCustomCursorAllowed(bool) = 0; + virtual void SetCursor(const Cursor&); + virtual void SetCustomCursorAllowed(bool); + /** + * If a cursor type is currently cached locally for this widget, clear the + * cached cursor to force an update on the next SetCursor call. + */ + void ClearCachedCursor() { + mCursor = {}; + mUpdateCursor = true; + } static nsIntSize CustomCursorSize(const Cursor&); @@ -954,6 +1046,8 @@ class nsIWidget : public nsISupports { * Get the window type of this widget. */ WindowType GetWindowType() const { return mWindowType; } + PopupType GetPopupType() const { return mPopupType; } + bool HasRemoteContent() const { return mHasRemoteContent; } /** * Set the transparency mode of the top-level window containing this widget. @@ -972,13 +1066,13 @@ class nsIWidget : public nsISupports { * all pixels are reset to 1. * Pixel RGB color values are already premultiplied with alpha channel values. */ - virtual void SetTransparencyMode(TransparencyMode aMode) = 0; + virtual void SetTransparencyMode(TransparencyMode aMode); /** * Get the transparency mode of the top-level window that contains this * widget. */ - virtual TransparencyMode GetTransparencyMode() = 0; + virtual TransparencyMode GetTransparencyMode(); // Cocoa and GTK round widget coordinates to the nearest global "display // pixel" integer value; see bug 892994. So we avoid fractional display pixel @@ -1000,7 +1094,7 @@ class nsIWidget : public nsISupports { * * Ignored on child widgets and on non-Mac platforms. */ - virtual void SetWindowShadowStyle(mozilla::WindowShadow aStyle) = 0; + virtual void SetWindowShadowStyle(mozilla::WindowShadow aStyle) {} /** * Set the opacity of the window. @@ -1043,7 +1137,7 @@ class nsIWidget : public nsISupports { * * Ignored on child widgets and on non-Mac platforms. */ - virtual void SetShowsToolbarButton(bool aShow) = 0; + virtual void SetShowsToolbarButton(bool aShow) {} /* * On macOS, this method determines whether we tell cocoa that the window @@ -1054,7 +1148,7 @@ class nsIWidget : public nsISupports { * * Ignored on child widgets and on non-Mac platforms. */ - virtual void SetSupportsNativeFullscreen(bool aSupportsNativeFullscreen) = 0; + virtual void SetSupportsNativeFullscreen(bool aSupportsNativeFullscreen) {} enum WindowAnimationType { eGenericWindowAnimation, @@ -1068,7 +1162,7 @@ class nsIWidget : public nsISupports { * * Ignored on child widgets and on non-Mac platforms. */ - virtual void SetWindowAnimationType(WindowAnimationType aType) = 0; + virtual void SetWindowAnimationType(WindowAnimationType aType) {} /** * Specifies whether the titlebar separator should be hidden. @@ -1082,9 +1176,8 @@ class nsIWidget : public nsISupports { /** * Hide window chrome (borders, buttons) for this widget. - * */ - virtual void HideWindowChrome(bool aShouldHide) = 0; + virtual void HideWindowChrome(bool aShouldHide) {} enum FullscreenTransitionStage { eBeforeFullscreenToggle, @@ -1101,7 +1194,9 @@ class nsIWidget : public nsISupports { * PerformFullscreenTransition() if any, and caller is responsible * for releasing that data. */ - virtual bool PrepareForFullscreenTransition(nsISupports** aData) = 0; + virtual bool PrepareForFullscreenTransition(nsISupports** aData) { + return false; + } /** * Performs fullscreen transition. This method returns immediately, @@ -1111,17 +1206,17 @@ class nsIWidget : public nsISupports { virtual void PerformFullscreenTransition(FullscreenTransitionStage aStage, uint16_t aDuration, nsISupports* aData, - nsIRunnable* aCallback) = 0; + nsIRunnable* aCallback); /** * Perform any actions needed after the fullscreen transition has ended. */ - virtual void CleanupFullscreenTransition() = 0; + virtual void CleanupFullscreenTransition() {} /** * Return the screen the widget is in, or null if we don't know. */ - virtual already_AddRefed<Screen> GetWidgetScreen() = 0; + virtual already_AddRefed<Screen> GetWidgetScreen(); /** * Put the toplevel window into or out of fullscreen mode. @@ -1130,7 +1225,8 @@ class nsIWidget : public nsISupports { * FullscreenChanged callback has been or will be called. If other * value is returned, the caller should continue the change itself. */ - virtual nsresult MakeFullScreen(bool aFullScreen) = 0; + virtual nsresult MakeFullScreen(bool aFullScreen); + void InfallibleMakeFullScreen(bool aFullScreen); /** * Same as MakeFullScreen, except that, on systems which natively @@ -1159,12 +1255,12 @@ class nsIWidget : public nsISupports { * * Note that this tries to create a renderer if it doesn't exist. */ - virtual WindowRenderer* GetWindowRenderer() = 0; + virtual WindowRenderer* GetWindowRenderer(); /** * Returns whether there's an existing window renderer. */ - virtual bool HasWindowRenderer() const = 0; + bool HasWindowRenderer() const { return !!mWindowRenderer; } /** * Called before each layer manager transaction to allow any preparation @@ -1172,7 +1268,7 @@ class nsIWidget : public nsISupports { * * Always called on the main thread. */ - virtual void PrepareWindowEffects() = 0; + virtual void PrepareWindowEffects() {} /** * Called when Gecko knows which themed widgets exist in this window. @@ -1185,8 +1281,7 @@ class nsIWidget : public nsISupports { * If called during painting, it will be called before we actually * paint anything. */ - virtual void UpdateThemeGeometries( - const nsTArray<ThemeGeometry>& aThemeGeometries) = 0; + virtual void UpdateThemeGeometries(const nsTArray<ThemeGeometry>&) {} /** * Informs the widget about the region of the window that is opaque. @@ -1207,19 +1302,145 @@ class nsIWidget : public nsISupports { * Should be called in response to a WidgetWheelEvent that has * mFlags.mCanTriggerSwipe set on it. */ - virtual void ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe) {} + virtual void ReportSwipeStarted(uint64_t aInputBlockId, bool aStartSwipe); + + // Returns true if |aPanInput| event was used for SwipeTracker, false + // otherwise. + bool MayStartSwipeForNonAPZ(const mozilla::PanGestureInput& aPanInput); + void TrackScrollEventAsSwipe(const mozilla::PanGestureInput& aSwipeStartEvent, + uint32_t aAllowedDirections, + uint64_t aInputBlockId); + struct SwipeInfo { + bool wantsSwipe; + uint32_t allowedDirections; + }; + SwipeInfo SendMayStartSwipe(const mozilla::PanGestureInput& aSwipeStartEvent); + // Returns a WidgetWheelEvent which needs to be handled by APZ regardless of + // whether |aPanInput| event was used for SwipeTracker or not. + mozilla::WidgetWheelEvent MayStartSwipeForAPZ( + const mozilla::PanGestureInput& aPanInput, + const mozilla::layers::APZEventResult& aApzResult); + + void NotifyWindowDestroyed(); + void NotifySizeMoveDone(); + using ByMoveToRect = nsIWidgetListener::ByMoveToRect; + void NotifyWindowMoved(int32_t aX, int32_t aY, + ByMoveToRect = ByMoveToRect::No); + // Should be called by derived implementations to notify on system color and + // theme changes. (Only one invocation per change is needed, not one + // invocation per change per window.) + void NotifyThemeChanged(mozilla::widget::ThemeChangeKind); + void NotifyAPZOfDPIChange(); + + // Return true if this is a simple widget (that is typically not worth + // accelerating) + bool IsSmallPopup() const; + + PopupLevel GetPopupLevel() { return mPopupLevel; } /** * Internal methods */ virtual void* GetNativeData(uint32_t aDataType) = 0; - virtual void FreeNativeData(void* data, uint32_t aDataType) = 0; //~~~ protected: + nsIWidget(); + virtual ~nsIWidget(); + explicit nsIWidget(BorderStyle); + + // These are methods for CompositorWidgetWrapper, and should only be + // accessed from that class. Derived widgets can choose which methods to + // implement, or none if supporting out-of-process compositing. + virtual bool PreRender(mozilla::widget::WidgetRenderingContext* aContext) { + return true; + } + virtual void PostRender(mozilla::widget::WidgetRenderingContext* aContext) {} + virtual RefPtr<mozilla::layers::NativeLayerRoot> GetNativeLayerRoot() { + return nullptr; + } + virtual already_AddRefed<DrawTarget> StartRemoteDrawing(); + virtual already_AddRefed<DrawTarget> StartRemoteDrawingInRegion( + const LayoutDeviceIntRegion& aInvalidRegion) { + return StartRemoteDrawing(); + } + virtual void EndRemoteDrawing() {} + virtual void EndRemoteDrawingInRegion( + DrawTarget* aDrawTarget, const LayoutDeviceIntRegion& aInvalidRegion) { + EndRemoteDrawing(); + } + virtual void CleanupRemoteDrawing() {} + virtual void CleanupWindowEffects() {} + virtual bool InitCompositor(mozilla::layers::Compositor* aCompositor) { + return true; + } + virtual uint32_t GetGLFrameBufferFormat(); + virtual bool CompositorInitiallyPaused() { return false; } + void AddToChildList(nsIWidget* aChild); void RemoveFromChildList(nsIWidget* aChild); void RemoveAllChildren(); + void ResolveIconName(const nsAString& aIconName, const nsAString& aIconSuffix, + nsIFile** aResult); + virtual void OnDestroy(); + void BaseCreate(nsIWidget* aParent, InitData* aInitData); + + virtual void ConfigureAPZCTreeManager(); + virtual void ConfigureAPZControllerThread(); + virtual already_AddRefed<GeckoContentController> + CreateRootContentController(); + + mozilla::dom::Document* GetDocument() const; + void EnsureTextEventDispatcher(); + // Notify the compositor that a device reset has occurred. + void OnRenderingDeviceReset(); + bool UseAPZ() const; + bool AllowWebRenderForThisWindow(); + + /** + * Dispatch the given MultiTouchInput through APZ to Gecko (if APZ is enabled) + * or directly to gecko (if APZ is not enabled). This function must only + * be called from the main thread, and if APZ is enabled, that must also be + * the APZ controller thread. + */ + void DispatchTouchInput(mozilla::MultiTouchInput& aInput); + + /** + * Dispatch the given PanGestureInput through APZ to Gecko (if APZ is enabled) + * or directly to gecko (if APZ is not enabled). This function must only + * be called from the main thread, and if APZ is enabled, that must also be + * the APZ controller thread. + */ + void DispatchPanGestureInput(mozilla::PanGestureInput& aInput); + void DispatchPinchGestureInput(mozilla::PinchGestureInput& aInput); + + static bool ConvertStatus(nsEventStatus aStatus) { + return aStatus == nsEventStatus_eConsumeNoDefault; + } + + protected: + // Returns whether compositing should use an external surface size. + virtual bool UseExternalCompositingSurface() const { return false; } + + /** + * Starts the OMTC compositor destruction sequence. + * + * When this function returns, the compositor should not be + * able to access the opengl context anymore. + * It is safe to call it several times if platform implementations + * require the compositor to be destroyed before ~nsIWidget is + * reached (This is the case with gtk2 for instance). + */ + virtual void DestroyCompositor(); + void DestroyLayerManager(); + void ReleaseContentController(); + void RevokeTransactionIdAllocator(); + + void FreeShutdownObserver(); + void FreeLocalesChangedObserver(); + + bool IsPIPWindow() const { return mIsPIPWindow; }; + public: /** * Set the widget's title. @@ -1237,7 +1458,7 @@ class nsIWidget : public nsISupports { * pass a resource: URL from which a platform-dependent * resource file name will be constructed */ - virtual void SetIcon(const nsAString& aIconSpec) = 0; + virtual void SetIcon(const nsAString& aIconSpec) {} /** * Return this widget's client origin in screen coordinates. @@ -1299,13 +1520,13 @@ class nsIWidget : public nsISupports { * Dispatches an event to APZ only. * No-op in the child process. */ - virtual void DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent) = 0; + virtual void DispatchEventToAPZOnly(mozilla::WidgetInputEvent* aEvent); /* * Dispatch a gecko event for this widget. * Returns true if it's consumed. Otherwise, false. */ - virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event) = 0; + virtual bool DispatchWindowEvent(mozilla::WidgetGUIEvent& event); // A structure that groups the statuses from APZ dispatch and content // dispatch. @@ -1322,7 +1543,7 @@ class nsIWidget : public nsISupports { * parent process synchronously. */ virtual ContentAndAPZEventStatus DispatchInputEvent( - mozilla::WidgetInputEvent* aEvent) = 0; + mozilla::WidgetInputEvent* aEvent); /** * Confirm an APZ-aware event target. This should be used when APZ will @@ -1330,22 +1551,22 @@ class nsIWidget : public nsISupports { */ virtual void SetConfirmedTargetAPZC( uint64_t aInputBlockId, - const nsTArray<ScrollableLayerGuid>& aTargets) const = 0; + const nsTArray<ScrollableLayerGuid>& aTargets) const; /** * Returns true if APZ is in use, false otherwise. */ - virtual bool AsyncPanZoomEnabled() const = 0; + virtual bool AsyncPanZoomEnabled() const; /** */ - virtual void SwipeFinished() = 0; + virtual void SwipeFinished(); /** * Enables the dropping of files to a widget. */ - virtual void EnableDragDrop(bool aEnable) = 0; - virtual nsresult AsyncEnableDragDrop(bool aEnable) = 0; + virtual void EnableDragDrop(bool aEnable) {} + nsresult AsyncEnableDragDrop(bool aEnable); /** * Classify the window for the window manager. Mostly for X11. @@ -1364,7 +1585,7 @@ class nsIWidget : public nsISupports { */ virtual void SetWindowClass(const nsAString& xulWinType, const nsAString& xulWinClass, - const nsAString& xulWinName) = 0; + const nsAString& xulWinName) {} virtual void SetIsEarlyBlankWindow(bool) {} @@ -1375,7 +1596,7 @@ class nsIWidget : public nsISupports { * @param aDoCapture true enables capture, false disables capture * */ - virtual void CaptureRollupEvents(bool aDoCapture) = 0; + virtual void CaptureRollupEvents(bool aDoCapture) {} /** * Bring this window to the user's attention. This is intended to be a more @@ -1388,13 +1609,15 @@ class nsIWidget : public nsISupports { * conventions. If set to -1, cycles indefinitely until * window is brought into the foreground. */ - [[nodiscard]] virtual nsresult GetAttention(int32_t aCycleCount) = 0; + [[nodiscard]] virtual nsresult GetAttention(int32_t aCycleCount) { + return NS_OK; + } /** * Ask whether there user input events pending. All input events are * included, including those not targeted at this nsIwidget instance. */ - virtual bool HasPendingInputEvent() = 0; + virtual bool HasPendingInputEvent(); /* * Determine whether the widget shows a resize widget. If it does, @@ -1405,7 +1628,12 @@ class nsIWidget : public nsISupports { * @param aResizerRect The resizer's rect in device pixels. * @return Whether a resize widget is shown. */ - virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect) = 0; + virtual bool ShowsResizeIndicator(LayoutDeviceIntRect* aResizerRect); + + // Dispatch an event that has already been routed through APZ. + nsEventStatus ProcessUntransformedAPZEvent( + mozilla::WidgetInputEvent* aEvent, + const mozilla::layers::APZEventResult& aApzResult); // TODO: Make this an enum class with MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS or // EnumSet class. @@ -1458,7 +1686,10 @@ class nsIWidget : public nsISupports { int32_t aNativeKeyboardLayout, int32_t aNativeKeyCode, uint32_t aModifierFlags, const nsAString& aCharacters, const nsAString& aUnmodifiedCharacters, - nsISynthesizedEventCallback* aCallback) = 0; + nsISynthesizedEventCallback* aCallback) { + mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); + return NS_ERROR_UNEXPECTED; + } /** * Utility method intended for testing. Dispatches native mouse events @@ -1487,7 +1718,10 @@ class nsIWidget : public nsISupports { virtual nsresult SynthesizeNativeMouseEvent( LayoutDeviceIntPoint aPoint, NativeMouseMessage aNativeMessage, mozilla::MouseButton aButton, nsIWidget::Modifiers aModifierFlags, - nsISynthesizedEventCallback* aCallback) = 0; + nsISynthesizedEventCallback* aCallback) { + mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); + return NS_ERROR_UNEXPECTED; + } /** * A shortcut to SynthesizeNativeMouseEvent, abstracting away the native @@ -1497,7 +1731,10 @@ class nsIWidget : public nsISupports { * have been dispatched. */ virtual nsresult SynthesizeNativeMouseMove( - LayoutDeviceIntPoint aPoint, nsISynthesizedEventCallback* aCallback) = 0; + LayoutDeviceIntPoint aPoint, nsISynthesizedEventCallback* aCallback) { + mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); + return NS_ERROR_UNEXPECTED; + } /** * Utility method intended for testing. Dispatching native mouse scroll @@ -1525,29 +1762,12 @@ class nsIWidget : public nsISupports { virtual nsresult SynthesizeNativeMouseScrollEvent( LayoutDeviceIntPoint aPoint, uint32_t aNativeMessage, double aDeltaX, double aDeltaY, double aDeltaZ, uint32_t aModifierFlags, - uint32_t aAdditionalFlags, nsISynthesizedEventCallback* aCallback) = 0; + uint32_t aAdditionalFlags, nsISynthesizedEventCallback* aCallback) { + mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); + return NS_ERROR_UNEXPECTED; + } /* - * TouchPointerState states for SynthesizeNativeTouchPoint. Match - * touch states in nsIDOMWindowUtils.idl. - */ - enum TouchPointerState { - // The pointer is in a hover state above the digitizer - TOUCH_HOVER = (1 << 0), - // The pointer is in contact with the digitizer - TOUCH_CONTACT = (1 << 1), - // The pointer has been removed from the digitizer detection area - TOUCH_REMOVE = (1 << 2), - // The pointer has been canceled. Will cancel any pending os level - // gestures that would triggered as a result of completion of the - // input sequence. This may not cancel moz platform related events - // that might get tirggered by input already delivered. - TOUCH_CANCEL = (1 << 3), - - // ALL_BITS used for validity checking during IPC serialization - ALL_BITS = (1 << 4) - 1 - }; - /* * TouchpadGesturePhase states for SynthesizeNativeTouchPadPinch and * SynthesizeNativeTouchpadPan. Match phase states in nsIDOMWindowUtils.idl. */ @@ -1574,13 +1794,19 @@ class nsIWidget : public nsISupports { virtual nsresult SynthesizeNativeTouchPoint( uint32_t aPointerId, TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, double aPointerPressure, - uint32_t aPointerOrientation, nsISynthesizedEventCallback* aCallback) = 0; + uint32_t aPointerOrientation, nsISynthesizedEventCallback* aCallback) { + mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(aCallback); + return NS_ERROR_UNEXPECTED; + } /* * See nsIDOMWindowUtils.sendNativeTouchpadPinch(). */ virtual nsresult SynthesizeNativeTouchPadPinch( TouchpadGesturePhase aEventPhase, float aScale, - LayoutDeviceIntPoint aPoint, int32_t aModifierFlags) = 0; + LayoutDeviceIntPoint aPoint, int32_t aModifierFlags) { + MOZ_CRASH("SynthesizeNativeTouchPadPinch not implemented on this platform"); + return NS_ERROR_UNEXPECTED; + } /* * Helper for simulating a simple tap event with one touch point. When @@ -1598,14 +1824,21 @@ class nsIWidget : public nsISupports { uint32_t aPointerId, TouchPointerState aPointerState, LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation, int32_t aTiltX, int32_t aTiltY, int32_t aButton, - nsISynthesizedEventCallback* aCallback) = 0; + nsISynthesizedEventCallback* aCallback) { + MOZ_CRASH("SynthesizeNativePenInput not implemented on this platform"); + return NS_ERROR_UNEXPECTED; + } /* * Send a native event as if the user double tapped the touchpad with two * fingers. */ virtual nsresult SynthesizeNativeTouchpadDoubleTap( - LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) = 0; + LayoutDeviceIntPoint aPoint, uint32_t aModifierFlags) { + MOZ_CRASH( + "SynthesizeNativeTouchpadDoubleTap not implemented on this platform"); + return NS_ERROR_UNEXPECTED; + } /* * See nsIDOMWindowUtils.sendNativeTouchpadPan(). @@ -1613,10 +1846,12 @@ class nsIWidget : public nsISupports { virtual nsresult SynthesizeNativeTouchpadPan( TouchpadGesturePhase aEventPhase, LayoutDeviceIntPoint aPoint, double aDeltaX, double aDeltaY, int32_t aModifierFlags, - nsISynthesizedEventCallback* aCallback) = 0; + nsISynthesizedEventCallback* aCallback) { + MOZ_CRASH("SynthesizeNativeTouchpadPan not implemented on this platform"); + return NS_ERROR_UNEXPECTED; + } - virtual void StartAsyncScrollbarDrag( - const AsyncDragMetrics& aDragMetrics) = 0; + virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics); /** * Notify APZ to start autoscrolling. @@ -1625,21 +1860,75 @@ class nsIWidget : public nsISupports { * @return true if APZ has been successfully notified */ virtual bool StartAsyncAutoscroll(const ScreenPoint& aAnchorLocation, - const ScrollableLayerGuid& aGuid) = 0; + const ScrollableLayerGuid& aGuid); /** * Notify APZ to stop autoscrolling. * @param aGuid identifies the scroll frame which is being autoscrolled. */ - virtual void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid) = 0; + virtual void StopAsyncAutoscroll(const ScrollableLayerGuid& aGuid); + + virtual LayersId GetRootLayerTreeId(); + + /** + * Use this when GetLayerManager() returns a BasicLayerManager + * (nsIWidget::GetLayerManager() does). This sets up the widget's + * layer manager to temporarily render into aTarget. + * + * |aNaturalWidgetBounds| is the un-rotated bounds of |aWidget|. + * |aRotation| is the "virtual rotation" to apply when rendering to + * the target. When |aRotation| is ROTATION_0, + * |aNaturalWidgetBounds| is not used. + */ + class AutoLayerManagerSetup { + public: + AutoLayerManagerSetup(nsIWidget* aWidget, gfxContext* aTarget); + ~AutoLayerManagerSetup(); - virtual LayersId GetRootLayerTreeId() = 0; + private: + nsIWidget* mWidget; + mozilla::FallbackRenderer* mRenderer = nullptr; + }; + friend class AutoLayerManagerSetup; + + virtual bool ShouldUseOffMainThreadCompositing(); + + static nsIRollupListener* GetActiveRollupListener(); + + void Shutdown(); + void QuitIME(); + + // These functions should be called at the start and end of a "live" widget + // resize (i.e. when the window contents are repainting during the resize, + // such as when the user drags a window border). It will suppress the + // displayport during the live resize to avoid unneccessary overpainting. + void NotifyLiveResizeStarted(); + void NotifyLiveResizeStopped(); // If this widget supports out-of-process compositing, it can override // this method to provide additional information to the compositor. virtual void GetCompositorWidgetInitData( mozilla::widget::CompositorWidgetInitData* aInitData) {} + // A remote compositor session tied to this window has been lost and IPC + // messages will no longer work. The widget must clean up any lingering + // resources and possibly schedule another paint. + // + // A reference to the session object is held until this function has + // returned. Callers should hold a reference to the widget, since this + // function could deallocate the widget if it is unparented. + virtual void NotifyCompositorSessionLost( + mozilla::layers::CompositorSession* aSession); + + already_AddRefed<mozilla::CompositorVsyncDispatcher> + GetCompositorVsyncDispatcher(); + virtual void CreateCompositorVsyncDispatcher(); + virtual void CreateCompositor(); + virtual void CreateCompositor(int aWidth, int aHeight); + virtual void SetCompositorWidgetDelegate(CompositorWidgetDelegate*) {} + + WindowRenderer* CreateFallbackRenderer(); + /** * Setter/Getter of the system font setting for testing. */ @@ -1724,7 +2013,9 @@ class nsIWidget : public nsISupports { * in the native menu bar. Within that, the first item (index 0) is a * submenu, and we want to activate the 5th item within that submenu. */ - virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) = 0; + virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { + return NS_ERROR_NOT_IMPLEMENTED; + } /** * This is used for native menu system testing. @@ -1742,7 +2033,9 @@ class nsIWidget : public nsISupports { * If this is called with an empty string it forces a full reload of the * menu system. */ - virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) = 0; + virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) { + return NS_ERROR_NOT_IMPLEMENTED; + } /** * This is used for testing macOS service menu code. @@ -1760,7 +2053,7 @@ class nsIWidget : public nsISupports { * @return If the notification is mouse button event and it's consumed by * IME, this returns NS_SUCCESS_EVENT_CONSUMED. */ - virtual nsresult NotifyIME(const IMENotification& aIMENotification) = 0; + nsresult NotifyIME(const IMENotification& aIMENotification); /** * MaybeDispatchInitialFocusEvent will dispatch a focus event after creation @@ -1787,7 +2080,13 @@ class nsIWidget : public nsISupports { * NS_RAW_NATIVE_IME_CONTEXT, the result is unique even if in a remote * process. */ - virtual NativeIMEContext GetNativeIMEContext() = 0; + virtual NativeIMEContext GetNativeIMEContext(); + + /** + * GetPseudoIMEContext() returns pseudo IME context when TextEventDispatcher + * has non-native input transaction. Otherwise, returns nullptr. + */ + void* GetPseudoIMEContext(); /* * Given a WidgetKeyboardEvent, this method synthesizes a corresponding @@ -1796,7 +2095,9 @@ class nsIWidget : public nsISupports { * event). */ [[nodiscard]] virtual nsresult AttachNativeKeyEvent( - mozilla::WidgetKeyboardEvent& aEvent) = 0; + mozilla::WidgetKeyboardEvent& aEvent) { + return NS_ERROR_NOT_IMPLEMENTED; + } /** * Retrieve edit commands when the key combination of aEvent is used @@ -1815,12 +2116,18 @@ class nsIWidget : public nsISupports { */ const IMENotificationRequests& IMENotificationRequestsRef(); + bool ComputeShouldAccelerate(); + virtual bool WidgetTypeSupportsAcceleration() { return true; } + virtual bool WidgetTypeSupportsNativeCompositing() { return true; } + /* * Call this method when a dialog is opened which has a default button. * The button's rectangle should be supplied in aButtonRect. */ [[nodiscard]] virtual nsresult OnDefaultButtonLoaded( - const LayoutDeviceIntRect& aButtonRect) = 0; + const LayoutDeviceIntRect& aButtonRect) { + return NS_ERROR_NOT_IMPLEMENTED; + } /** * Return true if this process shouldn't use platform widgets, and @@ -1834,6 +2141,10 @@ class nsIWidget : public nsISupports { static already_AddRefed<nsIWidget> CreateChildWindow(); + virtual already_AddRefed<nsIWidget> AllocateChildPopupWidget() { + return CreateChildWindow(); + } + /** * Allocate and return a "puppet widget" that doesn't directly * correlate to a platform widget; platform events and data must @@ -1888,14 +2199,19 @@ class nsIWidget : public nsISupports { * * @param aConstraints: the size constraints in device pixels */ - virtual void SetSizeConstraints(const SizeConstraints& aConstraints) = 0; + virtual void SetSizeConstraints(const SizeConstraints& aConstraints); + +#ifdef ACCESSIBILITY + // Get the accessible for the window. + mozilla::a11y::LocalAccessible* GetRootAccessible(); +#endif /** * Return the size constraints currently observed by the widget. * * @return the constraints in device pixels */ - virtual const SizeConstraints GetSizeConstraints() = 0; + virtual const SizeConstraints GetSizeConstraints(); /** * Apply the current size constraints to the given size. @@ -1903,7 +2219,11 @@ class nsIWidget : public nsISupports { * @param aWidth width to constrain * @param aHeight height to constrain */ - virtual void ConstrainSize(int32_t* aWidth, int32_t* aHeight) = 0; + virtual void ConstrainSize(int32_t* aWidth, int32_t* aHeight) { + SizeConstraints c = GetSizeConstraints(); + *aWidth = std::clamp(*aWidth, c.mMinSize.width, c.mMaxSize.width); + *aHeight = std::clamp(*aHeight, c.mMinSize.height, c.mMaxSize.height); + } /** * If this is owned by a BrowserChild, return that. Otherwise return @@ -1914,13 +2234,13 @@ class nsIWidget : public nsISupports { /* * Returns the layersId for this widget. */ - virtual LayersId GetLayersId() const = 0; + virtual LayersId GetLayersId() const; /** * If this isn't directly compositing to its window surface, * return the compositor which is doing that on our behalf. */ - virtual CompositorBridgeChild* GetRemoteRenderer() { return nullptr; } + virtual CompositorBridgeChild* GetRemoteRenderer(); /** * If there is a remote renderer, pause or resume it. @@ -1930,13 +2250,17 @@ class nsIWidget : public nsISupports { /** * Clear WebRender resources */ - virtual void ClearCachedWebrenderResources() {} + virtual void ClearCachedWebrenderResources(); /** * Request fast snapshot at RenderCompositor of WebRender. * Since readback of Windows DirectComposition is very slow. */ - virtual bool SetNeedFastSnaphot() { return false; } + virtual bool SetNeedFastSnaphot(); + + /** Notify the widget that this window is being used with OMTC. */ + virtual void WindowUsesOMTC() {} + virtual void RegisterTouchWindow() {} /** * If this widget has its own vsync dispatcher, return it, otherwise return @@ -1953,21 +2277,20 @@ class nsIWidget : public nsISupports { virtual void UpdateZoomConstraints( const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, - const mozilla::Maybe<ZoomConstraints>& aConstraints) {}; + const mozilla::Maybe<ZoomConstraints>& aConstraints); /** * GetTextEventDispatcher() returns TextEventDispatcher belonging to the * widget. Note that this never returns nullptr. */ - virtual TextEventDispatcher* GetTextEventDispatcher() = 0; + TextEventDispatcher* GetTextEventDispatcher(); /** * GetNativeTextEventDispatcherListener() returns a * TextEventDispatcherListener instance which is used when the widget * instance handles native IME and/or keyboard events. */ - virtual TextEventDispatcherListener* - GetNativeTextEventDispatcherListener() = 0; + virtual TextEventDispatcherListener* GetNativeTextEventDispatcherListener(); /** * Trigger an animation to zoom to the given |aRect|. @@ -1976,7 +2299,7 @@ class nsIWidget : public nsISupports { */ virtual void ZoomToRect(const uint32_t& aPresShellId, const ScrollableLayerGuid::ViewID& aViewId, - const CSSRect& aRect, const uint32_t& aFlags) = 0; + const CSSRect& aRect, const uint32_t& aFlags); /** * LookUpDictionary shows the dictionary for the word around current point. @@ -2000,7 +2323,7 @@ class nsIWidget : public nsISupports { * composited scroll offset and zoom */ virtual void NotifyCompositorScrollUpdate( - const mozilla::layers::CompositorScrollUpdate& aUpdate) = 0; + const mozilla::layers::CompositorScrollUpdate& aUpdate) {} #if defined(MOZ_WIDGET_ANDROID) /** @@ -2009,7 +2332,7 @@ class nsIWidget : public nsISupports { * * @param aMessage message being sent to Android UI thread. */ - virtual void RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage) = 0; + virtual void RecvToolbarAnimatorMessageFromCompositor(int32_t aMessage) {} /** * RecvScreenPixels Buffer containing the pixel from the frame buffer. Used @@ -2019,8 +2342,7 @@ class nsIWidget : public nsISupports { * @param aSize size of the buffer in screen pixels. */ virtual void RecvScreenPixels(mozilla::ipc::Shmem&& aMem, - const ScreenIntSize& aSize, - bool aNeedsYFlip) = 0; + const ScreenIntSize& aSize, bool aNeedsYFlip) {} virtual void UpdateDynamicToolbarMaxHeight(mozilla::ScreenIntCoord aHeight) {} virtual mozilla::ScreenIntCoord GetDynamicToolbarMaxHeight() const { @@ -2028,6 +2350,10 @@ class nsIWidget : public nsISupports { } #endif + void EnsureLocalesChangedObserver(); + virtual void LocalesChanged() {} + virtual void NotifyOcclusionState(mozilla::widget::OcclusionState) {} + static already_AddRefed<nsIBidiKeyboard> CreateBidiKeyboard(); // If this is a popup, returns the associated frame if any. @@ -2086,6 +2412,100 @@ class nsIWidget : public nsISupports { bool mOnDestroyCalled = false; WindowType mWindowType = WindowType::TopLevel; WidgetType mWidgetType = WidgetType::Native; + + nsIWidgetListener* mWidgetListener = nullptr; + nsIWidgetListener* mAttachedWidgetListener = nullptr; + nsIWidgetListener* mPreviouslyAttachedWidgetListener = nullptr; + RefPtr<WindowRenderer> mWindowRenderer; + RefPtr<CompositorSession> mCompositorSession; + RefPtr<CompositorBridgeChild> mCompositorBridgeChild; + + mozilla::UniquePtr<mozilla::Mutex> mCompositorVsyncDispatcherLock; + RefPtr<mozilla::CompositorVsyncDispatcher> mCompositorVsyncDispatcher; + + RefPtr<IAPZCTreeManager> mAPZC; + RefPtr<GeckoContentController> mRootContentController; + RefPtr<APZEventState> mAPZEventState; + RefPtr<mozilla::widget::WidgetShutdownObserver> mShutdownObserver; + RefPtr<mozilla::widget::LocalesChangedObserver> mLocalesChangedObserver; + RefPtr<TextEventDispatcher> mTextEventDispatcher; + RefPtr<mozilla::SwipeTracker> mSwipeTracker; + mozilla::UniquePtr<mozilla::SwipeEventQueue> mSwipeEventQueue; + Cursor mCursor; + bool mCustomCursorAllowed = true; + BorderStyle mBorderStyle; + LayoutDeviceIntRect mBounds; + bool mIsTiled; + PopupLevel mPopupLevel; + PopupType mPopupType; + SizeConstraints mSizeConstraints; + bool mHasRemoteContent; + + struct FullscreenSavedState { + DesktopRect windowRect; + DesktopRect screenRect; + }; + mozilla::Maybe<FullscreenSavedState> mSavedBounds; + + bool mUpdateCursor; + bool mUseAttachedEvents; + bool mIMEHasFocus; + bool mIMEHasQuit; + // if the window is fully occluded (rendering may be paused in response) + bool mIsFullyOccluded; + bool mNeedFastSnaphot; + // This flag is only used when APZ is off. It indicates that the current pan + // gesture was processed as a swipe. Sometimes the swipe animation can finish + // before momentum events of the pan gesture have stopped firing, so this + // flag tells us that we shouldn't allow the remaining events to cause + // scrolling. It is reset to false once a new gesture starts (as indicated by + // a PANGESTURE_(MAY)START event). + bool mCurrentPanGestureBelongsToSwipe; + + // It's PictureInPicture window. + bool mIsPIPWindow : 1; + + struct InitialZoomConstraints { + InitialZoomConstraints(const uint32_t& aPresShellID, + const ScrollableLayerGuid::ViewID& aViewID, + const ZoomConstraints& aConstraints) + : mPresShellID(aPresShellID), + mViewID(aViewID), + mConstraints(aConstraints) {} + + uint32_t mPresShellID; + ScrollableLayerGuid::ViewID mViewID; + ZoomConstraints mConstraints; + }; + + mozilla::Maybe<InitialZoomConstraints> mInitialZoomConstraints; + + // This points to the resize listeners who have been notified that a live + // resize is in progress. This should always be empty when a live-resize is + // not in progress. + nsTArray<RefPtr<mozilla::LiveResizeListener>> mLiveResizeListeners; + +#ifdef DEBUG + protected: + static void debug_DumpInvalidate(FILE* aFileOut, nsIWidget* aWidget, + const LayoutDeviceIntRect* aRect, + const char* aWidgetName, int32_t aWindowID); + + static void debug_DumpEvent(FILE* aFileOut, nsIWidget* aWidget, + mozilla::WidgetGUIEvent* aGuiEvent, + const char* aWidgetName, int32_t aWindowID); + + static void debug_DumpPaintEvent(FILE* aFileOut, nsIWidget* aWidget, + const nsIntRegion& aPaintEvent, + const char* aWidgetName, int32_t aWindowID); + + static bool debug_GetCachedBoolPref(const char* aPrefName); +#endif + + private: + already_AddRefed<mozilla::layers::WebRenderLayerManager> + CreateCompositorSession(int aWidth, int aHeight, + mozilla::layers::CompositorOptions* aOptionsOut); }; #endif // nsIWidget_h__ diff --git a/widget/tests/gtest/MockWinWidget.cpp b/widget/tests/gtest/MockWinWidget.cpp @@ -7,7 +7,7 @@ #include "mozilla/gfx/Logging.h" -NS_IMPL_ISUPPORTS_INHERITED0(MockWinWidget, nsBaseWidget) +NS_IMPL_ISUPPORTS_INHERITED0(MockWinWidget, nsIWidget) // static RefPtr<MockWinWidget> MockWinWidget::Create(DWORD aStyle, DWORD aExStyle, diff --git a/widget/tests/gtest/MockWinWidget.h b/widget/tests/gtest/MockWinWidget.h @@ -9,10 +9,11 @@ #include <windows.h> -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "Units.h" +#include "mozilla/widget/WindowOcclusionState.h" -class MockWinWidget : public nsBaseWidget { +class MockWinWidget : public nsIWidget { public: static RefPtr<MockWinWidget> Create(DWORD aStyle, DWORD aExStyle, const LayoutDeviceIntRect& aRect); diff --git a/widget/uikit/nsAppShell.mm b/widget/uikit/nsAppShell.mm @@ -8,6 +8,7 @@ #import <UIKit/UIWindow.h> #include "mozilla/Components.h" +#include "nsIObserverService.h" #include "gfxPlatform.h" #include "nsAppShell.h" #include "nsCOMPtr.h" diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h @@ -10,7 +10,7 @@ #include <CoreFoundation/CoreFoundation.h> #include "mozilla/widget/IOSView.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "gfxPoint.h" #include "nsTArray.h" @@ -33,7 +33,7 @@ class TextInputHandler; #define NS_WINDOW_IID \ {0x5e6fd559, 0xb3f9, 0x40c9, {0x92, 0xd1, 0xef, 0x80, 0xb4, 0xf9, 0x69, 0xe9}} -class nsWindow final : public nsBaseWidget { +class nsWindow final : public nsIWidget { public: nsWindow(); diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm @@ -745,7 +745,7 @@ class nsAutoRetainUIKitObject { @end -NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsBaseWidget, nsWindow); +NS_IMPL_ISUPPORTS_INHERITED(nsWindow, nsIWidget, nsWindow); nsWindow::nsWindow() : mNativeView(nullptr), @@ -792,7 +792,7 @@ nsresult nsWindow::Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, mWindowType = WindowType::TopLevel; mBorderStyle = BorderStyle::Default; - nsBaseWidget::BaseCreate(aParent, aInitData); + nsIWidget::BaseCreate(aParent, aInitData); NS_ASSERTION(IsTopLevel() || parent, "non top level window doesn't have a parent!"); @@ -839,13 +839,13 @@ void nsWindow::Destroy() { nsCOMPtr<nsIWidget> kungFuDeathGrip(this); - nsBaseWidget::Destroy(); + nsIWidget::Destroy(); // ReportDestroyEvent(); TearDownView(); - nsBaseWidget::OnDestroy(); + nsIWidget::OnDestroy(); } void nsWindow::Show(bool aState) { @@ -932,7 +932,7 @@ void nsWindow::SetSizeMode(nsSizeMode aMode) { mSizeMode = static_cast<nsSizeMode>(aMode); if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) { // Resize to fill screen - nsBaseWidget::InfallibleMakeFullScreen(true); + nsIWidget::InfallibleMakeFullScreen(true); } ReportSizeModeEvent(aMode); } diff --git a/widget/windows/CompositorWidgetChild.cpp b/widget/windows/CompositorWidgetChild.cpp @@ -8,7 +8,7 @@ #include "mozilla/gfx/Logging.h" #include "mozilla/widget/CompositorWidgetVsyncObserver.h" #include "mozilla/widget/PlatformWidgetTypes.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "VsyncDispatcher.h" #include "gfxPlatform.h" #include "RemoteBackbuffer.h" diff --git a/widget/windows/DirectManipulationOwner.cpp b/widget/windows/DirectManipulationOwner.cpp @@ -7,6 +7,7 @@ #include "nsWindow.h" #include "WinModifierKeyState.h" #include "InputData.h" +#include "UnitTransforms.h" #include "mozilla/StaticPrefs_apz.h" #include "mozilla/SwipeTracker.h" #include "mozilla/TimeStamp.h" diff --git a/widget/windows/WinWindowOcclusionTracker.cpp b/widget/windows/WinWindowOcclusionTracker.cpp @@ -22,7 +22,7 @@ #include "mozilla/Logging.h" #include "mozilla/StaticPrefs_widget.h" #include "mozilla/StaticPtr.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsWindow.h" #include "transport/runnable_utils.h" #include "WinEventObserver.h" @@ -439,7 +439,7 @@ bool WinWindowOcclusionTracker::IsInWinWindowOcclusionThread() { sTracker->mThread->thread_id() == PlatformThread::CurrentId(); } -void WinWindowOcclusionTracker::Enable(nsBaseWidget* aWindow, HWND aHwnd) { +void WinWindowOcclusionTracker::Enable(nsIWidget* aWindow, HWND aHwnd) { MOZ_ASSERT(NS_IsMainThread()); LOG(LogLevel::Info, "WinWindowOcclusionTracker::Enable() aWindow %p aHwnd %p", aWindow, aHwnd); @@ -459,7 +459,7 @@ void WinWindowOcclusionTracker::Enable(nsBaseWidget* aWindow, HWND aHwnd) { mSerializedTaskDispatcher->PostTaskToCalculator(runnable.forget()); } -void WinWindowOcclusionTracker::Disable(nsBaseWidget* aWindow, HWND aHwnd) { +void WinWindowOcclusionTracker::Disable(nsIWidget* aWindow, HWND aHwnd) { MOZ_ASSERT(NS_IsMainThread()); LOG(LogLevel::Info, "WinWindowOcclusionTracker::Disable() aWindow %p aHwnd %p", aWindow, @@ -479,7 +479,7 @@ void WinWindowOcclusionTracker::Disable(nsBaseWidget* aWindow, HWND aHwnd) { mSerializedTaskDispatcher->PostTaskToCalculator(runnable.forget()); } -void WinWindowOcclusionTracker::OnWindowVisibilityChanged(nsBaseWidget* aWindow, +void WinWindowOcclusionTracker::OnWindowVisibilityChanged(nsIWidget* aWindow, bool aVisible) { MOZ_ASSERT(NS_IsMainThread()); LOG(LogLevel::Info, @@ -695,7 +695,7 @@ void WinWindowOcclusionTracker::UpdateOcclusionState( if (!widget) { continue; } - auto* baseWidget = static_cast<nsBaseWidget*>(widget.get()); + auto* baseWidget = static_cast<nsIWidget*>(widget.get()); baseWidget->NotifyOcclusionState(occlState); if (baseWidget->SizeMode() != nsSizeMode_Minimized) { mNumVisibleRootWindows++; @@ -769,7 +769,7 @@ void WinWindowOcclusionTracker::MarkNonIconicWindowsOccluded() { if (!widget) { continue; } - auto* baseWidget = static_cast<nsBaseWidget*>(widget.get()); + auto* baseWidget = static_cast<nsIWidget*>(widget.get()); auto state = (baseWidget->SizeMode() == nsSizeMode_Minimized) ? OcclusionState::HIDDEN : OcclusionState::OCCLUDED; diff --git a/widget/windows/WinWindowOcclusionTracker.h b/widget/windows/WinWindowOcclusionTracker.h @@ -20,7 +20,7 @@ #include "Units.h" #include "nsThreadUtils.h" -class nsBaseWidget; +class nsIWidget; struct IVirtualDesktopManager; class WinWindowOcclusionTrackerTest; class WinWindowOcclusionTrackerInteractiveTest; @@ -60,14 +60,14 @@ class WinWindowOcclusionTracker final { // Enables notifying to widget via NotifyOcclusionState() when the occlusion // state has been computed. - void Enable(nsBaseWidget* aWindow, HWND aHwnd); + void Enable(nsIWidget* aWindow, HWND aHwnd); // Disables notifying to widget via NotifyOcclusionState() when the occlusion // state has been computed. - void Disable(nsBaseWidget* aWindow, HWND aHwnd); + void Disable(nsIWidget* aWindow, HWND aHwnd); // Called when widget's visibility is changed - void OnWindowVisibilityChanged(nsBaseWidget* aWindow, bool aVisible); + void OnWindowVisibilityChanged(nsIWidget* aWindow, bool aVisible); SerializedTaskDispatcher* GetSerializedTaskDispatcher() { return mSerializedTaskDispatcher; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp @@ -817,7 +817,7 @@ static bool IsCloaked(HWND hwnd) { **************************************************************/ nsWindow::nsWindow() - : nsBaseWidget(BorderStyle::Default), + : nsIWidget(BorderStyle::Default), mFrameState(std::in_place, this), mPIPWindow(false), mMicaBackdrop(false), @@ -1943,7 +1943,7 @@ void nsWindow::SetSizeConstraints(const SizeConstraints& aConstraints) { mSizeConstraintsScale = GetDefaultScale().scale; - nsBaseWidget::SetSizeConstraints(c); + nsIWidget::SetSizeConstraints(c); } const SizeConstraints nsWindow::GetSizeConstraints() { @@ -3505,7 +3505,6 @@ nsresult nsWindow::MakeFullScreen(bool aFullScreen) { * SECTION: Native data storage * * nsIWidget::GetNativeData - * nsIWidget::FreeNativeData * * Set or clear native data based on a constant. * @@ -3535,18 +3534,6 @@ void* nsWindow::GetNativeData(uint32_t aDataType) { return nullptr; } -// 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: - break; - default: - break; - } -} - /************************************************************** * * SECTION: nsIWidget::SetTitle @@ -3845,9 +3832,7 @@ WindowRenderer* nsWindow::GetWindowRenderer() { return mWindowRenderer; } - if (!mLocalesChangedObserver) { - mLocalesChangedObserver = new LocalesChangedObserver(this); - } + EnsureLocalesChangedObserver(); // Try OMTC first. if (!mWindowRenderer && ShouldUseOffMainThreadCompositing()) { @@ -3884,7 +3869,7 @@ WindowRenderer* nsWindow::GetWindowRenderer() { mMaxTextureSize = knowsCompositor->GetMaxTextureSize(); c.mMaxSize.width = std::min(c.mMaxSize.width, mMaxTextureSize); c.mMaxSize.height = std::min(c.mMaxSize.height, mMaxTextureSize); - nsBaseWidget::SetSizeConstraints(c); + nsIWidget::SetSizeConstraints(c); } } @@ -3893,7 +3878,7 @@ WindowRenderer* nsWindow::GetWindowRenderer() { /************************************************************** * - * SECTION: nsBaseWidget::SetCompositorWidgetDelegate + * SECTION: nsIWidget::SetCompositorWidgetDelegate * * Called to connect the nsWindow to the delegate providing * platform compositing API access. @@ -6939,10 +6924,10 @@ void nsWindow::OnDestroy() { if (sCurrentWindow == this) sCurrentWindow = nullptr; // Disconnects us from our parent, will call our GetParent(). - nsBaseWidget::Destroy(); + nsIWidget::Destroy(); // Release references to children, device context, toolkit, and app shell. - nsBaseWidget::OnDestroy(); + nsIWidget::OnDestroy(); // We have to destroy the native drag target before we null out our window // pointer. @@ -6950,7 +6935,7 @@ void nsWindow::OnDestroy() { // If we're going away and for some reason we're still the rollup widget, // rollup and turn off capture. - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); nsCOMPtr<nsIWidget> rollupWidget; if (rollupListener) { rollupWidget = rollupListener->GetRollupWidget(); @@ -7025,7 +7010,7 @@ bool nsWindow::ShouldUseOffMainThreadCompositing() { if (mWindowType == WindowType::Popup && mPopupType == PopupType::Tooltip) { return false; } - return nsBaseWidget::ShouldUseOffMainThreadCompositing(); + return nsIWidget::ShouldUseOffMainThreadCompositing(); } void nsWindow::WindowUsesOMTC() { @@ -7634,7 +7619,7 @@ bool nsWindow::DealWithPopups(HWND aWnd, UINT aMessage, WPARAM aWParam, } } - nsIRollupListener* rollupListener = nsBaseWidget::GetActiveRollupListener(); + nsIRollupListener* rollupListener = nsIWidget::GetActiveRollupListener(); NS_ENSURE_TRUE(rollupListener, false); nsCOMPtr<nsIWidget> popup = rollupListener->GetRollupWidget(); @@ -8479,11 +8464,11 @@ void nsWindow::ChangedDPI() { } static Result<POINTER_FLAGS, nsresult> PointerStateToFlag( - nsWindow::TouchPointerState aPointerState, bool isUpdate) { - bool hover = aPointerState & nsWindow::TOUCH_HOVER; - bool contact = aPointerState & nsWindow::TOUCH_CONTACT; - bool remove = aPointerState & nsWindow::TOUCH_REMOVE; - bool cancel = aPointerState & nsWindow::TOUCH_CANCEL; + TouchPointerState aPointerState, bool isUpdate) { + bool hover = aPointerState & TOUCH_HOVER; + bool contact = aPointerState & TOUCH_CONTACT; + bool remove = aPointerState & TOUCH_REMOVE; + bool cancel = aPointerState & TOUCH_CANCEL; POINTER_FLAGS flags; if (isUpdate) { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h @@ -11,7 +11,7 @@ */ #include "mozilla/RefPtr.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "CompositorWidget.h" #include "mozilla/EventForwards.h" #include "nsClassHashtable.h" @@ -64,6 +64,7 @@ class imgIContainer; namespace mozilla { class WidgetMouseEvent; +class InputData; namespace widget { class NativeKey; class InProcessWinCompositorWidget; @@ -133,14 +134,14 @@ void SetWindowStyles(HWND, const WindowStyles&); } // namespace mozilla::widget -class nsWindow final : public nsBaseWidget { +class nsWindow final : public nsIWidget { public: using Styles = mozilla::widget::WindowStyles; using WindowHook = mozilla::widget::WindowHook; using IMEContext = mozilla::widget::IMEContext; using WidgetEventTime = mozilla::WidgetEventTime; - NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsBaseWidget) + NS_INLINE_DECL_REFCOUNTING_INHERITED(nsWindow, nsIWidget) nsWindow(); @@ -187,7 +188,7 @@ class nsWindow final : public nsBaseWidget { nsWindow* GetParentWindowBase(bool aIncludeOwner); // nsIWidget interface - using nsBaseWidget::Create; // for Create signature not overridden here + using nsIWidget::Create; // for Create signature not overridden here [[nodiscard]] nsresult Create(nsIWidget* aParent, const LayoutDeviceIntRect& aRect, InitData* aInitData = nullptr) override; @@ -241,7 +242,6 @@ class nsWindow final : public nsBaseWidget { bool aIncludeChildren = false); void Invalidate(const LayoutDeviceIntRect& aRect) override; void* GetNativeData(uint32_t aDataType) override; - void FreeNativeData(void* data, uint32_t aDataType) override; nsresult SetTitle(const nsAString& aTitle) override; void SetIcon(const nsAString& aIconSpec) override; LayoutDeviceIntPoint WidgetToScreenOffset() override; diff --git a/widget/windows/nsWindowDefs.h b/widget/windows/nsWindowDefs.h @@ -11,7 +11,7 @@ */ #include "mozilla/widget/WinMessages.h" -#include "nsBaseWidget.h" +#include "nsIWidget.h" #include "nsdefs.h" #include "resource.h" diff --git a/widget/windows/nsWindowGfx.cpp b/widget/windows/nsWindowGfx.cpp @@ -395,7 +395,7 @@ void nsWindow::MaybeEnableWindowOcclusion(bool aEnable) { // call for RequesetFxrOutput as soon as the compositor for this widget is // available. void nsWindow::CreateCompositor() { - nsBaseWidget::CreateCompositor(); + nsIWidget::CreateCompositor(); MaybeEnableWindowOcclusion(/* aEnable */ true); @@ -407,7 +407,7 @@ void nsWindow::CreateCompositor() { void nsWindow::DestroyCompositor() { MaybeEnableWindowOcclusion(/* aEnable */ false); - nsBaseWidget::DestroyCompositor(); + nsIWidget::DestroyCompositor(); } void nsWindow::RequestFxrOutput() {