[SCM] WebKit Debian packaging branch, debian/experimental, updated. upstream/1.3.3-9427-gc2be6fc

commit-queue at webkit.org commit-queue at webkit.org
Wed Dec 22 12:55:27 UTC 2010


The following commit has been merged in the debian/experimental branch:
commit 7235406e5601d015f277d5d5115483a04e44a7b9
Author: commit-queue at webkit.org <commit-queue at webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Thu Sep 2 01:00:51 2010 +0000

    2010-09-01  Mihai Parparita  <mihaip at chromium.org>
    
            Reviewed by Brady Eidson.
    
            popstate event is not fired until document opts in by calling pushstate.
            https://bugs.webkit.org/show_bug.cgi?id=41372
    
            Add two tests to check how often popstate is being fired (for both
            fragment changes and page changes, especially with the page cache
            enabled).
    
            Update existing state object tests to handle popstate being fired for
            the page being navigated to (right after onload).
    
            * fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html:
            * fast/loader/stateobjects/document-destroyed-navigate-back.html:
            * fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html:
            * fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html:
            * fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html:
            * fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html:
            * fast/loader/stateobjects/popstate-fires-on-history-traversal-expected.txt: Added.
            * fast/loader/stateobjects/popstate-fires-on-history-traversal.html: Added.
            * fast/loader/stateobjects/popstate-fires-with-page-cache-expected.txt: Added.
            * fast/loader/stateobjects/popstate-fires-with-page-cache.html: Added.
            * fast/loader/stateobjects/pushstate-object-types.html:
            * fast/loader/stateobjects/pushstate-then-replacestate.html:
            * fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html:
            * fast/loader/stateobjects/replacestate-in-iframe.html:
            * fast/loader/stateobjects/replacestate-then-pushstate.html:
            * fast/loader/stateobjects/resources/popstate-fires-with-page-cache-1.html: Added.
            * fast/loader/stateobjects/resources/popstate-fires-with-page-cache-2.html: Added.
            * fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html:
    2010-09-01  Mihai Parparita  <mihaip at chromium.org>
    
            Reviewed by Brady Eidson.
    
            popstate event is not fired until document opts in by calling pushstate.
            https://bugs.webkit.org/show_bug.cgi?id=41372
    
            Fire popstate even when we don't have a state object when a page is
            loaded (for both regular loads and those from the page cache). Also
            fire popstate when doing in-document navigation via fragment changes.
            This is consistent with both Gecko and recent the HTML5 spec change:
            http://html5.org/tools/web-apps-tracker?from=5376&to=5377
    
            Tests: fast/loader/stateobjects/popstate-fires-on-history-traversal.html
                   fast/loader/stateobjects/popstate-fires-with-page-cache.html
    
            * bindings/js/SerializedScriptValue.cpp:
            * bindings/js/SerializedScriptValue.h:
            (WebCore::SerializedScriptValue::create):
            * bindings/v8/SerializedScriptValue.cpp:
            * bindings/v8/SerializedScriptValue.h:
            * dom/Document.cpp:
            (WebCore::Document::implicitClose):
            * dom/Document.h:
            * history/CachedFrame.cpp:
            (WebCore::CachedFrameBase::restore):
            * loader/FrameLoader.cpp:
            (WebCore::FrameLoader::loadInSameDocument):
            (WebCore::FrameLoader::transitionToCommitted):
            * loader/HistoryController.cpp:
            (WebCore::HistoryController::pushState):
            (WebCore::HistoryController::replaceState):
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@66628 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 494da44..30d2bac 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,36 @@
+2010-09-01  Mihai Parparita  <mihaip at chromium.org>
+
+        Reviewed by Brady Eidson.
+
+        popstate event is not fired until document opts in by calling pushstate.
+        https://bugs.webkit.org/show_bug.cgi?id=41372
+
+        Add two tests to check how often popstate is being fired (for both
+        fragment changes and page changes, especially with the page cache
+        enabled).
+
+        Update existing state object tests to handle popstate being fired for
+        the page being navigated to (right after onload).
+
+        * fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html:
+        * fast/loader/stateobjects/document-destroyed-navigate-back.html:
+        * fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html:
+        * fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html:
+        * fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html:
+        * fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html:
+        * fast/loader/stateobjects/popstate-fires-on-history-traversal-expected.txt: Added.
+        * fast/loader/stateobjects/popstate-fires-on-history-traversal.html: Added.
+        * fast/loader/stateobjects/popstate-fires-with-page-cache-expected.txt: Added.
+        * fast/loader/stateobjects/popstate-fires-with-page-cache.html: Added.
+        * fast/loader/stateobjects/pushstate-object-types.html:
+        * fast/loader/stateobjects/pushstate-then-replacestate.html:
+        * fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html:
+        * fast/loader/stateobjects/replacestate-in-iframe.html:
+        * fast/loader/stateobjects/replacestate-then-pushstate.html:
+        * fast/loader/stateobjects/resources/popstate-fires-with-page-cache-1.html: Added.
+        * fast/loader/stateobjects/resources/popstate-fires-with-page-cache-2.html: Added.
+        * fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html:
+
 2010-09-01  Matthew Willis  <lilmatt at flock.com>
 
         Reviewed by Joseph Pecoraro.
