[whatwg] Proposed changes to the History API

On Tue, Aug 18, 2009 at 5:04 PM, Justin Lebar<justin.lebar at gmail.com> wrote:
> I'm in the process of implementing the HTML5 History API
> (History.pushState(), History.clearState(), and the PopState event) in
> Firefox. ?I'd like to discuss whether the API might benefit from some
> changes. ?To my knowledge, no other browser implements this API, so
> I'm assuming we have freedom to make large alterations to it.
>
> My basic proposal is that History.pushState() be split into a function
> for creating new history entries and functions or a property for
> getting/setting an object associated with that entry.
>
> In its current form, the History API allows us to identify session
> history entries by way of an arbitrary object, which we pass as the
> first argument to pushState() and which we receive as part of the
> PopState event when that history entry is activated. ?If the page gets
> a null popstate, it's supposed to use the URL to decide what state to
> display.
>
> Notably unsupported by this API is support for pages altering their
> saved state. ?For instance, a page might want to save a text box's
> edit history to implement a fancy undo. ?It could store the edit
> history in a cookie or in the session storage, but then if we loaded
> the page twice in the same tab, those two instances would step on each
> other when we went back and forth between them.
>
> The page could just store its state in variables in the document, but
> then it would loose that state when the browser crashed or was closed,
> or when the browser decided to kick the document out of the history.
>
> I think this page would be better served by a History.setStateObject()
> function, which does exactly what the page wants in a simple fashion.
>
> We'd still keep the history-entry-creating functionality of
> History.pushState() in a new History function (I'll call it
> createNewEntry(), but it probably needs a better name), which takes a
> title and URL, as pushState() does now.
>
> The API might be more intuitive if we had a History.stateObject
> propery, but I'm concerned that then we'd be promising the page that
> we'll keep around literally any objects it wants, including DOM
> objects. ?In fact, I'd be happy restricting the state object to being
> a string. ?If a page wants to store an object, it can convert it to
> JSON, or it can store a GUID as its state string and index into the
> session storage.
>
> Pages could retrieve the state object just as they do now, in a
> PopState event, although we'd probably want to change the name of the
> event. ?We'd probably want to fire PopState on all loads and history
> navigations, since any document might have a state to pop, and even
> those documents which didn't call setStateObject() might store state
> in their URI which they need to restore when their history entry is
> activated.
>
> Last, I'm not sure that we need the History.clearState() function.
> It's confusing (why do we end up at the last entry for the current
> document instead of staying at the current entry?) and I haven't been
> able to come up with a compelling use case.
>
> I think the main benefit of these changes is added simplicity.
> There's a right and wrong way to use pushState, and
> setState/createNewEntry doesn't require such rules. ?But additionally,
> these changes allow pages flexibility to do things we haven't yet
> thought of. ?I don't know what those things might be, but I suspect
> they may be pretty cool. ?:)

I agree with Justin (unsurprisingly since we've been doing a fair
amount of discussing on the subject).

In general when I've been thinking about how to use this API i've had
a hard time wrapping my head around the intended way to use it. It
wasn't until I started thinking of it in the following way that it
made sense:

There's two aspects to the API:

1.
The ability navigate the user to a new URI without forcing the UA to
load a new page. So for example instead of using fragment-identifier
hacks, actually change the uri. So for example gmail uses URIs like:
  https://mail.google.com/mail/?#inbox
  https://mail.google.com/mail/?#label/personal
  https://mail.google.com/mail/?#label/whatwg
  https://mail.google.com/mail/?#label/whatwg/13b4711edac9c1e2

it would be better if you could actually navigate between

  https://mail.google.com/mail/inbox
  https://mail.google.com/mail/label/personal
  https://mail.google.com/mail/label/whatwg
  https://mail.google.com/mail/label/whatwg/13b4711edac9c1e2

and then use the fragment identifier for what it was intended.

2.
The ability to create several session history entries for the same
page which the user can navigate between, and use some type of state
object to disambiguate between these states. For example in our bug
database we allow users to look at attachments, as well as comment on
attachments. When starting to comment on an attachment the page
changes from displaying the attachment, to displaying a <textarea>
pre-populated with the contents of the attachment such that you can
add comments to it. It would be nice if switching modes acted like
navigation so that you could go back

 It would further be nice if your comments weren't lost even if you
navigate away from the page. Thus if the page could store the comment
in the session history entry it could always be retrieved when the
user goes page to that state. Ideally this would happen even if the UA
crashes, or if user navigates far enough away from the page that the
page's Document is unloaded from memory.


When thinking about it this way it became pretty clear that pushState
was not the right API for two reasons. First of all it combines the
two aspects into a single function call. The same function is used to
navigate the user to a different API, as is used to associate data
with a history entry.

Instead having createNewEntry which deals with the first use case, and
setState which (partially) deals with the second makes more sense.
Though there is actually two features in the second use case:

2a. Create new session history entries for the current page.
2b. Associate data with the current session history entry.

setState only does 2b. However createNewEntry could cover 2a if we
make both the uri and title arguments optional.

Further, it makes sense if the browser tries to save the stored state
across restarts or crashes. And across the Document being unloaded
from memory.

Lastly, I don't see where clearState fits into this. It seems to me
that it can be removed.

/ Jonas

Received on Wednesday, 19 August 2009 17:35:46 UTC