Open Bug 2031963 Opened 2 months ago Updated 20 hours ago

www.temu.com - Scrolls the page when dragging the captcha elements

Categories

(Web Compatibility :: Site Reports, defect, P1)

ARM
Android

Tracking

(Webcompat Priority:P1, Webcompat Score:9)

Webcompat Priority P1
Webcompat Score 9

People

(Reporter: ctanase, Unassigned)

References

()

Details

(Keywords: webcompat:needs-diagnosis, webcompat:site-report, Whiteboard: [webcompat-source:web-bugs][webcompat:sightline][webcompat:japan][webcompat:core])

User Story

platform:android
impact:site-broken
configuration:general
affects:all
branch:release
diagnosis-team:performance
user-impact-score:1500

Attachments

(1 file, 1 obsolete file)

Environment:
Operating system: Android 16
Firefox version: Firefox Mobile 149.0/151

Steps to reproduce:

  1. Go to https://www.temu.com/pl/ul/kuiper/un3.html?_bg_fs=1
  2. Wait for the captcha to appear.
  3. Drag the elements on their corresponding places. (unless a different type of captcha gets displayed)

Expected Behavior:
The page does not get scrolled.

Actual Behavior:
The page gets scrolled in the background.

Notes:

  • The experience is worse if "Pull to refresh" is enabled, it won't let you drag the elements in most cases
  • Reproduces regardless of the status of ETP
  • Reproduces in firefox-nightly, and firefox-release
  • Does not reproduce in chrome

Created from https://github.com/webcompat/web-bugs/issues/215958

Attached video FF vs Chrome.mp4
Whiteboard: [webcompat-source:web-bugs] → [webcompat-source:web-bugs][webcompat:sightline][webcompat:japan][webcompat:core]
Severity: -- → S2
User Story: (updated)
Webcompat Priority: --- → P1
Webcompat Score: --- → 9
Priority: -- → P1
User Story: (updated)

APZ team will diagnose this.

User Story: (updated)

Geez, while I was poking the site, something has been changed. I can no longer see the capcha.

What I've noticed is that the site adds an active touchmove event handler dynamically and does preventDefault() in the handler. If the event listener is registered in a passive event handler, that's bug 1839095.

Can someone try the patch on bug 1839095 to see whether this bug can be solved by the patches? The patches are not perfect, in the test case in bug 1839095 comment 0 if you move your finger quickly it will not prevent scrolling though, if you move the finger slowly, I mean, if there's enough time in between the touch-start and the first touch-move event, it will prevent scrolling. So if the temu site does something similar to bug 1839095, the patches should work.

See Also: → 1839095

Here is a Pernosco recording of the bug occurring in RDM, in a build with the bug 1839095 patches applied. I've added two notebook entries, the first is a place where APZ received a preventDefault=1 content response for a touch input block, and the second is a place where APZ nonetheless scrolls in response to an event in that input block.

I've added a new note where an active touchmove event listener is setup. In the JS call stack at the moment, I do see react string so I suspect the event listener is not setup directly in a passive touchstart event listener just like bug 1839095, rather it's probably deferred to somewhere else so that the patch for bug 1839095 doesn't help.

Note for references how I reached to the active touchmove event listener;

  1. Searched executions of "mozilla::dom::EventTarget::AddEventListener" with aType=="touchmove"
  2. there are a bunch of executions including the parent process
  3. On every execution I did p ((nsIURI*)((mozilla::dom::NodeInfo*)((nsINode*)this)->mNodeInfo)->mDocument->mDocumentURI)->GetSpecOrDefault() in the debugger to see the URL
  4. In the case of temu.com I did p ((mozilla::dom::AddEventListenerOptions)aOptions.mValue.mAddEventListenerOptions.mStorage).mPassive to see whether passive is false or not

Here is another Pernosco recording, this time taken with the settings intended to allow JS debugging. Two notebook entries are marked similar to the previous recording.

I have further diagnosis on the first pernosco session. I found an interesting fact: Before APZ received the preventDefault-ed information there were 3 touch events, a touch-start, a touch-move and a touch-move. I checked the WR hit test results on both touch-move events, in both cases the result are eVisibleToHitTest. So APZ had not received eApzAwareListeners at the moment. I suspect this is kinda performance issue on the main-thread. If the main-thread had sent the up-to-date event listener setup within a reasonable time span, the patch for bug 1839095 should just work.

In the new pernosco session in comment 8, there were 4 active touchmove event listeners which were registered in one or two passive touchstart event handlers. Since the active touchmove event listeners were registered, this code

https://searchfox.org/firefox-main/rev/be31b3948198286e39a9855e414823cb17b6e94c/layout/generic/nsIFrame.cpp#12226

result += CompositorHitTestFlags::eApzAwareListeners;

didn't run at all. That's the reason why APZ didn't prevent scrolling even though APZ had received the preventDefault=1.

So the next question is why result += CompositorHitTestFlags::eApzAwareListeners; was not executed.

EDIT: This was wrong. The line was executed. On pernosco the break point set to the line somehow doesn't break.

Okay, what I've found:

  1. The eApzAwareListeners; code was not executed in between when APZ received the touch-start and when APZ received the first touch-move
  2. During the above time span, 4 active touchmove event listeners were setup
  3. when the first touch-move arrived at APZCTreeManager::ProcessTouchInput the hit results were:
(10, eVisibleToHitTest)                                                                                  
( 9, eVisibleToHitTest)                                                                                  
( 9, eVisibleToHitTest)                                                                                  
( 0, eVisibleToHitTest)
  1. when the second touch-move event arrived at APZCTreeManager::ProcessTouchInput, I manually did a hit-testing by:
set $pt = (wr::WorldPoint*)malloc(8)                                                   
set ((float*)$pt)[0] = 634.0                             
set ((float*)$pt)[1] = 341.0                   
call $wr->HitTest(*$pt)
  1. Then the results had eApzAwareListeners, so the active touchmove event listeners information had arrived:
(10, eVisibleToHitTest | eApzAwareListeners)
( 9, eVisibleToHitTest | eApzAwareListeners)
( 9, eVisibleToHitTest | eApzAwareListeners)
( 0, eVisibleToHitTest | eApzAwareListeners) <= these scrollId:0 results might be irrelevant
( 0, eVisibleToHitTest) 

So from what I can tell is that this bug is a sort of performance issue on the main-thread. If the up-to-date information can be arrived in APZ within a reasonable time span, the patches for bug 1839095 should have been fixed this bug too.

With that I am setting bug 1839095 in this bug's dependency list, also changing the diagnosis team from APZ to Performance.

User Story: (updated)
Depends on: 1839095

Botond, in the meantime, would you mind taking a performance profile result? It would be nice to have I think.

Flags: needinfo?(botond)