diff --git a/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html b/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html
index 775e86c..da500ab 100644
--- a/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html
+++ b/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back-with-fragment-scroll.html
@@ -32,7 +32,7 @@ function runThirdStageOfTest()
     alert("Final stage of test loaded");
 }
 
-function loaded()
+function runTest()
 {
     alert("LOADED");
     if (!sessionStorage.stage)
@@ -43,8 +43,20 @@ function loaded()
         runThirdStageOfTest();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        // Continue with the handler if we've already began the test.
+        if (!sessionStorage.stage)
+            return;
+    }
+    
     alert("State popped - " + event.state + " (type " + typeof event.state + ")");
 
     // FIXME: Once the popstate and hashchange events fire asynchronously, we
@@ -79,7 +91,7 @@ function hashChanged()
 }
 
 </script>
-<body onload="loaded();" onpopstate="statePopped();" onhashchange="hashChanged();" onunload="/* disable page cache */">
+<body onpopstate="statePopped();" onhashchange="hashChanged();" onunload="/* disable page cache */">
 <pre>
 This test:
 -Builds up a list of state object entries with fragment URLs.
diff --git a/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back.html b/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back.html
index edbb1c2..982fe81 100644
--- a/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back.html
+++ b/LayoutTests/fast/loader/stateobjects/document-destroyed-navigate-back.html
@@ -31,7 +31,7 @@ function runThirdStageOfTest()
     alert("Final stage of test loaded");
 }
 
-function loaded()
+function runTest()
 {
     if (!sessionStorage.stage) {
         // Location changes need to happen outside the onload handler to generate history entries.
@@ -42,8 +42,20 @@ function loaded()
         runThirdStageOfTest();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        // Continue with the handler if we've already began the test.
+        if (!sessionStorage.stage)
+            return;
+    }
+    
     alert("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == "FirstEntry") {
         history.replaceState("FirstEntryWillLaterBeReactivated", null, "?FirstEntryWillLaterBeReactivated");
@@ -62,7 +74,7 @@ function statePopped()
 }
 
 </script>
-<body onload="loaded();" onpopstate="statePopped();" onunload="/* disable page cache */">
+<body onpopstate="statePopped();" onunload="/* disable page cache */">
 <pre>
 This test:
 -Builds up a list of state object entries with fragment URLs.
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html
index 75b969f..02e344c 100644
--- a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html
+++ b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-addeventlistener.html
@@ -20,8 +20,18 @@ function runTest()
     history.back();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+    
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == null)
         history.forward();
@@ -32,7 +42,7 @@ function statePopped()
 window.addEventListener("popstate", statePopped);
 
 </script>
-<body onload="runTest();">
+<body>
 <pre>
 This test does the following:
 -Listens for the popstate event using addEventListener
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html
index 9505736..cc17c0c 100644
--- a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html
+++ b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-attribute.html
@@ -20,8 +20,18 @@ function runTest()
     history.back();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+    
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == null) {
         document.body.onpopstate = statePopped;
@@ -31,7 +41,7 @@ function statePopped()
 }
 
 </script>
-<body onload="runTest();">
+<body>
 <pre>
 This test does the following:
 -Uses body.onpopstate to add a popstate handler (both by using the inline attribute and a script-assigned attribute)
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html
index 26fca1c..05da363 100644
--- a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html
+++ b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-body-inline-attribute.html
@@ -20,8 +20,18 @@ function runTest()
     history.back();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+    
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == null) {
         document.body.onpopstate = statePopped;
@@ -31,7 +41,7 @@ function statePopped()
 }
 
 </script>
-<body onload="runTest();" onpopstate="statePopped();">
+<body onpopstate="statePopped();">
 <pre>
 This test does the following:
 -Uses body.onpopstate to add a popstate handler (both by using the inline attribute and a script-assigned attribute)
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html
index 62569a5..4953968 100644
--- a/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html
+++ b/LayoutTests/fast/loader/stateobjects/popstate-after-load-complete-window-attribute.html
@@ -20,8 +20,18 @@ function runTest()
     history.back();
 }
 
+var beganTest = false;
+
 function statePopped()
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == null)
         history.forward();
@@ -32,7 +42,7 @@ function statePopped()
 window.onpopstate = statePopped;
 
 </script>
-<body onload="runTest();">
+<body>
 <pre>
 This test does the following:
 -Uses window.onpopstate to add a popstate handler
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal-expected.txt b/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal-expected.txt
new file mode 100644
index 0000000..52b5e26
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal-expected.txt
@@ -0,0 +1,18 @@
+Tests that popstate events fire for all history traversals (including navigation to fragments), not just when going back to a state object created via pushState.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+popstate fired with state null
+setting hash to #state1
+popstate fired with state null
+setting hash to #state2
+popstate fired with state null
+going back
+popstate fired with state null
+going back
+popstate fired with state null
+PASS popstateFireCount is 5
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal.html b/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal.html
new file mode 100644
index 0000000..d300334
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/popstate-fires-on-history-traversal.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html>
+<body>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css" type="text/css">
+<script src="../../js/resources/js-test-pre.js"></script>
+<div id="description"></div>
+<pre id="console"></pre>
+<script>
+description('Tests that popstate events fire for all history traversals (including navigation to fragments), not just when going back to a state object created via pushState.');
+
+onload = function()
+{
+    setTimeout(step, 0);
+}
+
+var currentStep = 0;
+var popstateFireCount = 0;
+
+function step()
+{
+    switch (currentStep) {
+    case 0:
+        debug('setting hash to #state1');
+        location.hash = '#state1';
+        break;
+    case 1:
+        debug('setting hash to #state2');
+        location.hash = '#state2';
+        break;
+    case 2:
+        debug('going back');
+        history.back();
+        break;
+    case 3:
+        debug('going back');
+        history.back();
+        break;
+    case 4:
+        // The 5 expected popstate events are when:
+        // 1. The page loads
+        // 2. Navigating to #state1
+        // 3. Navigating to #state2
+        // 4. Going back to #state1
+        // 5. Going back to the initial page state
+        shouldBe('popstateFireCount', '5');
+        finishJSTest();
+        return;
+    default:
+        testFailed('unexpected state: ' + currentStep);
+        break;
+    }
+    
+    currentStep++;
+    
+    setTimeout(step, 0);
+}
+
+onpopstate = function(event)
+{
+    debug('popstate fired with state ' + event.state);
+    popstateFireCount++;
+}
+
+var successfullyParsed = true;
+var jsTestIsAsync = true;
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache-expected.txt b/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache-expected.txt
new file mode 100644
index 0000000..d243936
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache-expected.txt
@@ -0,0 +1,16 @@
+Tests that popstate events fire when navigating between pages (and history entries created via pushState) that are in the page cache.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+popstate fired with state null
+pushState with new state object for page 1
+going to page 2
+popstate fired with state null
+going back to page 1 with new state object
+popstate fired with state newState
+going back to page 1 in initial state
+popstate fired with state null
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache.html b/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache.html
new file mode 100644
index 0000000..6492cf4
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/popstate-fires-with-page-cache.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<body>
+<link rel="stylesheet" href="../../js/resources/js-test-style.css" type="text/css">
+<script src="../../js/resources/js-test-pre.js"></script>
+<div id="description"></div>
+<pre id="console"></pre>
+<script>
+description('Tests that popstate events fire when navigating between pages (and history entries created via pushState) that are in the page cache.');
+
+var testWindow;
+
+onload = function()
+{
+    if (window.layoutTestController) {
+        layoutTestController.setCanOpenWindows();
+        layoutTestController.overridePreference('WebKitUsesPageCachePreferenceKey', 1);
+    }
+    testWindow = window.open('resources/popstate-fires-with-page-cache-1.html');
+    if (!testWindow)
+        testFailed('Could not open test window');
+}
+
+var testWindowPopstateFireCount = 0;
+
+function onTestWindowPopState(event)
+{
+    debug('popstate fired with state ' + event.state);
+    testWindowPopstateFireCount++;
+    
+    switch (testWindowPopstateFireCount) {
+        case 1:
+            debug('pushState with new state object for page 1');
+            testWindow.history.pushState('newState');
+            debug('going to page 2');
+            // Set the location in a timeout to generate a history entry
+            setTimeout(function() {testWindow.location.href = 'resources/popstate-fires-with-page-cache-2.html';}, 0);            
+            break;            
+        case 2:
+            debug('going back to page 1 with new state object');
+            setTimeout(function() {testWindow.history.back();}, 0);
+            break;
+        case 3:
+            debug('going back to page 1 in initial state');
+            setTimeout(function() {testWindow.history.back();}, 0);
+            break;
+        case 4:
+            testWindow.close();
+            finishJSTest();        
+            break;
+        default:
+            testFailed('unexpected pop state event state');
+            break;            
+    }
+}
+
+var successfullyParsed = true;
+var jsTestIsAsync = true;
+</script>
+<script src="../../js/resources/js-test-post.js"></script>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/fast/loader/stateobjects/pushstate-object-types.html b/LayoutTests/fast/loader/stateobjects/pushstate-object-types.html
index 3992c4d..0989114 100644
--- a/LayoutTests/fast/loader/stateobjects/pushstate-object-types.html
+++ b/LayoutTests/fast/loader/stateobjects/pushstate-object-types.html
@@ -10,11 +10,12 @@ if (window.layoutTestController) {
 
 function log(txt)
 {
-    document.getElementById("logger").innerText += txt + "\n";
+    document.getElementById("logger").appendChild(document.createTextNode(txt + "\n"));
 }
-    
+
 function runTest()
 {
+    beganTest = true;
     history.replaceState("FirstEntry", "Initial entry");
     history.pushState(undefined, "undefined entry");
     history.pushState(null, "null entry");
@@ -39,10 +40,20 @@ history.pushState([FileList], "FileList entry");
     history.back();
 }
 
-function statePopped()
+var beganTest = false;
+
+onpopstate = function(event)
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+    
     if (event.state instanceof Date)
-        log("State popped - " + event.state * 1 + " (type " + typeof event.state + ")");
+        log("State popped - " + event.state.getTime()  + " (type " + typeof event.state + ")");
     else
         log("State popped - " + event.state + " (type " + typeof event.state + ")");
 
@@ -54,10 +65,10 @@ function statePopped()
 }
 
 </script>
-<body onload="runTest();" onpopstate="statePopped();">
-<pre id="someelement">
+<body>
+<p>
 This test calls pushState with state objects of all the different object types supported by the HTML5 "internal structured cloning algorithm" and makes sure the events contain the expected objects when the states are popped.
-</pre><br>
+</p>
 <pre id="logger"></pre>
 </body>
 </html>
diff --git a/LayoutTests/fast/loader/stateobjects/pushstate-then-replacestate.html b/LayoutTests/fast/loader/stateobjects/pushstate-then-replacestate.html
index 976013e..de6f093 100644
--- a/LayoutTests/fast/loader/stateobjects/pushstate-then-replacestate.html
+++ b/LayoutTests/fast/loader/stateobjects/pushstate-then-replacestate.html
@@ -22,8 +22,18 @@ function runTest()
     history.back();
 }
 
-function statePopped()
+var beganTest = false;
+
+onpopstate = function(event)
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == null)
         history.forward();
@@ -32,7 +42,7 @@ function statePopped()
 }
 
 </script>
-<body onload="runTest();" onpopstate="statePopped();">
+<body>
 <pre>
 This test does the following:
 -Makes a call to pushState()
diff --git a/LayoutTests/fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html b/LayoutTests/fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html
index e701601..a92d7cc 100644
--- a/LayoutTests/fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html
+++ b/LayoutTests/fast/loader/stateobjects/pushstate-with-fragment-urls-and-hashchange.html
@@ -44,8 +44,18 @@ function lastPathComponent()
     return window.location.href.split('/').pop();
 }
 
-function statePopped()
+var beganTest = false;
+
+onpopstate = function(event)
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+
     log("State popped with event " + event.state + " (type " + typeof event.state + ") and last path component " + lastPathComponent());
     if (event.state != "OriginalEntry")
         setTimeout("history.back();", 0);
@@ -53,13 +63,13 @@ function statePopped()
         layoutTestController.notifyDone();
 }
 
-function hashChanged()
+onhashchange = function()
 {
     log("Hash change fired and last path component is " + lastPathComponent());
 }
 
 </script>
-<body onload="runTest();" onpopstate="statePopped();" onhashchange="hashChanged();">
+<body>
 <pre>
 This test pushes a series of state objects with different URLs and fragment identifiers meant to test the hashChange event as states are popped.
 </pre><br>
diff --git a/LayoutTests/fast/loader/stateobjects/replacestate-in-iframe.html b/LayoutTests/fast/loader/stateobjects/replacestate-in-iframe.html
index b01c21e..031b027 100644
--- a/LayoutTests/fast/loader/stateobjects/replacestate-in-iframe.html
+++ b/LayoutTests/fast/loader/stateobjects/replacestate-in-iframe.html
@@ -33,7 +33,6 @@ function windowLoaded() {
 
 onload = function() {
   testWin = open("resources/replacestate-in-iframe-window.html");
-  testWin.onload = windowLoaded;
 }
 </script>
 <body>PENDING</body>
diff --git a/LayoutTests/fast/loader/stateobjects/replacestate-then-pushstate.html b/LayoutTests/fast/loader/stateobjects/replacestate-then-pushstate.html
index 826264a..76a5e1b 100644
--- a/LayoutTests/fast/loader/stateobjects/replacestate-then-pushstate.html
+++ b/LayoutTests/fast/loader/stateobjects/replacestate-then-pushstate.html
@@ -22,8 +22,18 @@ function runTest()
     history.back();
 }
 
-function statePopped()
+var beganTest = false;
+
+onpopstate = function(event)
 {
+    // The first time popstate fires, it's because the page has finished loading.
+    // Only then can we begin the test.
+    if (!beganTest) {
+        beganTest = true;
+        runTest();
+        return;
+    }
+
     log("State popped - " + event.state + " (type " + typeof event.state + ")");
     if (event.state == "OriginalHistoryItem")
         history.forward();
@@ -32,7 +42,7 @@ function statePopped()
 }
 
 </script>
-<body onload="runTest();" onpopstate="statePopped();">
+<body>
 <pre>
 This test does the following:
 -Makes a call to replaceState()
