Skip to content

Conversation

@francovs
Copy link
Contributor

@francovs francovs commented Aug 13, 2025

5e3ae48

Event Timing: implement interactionId logic
https://bugs.webkit.org/show_bug.cgi?id=297361
rdar://158264903

Reviewed by Ryosuke Niwa.

Assigns interactionId to PerformanceEventTiming entries that qualify,
according to https://www.w3.org/TR/event-timing/.

Only certain deliberate user actions such as clicks or keypresses are
considered; passive events such as pointerover or pointerexit do not
qualify. Some events are grouped together, receiving the same
interactionId. One such example is pointerdown, pointerup and click.

We currently only support a single pointer, so the map variables
related to pointer interactions (pointerMap[] and pendingPointerDown[])
mentioned in the spec (https://www.w3.org/TR/event-timing/#pointer-interaction-value-map)
were implemented as a struct (PointerInteractionState) containing
the necessary state for a single pointer. In that conversion,
pointerMap was renamed to PointerInteractionState::interactionID.

New files created to achieve more fine grained header inclusion:
* Source/WebCore/page/EventTimingInteractionID.h
* Source/WebCore/page/PerformanceEventTimingCandidate.h

Canonical link: https://commits.webkit.org/299033@main

787fe6a

Misc iOS, visionOS, tvOS & watchOS macOS Linux Windows
✅ 🧪 style ✅ 🛠 ios ✅ 🛠 mac ✅ 🛠 wpe 🛠 win
✅ 🧪 bindings 🛠 ios-sim ✅ 🛠 mac-AS-debug 🧪 wpe-wk2 🧪 win-tests
✅ 🧪 webkitperl 🧪 ios-wk2 ✅ 🧪 api-mac 🧪 api-wpe
🧪 ios-wk2-wpt ✅ 🧪 mac-wk1 ✅ 🛠 wpe-cairo
🧪 api-ios 🧪 mac-wk2 ❌ 🛠 gtk
✅ 🛠 vision 🧪 mac-AS-debug-wk2 ❌ 🧪 gtk-wk2
🛠 vision-sim ❌ 🧪 mac-wk2-stress ❌ 🧪 api-gtk
✅ 🛠 🧪 merge ⏳ 🧪 vision-wk2 🧪 mac-intel-wk2 🛠 playstation
🛠 tv 🛠 mac-safer-cpp
🛠 tv-sim
🛠 watch
🛠 watch-sim

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Aug 13, 2025
@francovs francovs force-pushed the event-timing-interaction branch from 7eebc23 to dbf98be Compare August 13, 2025 11:24
@francovs francovs force-pushed the event-timing-interaction branch from a19279c to ff198d8 Compare August 13, 2025 18:50
@francovs francovs force-pushed the event-timing-interaction branch from ff198d8 to 53d7738 Compare August 13, 2025 21:25
@francovs francovs force-pushed the event-timing-interaction branch from f69c260 to 48bcd5c Compare August 13, 2025 22:14
@francovs francovs marked this pull request as ready for review August 13, 2025 22:21
@francovs francovs requested a review from cdumez as a code owner August 13, 2025 22:21
@rreno rreno removed the merging-blocked Applied to prevent a change from being merged label Aug 14, 2025
class PerformanceEventTiming final : public PerformanceEntry {
public:
using InteractionID = uint64_t;
struct Candidate {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider running dump-class-layout on this to make sure you've got an efficiently laid out struct.

@francovs francovs force-pushed the event-timing-interaction branch from 48bcd5c to 8ec253d Compare August 15, 2025 00:04
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Aug 15, 2025
@francovs francovs force-pushed the event-timing-interaction branch from 8ec253d to 8ef5b40 Compare August 15, 2025 17:50
@rreno rreno removed the merging-blocked Applied to prevent a change from being merged label Aug 15, 2025
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Aug 15, 2025
#include "PushSubscriptionOwner.h"
#include "Supplementable.h"
#include "WindowOrWorkerGlobalScope.h"
#include <JavaScriptCore/HandleForward.h>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be including PerformanceEventTiming.h here.
We should move InteractionID out of PerformanceEventTiming and forward declare it here instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PerformanceEventTiming.h is also required due PerformanceEventTiming::Candidate, which is used in m_performanceEventTimingCandidates (and others), so it cannot be forward-declared.

I believe the PerformanceEventTiming.h is lightweight now (it does not bring in Performance.h anymore) so it shouldn't be a disaster.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move PerformanceEventTiming::Candidate out of PerformanceEventTiming as well so that we don't need to include the header file. Generally speaking, we shouldn't be including any header files unless absolutely necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to a separate header (page/PerformanceEventTimingCandidate.h)


increaseInteractionCount();
auto it = m_pendingKeyDowns.find(code);
it->value.interactionID = ensureUserInteractionValue();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would have called this function generateUserInteractionValue after merging it with increaseInteractionCount. Having separate functions to increase the count and return the current value is not a common pattern we use in WebCore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is hard to merge these functions cleanly because contextmenu and pointerup sometimes use the current interaction count without incrementing it.

I added a return to increaseInteractionCount(), simplifying some logic, but kept ensureUserInteractionValue(). I also created maintainInteractionCount() to document cases in which the spec uses the current interaction value without incrementing.

@francovs francovs force-pushed the event-timing-interaction branch from 4cdc128 to ec91133 Compare August 20, 2025 20:30
{
// User interaction value should be increased by a small integer to
// "discourage developers from considering it as a counter":
ensureUserInteractionValue().value += 7;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... doesn't this mean the developer could just divide the number by 7 to get an effective counter?
Do other browsers hard-code a value like this?

Copy link
Contributor Author

@francovs francovs Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, though this isn't really a problem since window.interactionCount is available. The spec explicitly allows a constant increment, it only warns agains exposing leaks:

A user agent may choose to increase it by a small random integer every time, or choose a constant. A user agent must not use a shared global user interaction values for all Windows, because this could introduce cross-origin leaks.

My understanding is that the point is to provide a soft warning to a user trying to match PerformanceEventTiming.interactionId to window.interactionCount. For that, maybe it would be better to pick something around 100, which would ensure these two are at least two orders of magnitude apart when considering the latest interaction.

I picked 7 on a whim because it is small and has no obvious divisibility test, but it turns out both chromium and Gecko picked the same number:

// Interaction ID increment. We increase this value by an integer greater than 1
// to discourage developers from using the value to 'count' the number of user
// interactions. This is consistent with the spec, which allows the increasing
// the user interaction value by a small number chosen by the user agent.
constexpr uint32_t kInteractionIdIncrement = 7;

@francovs francovs force-pushed the event-timing-interaction branch from ec91133 to 1fa4f7f Compare August 21, 2025 01:50
struct PerformanceEventTimingCandidate {
EventType type { };
bool cancelable { false };
Seconds startTime { 0 };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to explicitly initialize Seconds like this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment will withstands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now fixed, but removing the explicit initializer caused some bots to hit -Werror=missing-field-initializers. I had to add some unnecessary stuff to the designated constructor call to get them to build:

// LocalDOMWindow::initializeEventTimingEntry():
    return PerformanceEventTimingCandidate {
        .type = type,
        .cancelable = event.cancelable(),
        .startTime =  startTime,
        .processingStart = processingStart,
        .processingEnd = { }, // here
        .duration = { }, // here
        .target = { }, // here
        .interactionID = computeInteractionID(event, type)
    };

I could also add explicit constructors to PerformanceEventTimingCandidate to avoid all this, but it felt very boilerplate-y.

--

I think this is a bad warning since missing elements should be copy initialized from { }, which feels safe to me (see "Implicitly initialized elements"). This example in Godbolt reproduces the warning.

@francovs francovs force-pushed the event-timing-interaction branch from 1fa4f7f to 6bb52d8 Compare August 21, 2025 14:16
@francovs francovs force-pushed the event-timing-interaction branch from 6bb52d8 to d66ae1f Compare August 21, 2025 14:49
@francovs francovs force-pushed the event-timing-interaction branch from d66ae1f to 6f74b34 Compare August 21, 2025 18:03
@francovs francovs force-pushed the event-timing-interaction branch from 6f74b34 to 787fe6a Compare August 21, 2025 21:10
@rniwa rniwa removed the merging-blocked Applied to prevent a change from being merged label Aug 21, 2025
@rniwa rniwa added the merge-queue Applied to send a pull request to merge-queue label Aug 21, 2025
https://bugs.webkit.org/show_bug.cgi?id=297361
rdar://158264903

Reviewed by Ryosuke Niwa.

Assigns interactionId to PerformanceEventTiming entries that qualify,
according to https://www.w3.org/TR/event-timing/.

Only certain deliberate user actions such as clicks or keypresses are
considered; passive events such as pointerover or pointerexit do not
qualify. Some events are grouped together, receiving the same
interactionId. One such example is pointerdown, pointerup and click.

We currently only support a single pointer, so the map variables
related to pointer interactions (pointerMap[] and pendingPointerDown[])
mentioned in the spec (https://www.w3.org/TR/event-timing/#pointer-interaction-value-map)
were implemented as a struct (PointerInteractionState) containing
the necessary state for a single pointer. In that conversion,
pointerMap was renamed to PointerInteractionState::interactionID.

New files created to achieve more fine grained header inclusion:
* Source/WebCore/page/EventTimingInteractionID.h
* Source/WebCore/page/PerformanceEventTimingCandidate.h

Canonical link: https://commits.webkit.org/299033@main
@webkit-commit-queue
Copy link
Collaborator

Committed 299033@main (5e3ae48): https://commits.webkit.org/299033@main

Reviewed commits have been landed. Closing PR #49304 and removing active labels.

@webkit-commit-queue webkit-commit-queue merged commit 5e3ae48 into WebKit:main Aug 21, 2025
@webkit-commit-queue webkit-commit-queue removed the merge-queue Applied to send a pull request to merge-queue label Aug 21, 2025
@francovs francovs deleted the event-timing-interaction branch August 22, 2025 20:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants