XHR/Fetch does not propagate the user activation flag |
|||||||
Issue descriptionChrome Version: 62 OS: Linux (though not OS-specific) What steps will reproduce the problem? Download the attached HTML file, and serve it on localhost (e.g., "python -m SimpleHTTPServer"). (1) Open the file in Chrome. (2) Open the JS Console. (3) Click all the buttons (exiting fullscreen in between them all). What is the expected result? Each button successfully enters fullscreen, except for "Sleep (5 s)" which is too long. What happens instead? The Fetch and XHR buttons do not enter fullscreen. The following message is printed to the console: Failed to execute 'requestFullscreen' on 'Element': API can only be initiated by a user gesture. Details This page uses the fullscreen API as a test, but really that is just a proxy for any API that requires "triggered by user activation" [1]. As you can see, using setTimeout for < 1 s does propagate the "user gesture" flag such that requestFullscreen can be successfully called after 0.9 s of an asynchronous operation. I believe that fetch() or XMLHttpRequest should do the same. Having said that, this issue exists in both Chrome and Firefox. (In fact, Firefox doesn't even let you do it after sleeping for 0 s.) My (limited) reading of the spec suggests this should work. There is nothing in the fetch [2] or XHR [3] specs relating to user gesture or user activation. However, the HTML spec [1] is pretty clear about this: "An algorithm is triggered by user activation if ... the task in which the algorithm is running was queued by an algorithm that was triggered by user activation, and the chain of such algorithms started within a user-agent defined timeframe." In this case: - The task in which the algorithm is running was queued by the fetch algorithm. - The fetch algorithm was run synchronously from the 'click' event handler, so it was triggered by user activation. - The "user-agent defined timeframe" should be 1000 ms in Chrome (as evidenced by the setTimeout behaviour). So I see no reason why the response handler for fetch() and XMLHttpRequest should not be allowed to request fullscreen and other user-activation operations. It's possible I'm wrong, in which case I'd like to be pointed at the wording in the spec that prevents this (and then I will raise an issue with the spec, because I don't think this should be prevented). [1] https://html.spec.whatwg.org/multipage/interaction.html#triggered-by-user-activation [2] https://fetch.spec.whatwg.org [3] https://xhr.spec.whatwg.org
,
Aug 31 2017
Makes sense. The text you quoted is suggesting that fetch() / XHR can also be in the chain. I'm not familiar with the activation. mustaq@, can I get your help? Is the analysis by mgiuca@ looks good also to you?
,
Sep 5 2017
The core problem here is that the current spec for "user activation" is minimal: leaves out important details like consumption of user gestures and timeout. We need to fill in the details but as you also pointed out, the inconsistencies in current implementations make it hard to update the spec. We tested 4 major browsers (Chrome, Edge, FF, Safari) extensively for popups, and observed glaring diffs there (as you observed for full-screen API). We found that every "way" to invoke a popup has its own unique inconsistency! Summary of our findings: https://docs.google.com/a/chromium.org/presentation/d/1syCoPlljJ4mXKvuxoMTuMS15td8JoEY5RwnKYntorDw Details: https://docs.google.com/document/d/1hYRTEkfWDl-KO4Y6cG469FBC3nyBy9_SYItZ1EEsXUA We are working on a simple user activation model to ultimately aim for a consistent cross-browser behavior with a matching spec. See the blocker bug.
,
Sep 6 2017
> The core problem here is that the current spec for "user activation" is minimal: leaves out important details like consumption of user gestures and timeout. That is a problem, but it isn't relevant to this bug. - Consumption: We are not looking to repeatedly perform actions (just one), so whether the gesture token is consumed or not isn't relevant here. - Timeout: Timeout is explicitly left as user-agent-defined in the spec, and similarly, that isn't relevant here because Chrome has already defined the timeout as 1000ms, and we only need it to work within that time frame. I don't think a spec update is necessary here; user agents are simply inconsistent with the spec on this particular point, and I think the right answer is to fix Chrome. Another point: the workaround we thought of is to repeatedly sleep for ~50ms and poll to see whether the fetch has returned. It turns out (as you've pointed out elsewhere) that we only chain setTimeout to a depth of 1, so this won't actually work unless the fetch responds within 50ms. This also goes against explicit text in the spec, which reads: "The task in which the algorithm is running was queued by an algorithm that was triggered by user activation, and the chain of such algorithms started within a user-agent defined timeframe." The "chain of such algorithms" implies you should be able to chain up any number of setTimeouts or other queued tasks, as long as they all complete within 1s.
,
Sep 6 2017
I've updated my test page (attached), with a chained setTimeout, to show that it doesn't work in Chrome.
,
Sep 6 2017
Thanks Matt for clarifying (through a separate thread) the use-case you are trying to solve. I agree that your fetch() repro should work even with the current language, and upon further digging it turns out that our spec-non-compliance for promise handling is the blocker here. I am not sure about the XMLHttpRequest case in your repro though, the onload callback is only /linked/ and not really /queued/ by the user-activation-triggered algorithm. I see our simple model a better solution for both cases, specially for cross-browser implementation possibilities.
,
Sep 7 2017
Hi Mustaq, I did some further investigation into this and found a horrible horrible work-around. My test site is here: https://github.com/mgiuca/user-gesture-testing https://mgiuca.github.io/user-gesture-testing/fetch-gesture.html The horrible work-around is this: upon receiving the user gesture, fire 20 setTimeouts, with delays from 0 ms to 1950 ms (in 50 ms increments). Also fire a fetch() and when it returns, set some state. When each timeout completes, check if the fetch has finished and if so, cancel the remaining timers and return it. All 20 setTimeouts have the user gesture token (because they have a depth of 1), so they are allowed to activate the gesture-requiring API. (Note: You can't do the much simpler approach of chaining the setTimeouts, because of the depth limit.) I've tested this with an artificially delayed network response (only available if you run the demo offline using the Python server) and it works as long as the request returns within 1 s. So if nothing else, this provides a work-around, and shows that there is no inherent security reason to prevent fetch and other async operations from being able to propagate a user gesture token (because it can already be done with this workaround). > I am not sure about the XMLHttpRequest case in your repro though, the onload > callback is only /linked/ and not really /queued/ by the user-activation-triggered > algorithm. It is queued. https://xhr.spec.whatwg.org/ "The send(body) method must run these steps:" [Assume this algorithm is triggered by user activation] ... "10. If the synchronous flag is unset, run these substeps:" [Same algorithm] "4. Fetch req. Handle the tasks queued on the networking task source per below." [Same algorithm] [fetch spec]: "queue a fetch task on request to process request end-of-body" [Have not gone into all the details but I'll assume this line is in an algorithm that was queued by the original Fetch algorithm] This *queues* the "process request end-of-body" algorithm defined by the XHR spec. [xhr spec]: "To process request end-of-body for request, run these subsubsteps:" ... "Fire a progress event named load on the XMLHttpRequestUpload object with transmitted and length." "Fire an event" is a synchronous process from that algorithm, so if "process request end-of-body" is queued from an algorithm triggered by user activation, then the "load" event should also be triggered by user activation.
,
Sep 8 2017
Thanks for the work-around to prove functional similarity, and for the explanation why XMLHttpRequest's onload is queued. I see three possible solutions for the problem: [A] Fix Promise (Issue 404161) for our current UGI-based model, and hope that fetch() starts working. [2] Add UGI token code for XMLHttpRequest tasks, as we currently do for exiting user APIs. [3] Wait for the new model to be shipped (Issue 696617). I am biased to suggest [3] since that's a long term solution. We can have [2] done to meet your deadline too. Let's meet to see what works best for you.
,
Sep 11 2017
If you can do #2 without too much work then I think it would be worth it (and would be grateful!). I think #A sounds like too much work if you are going to refactor it anyway. Does that sound like a fair assessment/compromise? (Lol.. [A], [2] and [3].)
,
Nov 1 2017
,
Feb 20 2018
Unassigning myself as I'm leaving the project soon. Some of the networking/loading team could take this, but needs priority evaluation. +kenjibaheux
,
Feb 21 2018
To gauge the priority, I would like to understand if this is impacting lots of users through specific websites facing said issue. Any examples?
,
Feb 22 2018
#12 I'll send you an example offline.
,
Feb 27 2018
As we have discussed in an email thread, User Activation v2 solves the problem. So we decided to wait for it to ship, instead of trying to fix v1 code just for this bug.
,
Feb 27 2018
|
|||||||
►
Sign in to add a comment |
|||||||
Comment 1 by mgiuca@chromium.org
, Aug 31 2017Owner: tyoshino@chromium.org