diff --git a/dom/base/nsGlobalWindowInner.cpp b/dom/base/nsGlobalWindowInner.cpp index 9ebacfe13f0e..6e8162851186 100644 --- a/dom/base/nsGlobalWindowInner.cpp +++ b/dom/base/nsGlobalWindowInner.cpp @@ -1133,6 +1133,8 @@ void nsGlobalWindowInner::FreeInnerObjects() { } StartDying(); + ClearHasPointerRawUpdateEventListeners(); + if (mDoc && mDoc->GetWindowContext()) { // The document is about to lose its window, so this is a good time to send // our page use counters. @@ -7687,6 +7689,26 @@ TrustedTypePolicyFactory* nsGlobalWindowInner::TrustedTypes() { return mTrustedTypePolicyFactory; } +void nsPIDOMWindowInner::MaybeSetHasPointerRawUpdateEventListeners() { + if (HasPointerRawUpdateEventListeners() || !IsSecureContext()) { + return; + } + mMayHavePointerRawUpdateEventListener = true; + if (BrowserChild* const browserChild = BrowserChild::GetFrom(this)) { + browserChild->OnPointerRawUpdateEventListenerAdded(this); + } +} + +void nsPIDOMWindowInner::ClearHasPointerRawUpdateEventListeners() { + if (!HasPointerRawUpdateEventListeners()) { + return; + } + mMayHavePointerRawUpdateEventListener = false; + if (BrowserChild* const browserChild = BrowserChild::GetFrom(this)) { + browserChild->OnPointerRawUpdateEventListenerRemoved(this); + } +} + nsIURI* nsPIDOMWindowInner::GetDocumentURI() const { return mDoc ? mDoc->GetDocumentURI() : mDocumentURI.get(); } @@ -7777,29 +7799,7 @@ CloseWatcherManager* nsPIDOMWindowInner::EnsureCloseWatcherManager() { nsPIDOMWindowInner::nsPIDOMWindowInner(nsPIDOMWindowOuter* aOuterWindow, WindowGlobalChild* aActor) - : mMutationBits(0), - mIsDocumentLoaded(false), - mIsHandlingResizeEvent(false), - mMayHaveDOMActivateEventListeners(false), - mMayHaveTouchEventListener(false), - mMayHaveSelectionChangeEventListener(false), - mMayHaveFormSelectEventListener(false), - mMayHaveMouseEnterLeaveEventListener(false), - mMayHavePointerEnterLeaveEventListener(false), - mMayHaveTransitionEventListener(false), - mMayHaveSMILTimeEventListener(false), - mMayHaveBeforeInputEventListenerForTelemetry(false), - mMutationObserverHasObservedNodeForTelemetry(false), - mOuterWindow(aOuterWindow), - mWindowID(0), - mHasNotifiedGlobalCreated(false), - mMarkedCCGeneration(0), - mHasTriedToCacheTopInnerWindow(false), - mNumOfIndexedDBDatabases(0), - mNumOfOpenWebSockets(0), - mEvent(nullptr), - mWindowGlobalChild(aActor), - mWasSuspendedByGroup(false) { + : mOuterWindow(aOuterWindow), mWindowGlobalChild(aActor) { MOZ_ASSERT(aOuterWindow); mBrowsingContext = aOuterWindow->GetBrowsingContext(); diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 79453a8dbbed..684e3dad1670 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -3656,6 +3656,9 @@ already_AddRefed nsINode::CloneAndAdopt( if (elm->MayHavePointerEnterLeaveEventListener()) { window->SetHasPointerEnterLeaveEventListeners(); } + if (elm->MayHavePointerRawUpdateEventListener()) { + window->MaybeSetHasPointerRawUpdateEventListeners(); + } if (elm->MayHaveSelectionChangeEventListener()) { window->SetHasSelectionChangeEventListeners(); } diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 4b4de94f0d6f..b3dbb1c450b8 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -224,6 +224,29 @@ class nsPIDOMWindowInner : public mozIDOMWindow { mMayHavePointerEnterLeaveEventListener = true; } + /** + * Call this to check whether some node (this window, its document, + * or content in that document) has a pointerrawupdate event listener. + */ + bool HasPointerRawUpdateEventListeners() const { + return mMayHavePointerRawUpdateEventListener; + } + + /** + * Call this to indicate that some node (this window, its document, + * or content in that document) has a pointerrawupdate event listener. + * This may not accept that if the event is not available in this window. + */ + void MaybeSetHasPointerRawUpdateEventListeners(); + + protected: + /** + * Call this to clear whether some nodes has a pointerrawupdate event + * listener. + */ + void ClearHasPointerRawUpdateEventListeners(); + + public: /** * Call this to check whether some node (this window, its document, * or content in that document) has a transition* event listeners. @@ -649,24 +672,25 @@ class nsPIDOMWindowInner : public mozIDOMWindow { RefPtr mNavigator; // These variables are only used on inner windows. - uint32_t mMutationBits; + uint32_t mMutationBits = 0; uint32_t mActivePeerConnections = 0; - bool mIsDocumentLoaded; - bool mIsHandlingResizeEvent; - bool mMayHaveDOMActivateEventListeners; - bool mMayHaveTouchEventListener; - bool mMayHaveSelectionChangeEventListener; - bool mMayHaveFormSelectEventListener; - bool mMayHaveMouseEnterLeaveEventListener; - bool mMayHavePointerEnterLeaveEventListener; - bool mMayHaveTransitionEventListener; - bool mMayHaveSMILTimeEventListener; + bool mIsDocumentLoaded = false; + bool mIsHandlingResizeEvent = false; + bool mMayHaveDOMActivateEventListeners = false; + bool mMayHaveTouchEventListener = false; + bool mMayHaveSelectionChangeEventListener = false; + bool mMayHaveFormSelectEventListener = false; + bool mMayHaveMouseEnterLeaveEventListener = false; + bool mMayHavePointerEnterLeaveEventListener = false; + bool mMayHavePointerRawUpdateEventListener = false; + bool mMayHaveTransitionEventListener = false; + bool mMayHaveSMILTimeEventListener = false; // Only used for telemetry probes. This may be wrong if some nodes have // come from another document with `Document.adoptNode`. - bool mMayHaveBeforeInputEventListenerForTelemetry; - bool mMutationObserverHasObservedNodeForTelemetry; + bool mMayHaveBeforeInputEventListenerForTelemetry = false; + bool mMutationObserverHasObservedNodeForTelemetry = false; // Our inner window's outer window. nsCOMPtr mOuterWindow; @@ -687,11 +711,11 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // A unique (as long as our 64-bit counter doesn't roll over) id for // this window. - uint64_t mWindowID; + uint64_t mWindowID = 0; // Set to true once we've sent the (chrome|content)-document-global-created // notification. - bool mHasNotifiedGlobalCreated; + bool mHasNotifiedGlobalCreated = false; // Whether when focused via an "unknown" focus method, we should show outlines // by default or not. The initial value of this is true (so as to show @@ -699,7 +723,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // without any other user interaction). bool mUnknownFocusMethodShouldShowOutline = true; - uint32_t mMarkedCCGeneration; + uint32_t mMarkedCCGeneration = 0; // mTopInnerWindow is used for tab-wise check by timeout throttling. It could // be null. @@ -708,17 +732,17 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // The evidence that we have tried to cache mTopInnerWindow only once from // SetNewDocument(). Note: We need this extra flag because mTopInnerWindow // could be null and we don't want it to be set multiple times. - bool mHasTriedToCacheTopInnerWindow; + bool mHasTriedToCacheTopInnerWindow = false; // The number of active IndexedDB databases. - uint32_t mNumOfIndexedDBDatabases; + uint32_t mNumOfIndexedDBDatabases = 0; // The number of open WebSockets. - uint32_t mNumOfOpenWebSockets; + uint32_t mNumOfOpenWebSockets = 0; // The event dispatch code sets and unsets this while keeping // the event object alive. - mozilla::dom::Event* mEvent; + mozilla::dom::Event* mEvent = nullptr; // The WindowGlobalChild actor for this window. // @@ -726,7 +750,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow { // during SetNewDocument, and cleared during FreeInnerObjects. RefPtr mWindowGlobalChild; - bool mWasSuspendedByGroup; + bool mWasSuspendedByGroup = false; /** * Count of the number of active LockRequest objects, including ones from diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp index 1b9ad21f44aa..09fe81178d9b 100644 --- a/dom/events/EventListenerManager.cpp +++ b/dom/events/EventListenerManager.cpp @@ -25,8 +25,12 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/PresShell.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/TimeStamp.h" #include "mozilla/dom/AbortSignal.h" #include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/ChromeUtils.h" #include "mozilla/dom/EventCallbackDebuggerNotification.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" @@ -37,9 +41,6 @@ #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/TouchEvent.h" #include "mozilla/dom/UserActivation.h" -#include "mozilla/ScopeExit.h" -#include "mozilla/TimeStamp.h" -#include "mozilla/dom/ChromeUtils.h" #include "EventListenerService.h" #include "nsCOMPtr.h" @@ -145,6 +146,7 @@ EventListenerManagerBase::EventListenerManagerBase() mMayHaveTouchEventListener(false), mMayHaveMouseEnterLeaveEventListener(false), mMayHavePointerEnterLeaveEventListener(false), + mMayHavePointerRawUpdateEventListener(false), mMayHaveSelectionChangeEventListener(false), mMayHaveFormSelectEventListener(false), mMayHaveTransitionEventListener(false), @@ -413,6 +415,18 @@ void EventListenerManager::AddEventListenerInternal( window->SetHasPointerEnterLeaveEventListeners(); } break; + case ePointerRawUpdate: + if (!StaticPrefs::dom_event_pointer_rawupdate_enabled()) { + break; + } + mMayHavePointerRawUpdateEventListener = true; + if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) { + NS_WARNING_ASSERTION( + !nsContentUtils::IsChromeDoc(window->GetExtantDoc()), + "Please do not use pointerrawupdate event in chrome."); + window->MaybeSetHasPointerRawUpdateEventListeners(); + } + break; case eGamepadButtonDown: case eGamepadButtonUp: case eGamepadAxisMove: @@ -862,6 +876,15 @@ void EventListenerManager::RemoveEventListenerInternal( DisableDevice(aUserType); } } + + // XXX Should we clear mMayHavePointerRawUpdateEventListener if the last + // pointerrawupdate event listener is removed? If so, nsPIDOMWindowInner + // needs to count how may event listener managers had some pointerrawupdate + // event listener. If we've notified the window of having a pointerrawupdate + // event listener, some behavior is changed because pointerrawupdate event + // dispatcher needs to handle some things before dispatching an event to the + // DOM. However, it is expected that web apps using `pointerrawupdate` don't + // remove the event listeners. } static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) { diff --git a/dom/events/EventListenerManager.h b/dom/events/EventListenerManager.h index 97acee129784..71eff9468e9d 100644 --- a/dom/events/EventListenerManager.h +++ b/dom/events/EventListenerManager.h @@ -159,6 +159,7 @@ class EventListenerManagerBase { uint16_t mMayHaveTouchEventListener : 1; uint16_t mMayHaveMouseEnterLeaveEventListener : 1; uint16_t mMayHavePointerEnterLeaveEventListener : 1; + uint16_t mMayHavePointerRawUpdateEventListener : 1; uint16_t mMayHaveSelectionChangeEventListener : 1; uint16_t mMayHaveFormSelectEventListener : 1; uint16_t mMayHaveTransitionEventListener : 1; @@ -554,6 +555,9 @@ class EventListenerManager final : public EventListenerManagerBase { bool MayHavePointerEnterLeaveEventListener() const { return mMayHavePointerEnterLeaveEventListener; } + bool MayHavePointerRawUpdateEventListener() const { + return mMayHavePointerRawUpdateEventListener; + } bool MayHaveSelectionChangeEventListener() const { return mMayHaveSelectionChangeEventListener; } diff --git a/dom/events/EventNameList.h b/dom/events/EventNameList.h index 26e146258247..466e739937c6 100644 --- a/dom/events/EventNameList.h +++ b/dom/events/EventNameList.h @@ -225,6 +225,8 @@ EVENT(pointerover, ePointerOver, EventNameType_All, ePointerEventClass) EVENT(pointerout, ePointerOut, EventNameType_All, ePointerEventClass) EVENT(pointerenter, ePointerEnter, EventNameType_All, ePointerEventClass) EVENT(pointerleave, ePointerLeave, EventNameType_All, ePointerEventClass) +EVENT(pointerrawupdate, ePointerRawUpdate, EventNameType_All, + ePointerEventClass) EVENT(gotpointercapture, ePointerGotCapture, EventNameType_All, ePointerEventClass) EVENT(lostpointercapture, ePointerLostCapture, EventNameType_All, diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp index 21079e9fc7cf..abe3c0db177f 100644 --- a/dom/ipc/BrowserChild.cpp +++ b/dom/ipc/BrowserChild.cpp @@ -1559,6 +1559,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent( if (data->CanCoalesce(aEvent, aGuid, aInputBlockId)) { data->Coalesce(aEvent, aGuid, aInputBlockId); mCoalescedMouseEventFlusher->StartObserver(); + if (mPointerRawUpdateWindowCount) { + // TODO: Dispatch ePointerRawUpdate here + } return IPC_OK(); } // Can't coalesce current mousemove event. Put the coalesced mousemove data @@ -1915,6 +1918,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent( if (sConsecutiveTouchMoveCount > 1) { mCoalescedTouchMoveEventFlusher->StartObserver(); + if (mPointerRawUpdateWindowCount) { + // TODO: Dispatch ePointerRawUpdate here + } } else { // Flush the pending coalesced touch in order to avoid the first // touchmove be overridden by the second one. @@ -4034,6 +4040,27 @@ void BrowserChild::SetDragSession(nsIDragSession* aSession) { mDragSession = aSession; } +LazyLogModule gPointerRawUpdateEventListenersLog( + "PointerRawUpdateEventListeners"); + +void BrowserChild::OnPointerRawUpdateEventListenerAdded( + const nsPIDOMWindowInner* aWindow) { + mPointerRawUpdateWindowCount++; + MOZ_LOG(gPointerRawUpdateEventListenersLog, LogLevel::Info, + ("Added for %p (total: %u)", aWindow, mPointerRawUpdateWindowCount)); +} + +void BrowserChild::OnPointerRawUpdateEventListenerRemoved( + const nsPIDOMWindowInner* aWindow) { + MOZ_ASSERT(mPointerRawUpdateWindowCount); + if (MOZ_LIKELY(mPointerRawUpdateWindowCount)) { + mPointerRawUpdateWindowCount--; + } + MOZ_LOG(gPointerRawUpdateEventListenersLog, LogLevel::Info, + ("Removed for %p (remaining: %u)", aWindow, + mPointerRawUpdateWindowCount)); +} + BrowserChildMessageManager::BrowserChildMessageManager( BrowserChild* aBrowserChild) : ContentFrameMessageManager(new nsFrameMessageManager(aBrowserChild)), diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h index 5c8e8de21146..1a7b377d8d4b 100644 --- a/dom/ipc/BrowserChild.h +++ b/dom/ipc/BrowserChild.h @@ -46,6 +46,7 @@ class nsIHttpChannel; class nsIRequest; class nsISerialEventTarget; class nsIWebProgress; +class nsPIDOMWindowInner; class nsWebBrowser; class nsDocShellLoadState; @@ -712,6 +713,13 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, mozilla::ipc::IPCResult RecvDispatchToDropTargetAndResumeEndDragSession( bool aShouldDrop); + void OnPointerRawUpdateEventListenerAdded(const nsPIDOMWindowInner* aWindow); + void OnPointerRawUpdateEventListenerRemoved( + const nsPIDOMWindowInner* aWindow); + [[nodiscard]] bool HasWindowHavingPointerRawUpdateEventListeners() const { + return !!mPointerRawUpdateWindowCount; + } + protected: virtual ~BrowserChild(); @@ -823,6 +831,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor, Maybe mPreviousConsumedKeyDownCode; uint32_t mChromeFlags; uint32_t mMaxTouchPoints; + // The number of windows which may have ePointerRawUpdate event listener. + uint32_t mPointerRawUpdateWindowCount = 0; layers::LayersId mLayersId; CSSRect mUnscaledOuterRect; Maybe mLayersConnected; diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index f3da86567f9c..2c48a5aa391b 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -1857,6 +1857,8 @@ let interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "onpointerover", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "onpointerrawupdate", disabled: true }, + // IMPORTANT: Do not change this list without review from a DOM peer! { name: "onpointerup", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "onpopstate", insecureContext: true }, diff --git a/dom/webidl/EventHandler.webidl b/dom/webidl/EventHandler.webidl index dc633f98232b..76a3b39d70ae 100644 --- a/dom/webidl/EventHandler.webidl +++ b/dom/webidl/EventHandler.webidl @@ -116,6 +116,8 @@ interface mixin GlobalEventHandlers { attribute EventHandler onpointerover; attribute EventHandler onpointerenter; attribute EventHandler onpointerleave; + [SecureContext, Pref="dom.event.pointer.rawupdate.enabled"] + attribute EventHandler onpointerrawupdate; attribute EventHandler ongotpointercapture; attribute EventHandler onlostpointercapture; diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 84fd1f7cac9e..f768341d53e7 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -2820,6 +2820,12 @@ value: true mirror: always +# Whether pointerrawupdate event is enabled or disabled. +- name: dom.event.pointer.rawupdate.enabled + type: bool + value: false + mirror: always + # Whether wheel event target's should be grouped. When enabled, all wheel # events that occur in a given wheel transaction have the same event target. - name: dom.event.wheel-event-groups.enabled diff --git a/widget/BasicEvents.h b/widget/BasicEvents.h index c9e43d5f7797..9c8ef55b67d4 100644 --- a/widget/BasicEvents.h +++ b/widget/BasicEvents.h @@ -454,9 +454,9 @@ class WidgetEvent : public WidgetEventTime { break; case ePointerEventClass: mFlags.mCancelable = - (mMessage != ePointerEnter && mMessage != ePointerLeave && - mMessage != ePointerCancel && mMessage != ePointerGotCapture && - mMessage != ePointerLostCapture); + (mMessage != ePointerRawUpdate && mMessage != ePointerEnter && + mMessage != ePointerLeave && mMessage != ePointerCancel && + mMessage != ePointerGotCapture && mMessage != ePointerLostCapture); mFlags.mBubbles = (mMessage != ePointerEnter && mMessage != ePointerLeave); break; @@ -931,12 +931,12 @@ class WidgetEvent : public WidgetEventTime { case ePointerEventClass: // All pointer events are composed mFlags.mComposed = + mMessage == ePointerRawUpdate || mMessage == ePointerMove || mMessage == ePointerClick || mMessage == ePointerAuxClick || mMessage == eContextMenu || mMessage == ePointerDown || - mMessage == ePointerMove || mMessage == ePointerUp || - mMessage == ePointerCancel || mMessage == ePointerOver || - mMessage == ePointerOut || mMessage == ePointerGotCapture || - mMessage == ePointerLostCapture; + mMessage == ePointerUp || mMessage == ePointerCancel || + mMessage == ePointerOver || mMessage == ePointerOut || + mMessage == ePointerGotCapture || mMessage == ePointerLostCapture; break; case eTouchEventClass: // All touch events are composed @@ -1011,6 +1011,7 @@ class WidgetEvent : public WidgetEventTime { aEventTypeArg.EqualsLiteral("pointerout") || aEventTypeArg.EqualsLiteral("pointerenter") || aEventTypeArg.EqualsLiteral("pointerleave") || + aEventTypeArg.EqualsLiteral("pointerrawupdate") || aEventTypeArg.EqualsLiteral("gotpointercapture") || aEventTypeArg.EqualsLiteral("lostpointercapture") || // touch events diff --git a/widget/EventMessageList.h b/widget/EventMessageList.h index c33d3cea101b..60d5af6f9e2e 100644 --- a/widget/EventMessageList.h +++ b/widget/EventMessageList.h @@ -108,6 +108,7 @@ NS_EVENT_MESSAGE(ePointerOut) NS_EVENT_MESSAGE(ePointerEnter) NS_EVENT_MESSAGE(ePointerLeave) NS_EVENT_MESSAGE(ePointerCancel) +NS_EVENT_MESSAGE(ePointerRawUpdate) NS_EVENT_MESSAGE(ePointerGotCapture) NS_EVENT_MESSAGE(ePointerLostCapture) NS_EVENT_MESSAGE_FIRST_LAST(ePointerEvent, ePointerMove, ePointerLostCapture) diff --git a/widget/WidgetEventImpl.cpp b/widget/WidgetEventImpl.cpp index 4551552d94dc..84016172b53e 100644 --- a/widget/WidgetEventImpl.cpp +++ b/widget/WidgetEventImpl.cpp @@ -73,6 +73,7 @@ bool IsPointerEventMessage(EventMessage aMessage) { case ePointerOut: case ePointerEnter: case ePointerLeave: + case ePointerRawUpdate: case ePointerGotCapture: case ePointerLostCapture: case ePointerClick: @@ -121,6 +122,7 @@ bool IsForbiddenDispatchingToNonElementContent(EventMessage aMessage) { case ePointerOut: case ePointerEnter: case ePointerLeave: + case ePointerRawUpdate: case ePointerCancel: case ePointerGotCapture: case ePointerLostCapture: diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py index 2800433fbf7a..a57dfe2d1f0a 100644 --- a/xpcom/ds/StaticAtoms.py +++ b/xpcom/ds/StaticAtoms.py @@ -1955,6 +1955,7 @@ STATIC_ATOMS = [ Atom("onpointerout", "onpointerout"), Atom("onpointerenter", "onpointerenter"), Atom("onpointerleave", "onpointerleave"), + Atom("onpointerrawupdate", "onpointerrawupdate"), Atom("ongotpointercapture", "ongotpointercapture"), Atom("onlostpointercapture", "onlostpointercapture"), # orientation support