WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
NEW
248303
REGRESSION (iOS 16): popstate events are not fired for swipe-back gesture
https://bugs.webkit.org/show_bug.cgi?id=248303
Summary
REGRESSION (iOS 16): popstate events are not fired for swipe-back gesture
SAKUMA Ryo
Reported
2022-11-24 03:11:37 PST
My device: - iOS: 16.1.1 - User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_1_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Mobile/15E148 Safari/604.1 Steps to reproduce: - Open test1.html in iOS 16.1.1 Safari - Tap "Link" to be navigated to test2.html - Do swipe-back gesture in test2.html Actual result: - alert(1) is not shown Expected result: - alert(1) should be shown Notes: - This issue is not occurred in iOS 15 (and earlier, maybe) - popstate events are fired when `history.back()` (you can confirm this by a button in test2.html)
Attachments
test1.html (test case)
(242 bytes, text/html)
2022-11-24 03:12 PST
,
SAKUMA Ryo
no flags
Details
test2.html (test case)
(490 bytes, text/html)
2022-11-24 03:12 PST
,
SAKUMA Ryo
no flags
Details
test of the duration-sensitive touchend behavior I described in my comment
(1.44 KB, text/html)
2023-03-08 00:08 PST
,
Matt Jacobson
no flags
Details
View All
Add attachment
proposed patch, testcase, etc.
SAKUMA Ryo
Comment 1
2022-11-24 03:12:19 PST
Created
attachment 463703
[details]
test1.html (test case)
SAKUMA Ryo
Comment 2
2022-11-24 03:12:59 PST
Created
attachment 463704
[details]
test2.html (test case)
Radar WebKit Bug Importer
Comment 3
2022-11-26 14:47:31 PST
<
rdar://problem/102683851
>
sideshowbarker
Comment 4
2022-12-26 23:19:29 PST
Also been noticed in questions at Stack Overflow
https://stackoverflow.com/questions/74233075/popstate-does-not-work-since-safari-16-it-was-working-until-safari-15
Chris Dumez
Comment 5
2023-01-03 09:33:11 PST
I just tried and can reproduce on macOS too. I see the alert when clicking the `history.back()` button but not when clicking the back button (or swiping back). I don't have an explanation yet for the different in behavior between history.back() and navigating back via the browser UI (though I suspect this is related to some of the back/forward list hijacking prevention work we did). What I noticed also is that our behavior seems consistent with Chrome. Therefore, if there is a bug, it is not WebKit-specific. Firefox seems to show the alert consistently, as is expected by the Web developer.
Chris Dumez
Comment 6
2023-01-03 09:38:21 PST
Oh, I just looked at the test case more closely and I understand what is happening now. Similarly to Chrome, WebKit recently did some security hardening to prevent bad JavaScript from hijacking the back/forward list. This means that history entries added by JS (e.g. via history.pushState()) get skipped on user navigation unless they were added with a user interaction. In your test case, test2.html calls history.pushState() without a user interaction. As a result, the created history item gets marked with a special flag. If the user swipes back or presses the back button, we will skip this "dummy" history item, and thus not fire a popstate event. This is new intentional behavior, which should be consistent with Blink. If you do not want your history item to get skipped, then you need a user gesture / activation when calling history.pushState() (e.g. calling history.pushState() as a result of a button getting clicked by the user).
Matt Jacobson
Comment 7
2023-03-08 00:06:53 PST
Hey Chris and WebKit team, Thanks for the explanation. For reference, I believe that the change Chris is describing is <
https://bugs.webkit.org/show_bug.cgi?id=241885
> (<
https://commits.webkit.org/251783@main
>), plus follow-up fix in <
https://bugs.webkit.org/show_bug.cgi?id=242947
> (<
https://commits.webkit.org/252649@main
>). Now: in my experimentation on an iPhone SE (2nd edition) running iOS 16.3.1 (20D67), the "is history item added by user interaction" detection is rather unpredictable when using the "touchend" event. Sometimes, a history item added in a "touchend" listener is considered to be "added by user interaction", and sometimes it isn't, with little apparent pattern. However, looking at the WebKit source changes in the commits above, I have a hypothesis. The core of the "added by user interaction?" logic is (from Document.cpp): ``` bool Document::hasRecentUserInteractionForNavigationFromJS() const { if (UserGestureIndicator::processingUserGesture(this)) return true; static constexpr Seconds maximumItervalForUserGestureForwarding { 10_s }; return (MonotonicTime::now() - lastHandledUserGestureTimestamp()) <= maximumItervalForUserGestureForwarding; } ``` That is, we return true if either `processingUserGesture()` returns true or if the time returned by `lastHandledUserGestureTimestamp()` is no more than a second ago. The return value of `lastHandledUserGestureTimestamp()` is a member updated by calls to `updateLastHandledUserGestureTimestamp()`. Among other places, the latter is called when a `UserGestureIndicator` object is constructed and creates a new `UserGestureToken`. Based on context clues, I suspect that `UserGestureToken` is an object that tracks the entire *lifetime* of a touch, not a single touch event. (Here are the clues: `UserGestureToken` has `startTime` member; "gesture" is the term used in AppKit and UIKit to mean an entire touch-event sequence.) This might mean that what `hasRecentUserInteractionForNavigationFromJS()` is *really* testing is whether the *beginning* of the last gesture was more than a second ago. If that's true, then one downstream effect might be the following: attempting to add a history item in a "touchend" listener might succeed or not based on the duration of the entire gesture. And in fact, that seems to be sort of what I'm seeing! I'm not able to measure an exact one-second threshold (from the measurements I'm able to do in JavaScript), but I definitely see that *short* gestures tend to succeed (i.e., add the history item in a way that the back button respects), and *long* gestures tend not to. (Maybe iOS changes that one-second value to something else? Maybe there is a race? I'm not breaking out a disassembler tonight, sorry! But I'm interested to know what's going on there.) I'll attach a test case that I'm using to demonstrate the above. Hope this is enough to help you track down the bug.
Matt Jacobson
Comment 8
2023-03-08 00:08:42 PST
Created
attachment 465356
[details]
test of the duration-sensitive touchend behavior I described in my comment
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug