Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

document.fullscreenElement in scroll event after entering fullscreen matters for compat #74

Closed
foolip opened this issue Jan 26, 2017 · 13 comments

Comments

@foolip
Copy link
Member

foolip commented Jan 26, 2017

In https://crbug.com/680467 there is fallout from aligning the timing of document.fullscreenElement and fullscreenchange event with animation frames in https://crbug.com/402376.

On https://twitter.com/BCCI, if ones scrolls down a bit and plays a video in fullscreen, the iframe the video is in is removed from the document, leaving things in a broken-looking state. I speculate that this code tries to remove video that have scrolled out of view.

The crucial change is that in the scroll event fired after entering fullscreen, the fullscreen steps have not yet run, so to the page it looks like the page has simply been resized, causing a scroll.

I investigated current behavior:

  • In Edge 14, the scroll event is fired after the fullscreenchange event, and so naturally the fullscreen element has already changed.
  • In Firefox 54, the scroll event is fired before the fullscreenchange event when the fullscreen element has not yet changed. For some reason, it still does not get this bug on Twitter.
  • In Chrome 55 (before my changes) and Safari 10, the scroll event is fired before the fullscreenchange event, and yet the fullscreen element has already changed.

I can see a few alternatives here, in rough order of preference:

  • Apply the fullscreen state changes immediately on resize and delay only the events until animation frame timing.
  • Tweak the order of scroll and fullscreen change events in https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model
  • Introduce a document.pendingFullscreenElement that could be used by Twitter to avoid this problem.
  • Ask Twitter to keep track of whether they are entering fullscreen themselves to avoid this.
  • Suppress scroll events in fullscreen entirely.

@upsuper @jernoble @aliams

@upsuper
Copy link
Member

upsuper commented Jan 26, 2017

Apply the fullscreen state changes immediately on resize and delay only the events until animation frame timing.

FWIW, this is what Gecko does, actually. I thought it is hard to distinguish between this behavior and the specced behavior. It seems I was wrong.

@upsuper
Copy link
Member

upsuper commented Jan 26, 2017

BTW, if the element is already removed from the document, I suppose browser should exit fullscreen anyway... unless they just hide the element.

@foolip
Copy link
Member Author

foolip commented Jan 26, 2017

@upsuper, would you support changing the spec to be closer to Gecko? It is rather unfortunate to not make the state changes right before the events, but then the resize has already happened so there's no way to have everything happen at the very same time. (Unless, I suppose, every observable side effect of the resize is hidden until the resize event, which seems like a lot of effort to sort through.)

BTW, if the element is already removed from the document, I suppose browser should exit fullscreen anyway... unless they just hide the element.

Yeah, I recall you mentioned a change like this in Gecko, and I have a TODO in Blink around this. FWIW, it will be slightly easier to handle this situation at resize time, because at least one doesn't need to consider how things can change between then and when the animation frame task is run (in the current model).

scheib pushed a commit to scheib/chromium that referenced this issue Jan 28, 2017
…th the spec (patchset #3 id:40001 of https://codereview.chromium.org/2573773002/ )

Reason for revert:
Delaying fullscreen element stack changes until the animation frame
after resize, and thus after resize-triggered scroll events, caused a
regression with twitter video embeds that may require spec changes to
fix: whatwg/fullscreen#74

BUG=680467

Original issue's description:
> Sync requestFullscreen() and exitFullscreen() algorithms with the spec
>
> The central change is the timing for when the fullscreen element stack
> is modified and when the events fire. This makes it possible to make
> webkitCurrentFullScreenElement an alias of fullscreenElement, as the old
> notion of a single fullscreen element is gone.
>
> Previously, fullscreen requests did:
>  1. In Fullscreen::requestFullscreen(), synchronously modify
>     m_fullscreenElementStack, visible in document.fullscreenElement, but
>     not document.webkitCurrentFullScreenElement. Also enqueue events to
>     fire, without starting the timer to fire them.
>  2. As soon as the resize happens, in Fullscreen::didEnterFullscreen(),
>     set m_currentFullScreenElement, affecting :-webkit-full-screen
>     document.webkitCurrentFullScreenElement. Start the event timer.
>  3. When the timer fires, events are dispatched. If the tree has changed
>     since the events were enqueued, additional events may be fired.
>
> And similarly for exit. For errors, requestFullscreen() would itself
> start the timer to fire the events.
>
> Now, fullscreen requests will:
>  1. In Fullscreen::requestFullscreen(), append to a list of pending
>     requests.
>  2. When the resize happens, enqueue an animation frame task for each
>     pending request.
>  3. In the animation frame task, apply all changes, decide which events
>     to fire and then fire them.
>
> TEST=run-webkit-tests fullscreen/ imported/wpt/fullscreen/
>      webkit_unit_tests --gtest_filter=*Fullscreen*
>      interactive_ui_tests --gtest_filter=SitePerProcessInteractiveBrowserTest.Fullscreen*
> BUG=402376,402421
> CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
>
> Committed: https://crrev.com/e1d42d636990425056ede44086ef49a1f8c6a0a5
> Cr-Commit-Position: refs/heads/master@{#439607}

TBR=mlamouri@chromium.org,alexmos@chromium.org,eae@chromium.org,esprehn@chromium.org
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=402376,402421
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2

Review-Url: https://codereview.chromium.org/2654083006
Cr-Commit-Position: refs/heads/master@{#446643}
@upsuper
Copy link
Member

upsuper commented Jan 29, 2017

I would support that. I think it makes sense to have the document changes its state as soon as possible, hopefully before any script can get a chance to execute, so that we can limit the number of necessary reflow to the minimum.

In the HTML spec, scroll and resize are both dispatched before fullscreen steps, which is probably bad based on that... So yes, let's make it change the state immediately, and keep dispatching events (and resolving promise) in the animation frame task.

Not sure how spec should say about that, though.

@foolip
Copy link
Member Author

foolip commented Jan 30, 2017

@annevk, does a change like that make sense to you, at least enough that I should try a PR?

@foolip
Copy link
Member Author

foolip commented Jan 30, 2017

(I'd probably try to implement and update tests first.)

@annevk
Copy link
Member

annevk commented Jan 30, 2017

So we'd make changes to state just before the "rendering task" and then during the "rendering task" we invoke all the JavaScript callbacks? That sounds reasonable.

One problem that seems likely to come up again is that the infrastructure in HTML is on a per-document basis, whereas browsers do rendering on a per event loop basis, no? So shouldn't we adjust that too/first?

@foolip
Copy link
Member Author

foolip commented Jan 30, 2017

Yeah, that is a problem I've had to grapple with especially when exiting. I can use the phrasing "enqueue animation frame task for document" to make it clear what I intend, and then I can finally define that in HTML.

MXEBot pushed a commit to mirror/chromium that referenced this issue Feb 1, 2017
…th the spec (patchset #3 id:40001 of https://codereview.chromium.org/2573773002/ )

Reason for revert:
Delaying fullscreen element stack changes until the animation frame
after resize, and thus after resize-triggered scroll events, caused a
regression with twitter video embeds that may require spec changes to
fix: whatwg/fullscreen#74

BUG=680467

Original issue's description:
> Sync requestFullscreen() and exitFullscreen() algorithms with the spec
>
> The central change is the timing for when the fullscreen element stack
> is modified and when the events fire. This makes it possible to make
> webkitCurrentFullScreenElement an alias of fullscreenElement, as the old
> notion of a single fullscreen element is gone.
>
> Previously, fullscreen requests did:
>  1. In Fullscreen::requestFullscreen(), synchronously modify
>     m_fullscreenElementStack, visible in document.fullscreenElement, but
>     not document.webkitCurrentFullScreenElement. Also enqueue events to
>     fire, without starting the timer to fire them.
>  2. As soon as the resize happens, in Fullscreen::didEnterFullscreen(),
>     set m_currentFullScreenElement, affecting :-webkit-full-screen
>     document.webkitCurrentFullScreenElement. Start the event timer.
>  3. When the timer fires, events are dispatched. If the tree has changed
>     since the events were enqueued, additional events may be fired.
>
> And similarly for exit. For errors, requestFullscreen() would itself
> start the timer to fire the events.
>
> Now, fullscreen requests will:
>  1. In Fullscreen::requestFullscreen(), append to a list of pending
>     requests.
>  2. When the resize happens, enqueue an animation frame task for each
>     pending request.
>  3. In the animation frame task, apply all changes, decide which events
>     to fire and then fire them.
>
> TEST=run-webkit-tests fullscreen/ imported/wpt/fullscreen/
>      webkit_unit_tests --gtest_filter=*Fullscreen*
>      interactive_ui_tests --gtest_filter=SitePerProcessInteractiveBrowserTest.Fullscreen*
> BUG=402376,402421
> CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2
>
> Committed: https://crrev.com/e1d42d636990425056ede44086ef49a1f8c6a0a5
> Cr-Commit-Position: refs/heads/master@{#439607}

TBR=mlamouri@chromium.org,alexmos@chromium.org,eae@chromium.org,esprehn@chromium.org
BUG=402376,402421
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2

Review-Url: https://codereview.chromium.org/2654083006
Cr-Commit-Position: refs/heads/master@{#446643}
(cherry picked from commit 40a515a53aa3510c608fe60c572045850632e720)

Review-Url: https://codereview.chromium.org/2667913002 .
Cr-Commit-Position: refs/branch-heads/2987@{#216}
Cr-Branched-From: ad51088-refs/heads/master@{#444943}
@foolip
Copy link
Member Author

foolip commented May 18, 2017

@upsuper, this issue is about the timing of fullscreenchange events, and some time in the past week I'm pretty sure I saw a comment for you, somewhere, about changes you were thinking about, to fire the events earlier than the spec currently says. I've searched and searched but can't find it.

Are there changes you think we should make timing-wise that would amount to not aligning the events with animation frame tasks?

I ask because I'm addition to https://crbug.com/680467 there was also a bit of trouble in https://crbug.com/679281 where the interaction with the Screen Orientation API was impacted, and it all boils down to timing.

@mounirlamouri FYI

@upsuper
Copy link
Member

upsuper commented May 18, 2017

I think you mean this comment.

But it seems to me the previous plan in this issue was to make the document state change immediately and only delay the event dispatch?

@mounirlamouri
Copy link
Member

To give more context about that interaction with Screen Orientation API: we implemented a feature in the Blink native controls to lock the orientation when the video goes fullscreen using the web events. @foolip told me that with the new event timing, that feature wouldn't work and we needed to have internal callbacks to keep the feature working. The way it was implemented in Blink is exactly the way a website would implement this feature which makes me think it might break some experiences. For example, there was a Web Fundamentals article advertising deeper interactions between fullscreen and orientation changes: https://developers.google.com/web/fundamentals/media/mobile-web-video-playback

@foolip
Copy link
Member Author

foolip commented May 18, 2017

@upsuper, that was it indeed, "I have a feeling that we may actually want fullscreen events to be dispatched at a very early stage, probably even before resize."

The problem has to do with restoring the original scale and scroll position after exiting fullscreen. This isn't something the spec covers, and I'd be surprised if it works exactly the same in all browsers. In any case, if the orientation lock changes are applied in reaction to fullscreenchange events, it means that when exiting fullscreen, the orientation will still be locked for a few more frames. The problem is the same as with this sequence of events, with a few seconds between each step:

  1. Scroll and zoom in portrait
  2. Enter fullscreen
  3. Lock orientation to landscape
  4. Exit fullscreen
  5. Unlock the orientation

After this, will the scale and scroll position be the same as in the beginning? If yes, then it must mean that unlocking the screen orientation is what restores the pre-fullscreen state, i.e. the APIs are entangled in a way that isn't suggested by either spec. Maybe they should be?

Or, possibly, some change to the timing would happen to fix this problem.

foolip added a commit that referenced this issue May 31, 2017
This means that changes to document.fullscreenElement and other state
will be observable as soon as the resize itself is (e.g. via
window.innerWidth) and before resize or scroll events are fired.

The fullscreenchange event is still delayed to animation frame timing.

This also includes a slight change when /resize/ is true in "exit
fullscreen". By changing /doc/ to /topLevelDoc/ in this case, we can
make sure that we always fully unfullscreen all documents in this case.
This makes a "fully exit fullscreen" corner case unnecessary.

Fixes #74.
foolip added a commit that referenced this issue May 31, 2017
This means that changes to document.fullscreenElement and other state
will be observable as soon as the resize itself is (e.g. via
window.innerWidth) and before resize or scroll events are fired.

The fullscreenchange event is still delayed to animation frame timing.

This also includes a slight change when /resize/ is true in "exit
fullscreen". By changing /doc/ to /topLevelDoc/ in this case, we can
make sure that we always fully unfullscreen all documents in this case.
This makes a "fully exit fullscreen" corner case unnecessary.

Fixes #74.
@foolip
Copy link
Member Author

foolip commented Jun 1, 2017

@mounirlamouri, #92 fixes my original problem in this issue but not the Screen Orientation API issues.

The earliest possible time that one could fire the events I think is in a microtask following the resize, but I don't think that'd really solve anything. The orientation change itself can involve an animation and is in any event out-of-process in some implementations, so it couldn't be guaranteed to happen before the layout at which the original scale and scroll position should be restored.

Maybe a good start would be to write (manual) testharness.js tests for the expected behavior that are as unforgiving timing-wise as possible. Is that something you have the bandwidth for?

foolip added a commit that referenced this issue Jun 15, 2017
This means that changes to document.fullscreenElement and other state
will be observable as soon as the resize itself is (e.g. via
window.innerWidth) and before resize or scroll events are fired.

The fullscreenchange event is still delayed to animation frame timing.

This also includes a slight change when /resize/ is true in "exit
fullscreen". By changing /doc/ to /topLevelDoc/ in this case, we can
make sure that we always fully unfullscreen all documents in this case.
This makes a "fully exit fullscreen" corner case unnecessary.

Fixes #74.
foolip added a commit that referenced this issue Jun 15, 2017
This means that changes to document.fullscreenElement and other state
will be observable as soon as the resize itself is (e.g. via
window.innerWidth) and before resize or scroll events are fired.

The fullscreenchange event is still delayed to animation frame timing.

This also includes a slight change when /resize/ is true in "exit
fullscreen". By changing /doc/ to /topLevelDoc/ in this case, we can
make sure that we always fully unfullscreen all documents in this case.
This makes a "fully exit fullscreen" corner case unnecessary.

Fixes #74.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants