Hi Everyone,
Since no one responded to my earlier message, let me be even more
specific, and propose a set of events, intended firing times, and
examples of how various progress feedback elements would react to
them. I propose the following set of events:
"loadstart" - fired as soon as the load is initiated, even if there
is no progress to report
"loadprogress" or just "progress" - has the semantics of the current
progress event, may fire any number of times, but must fire at least
once when length is known (or known to be indeterminate until load
completes), and at least once when all data has been received that is
going to be (these may be the same time). It may fire any number of
times in between, but we should give guidelines.
"error" - may fire any time after "loadstart" if the resource fails
loading. At least one of "error", "load" or "loadcancel" must be fired.
"load" - fires after "loadstart" and one or more "progress" events
when the load completes successfully.
"loadcancel" - fires if the load is somehow cancelled (for example an
<img> element may have had a new src attribute assigned in the middle
of loading a large image).
There would also be upload versions of all five: "uploadstart",
"uploadprogress", "uploaderror", "upload", "uploadcancel". These do
all the analogous things for upload.
So let's look at a sample timeline:
XMLHttpRequest for 0-length resource
XHR starts load load is complete
| |
|------------------------|------------------|
| | |
"loadstart" "progress" "load"
[0, 0, true]
Here's how the controls react:
Progress bar: at "loadstart" displays empty determinate progress bar,
at "progress" flashes full, at "load" goes back to disabled
Counter: at "loadstart" shows "-" at "progress" shows "0 bytes / 0
bytes"
Simple spinner: start spinning at "loadstart", stop at "load"
XMLHttpRequest for 32k resource, known length
XHR starts load load is complete
| |
|------------------------|------- ... ---|-----------|
| | | |
"loadstart" "progress" "progress" "load"
[0,32k,true] [32k,32k,true]
Here's how the controls react:
Progress bar: at "loadstart" displays determinate empty progress
bar,, at last "progress" flashes full, at "load" goes back to disabled
Counter: at "loadstart" shows "?" at first "progress" shows "0 /
32k" at last "progress" shows "32k / 32k"
Simple spinner: start spinning at "loadstart", stop at "load"
XMLHttpRequest for 32k resource, unknown length
XHR starts load load is complete
| |
|------------------------|------- ... ---|-----------|
| | | |
"loadstart" "progress" "progress" "load"
[0,0,false] [32k,0,false]
Here's how the controls react:
Progress bar: at "loadstart" displays determinate empty progress bar,
at first "progress" shows empty indeterminate, at "load" goes back to
disabled
Counter: at "loadstart" shows "?" at first "progress" shows "0 / ?"
at last "progress" shows "32k / ?" at "load" shows "32k / 32k".
Simple spinner: start spinning at "loadstart", stop at "load"
As you can see, to cover all the use cases, "loadstart", a "progress"
event at the beginning, a "progress" event at the end, and "load" are
all needed and serve different purposes. And you need the boolean to
in "progress" to tell you whether the length is determinate or not.
You cannot overload these for the following reasons:
1) You can't combine "progress" [0,0,true] and "progress" [0,0,false]
because: then you don't know whether to flash full (many progress UIs
like to flash the completed progress bar for at least a moment) or to
switch to the indeterminat progress bar.
2) You can't combine "loadstart" with "progress" [0,?,?] because: You
want to start showing feedback the moment loading starts, even if no
server response has yet told you what the content length will be, or
whether it is determinate, but you want to show the total or reflect
that it is unknown the moment that is the case.
3) You can't combine the final "progress" with "load" because: if you
only have "load", there's no way to know the final total of bytes in
the indeterminate case, and if you only have the final "progress",
you can never tell in the indeterminate case that the load has
finished. After all, "load" may never happen, you could get "error"
instead.
How the "error" and "loadcancel" events would fit in is left as an
excercise to the reader; I hope it is obvious that you would need it
to have a working progress UI driven solely by events, which I think
is a desirable goal. It may sound overengineered to have so many
events, but it's the only way to make it possible to build correct UI.
Regards,
Maciej
On Jan 28, 2007, at 1:25 AM, Maciej Stachowiak wrote:
Hi Chaals,
I think to have further fruitful discussion, I think we need to
agree on what the use cases for the progress event are. Let me
propose what I think is at least one valid use case; if progress
events cannot satisfy it, then I hope we can all agree it is
insufficient.
The use case I propose is as follows: a reusable XBL2 control that
shows a progress bar (either determinate or indeterminate), a
percentage complete (when applicable), and a display of currently
received bytes out of total bytes. It connects to an arbitrary
event target via an event listener. I hope we will agree that we
want something like this to be possible. This is the kind of very
basic progress UI that native applications show, and if it was not
correctly doable then clearly progress notification is insufficient.
Now, let's consider one aspect of this UI, the progress bar. The
progress bar can be in one of three basic states, "disabled",
"determinate" (showing progress out of a known total) or
"indeterminate" (the barber pole or cylon eye that indicates
progress out of an unknown total). When in the determinate state,
it has an additional parameter that affects it's display,
proportion complete, which goes from 0.0 to 1.0. Fundamentally,
this progress bar is a state machine, with state transitions
triggered by progress events received.
Now, since this control is general, it has to handle any kind of
resource you might find on the internet. Just looking at http, this
includes all the following cases:
A) A resource of known 0 size.
B) A resource of known nonzero size.
C) A resource of initially unknown size that ends up being 0 size.
D) A resource of initially unknown size that ends up being nonzero
size.
Let's say the progress bar starts in disabled state. As soon as a
load starts, you want it to go to either determinate (with some
value, maybe 0) or indeterminate. In between, you want the progress
bar to update. At the end, you want it to go back to indeterminate
state (possibly after a brief pause at 100%). The events need to be
able to disambiguate all of these state transitions. In addition,
they should be able to handle an error part way through downloading
or uploading a resource.
Now, let's go back to the issues I raised:
On Jan 27, 2007, at 6:21 PM, Charles McCathieNevile wrote:
[Please follow up only to webapi...]
On Sat, 27 Jan 2007 19:14:24 -0500, Maciej Stachowiak
<[EMAIL PROTECTED]> wrote:
On Jan 26, 2007, at 1:54 PM, Charles McCathieNevile wrote:
Hi,
following our face to face meeting, we are planning some changes to
progress:
Based on my experience designing the Objective-C API that drives the
progress bar and other progress indicators in Safari, I think these
proposed changes make the API unsuitable for common UI use cases.
1. Make the "total" attribute 0 if the length is unknown, and drop
the boolean "lengthComputable".
The rationale is that if you really have a zero-length load, it is
unlikely to
ever have time to fire a progress event, and will almost certainly
only fire any
in a really degenerate case. Having a large number was a bad idea,
since one day
you will have a large number of bytes, and having anegative number
meant having
a signed instead of unsigned integer.
That means on the first progress event, if there is no data, 0
progress out of a 0 total is indistinguishable from 0 progress
out of
an indeterminate total. I think one common case for these events
will
be to drive progress UI. Generally, common user interface toolkits
have different widgets for indeterminate and determinate progress.
This way, you can't tell which one to display on the first event.
Agreed in principle. In practice, we felt that you were unlikely
to get a
progress event with a value 0 except in the indeterminate case -
if you know
that you are transferring 0 of a total of 0 bytes you simply fire
the load event
instead rather than setting up and instantly destroying a transfer
widget. (If
you want to do that, you can do it on load completing if you
hadn't got a
progress event yet...)
So does the spec forbid dispatching a 0-current 0-total event when
the total is known to be 0? I don't think it does. If it did,
though, you would have know way of knowing when the load started
for a known-empty resource, and such things do exist. If it
doesn't, and your general-purpose progress bar widget receives a
0/0 event, you don't know whether to go to empty determinate
progress bar or indeterminate progress bar. And you won't know
which is right until the next "load" or "progress" event; in the
case of a truly indeterminate resource, which could be a long time.
Either way, I think the API would not cover all the needed state
transitions.
But
on the other hand, the network layer almost always knows the
total is
indeterminate very early, so having a "totalKnown" or
"lengthComputable" boolean or whatever is no great burden.
2. Remove the preload and postload events.
You know when it finished, because the load event or whatever is
spitting out progress will have finished.
Would all things subject to progress events have a "load" event as
well? If so, I am ok with this. Otherwise, you can't tell when
something with an indeterminate total is done, so I would object.
This is the crux of the issue. I would be interested in a use case
for a
progress event that doesn't do this.
I'm not sure what you are asking. Progress events spec could be
used for anything, and not all of them will necessarily fire a
"load" event. For example, in HTML <link rel="stylesheet"
href="foo.css"> results in a load but does not, in current UAs,
fire a "load" event. If progress events rely on a "load" event to
give complete functionality, they should spec it.
Note also that many things currently subject to "load" events have
weird rules for when it does and doesn't fire (as opposed to the
"error" event or none at all).
You know when it started, because you got a progress event.
The moment the load starts, the total is not known, and there are 0
bytes received, but with many protocols (for instance http) you will
know the total at or before the time the first data chunk is
received. Given this, I think it's good to have an event that tells
you when the connection has been initiated, before when you get any
network response back; the latter should be a progress event.
Right. That is perfectly in line with what you can do under the
current spec,
but we don't force it...
I'm not sure what you are saying is in line with the current spec.
Are you saying it is permitted to send a 0/0 event, and then soon
after 0/total for some known total? That would be weird. Also, our
hypothetical general progress bar control could not rely on it,
since, as you say, the spec does not require it. So there would be
no way to write interoperable code that made use of this.
3. Add an uploadprogress
It is possible to construct an XHR that is moving content up and
down at the
same time, so knowing when progress refers to one or the other is
useful.
This seems like a good change.
4. Rename loadprogress to progress
It's shorter.
Seems ok but I wonder whether the event is appropriate for non-
loading cases of progress, and whether such cases will come up.
Me too. In principle you might have a script which sends progress
events as it
does some monstrous calculation on a table or something, and it
wouldn't load.
It also wouldn't be transferring bytes. So it might do something
that looks very
similar but is defined seperately. Should we be covering that
range of use case
in this spec, or leave it for authors or a later group to spec out?
It would be hard to define a fully general notion of progress. The
question is really whether to reserve the generic "progress" name
for other things. I don't think it is that important though.
Regards,
Maciej