Bug 1550462 - part 1: Define pointerawupdate event r=smaug

This patch just defines the event message, an atom for event listener attribute
and managing whether the window may have a listener.

Differential Revision: https://phabricator.services.mozilla.com/D243403
This commit is contained in:
Masayuki Nakano
2025-05-16 09:49:43 +00:00
committed by masayuki@d-toybox.com
parent 9b9b7194e1
commit 0835ca3b3f
15 changed files with 162 additions and 54 deletions

View File

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

View File

@@ -3656,6 +3656,9 @@ already_AddRefed<nsINode> nsINode::CloneAndAdopt(
if (elm->MayHavePointerEnterLeaveEventListener()) {
window->SetHasPointerEnterLeaveEventListeners();
}
if (elm->MayHavePointerRawUpdateEventListener()) {
window->MaybeSetHasPointerRawUpdateEventListeners();
}
if (elm->MayHaveSelectionChangeEventListener()) {
window->SetHasSelectionChangeEventListeners();
}

View File

@@ -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<mozilla::dom::Navigator> 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<nsPIDOMWindowOuter> 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<mozilla::dom::WindowGlobalChild> mWindowGlobalChild;
bool mWasSuspendedByGroup;
bool mWasSuspendedByGroup = false;
/**
* Count of the number of active LockRequest objects, including ones from

View File

@@ -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) {

View File

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

View File

@@ -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,

View File

@@ -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)),

View File

@@ -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<CodeNameIndex> 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<bool> mLayersConnected;

View File

@@ -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 },

View File

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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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:

View File

@@ -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