diff --git a/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-1.html b/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-1.html
new file mode 100644
index 0000000..db19ebd
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-1.html
@@ -0,0 +1 @@
+<body onpopstate="opener.onTestWindowPopState(event)">page 1</body>
\ No newline at end of file
diff --git a/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-2.html b/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-2.html
new file mode 100644
index 0000000..1929612
--- /dev/null
+++ b/LayoutTests/fast/loader/stateobjects/resources/popstate-fires-with-page-cache-2.html
@@ -0,0 +1 @@
+<body onpopstate="opener.onTestWindowPopState(event)">page 2</body>
\ No newline at end of file
diff --git a/LayoutTests/fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html b/LayoutTests/fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html
index 863e7e4..c83f9c8 100644
--- a/LayoutTests/fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html
+++ b/LayoutTests/fast/loader/stateobjects/resources/replacestate-in-iframe-window-child.html
@@ -4,6 +4,12 @@ onunload = function() {
 }
 
 onpopstate = function(e) {
+  if (!sessionStorage.beganTest) {
+      sessionStorage.beganTest = true; 
+      top.opener.windowLoaded();
+      return;
+  }
+  
   alert("onpopstate");
   top.opener.notifyDone(window == parent ? "FAIL" : "PASS");
 }
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index a0578be..5e5e446 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,36 @@
+2010-09-01  Mihai Parparita  <mihaip at chromium.org>
+
+        Reviewed by Brady Eidson.
+
+        popstate event is not fired until document opts in by calling pushstate.
+        https://bugs.webkit.org/show_bug.cgi?id=41372
+
+        Fire popstate even when we don't have a state object when a page is 
+        loaded (for both regular loads and those from the page cache). Also
+        fire popstate when doing in-document navigation via fragment changes.
+        This is consistent with both Gecko and recent the HTML5 spec change:
+        http://html5.org/tools/web-apps-tracker?from=5376&to=5377
+
+        Tests: fast/loader/stateobjects/popstate-fires-on-history-traversal.html
+               fast/loader/stateobjects/popstate-fires-with-page-cache.html
+
+        * bindings/js/SerializedScriptValue.cpp:
+        * bindings/js/SerializedScriptValue.h:
+        (WebCore::SerializedScriptValue::create):
+        * bindings/v8/SerializedScriptValue.cpp:
+        * bindings/v8/SerializedScriptValue.h:
+        * dom/Document.cpp:
+        (WebCore::Document::implicitClose):
+        * dom/Document.h:
+        * history/CachedFrame.cpp:
+        (WebCore::CachedFrameBase::restore):
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::loadInSameDocument):
+        (WebCore::FrameLoader::transitionToCommitted):
+        * loader/HistoryController.cpp:
+        (WebCore::HistoryController::pushState):
+        (WebCore::HistoryController::replaceState):
+
 2010-09-01  Ryosuke Niwa  <rniwa at webkit.org>
 
         Reviewed by Dimitri Glazkov.
diff --git a/WebCore/bindings/js/SerializedScriptValue.cpp b/WebCore/bindings/js/SerializedScriptValue.cpp
index 00ec25f..ca18ec0 100644
--- a/WebCore/bindings/js/SerializedScriptValue.cpp
+++ b/WebCore/bindings/js/SerializedScriptValue.cpp
@@ -1080,6 +1080,12 @@ PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(JSContextRef ori
     return serializedValue;
 }
 