(In reply to Hiroyuki Ikezoe (:hiro) from comment #12)

Botond, in the meantime, would you mind taking a performance profile result? It would be nice to have I think.

Sure, here is a profile taken with the WebCompat preset.

You can see in the screenshots in the 6-9s interval that as the captcha element is being dragged, the page behind the dialog is also scrolling.

User Story: (updated)
Flags: needinfo?(botond)

(I didn't mean to edit the "User Story" field; the browser just seems to remember some stale state sometimes. Reverted now.)

User Story: (updated)

As far as I can tell when an active touchmove event listener is registered Chrome uses this SetEventListenerProperties to inform it to the compositor, which is an independent route from the route where the layer information (DOM structure or some such) is informed to the compositor.

On the other hand in Gecko we inform the both info altogether via triggering a schedule paint.

This difference may have resulted the behavior difference between Chrome and Firefox.

It seems to me that the Chrome's machinery definitely has a race where the compositor hit-testing fails, and Claude told me there is, but I haven't confirmed it yet.

(In reply to Hiroyuki Ikezoe (:hiro) from comment #15)

It seems to me that the Chrome's machinery definitely has a race where the compositor hit-testing fails, and Claude told me there is, but I haven't confirmed it yet.

Now I am sure that there's no race. From the touch event spec:

target
The EventTarget on which the touch point started when it was first placed on the surface, even if the touch point has since moved outside the interactive area of that element.

So browsers need to use the target of the initial touch-start event on subsequent touch events.

I am posting a profile analysis by Claude Code:

Bug 2031963 — profile analysis (build with bug 1839095 patches applied)

Profile: https://profiler.firefox.com/public/psrvytw7h8xgx3dpgqertk8gjvxtsma00r7vyag/calltree/?thread=b
(WebCompat preset, Firefox Nightly 152 on Android 13.)

The profile confirms the diagnosis from comments 11 and 15: APZ in the GPU
process has already received and processed several touchmoves before the
content process can communicate the newly-registered active touchmove
listeners through the paint pipeline.

Timeline of the gesture (Web Content thread t-11 vs GPU thread t-7)

Time Where Event
5.792s content touchstart dispatch begins (32.6 ms duration)
5.794s GPU/APZ 1st PAPZInputBridge::Msg_ReceiveMultiTouchInputEvent (touchstart)
5.802–5.823s content Perform microtasks (21.8 ms) — React commit + listener registration
5.805s, 5.817s GPU/APZ 2nd, 3rd touch events arrive — still no eApzAwareListeners
~5.823s content EventTarget.addEventListener for touchmove runs inside the touchstart handler (visible in bottom-up tree under static.kwcdn.com!K/y and static.kwcdn.com!K/v) → triggers SchedulePaint
5.825s content touchstart returns; RefreshDriverTick + ViewManagerFlush Transaction 1504 sent to GPU
5.827–5.883s GPU/APZ 4 more Msg_ReceiveMultiTouchInputEvent arrive
5.829s content 35.5 ms dom::PostMessageRunnable/message handler runs (more main-thread JS work)
5.865s content PCompositorBridge::Msg_DidComposite for tx 1504
6.007s content PanZoom:StateChange — APZ has already started panning
6.008s content First PBrowser::Msg_RealTouchMoveEvent2 reaches main thread, priority InputLow (3) (subsequent ones are InputHighest (8)) — APZ has downgraded the channel because it's already scrolling

Why the patches from bug 1839095 don't help here

Per comment 11, APZ's hit-test result for the touch block is captured at
touchstart and used for the rest of the block. The fix in bug 1839095 relies
on APZ knowing about active touchmove listeners by the time it would otherwise
begin panning. In this profile:

  1. The site registers active touchmove listeners synchronously inside the
    touchstart handler (the bottom-up tree shows EventTarget.addEventListener
    on the path touchstart → … → static.kwcdn.com!K/y and K/v).
  2. The handler itself runs for 32.6 ms — JavaScript-heavy. While it runs,
    APZ in the GPU process is already receiving touchmoves (4 events in the
    GPU between 5.794s and 5.838s).
  3. The SchedulePaint triggered by addEventListener cannot send a
    transaction to WebRender until after the touchstart handler returns. The
    first paint commit (tx 1504) goes out at 5.825s and is composited around
    5.865s — but APZ has already taken its decision by then.
  4. Top samples for the 5.79–6.01 s window: R (PostMessage handler, 56% of
    active CPU) and touchstart (31%). All in obfuscated kwcdn JS.

What this points at

This matches Hiro's analysis in comment 15: Gecko's only path to inform APZ
about an eApzAwareListeners change goes through a paint commit + scene
update + WebRender hit-test data refresh. Chrome's
EventHandlerRegistry::SetEventListenerProperties sends listener-property
bitfields to the compositor on a dedicated IPC channel that bypasses the
paint/scene pipeline, so the compositor can update its cc::TouchActionRegion
without waiting for the next paint.

A Gecko fix in the same shape — a direct content→GPU IPC for "this layer now
has an active touchmove/touchstart listener", landing in APZCTreeManager
immediately rather than via WR scene rebuild — would let the patches from
bug 1839095 actually take effect on this site even when the listener is
registered inside the touchstart handler.

-- END OF ANALYSIS --

So Claude suggests a new light weight path to inform eApzAwareListeners from the content to APZ. But I am skeptical about the suggestion. As you can see the timeline table above, at 5.805s 2nd touch event arrived in APZ, ~5.823s in the content SchedulePaint was triggered. It's already late, 20ms or so.

See Also: → 2041154

When a touchstart JS handler dynamically registers a non-passive
touchmove (or similar APZ-aware) listener, APZ doesn't learn about
eApzAwareListeners until the next painted WebRender transaction has
been scene-swapped on the compositor -- ~70 ms after the listener
add on temu.com. The first touchmove arrives at APZ well before
that, the cached touchstart hit-test is reused, and the block stays
committed to async-scroll.

Add a direct content -> APZ IPC (NotifyApzAwareListenerAdded) carrying
the nearest scroll container's ViewID. APZCTreeManager records the
(LayersId, ScrollId) pair under mMapLock; WRHitTester ORs in
eApzAwareListeners when a hit-test result's target APZC equals that
pair or descends from it within the same layers id.

EventListenerManager::ProcessApzAwareEventListenerAdd resolves the
ScrollId via FindIDFor (not FindOrCreateIDFor): if no ViewID has
been assigned yet, or the element has no primary frame / scroll
container ancestor, fall back to the existing SchedulePaint path so
the regular display-list rebuild propagates the flag.

Fast-path entries are kept until the layer tree is removed
(NotifyLayerTreeRemoved). Clearing them in PrepareNodeForLayer would
race against a paint that was in flight when the listener was added.

Add MOZ_LOG=apz.fastpath logging on both sides to trace the flow.

Comment on attachment 9588270 [details]
WIP: Bug 2031963 - Fast-path notify APZ on non-passive APZ-aware listener registration.

Revision D301665 was moved to bug 2041154. Setting attachment 9588270 [details] to obsolete.

Attachment #9588270 - Attachment is obsolete: true
Depends on: 2041154

I can still reproduce the issue in a 20260607212012 Fenix Nightly build, so it seems like this needs more diagnosis.

See Also: 1839095
See Also: 2041154

Okay, my hypothesis is that the original version of the fast-path notification landed in bug 2041154 was to permissive, thus it has affected on temu.com accidentally. Then the original version was backed out due to bug 2043349 etc. Then the revised version of thefast-path notification is no longer permissive, it works only for a newly setup active touchmove event listener while APZ hasn't received any touchmove events. Thus, it has no longer affected on temu.com.

That matches my analysis in comment 11 (and comment 17).

So I would say, now we need to diagnose why the main-thread in the content process doesn't have a chance to send the notification. I think performance team would be the best team to do an initial diagnosis.

I managed to see the drag-and-drop type captcha on my Windows touch screen laptop, surprisingly the images are not able to drag with touch at all. I think the site does something different depends on UA string?

I wanted to differentiate this bug from bug 1764177, but as per the above result, we can't.

Also note that I've tried a variety of ways to register preventDefault() touchmove event listeners on Chrome with debugging, as far as I can tell Chrome's behavior is same as ours since bug 2041154. A difference I noticed is that Chrome doesn't prevent scrolling if an active with `once: true} touchmove event listener is registered in a passive touchstart event listener. But Firefox does.

See Also: → 2052759
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: