This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 24710 - Firing of progress events is inconsistent (sync vs queue a task)
Summary: Firing of progress events is inconsistent (sync vs queue a task)
Status: RESOLVED FIXED
Alias: None
Product: WHATWG
Classification: Unclassified
Component: HTML (show other bugs)
Version: unspecified
Hardware: Other other
: P3 normal
Target Milestone: Unsorted
Assignee: Ian 'Hixie' Hickson
QA Contact: contributor
URL: http://www.whatwg.org/specs/web-apps/...
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-02-18 11:11 UTC by contributor
Modified: 2014-03-18 18:24 UTC (History)
4 users (show)

See Also:


Attachments

Description contributor 2014-02-18 11:11:05 UTC
Specification: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html
Multipage: http://www.whatwg.org/C#loading-the-media-resource
Complete: http://www.whatwg.org/c#loading-the-media-resource
Referrer: http://www.whatwg.org/specs/web-apps/current-work/multipage/

Comment:
Firing of progress events is inconsistent (sync vs queue a task)

Posted from: 58.187.186.41
User agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.58 Safari/537.36 OPR/20.0.1387.30 (Edition Next)
Comment 1 Philip Jägenstedt 2014-02-18 11:29:58 UTC
Per spec, progress events are fired in two cases:

1. "While the load is not suspended (see below), every 350ms (±200ms) or for every byte received, whichever is least frequent, queue a task to fire a simple event named progress at the element."

2. "Once the entire media resource has been fetched (but potentially before any of it has been decoded): Fire a simple event named progress at the media element."

Case 2 is one of very few cases where an event is fired outside of a task for media elements. This is not great, because it means that for a fast resource (e.g. a data: URL) that progress event could be fired even before the loadstart event. This is probably not intentional.

Suggest queuing a task to fire the last progress event and then update networkState, which should both make the event order and networkState during the event deterministic.

This issue was noted while looking at a flaky test for networkState during the progress event: https://code.google.com/p/chromium/issues/detail?id=306249#c4
Comment 2 Ian 'Hixie' Hickson 2014-02-20 20:42:09 UTC
It's not outside a task, it's on the networking task queued by the 'fetch' algorithm. Many events are fired from those tasks, actually, like addtrack, error, suspend, etc.

That the 'loadstart' event happens on a different task source is an interesting point, though. Maybe I should move 'loadstart' to the network task source? Maybe the 'progress' one as well? Or maybe we should just move all the networking tasks queued for a media element to the media element's task source. That's probably the saner solution.
Comment 3 Ian 'Hixie' Hickson 2014-02-20 22:14:33 UTC
I think the right way to fix this is to have the tasks queued by the 'fetch' algorithm immediately just queue up a new task on the media element's task source to do all the work that they're currently doing. It's ugly in the spec, but the result is to put all the events into a defined order, which seems desireable.
Comment 4 Ian 'Hixie' Hickson 2014-02-21 00:03:55 UTC
If you agree with the change done here, and think we should also do it for the text track fetching, then please reopen this bug to have me do that.

If you disagree with the change done here, please reopen the bug so I can revert it and change it in a different way (what way?).
Comment 5 contributor 2014-02-21 00:04:12 UTC
Checked in as WHATWG revision r8496.
Check-in comment: Clarify the order of events for media elements
http://html5.org/tools/web-apps-tracker?from=8495&to=8496
Comment 6 Philip Jägenstedt 2014-02-21 07:03:36 UTC
I agree with the change. I think that any tasks that are queued in HTMLMediaElement-related algorithms should use the media element event task source and that events fired at audio/video/source/track elements should only be fired from such tasks. Here are the cases I can see where that's currently not true:

In "Failed with attribute" and "Failed with elements" the DOM manipulation task source is used. That means the pending error event won't be canceled by load(), which is bad.

"Queue a task to fire a simple event named suspend at the element, using the DOM manipulation task source."

"When a media element's download has been suspended, the user agent must queue a task, using the DOM manipulation task source, to set the networkState to NETWORK_IDLE and fire a simple event named suspend at the element."

I'm actually not able to find the bits that need updating for track fetching, but I trust you know what you're talking about.

Finally, it's a bit odd to have "queue a task" in "For video elements, set the videoWidth and videoHeight attributes, and queue a task to fire a simple event named resize at the media element." now that those steps are already running in a task. Not sure how to fix that, maybe revert your recent change and queue a task for each step individually instead?
Comment 7 Simon Pieters 2014-02-21 08:50:04 UTC
(In reply to Philip Jägenstedt from comment #6)
> In "Failed with attribute" and "Failed with elements" the DOM manipulation
> task source is used.

At least this one I think is to ensure that the error event on video/source is fired before window.onload. There are tests that assume that is the case.

> That means the pending error event won't be canceled by
> load(), which is bad.

I agree that is bad. Any ideas?
Comment 8 Philip Jägenstedt 2014-02-22 17:48:52 UTC
Oh wait, the load algorithm already says "If there are any tasks that were queued by the resource selection algorithm (including the algorithms that it itself invokes) for this same media element from the DOM manipulation task source in one of the task queues, then remove those tasks." so the problem doesn't exist.

However, it sure would be nicer to implement this if all tasks that can be cancelled are on the same task source...

As long as clearing the delaying-the-load-event is done in the same task as the error event is fired or in a later task on the same task source, there won't be any race condition. For both "Failed with attribute" and "Failed with elements", it seems to me that just using the media media task source and not clearing the flag until the error event has fired would be fine.
Comment 9 Philip Jägenstedt 2014-02-22 17:54:02 UTC
If I understand task sources correctly, there's currently actually no guarantee that loadstart will fire before error, since they're on different task sources? That seems very broken if true.
Comment 10 Ian 'Hixie' Hickson 2014-02-24 18:48:42 UTC
I should just do the same for the events that use the DOM manipulation task source as I did for the events that use the fetching task source, but in reverse: queue a task on the media element one, which just queues a task on the DOM one. (We already wait for the DOM one to be run before continuing, so the reverse problem doesn't exist.) Does that make sense?
Comment 11 Philip Jägenstedt 2014-02-25 03:09:14 UTC
Queueing and task to queue a task is rather strange, is that really necessary?

For "Failed with attribute" I don't understand why we need to involve the DOM manipulation task source at all. Why not just queue a task to do those substeps on the media element task source and then drop "queue a task" for clearing delaying-the-load-event flag?

For "Failed with elements" can we just "Queue a task to fire a simple event named error at the candidate element."? That task is guaranteed to be run before task that is queued after a stable state to clear the delaying-the-load-event flag, so I can't see a race condition.
Comment 12 contributor 2014-02-27 20:47:36 UTC
Checked in as WHATWG revision r8514.
Check-in comment: Simplify task ordering in media elements to aid implementations.
http://html5.org/tools/web-apps-tracker?from=8513&to=8514
Comment 13 Ian 'Hixie' Hickson 2014-02-27 20:47:45 UTC
Ok in the "Failed with attribute" case queueing a task from the task was just an error I introduced in January (bug 24353). That's fixed. Using the DOM manipulation task source in theory was to avoid races between 'load' and 'error', but it turns out at some point I over-fixed this and the DOM manipulation task source is no longer needed as far as I can tell (bug 18570). Fixed that too.

In the "Failed with element" case, you're right that since we now stop delaying the load event using the media element task source, if we fire 'error' using the media element task source immediately before, the 'load' event can't fire before 'error'. So that doesn't need the DOM manipulation task source either now. Fixed.

There's a couple other events that used the DOM manipulation task source, 'suspend'. I've changed those too. It seems to me that other changes since have fixed the event ordering problems for those in a similar way.

MediaControllers and track lists still use the DOM source. Not sure what to do with those exactly. File a separate bug if you want that changed too (I didn't look into it very far).

I haven't yet done anything for these two comments, so leaving this open:

(In reply to Philip Jägenstedt from comment #6)
> 
> I'm actually not able to find the bits that need updating for track
> fetching, but I trust you know what you're talking about.
> 
> Finally, it's a bit odd to have "queue a task" in "For video elements, set
> the videoWidth and videoHeight attributes, and queue a task to fire a simple
> event named resize at the media element." now that those steps are already
> running in a task. Not sure how to fix that, maybe revert your recent change
> and queue a task for each step individually instead?
Comment 14 Philip Jägenstedt 2014-03-03 04:00:12 UTC
(In reply to Ian 'Hixie' Hickson from comment #13)
> Ok in the "Failed with attribute" case queueing a task from the task was
> just an error I introduced in January (bug 24353). That's fixed. Using the
> DOM manipulation task source in theory was to avoid races between 'load' and
> 'error', but it turns out at some point I over-fixed this and the DOM
> manipulation task source is no longer needed as far as I can tell (bug
> 18570). Fixed that too.
> 
> In the "Failed with element" case, you're right that since we now stop
> delaying the load event using the media element task source, if we fire
> 'error' using the media element task source immediately before, the 'load'
> event can't fire before 'error'. So that doesn't need the DOM manipulation
> task source either now. Fixed.
> 
> There's a couple other events that used the DOM manipulation task source,
> 'suspend'. I've changed those too. It seems to me that other changes since
> have fixed the event ordering problems for those in a similar way.

I've reviewed these changes and they look good.

> MediaControllers and track lists still use the DOM source. Not sure what to
> do with those exactly. File a separate bug if you want that changed too (I
> didn't look into it very far).

On second thought, maybe it isn't a problem. I'll file a bug if I find it to be problematic in test cases or implementation.
Comment 15 Philip Jägenstedt 2014-03-03 04:07:58 UTC
Going back to that flaky test case, it seems like the spec'd behavior for progress events still doesn't guarantee that networkState is NETWORK_LOADING during the progress event, because even though that's guaranteed for the last progress event fired at end of load, one queued to be fired while loading could still arrive after that point. Is that intentional?

Maybe "While the load is not suspended (see below), every 350ms (±200ms) or for every byte received, whichever is least frequent, queue a task to fire a simple event named progress at the element." could be rephrased as a step of "The networking task source tasks to process the data as it is being fetched must each immediately queue a task to run the first appropriate steps from the following list."?
Comment 16 Ian 'Hixie' Hickson 2014-03-13 21:01:55 UTC
(In reply to Philip Jägenstedt from comment #6)
> 
> Finally, it's a bit odd to have "queue a task" in "For video elements, set
> the videoWidth and videoHeight attributes, and queue a task to fire a simple
> event named resize at the media element." now that those steps are already
> running in a task. Not sure how to fix that, maybe revert your recent change
> and queue a task for each step individually instead?

I don't see why it's weird for 'resize', but not for 'durationchange' in the immediately previous step and 'loadedmetadata' in the immediately next step. If you want a change for this, please file a separate bug.

(In reply to Philip Jägenstedt from comment #15)
> Going back to that flaky test case, it seems like the spec'd behavior for
> progress events still doesn't guarantee that networkState is NETWORK_LOADING
> during the progress event, because even though that's guaranteed for the
> last progress event fired at end of load, one queued to be fired while
> loading could still arrive after that point. Is that intentional?
>
> Maybe "While the load is not suspended (see below), every 350ms (±200ms) or
> for every byte received, whichever is least frequent, queue a task to fire a
> simple event named progress at the element." could be rephrased as a step of
> "The networking task source tasks to process the data as it is being fetched
> must each immediately queue a task to run the first appropriate steps from
> the following list."?

How could a progress event fired before the last one get delivered after the last one? Aren't they in the same queue?
Comment 17 Philip Jägenstedt 2014-03-18 04:02:10 UTC
(In reply to Ian 'Hixie' Hickson from comment #16)
> (In reply to Philip Jägenstedt from comment #6)
> > 
> > Finally, it's a bit odd to have "queue a task" in "For video elements, set
> > the videoWidth and videoHeight attributes, and queue a task to fire a simple
> > event named resize at the media element." now that those steps are already
> > running in a task. Not sure how to fix that, maybe revert your recent change
> > and queue a task for each step individually instead?
> 
> I don't see why it's weird for 'resize', but not for 'durationchange' in the
> immediately previous step and 'loadedmetadata' in the immediately next step.
> If you want a change for this, please file a separate bug.

I thought it was odd to essentially "queue a task to queue a task", but in this case it seems simpler and I can't see an event ordering problem right now. Nothing to be done.

> (In reply to Philip Jägenstedt from comment #15)
> > Going back to that flaky test case, it seems like the spec'd behavior for
> > progress events still doesn't guarantee that networkState is NETWORK_LOADING
> > during the progress event, because even though that's guaranteed for the
> > last progress event fired at end of load, one queued to be fired while
> > loading could still arrive after that point. Is that intentional?
> >
> > Maybe "While the load is not suspended (see below), every 350ms (±200ms) or
> > for every byte received, whichever is least frequent, queue a task to fire a
> > simple event named progress at the element." could be rephrased as a step of
> > "The networking task source tasks to process the data as it is being fetched
> > must each immediately queue a task to run the first appropriate steps from
> > the following list."?
> 
> How could a progress event fired before the last one get delivered after the
> last one? Aren't they in the same queue?

I imagined a race between "for every byte received" and "Once the entire media resource has been fetched", but when spelled out that doesn't make sense. The problem has evaporated.

Thanks for the other fixes, this bug can be closed now as far as I am concerned.
Comment 18 Ian 'Hixie' Hickson 2014-03-18 18:24:28 UTC
Thanks for all the help getting this stuff better! It's non-trivial!