tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_bug500328.html (25599B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <!--
      4 https://bugzilla.mozilla.org/show_bug.cgi?id=500328
      5 -->
      6 <head>
      7  <title>Test for Bug 500328</title>
      8  <script src="/tests/SimpleTest/SimpleTest.js"></script>
      9  <script src="/tests/SimpleTest/EventUtils.js"></script>
     10  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
     11 </head>
     12 <body>
     13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500328">Mozilla Bug 500328</a>
     14 <p id="display"></p>
     15 <div id="status"></div>
     16 <div id="content">
     17  <iframe id="iframe"></iframe>
     18  <iframe id="iframe2"></iframe>
     19  <a id="link">link</a>
     20 </div>
     21 <pre id="test">
     22 <script type="application/javascript">
     23 
     24 /** Test for Bug 500328 */
     25 
     26 SimpleTest.waitForExplicitFinish();
     27 
     28 var iframe = document.getElementById("iframe");
     29 var iframeCw = iframe.contentWindow;
     30 
     31 var iframe2 = document.getElementById("iframe2");
     32 var iframe2Cw = iframe2.contentWindow;
     33 
     34 const unvisitedColor = "rgb(0, 0, 238)";
     35 const visitedColor = "rgb(85, 26, 139)";
     36 
     37 var gCallbackOnIframeLoad = false;
     38 var gCallbackOnIframePageShow = false;
     39 var gCallbackOnPopState = false;
     40 var gNumPopStates = 0;
     41 var gLastPopStateEvent;
     42 var gLastScriptHistoryState;
     43 
     44 var gGen;
     45 
     46 function statusMsg(msg) {
     47  var msgElem = document.createElement("p");
     48  msgElem.appendChild(document.createTextNode(msg));
     49 
     50  document.getElementById("status").appendChild(msgElem);
     51 }
     52 
     53 function longWait() {
     54  function hitEventLoop(times, func) {
     55    if (times > 0) {
     56      setTimeout(hitEventLoop, 0, times - 1, func);
     57    } else {
     58      setTimeout(func, 0);
     59    }
     60  }
     61  hitEventLoop(100, function() { gGen.next(); });
     62 }
     63 
     64 function shortWait() {
     65  setTimeout(function() { gGen.next(); }, 0);
     66 }
     67 
     68 function onChildPopState(e) {
     69  gNumPopStates++;
     70  gLastPopStateEvent = e;
     71  if (gCallbackOnPopState) {
     72    statusMsg("Popstate(" + JSON.stringify(e.state) + ").  Calling gGen.next().");
     73    gCallbackOnPopState = false;
     74    gGen.next();
     75  }
     76  else {
     77    statusMsg("Popstate(" + JSON.stringify(e.state) + ").  NOT calling gGen.next().");
     78  }
     79 }
     80 
     81 function onChildScript(state) {
     82  gLastScriptHistoryState = state;
     83 }
     84 
     85 function getURLFromEvent(e) {
     86  try {
     87    var target = e.target;
     88    if ("contentWindow" in target) {
     89      return target.contentWindow.location.toString();
     90    }
     91    if ("ownerDocument" in target && target.ownerDocument) {
     92      return target.ownerDocument.location.toString();
     93    }
     94    if ("location" in target) {
     95      return target.location.toString();
     96    }
     97    return target.toString();
     98  }
     99  catch(ex) {
    100    return "<cross-site object>";
    101  }
    102 }
    103 
    104 function onChildLoad(e) {
    105  if(gCallbackOnIframeLoad) {
    106    statusMsg("Got load for " + getURLFromEvent(e) + ".  About to call gGen.next().");
    107    gCallbackOnIframeLoad = false;
    108    gGen.next();
    109  }
    110  else {
    111    statusMsg("Got load for " + getURLFromEvent(e) + ", but not calling gGen.next() because gCallbackOnIframeLoad was false.");
    112  }
    113 }
    114 
    115 function onChildPageShow(e) {
    116  if(gCallbackOnIframePageShow) {
    117    statusMsg("Got pageshow for " + getURLFromEvent(e) + ".  About to call gGen.next().");
    118    gCallbackOnIframePageShow = false;
    119    SimpleTest.executeSoon(function() { gGen.next(); });
    120  }
    121  else {
    122    statusMsg("Got pageshow for " + getURLFromEvent(e) + ", but not calling gGen.next() because gCallbackOnIframePageShow was false.");
    123  }
    124 }
    125 
    126 function enableChildLoadCallback() {
    127  gCallbackOnIframeLoad = true;
    128 }
    129 
    130 function enableChildPageShowCallback() {
    131  gCallbackOnIframePageShow = true;
    132 }
    133 
    134 function enableChildPopStateCallback() {
    135  gCallbackOnPopState = true;
    136 }
    137 
    138 function clearPopStateCounter() {
    139  gNumPopStates = 0;
    140 }
    141 
    142 function noPopStateExpected(msg) {
    143  is(gNumPopStates, 0, msg);
    144 
    145  // Even if there's an error, set gNumPopStates to 0 so other tests don't
    146  // fail.
    147  gNumPopStates = 0;
    148 }
    149 
    150 function popstateExpected(msg) {
    151  is(gNumPopStates, 1, msg);
    152  gNumPopStates = 0;
    153 }
    154 
    155 function getColor(elem) {
    156  var utils = SpecialPowers.getDOMWindowUtils(document.defaultView);
    157  return utils.getVisitedDependentComputedStyle(elem, "", "color");
    158 }
    159 
    160 function getSHistory(theWindow)
    161 {
    162  const Ci = SpecialPowers.Ci;
    163  var sh = SpecialPowers.wrap(theWindow).docShell
    164                        .QueryInterface(Ci.nsIWebNavigation)
    165                        .sessionHistory;
    166  if (!sh || sh == null)
    167    throw("Couldn't get shistory for window!");
    168 
    169  return sh;
    170 }
    171 
    172 function getSHTitle(sh, offset)
    173 {
    174  if (!offset)
    175    offset = 0;
    176 
    177  // False instructs the SHistory not to modify its current index.
    178  return sh.legacySHistory.getEntryAtIndex(sh.index + offset).title;
    179 }
    180 
    181 // Tests that win's location ends with str
    182 function locationEndsWith(win, str) {
    183  var exp = new RegExp(str + "$");
    184  ok(win.location.toString().match(exp),
    185     "Wrong window location.  Expected it to end with " +
    186     str + ", but actuall was " + win.location);
    187 }
    188 
    189 function expectException(func, msg) {
    190  var failed = false;
    191  try {
    192    func();
    193  } catch(ex) {
    194    failed = true;
    195  }
    196 
    197  ok(failed, msg + " succeeded, but should have failed.");
    198 }
    199 
    200 function* runTest() {
    201  // We can't enable universal XPConnect privleges in this function, because
    202  // test 5 needs to be running at normal privleges in order to test the
    203  // same-origin policy.
    204 
    205  /**
    206   * PRELIMINARY:
    207   *  1. Clear the popstate counter
    208   */
    209 
    210  clearPopStateCounter();
    211 
    212  // The URL of file_bug500328_1.html on http://localhost:8888
    213  var innerLoc;
    214 
    215  // Now we can start the tests
    216 
    217  /**
    218   * TEST 1 tests basic pushState functionality
    219   */
    220  enableChildLoadCallback();
    221  iframeCw.location = "file_bug500328_1.html";
    222  yield undefined;
    223  innerLoc = iframeCw.location.toString();
    224  // No popstate during initial load.
    225  shortWait();
    226  yield undefined;
    227  noPopStateExpected("No initial popstate.");
    228  is(JSON.stringify(gLastScriptHistoryState), "null", "null initial state.");
    229  statusMsg("Awake after first load.");
    230 
    231  // Make sure that the pushstate below doesn't trigger a hashchange.
    232  iframeCw.onhashchange = function() {
    233    ok(false, "Pushstate shouldn't trigger a hashchange.");
    234  };
    235 
    236  var testObj1 = 42;
    237  var testObj2 = { x: 4.2 };
    238  iframeCw.history.pushState(testObj1, "test 1");
    239  is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj1),
    240     "correct state after pushState");
    241  is(iframeCw.location.search, "",
    242     "First pushstate should leave us where we were.");
    243 
    244  iframeCw.history.pushState(testObj2, "test 1#foo", "?test1#foo");
    245  is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj2),
    246     "correct state after pushState");
    247  isnot(iframeCw.history.state, testObj2,
    248     "correct state object identity after pushState");
    249  is(iframeCw.location.search, "?test1",
    250     "Second pushstate should push us to '?test1'.");
    251  is(iframeCw.location.hash, "#foo",
    252     "Second pushstate should push us to '#foo'");
    253  shortWait();
    254  yield undefined;
    255 
    256  // Let the hashchange event fire, if it's going to.
    257  longWait();
    258  yield undefined;
    259  iframeCw.onhashchange = null;
    260 
    261  statusMsg("About to go back to page 1.");
    262  // We don't have to yield here because this back() and the resulting popstate
    263  // are completely synchronous.  In fact, if we did yield, JS would throw an
    264  // error because we'd be calling gGen.next from within gGen.next.
    265  iframeCw.history.back();
    266 
    267  statusMsg("Awake after going back to page 1.");
    268  popstateExpected("Going back to page 1 should trigger a popstate.");
    269  is(gLastPopStateEvent.isTrusted, true, 'Popstate event should be trusted.');
    270  is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1),
    271     "Wrong state object popped after going back to page 1.");
    272  ok(gLastPopStateEvent.state === iframeCw.history.state,
    273     "Wrong state object in document after going back to page 1.");
    274  ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
    275      "Going back to page 1 hould take us to original page.");
    276 
    277  iframeCw.history.back();
    278  popstateExpected("Going back to page 0 should trigger a popstate.");
    279  is(gLastPopStateEvent.state, null,
    280     "Going back to page 0 should pop a null state.");
    281  is(iframeCw.history.state, null,
    282     "Going back to page 0 should pop a null state.");
    283  is(iframeCw.location.search, "",
    284     "Going back to page 0 should clear the querystring.");
    285 
    286  iframeCw.history.forward();
    287  popstateExpected("Going forward to page 1 should trigger a popstate.");
    288  is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj1),
    289      "Wrong state object popped after going forward to page 1.");
    290  is(gLastPopStateEvent.state, iframeCw.history.state,
    291     "Wrong state object in document after going forward to page 1.");
    292  ok(iframeCw.location.toString().match(/file_bug500328_1.html$/),
    293      "Going forward to page 1 should leave us at original page.");
    294 
    295  statusMsg("About to go forward to page 2.");
    296  iframeCw.history.forward();
    297  statusMsg("Awake after going forward to page 2.");
    298  popstateExpected("Going forward to page 2 should trigger a popstate.");
    299  is(JSON.stringify(gLastPopStateEvent.state), JSON.stringify(testObj2),
    300     "Wrong state object popped after going forward to page 2.");
    301  is(iframeCw.history.state, gLastPopStateEvent.state,
    302     "Wrong state object in document after going forward to page 2.");
    303  ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/),
    304     "Going forward to page 2 took us to " + iframeCw.location.toString());
    305 
    306  statusMsg("About to reload page 2.");
    307  iframeCw.location.reload();
    308  enableChildLoadCallback();
    309  yield undefined;
    310  statusMsg("Awake after reloading page 2.");
    311  noPopStateExpected("Reloading page 2 should not trigger popstate.");
    312  is(JSON.stringify(iframeCw.history.state), JSON.stringify(testObj2),
    313     "Wrong state object after reloading page 2.");
    314  is(JSON.stringify(gLastScriptHistoryState), JSON.stringify(testObj2),
    315     "Wrong state object while reloading page 2.");
    316  ok(iframeCw.location.toString().match(/file_bug500328_1.html\?test1#foo$/),
    317     "Reloading page 2 took us to " + iframeCw.location.toString());
    318 
    319  // The iframe's current location is file_bug500328_1.html?test1#foo.
    320  // Clicking link1 should take us to file_bug500328_1.html?test1#1.
    321 
    322  enableChildPopStateCallback();
    323  sendMouseEvent({type:'click'}, 'link-anchor1', iframeCw);
    324  yield undefined;
    325  popstateExpected("Clicking on link-anchor1 should trigger a popstate.");
    326  is(iframeCw.location.search, "?test1",
    327      "search should be ?test1 after clicking link.");
    328  is(iframeCw.location.hash, "#1",
    329      "hash should be #1 after clicking link.");
    330  is(iframeCw.history.state, null,
    331     "Wrong state object in document after clicking link to hash '#1'.");
    332 
    333  /*
    334   * Reload file_bug500328_1.html; we're now going to test that link hrefs
    335   * and colors are updated correctly on push/popstates.
    336   */
    337 
    338  iframe.onload = onChildLoad;
    339  enableChildLoadCallback();
    340  iframeCw.location = "about:blank";
    341  yield undefined;
    342  enableChildLoadCallback();
    343  iframeCw.location = "file_bug500328_1.html";
    344  yield undefined;
    345  noPopStateExpected("No popstate after re-loading file_bug500328_1.html");
    346  statusMsg("Done loading file_bug500328_1.html for the second time.");
    347 
    348  var ifLink = iframeCw.document.getElementById("link-anchor1");
    349  var rand = Date.now() + "-" + Math.random();
    350  ifLink.href = rand;
    351 
    352  // Poll the document until the link has the correct color, or this test times
    353  // out.  Unfortunately I can't come up with a more elegant way to do this.
    354  // We could listen to MozAfterPaint, but that doesn't guarantee that we'll
    355  // observe the new color.
    356  while (getColor(ifLink) != unvisitedColor) {
    357    // Dump so something shows up in the mochitest logs if we spin here.
    358    dump("ifLink has wrong initial color.  Spinning...\n");
    359    setTimeout(function() { gGen.next(); }, 0);
    360    yield undefined;
    361  }
    362 
    363  // Navigate iframe2 to dir/${rand}
    364  iframe2.onload = onChildLoad;
    365  enableChildLoadCallback();
    366  iframe2Cw.location = "mytestdir/" + rand;
    367  yield undefined;
    368 
    369  // PushState the iframe into the mytestdir directory.  This should cause
    370  // ifLink to turn purple, since we just visited mytestdir/${rand} in iframe2.
    371  iframeCw.history.pushState(null, "foo", "mytestdir/foo");
    372 
    373  // Check that the link's color is now visitedColor
    374  while (getColor(ifLink) != visitedColor) {
    375    dump("ifLink has wrong color after pushstate.  Spinning...\n");
    376    setTimeout(function() { gGen.next(); }, 0);
    377    yield undefined;
    378  }
    379 
    380  ok(ifLink.href.match("mytestdir\\/" + rand + "$"),
    381     "inner frame's link should end with 'mytestdir/${rand}'");
    382 
    383  // Navigate out of the mytestdir directory.  This should cause ifLink to turn
    384  // blue again.
    385  iframeCw.history.pushState(null, "bar", "../file_bug500328_1.html");
    386 
    387  // Check that the link's color is back to the unvisited color.
    388  while (getColor(ifLink) != unvisitedColor) {
    389    dump("ifLink has wrong color after pushstating out of dir.  Spinning...\n");
    390    setTimeout(function() { gGen.next(); }, 0);
    391    yield undefined;
    392  }
    393 
    394  ok(!ifLink.href.match("mytestdir"),
    395     "inner frame's link shouldn't contain 'mytestdir'.");
    396 
    397  /*
    398   * TEST 2 tests that pushstate's same-origin checks are correct.
    399   */
    400  var filename = 'file_bug500328_2.html';
    401  var dirname = document.location.pathname.replace(/[^\/]*$/, '');
    402  statusMsg("Dirname is: " + dirname);
    403  iframeCw.location = filename;
    404  iframe.onload = onChildLoad;
    405  enableChildLoadCallback();
    406  yield undefined;
    407 
    408  // This function tries to pushstate and replacestate to the given URL and
    409  // fails the test if the calls succeed.
    410  var tryBadPushAndReplaceState = function(url) {
    411    // XXX ex should be a SECURITY_ERR, not a plain Error.
    412 
    413    var hist = iframeCw.history;
    414    var url2 = url + dirname + filename;
    415 
    416    expectException(function() { hist.pushState({}, "foo", url); },
    417                    'pushState to ' + url);
    418 
    419    expectException(function() { hist.pushState({}, "foo", url2); },
    420                    'pushState to ' + url2);
    421 
    422    expectException(function() { hist.replaceState({}, "foo", url); },
    423                    'replaceState to ' + url);
    424 
    425    expectException(function() { hist.replaceState({}, "foo", url2); },
    426                    'replaceState to ' + url2);
    427  }
    428 
    429  // We're currently at http://example.com/[dirname]/[filename]
    430  tryBadPushAndReplaceState("https://mochi.test:8888");
    431  tryBadPushAndReplaceState("http://foo.mochitest:8888");
    432  tryBadPushAndReplaceState("http://mochi.test:1234");
    433  tryBadPushAndReplaceState("http://mochi.test.a:8888");
    434  tryBadPushAndReplaceState("http://mochi.tes:8888");
    435  tryBadPushAndReplaceState("http://mmochi.test:8888");
    436  tryBadPushAndReplaceState("http://me@mochi.test:8888");
    437 
    438  /**
    439   * TEST 3 tests that the session history entries' titles are properly sync'ed
    440   * after push/pop states.
    441   *
    442   * We have to run this test in a popup rather than an iframe because only the
    443   * root docshell has a session history object.
    444   */
    445  statusMsg("About to open popup.");
    446  var popup = window.open("file_bug500328_1.html", "popup0",
    447                          "height=200,width=200,location=yes," +
    448                          "menubar=yes,status=yes,toolbar=yes,dependent=yes");
    449 
    450  enableChildLoadCallback();
    451  var shistory = getSHistory(popup);
    452  yield undefined;
    453  shortWait();
    454  yield undefined;
    455  noPopStateExpected("Shouldn't get popstate after opening window.");
    456 
    457  popup.history.pushState(null, "title 0");
    458  ok(SpecialPowers.isBackButtonEnabled(popup),
    459     "Back button was not enabled after initial pushstate.");
    460 
    461  popup.document.title = "title 1";
    462 
    463  // Yield to the event loop so listeners will be notified of the title change
    464  // and so that the hash change we trigger below generates a new session
    465  // history entry.
    466  shortWait();
    467  yield undefined;
    468 
    469  // Check that the current session history entry's title has been updated to
    470  // reflect the new document title.
    471  is(getSHTitle(shistory), "title 1", "SHEntry title test 1");
    472 
    473  // Change the page's hash to #1, which will trigger a popstate event.
    474  // We don't have to wait, because this happens synchronously.
    475  popup.location.hash = "#1";
    476  popstateExpected("Didn't get popstate after changing hash.");
    477 
    478  popup.document.title = "title 2";
    479 
    480  // Yield so listeners will be notified of the title change we just performed.
    481  shortWait();
    482  yield undefined;
    483 
    484  is(getSHTitle(shistory), "title 2", "SHEntry title test 2");
    485 
    486  // Go back.  Happens synchronously.  We should get a popstate.
    487  statusMsg("About to go back.");
    488  popup.history.go(-1);
    489  popstateExpected("Didn't get a popstate after going back.");
    490 
    491  // Even though we went back, we expect the SHEntry title to remain the same
    492  // because the document didn't change.
    493  is(getSHTitle(shistory), "title 2", "SHEntry title test 3");
    494 
    495  popup.document.title = "Changed 1";
    496  shortWait();
    497  yield undefined;
    498 
    499  // This check is really a test of bug 509055.
    500  is(getSHTitle(shistory), "Changed 1", "SHEntry title test 4");
    501 
    502  popup.close();
    503 
    504  /**
    505   * TEST 4 tests replaceState and that we don't get double popstates on
    506   * window.open.  It also stress-tests the system and its interaction with
    507   * bfcache by making many push/replace state calls.
    508   */
    509  popup = window.open("file_bug500328_1.html", "popup1",
    510                      "height=200,width=200,location=yes," +
    511                      "menubar=yes,status=yes,toolbar=yes,dependent=yes");
    512 
    513  // The initial about:blank load into the new window shouldn't result in us
    514  // seeing a popstate.  Once file_bug500328_1.html is loaded, it'll overwrite
    515  // popup.onpopstate, and this assertion won't fire for that popstate and
    516  // others after.
    517  //
    518  // If we fired the popstate event asynchronously, we'd expect this assert to
    519  // fire.
    520  popup.onpopstate = function() {
    521    ok(false, "Initial load of popup shouldn't give us a popstate.");
    522  };
    523 
    524  shistory = getSHistory(popup);
    525 
    526  enableChildLoadCallback();
    527  yield undefined;
    528  statusMsg("Awake after loading content into popup.");
    529 
    530  popup.history.replaceState({n:1, ok:true}, "state 1", "good1.html");
    531  locationEndsWith(popup, "good1.html");
    532 
    533  // Even though we replaceState with title "state 1", the title should remain
    534  // "test 1" because we ignore the title argument in push/replaceState.
    535  // See bug 544535.
    536  is(getSHTitle(shistory), "test 1", "SHEntry title 'state 1'");
    537 
    538  // Flush the event loop so our next load creates a new session history entry.
    539  shortWait();
    540  yield undefined;
    541 
    542  enableChildLoadCallback();
    543  popup.location = "file_bug500328_1.html";
    544  yield undefined;
    545 
    546  // Flush the event loop so nsDocShell::OnNewURI runs and our load is recorded
    547  // properly.
    548  shortWait();
    549  yield undefined;
    550 
    551  // Now go back and make sure everything is as it should be.
    552  enableChildPageShowCallback();
    553  popup.history.back();
    554  yield undefined;
    555  // Flush the event loop so the document's location is updated and any
    556  // popstates fire.
    557  shortWait();
    558  yield undefined;
    559  noPopStateExpected("no popstate during initial load");
    560 
    561  locationEndsWith(popup, "good1.html");
    562  is(JSON.stringify(popup.history.state), '{"n":1,"ok":true}',
    563     "Wrong state popped after going back to initial state.");
    564 
    565  // We're back at state 0, which was replaceState-ed to state1.html.  Let's do
    566  // some push/pop/replaces to make sure everything works OK when we involve
    567  // large numbers of SHEntries.
    568  for(var i = 2; i <= 30; i++) {
    569    if (i % 3 == 0) {
    570      popup.history.pushState({n:i, ok:true}, "state " + i, "good" + i + ".html");
    571    }
    572    else {
    573      popup.history.pushState({n:i}, "state " + i, "state" + i + ".html");
    574      for(var j = 0; j < i % 4; j++) {
    575        popup.history.replaceState({n:i, nn:j}, "state " + i + ", " + j);
    576      }
    577      popup.history.replaceState({n:i, ok:true}, "state " + i, "good" + i + ".html");
    578    }
    579  }
    580 
    581  for(var i = 29; i >= 1; i--) {
    582    popup.history.back();
    583    popstateExpected("Didn't get a popstate on iteration " + i);
    584    locationEndsWith(popup, "good" + i + ".html");
    585    is(gLastPopStateEvent.state.n, i, "Bad counter on last popstate event.");
    586    ok(gLastPopStateEvent.state.ok,
    587       "Last popstate event should have 'ok' set to true.");
    588  }
    589 
    590  popup.close();
    591 
    592  /**
    593   * TEST 5 tests misc security features and implementation details of
    594   * Push/ReplaceState
    595   */
    596 
    597  /*
    598   * Test that we can't push/replace an object with a large (over 640k
    599   * characters) JSON representation.
    600   */
    601 
    602  // (In case you're curious, this loop generates an object which serializes to
    603  // 694581 characters.)
    604  var bigObject = new Object();
    605  for(var i = 0; i < 51200; i++) {
    606    bigObject[i] = i;
    607  }
    608  // statusMsg("Big object has size " + JSON.stringify(bigObject).length);
    609 
    610  // We shouldn't be able to pushstate this large object, due to space
    611  // constraints.
    612  expectException(
    613    function() { iframeCw.history.pushState(bigObject, "foo"); },
    614    "pushState-ing large object");
    615 
    616  expectException(
    617    function() { iframeCw.history.replaceState(bigObject, "foo"); },
    618    "replaceState-ing large object");
    619 
    620  /*
    621   * Make sure we can't push/replace state on an iframe of a different origin.
    622   * This will work if this function has requested Universal XPConnect
    623   * privileges, so any code which needs those privileges can't be in this
    624   * function.
    625   */
    626  enableChildLoadCallback();
    627  iframeCw.location = "http://example.com";
    628  iframe.onload = onChildLoad;
    629  yield undefined;
    630  iframe.onload = null;
    631 
    632  expectException(
    633    function() { iframeCw.history.pushState({}, "foo"); },
    634    "pushState-ing in a different origin");
    635 
    636  expectException(
    637    function() { iframeCw.history.replaceState({}, "foo"); },
    638    "replaceState-ing in a different origin");
    639 
    640  /*
    641   * If we do the following:
    642   *   * Start at page A.
    643   *   * PushState to page B.
    644   *   * Refresh.  The server responds with a 404
    645   *   * Go back.
    646   * Then at the end, page A should be displayed, not the 404 page.
    647   */
    648  enableChildLoadCallback();
    649  iframe.onload = onChildLoad;
    650  iframeCw.location = "about:blank";
    651  yield undefined;
    652  iframe.onload = null;
    653 
    654  enableChildLoadCallback();
    655  // navigate to http://mochi.test:8888/[...]/file_bug500328_1.html
    656  iframeCw.location = innerLoc;
    657  yield undefined;
    658 
    659  // PushState to a URL which doesn't exist
    660  iframeCw.history.pushState({}, "", rand);
    661 
    662  // Refresh.  We'll end up a 404 page.
    663  iframe.onload = onChildLoad;
    664  enableChildLoadCallback();
    665  iframeCw.location.reload(true);
    666  yield undefined;
    667  iframe.onload = null;
    668 
    669  // Since the last page was a 404, going back should actually show the
    670  // contents of the old page, instead of persisting the contents of the 404
    671  // page.
    672  enableChildPageShowCallback();
    673  iframeCw.history.back();
    674  yield undefined;
    675 
    676  // Make sure that we're actually showing the contents of
    677  // file_bug500328_1.html, as opposed to the 404 page.
    678  var identifierElem = iframeCw.document.getElementById("link-anchor1");
    679  ok(identifierElem != undefined && identifierElem != null,
    680     "iframe didn't contain file_bug500328_1.html's contents.");
    681 
    682  /**
    683   * TEST 6 tests that the referrer is set properly after push/replace states.
    684   */
    685 
    686  /*
    687   * First, a simple test:
    688   *   * Load file_bug500328_1.html into iframe
    689   *   * PushState to newpage1.html#foo
    690   *   * Instruct the iframe to load file_bug500328_1.html into itself.
    691   * The referer should be newpage1.html, without the hash.
    692   *
    693   * This also tests that we can call pushState from within the onload handler.
    694   */
    695  enableChildLoadCallback();
    696  iframeCw.location = "file_bug500328_1.html";
    697  yield undefined;
    698 
    699  // Run within the onload handler.  This should work without issue.
    700  iframeCw.history.pushState(null, "", "newpage1.html");
    701 
    702  // iframeCw.navigateTo() causes the iframe to set its location on our
    703  // behalf.  We can't just set its location ourselves, because then *we*
    704  // become the referrer.
    705  enableChildLoadCallback();
    706  iframeCw.navigateTo("file_bug500328_1.html");
    707  yield undefined;
    708 
    709  ok(iframeCw.document.referrer.toString().match(/newpage1.html$/),
    710     "Wrong referrer after pushState.  Expected newpage1.html, but was " +
    711     iframeCw.document.referrer);
    712 
    713  /*
    714   * We're back at file_bug500328_1.html.  Now do the following:
    715   *   * replaceState to newpage2.html#foo
    716   *   * Click a link back to file_bug500328_1.html
    717   * The referrer should be newpage2.html, without the hash.
    718   */
    719  iframeCw.history.replaceState(null, null, "newpage2.html#foo");
    720  enableChildLoadCallback();
    721  sendMouseEvent({type:'click'}, 'link-self', iframeCw);
    722  yield undefined;
    723 
    724  ok(iframeCw.document.referrer.toString().match(/newpage2.html$/),
    725     "Wrong referrer after replaceState.  Expected newpage2.html, but was " +
    726     iframeCw.document.referrer);
    727 
    728  /*
    729   * Set up a cycle with the popstate event to make sure it's properly
    730   * collected.
    731   */
    732  var evt = document.createEvent("popstateevent");
    733  evt.initEvent("foo", false, false, evt);
    734 
    735  /* */
    736  SimpleTest.finish();
    737  statusMsg("********** Finished tests ***********");
    738  while(true)
    739  {
    740    yield undefined;
    741 
    742    // I don't think this will actually make the mochitest fail, but there's
    743    // not much we can do about this.  Realistically, though, regressions are
    744    // not likely to fire extra events -- this trap is here mostly to catch
    745    // errors made while wriring tests.
    746    ok(false, "Got extra event!");
    747  }
    748 
    749  /*
    750  statusMsg("XXXXXXXXXXXXXX");
    751  while(true) {
    752    yield undefined;
    753    statusMsg("Woken up.");
    754  }
    755  */
    756 }
    757 
    758 // Important: Wait to start the tests until the page has loaded.  Otherwise,
    759 // the test will occasionally fail when it begins running before the iframes
    760 // have finished their initial load of about:blank.
    761 window.addEventListener('load', function() {
    762  gGen = runTest();
    763  gGen.next();
    764 });
    765 
    766 </script>
    767 </pre>
    768 </body>
    769 </html>