+SerializedScriptValue* SerializedScriptValue::nullValue() 
+{
+    DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, nullValue, (SerializedScriptValue::create()));
+    return nullValue.get();
+}
+
 JSValueRef SerializedScriptValue::deserialize(JSContextRef destinationContext, JSValueRef* exception)
 {
     JSLock lock(SilenceAssertionsOnly);
diff --git a/WebCore/bindings/js/SerializedScriptValue.h b/WebCore/bindings/js/SerializedScriptValue.h
index 2b7d222..32ce418 100644
--- a/WebCore/bindings/js/SerializedScriptValue.h
+++ b/WebCore/bindings/js/SerializedScriptValue.h
@@ -213,6 +213,8 @@ namespace WebCore {
         {
             return adoptRef(new SerializedScriptValue(SerializedScriptValueData()));
         }
+        
+        static SerializedScriptValue* nullValue();
 
         PassRefPtr<SerializedScriptValue> release()
         {
diff --git a/WebCore/bindings/v8/SerializedScriptValue.cpp b/WebCore/bindings/v8/SerializedScriptValue.cpp
index 395ef1a..0b908b8 100644
--- a/WebCore/bindings/v8/SerializedScriptValue.cpp
+++ b/WebCore/bindings/v8/SerializedScriptValue.cpp
@@ -1096,6 +1096,12 @@ PassRefPtr<SerializedScriptValue> SerializedScriptValue::create()
     return adoptRef(new SerializedScriptValue());
 }
 
+SerializedScriptValue* SerializedScriptValue::nullValue() 
+{
+    DEFINE_STATIC_LOCAL(RefPtr<SerializedScriptValue>, nullValue, (SerializedScriptValue::create()));
+    return nullValue.get();
+}
+
 PassRefPtr<SerializedScriptValue> SerializedScriptValue::release()
 {
     RefPtr<SerializedScriptValue> result = adoptRef(new SerializedScriptValue(m_data, WireData));
diff --git a/WebCore/bindings/v8/SerializedScriptValue.h b/WebCore/bindings/v8/SerializedScriptValue.h
index 3710b5a..275e5f2 100644
--- a/WebCore/bindings/v8/SerializedScriptValue.h
+++ b/WebCore/bindings/v8/SerializedScriptValue.h
@@ -52,6 +52,8 @@ public:
     static PassRefPtr<SerializedScriptValue> createFromWire(String data);
     static PassRefPtr<SerializedScriptValue> create(String data);
     static PassRefPtr<SerializedScriptValue> create();
+    
+    static SerializedScriptValue* nullValue();    
 
     PassRefPtr<SerializedScriptValue> release();
 
diff --git a/WebCore/dom/Document.cpp b/WebCore/dom/Document.cpp
index 8a35abc..741f98a 100644
--- a/WebCore/dom/Document.cpp
+++ b/WebCore/dom/Document.cpp
@@ -82,7 +82,6 @@
 #include "HTMLStyleElement.h"
 #include "HTMLTitleElement.h"
 #include "HTTPParsers.h"
-#include "HistoryItem.h"
 #include "HitTestRequest.h"
 #include "HitTestResult.h"
 #include "ImageLoader.h"
@@ -2021,8 +2020,7 @@ void Document::implicitClose()
     ImageLoader::dispatchPendingLoadEvents();
     dispatchWindowLoadEvent();
     enqueuePageshowEvent(PageshowEventNotPersisted);
-    if (m_pendingStateObject)
-        enqueuePopstateEvent(m_pendingStateObject.release());
+    enqueuePopstateEvent(m_pendingStateObject ? m_pendingStateObject.release() : SerializedScriptValue::nullValue());
     
     if (f)
         f->loader()->handledOnloadEvents();
diff --git a/WebCore/dom/Document.h b/WebCore/dom/Document.h
index b458d26..3e804f7 100644
--- a/WebCore/dom/Document.h
+++ b/WebCore/dom/Document.h
@@ -85,7 +85,6 @@ class HTMLFrameOwnerElement;
 class HTMLHeadElement;
 class HTMLInputElement;
 class HTMLMapElement;
-class HistoryItem;
 class HitTestRequest;
 class HitTestResult;
 class InspectorTimelineAgent;
@@ -990,6 +989,7 @@ public:
     void enqueueEvent(PassRefPtr<Event>);
     void enqueuePageshowEvent(PageshowEventPersistence);
     void enqueueHashchangeEvent(const String& oldURL, const String& newURL);
+    void enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject);
 
     void addMediaCanStartListener(MediaCanStartListener*);
     void removeMediaCanStartListener(MediaCanStartListener*);
@@ -1058,7 +1058,6 @@ private:
 
     void createStyleSelector();
 
-    void enqueuePopstateEvent(PassRefPtr<SerializedScriptValue> stateObject);
     void pendingEventTimerFired(Timer<Document>*);
 
     PassRefPtr<NodeList> handleZeroPadding(const HitTestRequest&, HitTestResult&) const;
diff --git a/WebCore/history/CachedFrame.cpp b/WebCore/history/CachedFrame.cpp
index 1f4895f..f0a4fed 100644
--- a/WebCore/history/CachedFrame.cpp
+++ b/WebCore/history/CachedFrame.cpp
@@ -33,6 +33,7 @@
 #include "Frame.h"
 #include "FrameLoaderClient.h"
 #include "FrameView.h"
+#include "HistoryItem.h"
 #include "Logging.h"
 #include "PageTransitionEvent.h"
 #include <wtf/text/CString.h>
@@ -106,6 +107,10 @@ void CachedFrameBase::restore()
         m_childFrames[i]->open();
 
     m_document->enqueuePageshowEvent(PageshowEventPersisted);
+    
+    HistoryItem* historyItem = frame->loader()->history()->currentItem();
+    m_document->enqueuePopstateEvent(historyItem && historyItem->stateObject() ? historyItem->stateObject() : SerializedScriptValue::nullValue());
+    
 #if ENABLE(TOUCH_EVENTS)
     if (m_document->hasListenerType(Document::TOUCH_LISTENER))
         m_document->page()->chrome()->client()->needTouchEvents(true);
diff --git a/WebCore/loader/FrameLoader.cpp b/WebCore/loader/FrameLoader.cpp
index b47ae5a..ccbccc9 100644
--- a/WebCore/loader/FrameLoader.cpp
+++ b/WebCore/loader/FrameLoader.cpp
@@ -1168,10 +1168,8 @@ void FrameLoader::loadInSameDocument(const KURL& url, SerializedScriptValue* sta
 
     m_client->dispatchDidNavigateWithinPage();
 
-    if (stateObject) {
-        m_frame->document()->statePopped(stateObject);
-        m_client->dispatchDidPopStateWithinPage();
-    }
+    m_frame->document()->statePopped(stateObject ? stateObject : SerializedScriptValue::nullValue());
+    m_client->dispatchDidPopStateWithinPage();
     
     if (hashChange) {
         m_frame->document()->enqueueHashchangeEvent(oldURL, url);
@@ -1947,7 +1945,8 @@ void FrameLoader::transitionToCommitted(PassRefPtr<CachedPage> cachedPage)
 
                     history()->updateForBackForwardNavigation();
 
-                    if (history()->currentItem())
+                    // For cached pages, CachedFrame::restore will take care of firing the popstate event with the history item's state object
+                    if (history()->currentItem() && !cachedPage)
                         m_pendingStateObject = history()->currentItem()->stateObject();
 
                     // Create a document view for this document, or used the cached view.
diff --git a/WebCore/loader/HistoryController.cpp b/WebCore/loader/HistoryController.cpp
index 11a0277..32a6a91 100644
--- a/WebCore/loader/HistoryController.cpp
+++ b/WebCore/loader/HistoryController.cpp
@@ -657,12 +657,6 @@ void HistoryController::pushState(PassRefPtr<SerializedScriptValue> stateObject,
     m_currentItem->setStateObject(stateObject);
     m_currentItem->setURLString(urlString);
 
-    // Create a null state object for the previous HistoryItem so that we will
-    // generate a popstate event when navigating back to it.
-    // FIXME: http://webkit.org/b/41372 implies that we shouldn't need this.
-    if (!m_previousItem->stateObject())
-        m_previousItem->setStateObject(SerializedScriptValue::create());
-
     page->backForwardList()->addItem(topItem.release());
 }
 

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list