Ticket #2532340 (closed defect)

Reporter


Eric Ferraiuolo
Opened: 05/18/12
Last modified: 08/1/12
Status: closed
Type: defect
Resolution: fixed

Owner


Eric Ferraiuolo
Target Release: 3.6.0
Priority: P1 (critical)
Summary: Multiple `html5` Routers can get into an infinite `Y.History` `replace()` loop.
Description:

Chrome (as of 19?) and Firefox actually use the `history.state` part of the HTML5 history APIs. This ends up creating a problem when trying to construct a new Router inside of a route handler. Here's
why (if I can explain this correctly :):

When initializing an HTML 5 Router, it creates its own HTML5History instance. That history instance seeds its `_initialState` from `window.history.state`, then chains to the superclass (HistoryBase)
to finish the initialization logic. HistoryBase checks if there's an `_initialState`, and if so calls `replace()` on itself. See:
http://yuilibrary.com/yui/docs/api/files/history_js_history-base.js.html#l257

The trouble comes in after the first time a HTML5 router's history instance calls `history.push/replaceState()`. The way HistoryBase is currently setup it will _always_ save at least an empty object,
`{}`, as the state, even when a `null` state is passed into these methods. See: http://yuilibrary.com/yui/docs/api/files/history_js_history-base.js.html#l557

Since all HTML5History instances seed their `_initialState` from `window.history.state`, a history instance created after another history instance calls its `add/replace()` methods, means that its
`_initialState` will be an empty object, `{}`. This then means, the HistoryBase init logic will think `replace()` needs to be called :-/

All of this combined ends creating a bad situation when a router is created inside a route handler of another router. The browser gets stuck in an infinite loop because the child router calls
`replace()` during init, which fires the global `history:change` event, which causes the parent router to dispatch again, and things get all loopy!

This is the problem, I'm not sure what the right solution is. I think a few things need to be looked at:

1) HistoryBase always creating an object, `{}` for its state not matter what.
2) HTML5History having an extra option to consider `Y.Object.isEmpty(window.history.state)` as being a `null` `_initialState`.
3) The way the `history:change` event both bubbles and is a global event. (not sure on this one)

Type: defect Observed in Version: 3.5.1
Component: Router Severity: S3 (normal)
Assigned To: Eric Ferraiuolo Target Release: 3.6.0
Location: Library Code Priority: P1 (critical)
Tags: Relates To:
Browsers: Firefox 4.x,Firefox - Latest,Firefox - Next,Chrome
URL:
Test Information:

I notice this behavior in latest Chrome (19) and latest Firefox (12), probably because they actually use `history.state` whereas Safari does not.

Change History

Eric Ferraiuolo

YUI Developer

Posted: 07/9/12
  • sprint changed to backlog

Eric Ferraiuolo

YUI Developer

Posted: 07/10/12

Option #2 sounds like the best solution and should become the default behavior.

Eric Ferraiuolo

YUI Developer

Posted: 07/11/12
  • completed changed from 0 to 0.2
  • estimated changed from 0 to 0.2
  • status changed from accepted to checkedin

Eric Ferraiuolo

YUI Developer

Posted: 07/11/12
  • resolution changed to fixed

Eric Ferraiuolo

YUI Developer

Posted: 07/11/12

Make HistoryHTML5 treat empty `window.history.state` objects as `null`.

Fixes #2532340
View Commit: ec07cd157f12459e8e33ba12b7f2293f386342bb

Jenny Donnelly

YUI Developer

Posted: 08/1/12
  • status changed from checkedin to closed

Shipped in 3.6.0. Marking closed/fixed.