Bug 602256: Using history.pushState in a page breaks history tracking for inner frames. r=bz, a=blocks-betaN

This commit is contained in:
Dave Townsend 2011-02-03 09:27:39 -08:00
Родитель c633a6c870
Коммит 90729fb263
7 изменённых файлов: 253 добавлений и 4 удалений

Просмотреть файл

@ -3369,6 +3369,7 @@ nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
nsCOMPtr<nsISHEntry> nextEntry;
aCloneRef->GetID(&cloneID);
rv = CloneAndReplace(currentEntry, this, cloneID, aNewEntry,
loadType == LOAD_PUSHSTATE,
getter_AddRefs(nextEntry));
if (NS_SUCCEEDED(rv)) {
@ -9918,6 +9919,16 @@ nsDocShell::AddToSessionHistory(nsIURI * aURI, nsIChannel * aChannel,
if (root == static_cast<nsIDocShellTreeItem *>(this) && mSessionHistory) {
// Bug 629559: Detect if this is an anchor navigation and clone the
// session history in that case too
if (mLoadType == LOAD_PUSHSTATE) {
PRUint32 cloneID;
mOSHE->GetID(&cloneID);
nsCOMPtr<nsISHEntry> newEntry;
CloneAndReplace(mOSHE, this, cloneID, entry, PR_TRUE, getter_AddRefs(newEntry));
NS_ASSERTION(entry == newEntry, "The new session history should be in the new entry");
}
// This is the root docshell
if (LOAD_TYPE_HAS_FLAGS(mLoadType, LOAD_FLAGS_REPLACE_HISTORY)) {
// Replace current entry in session history.
@ -10131,12 +10142,14 @@ nsDocShell::WalkHistoryEntries(nsISHEntry *aRootEntry,
struct NS_STACK_CLASS CloneAndReplaceData
{
CloneAndReplaceData(PRUint32 aCloneID, nsISHEntry *aReplaceEntry,
nsISHEntry *aDestTreeParent)
PRBool aCloneChildren, nsISHEntry *aDestTreeParent)
: cloneID(aCloneID),
cloneChildren(aCloneChildren),
replaceEntry(aReplaceEntry),
destTreeParent(aDestTreeParent) { }
PRUint32 cloneID;
PRBool cloneChildren;
nsISHEntry *replaceEntry;
nsISHEntry *destTreeParent;
nsCOMPtr<nsISHEntry> resultEntry;
@ -10166,9 +10179,19 @@ nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
aEntry->GetID(&srcID);
if (srcID == cloneID) {
// Just replace the entry, and don't walk the children.
// Replace the entry
dest = replaceEntry;
dest->SetIsSubFrame(PR_TRUE);
if (data->cloneChildren) {
// Walk the children
CloneAndReplaceData childData(cloneID, replaceEntry,
data->cloneChildren, dest);
result = WalkHistoryEntries(aEntry, aShell,
CloneAndReplaceChild, &childData);
if (NS_FAILED(result))
return result;
}
} else {
// Clone the SHEntry...
result = aEntry->Clone(getter_AddRefs(dest));
@ -10179,7 +10202,8 @@ nsDocShell::CloneAndReplaceChild(nsISHEntry *aEntry, nsDocShell *aShell,
dest->SetIsSubFrame(PR_TRUE);
// Walk the children
CloneAndReplaceData childData(cloneID, replaceEntry, dest);
CloneAndReplaceData childData(cloneID, replaceEntry,
data->cloneChildren, dest);
result = WalkHistoryEntries(aEntry, aShell,
CloneAndReplaceChild, &childData);
if (NS_FAILED(result))
@ -10201,12 +10225,13 @@ nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
nsDocShell *aSrcShell,
PRUint32 aCloneID,
nsISHEntry *aReplaceEntry,
PRBool aCloneChildren,
nsISHEntry **aResultEntry)
{
NS_ENSURE_ARG_POINTER(aResultEntry);
NS_ENSURE_TRUE(aReplaceEntry, NS_ERROR_FAILURE);
CloneAndReplaceData data(aCloneID, aReplaceEntry, nsnull);
CloneAndReplaceData data(aCloneID, aReplaceEntry, aCloneChildren, nsnull);
nsresult rv = CloneAndReplaceChild(aSrcEntry, aSrcShell, 0, &data);
data.resultEntry.swap(*aResultEntry);

Просмотреть файл

@ -383,10 +383,13 @@ protected:
// |aReplaceEntry|. |aSrcShell| is a (possibly null) docshell which
// corresponds to |aSrcEntry| via its mLSHE or mOHE pointers, and will
// have that pointer updated to point to the cloned history entry.
// If aCloneChildren is true then the children of the entry with id
// |aCloneID| will be cloned into |aReplaceEntry|.
static nsresult CloneAndReplace(nsISHEntry *aSrcEntry,
nsDocShell *aSrcShell,
PRUint32 aCloneID,
nsISHEntry *aReplaceEntry,
PRBool aCloneChildren,
nsISHEntry **aDestEntry);
// Child-walking callback for CloneAndReplace

Просмотреть файл

@ -144,6 +144,7 @@ nsSHEntry::nsSHEntry(const nsSHEntry &other)
, mParent(other.mParent)
, mViewerBounds(0, 0, 0, 0)
, mOwner(other.mOwner)
, mStateData(other.mStateData)
, mDocShellID(other.mDocShellID)
{
}

Просмотреть файл

@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
497633.html \
489127.html \
historyframes.html \
test_497898.html \
test_bug504220.html \
test_consoleAPI.html \
@ -62,6 +63,8 @@ _TEST_FILES = \
test_nodesFromRect.html \
test_frameElementWrapping.html \
file_frameElementWrapping.html \
test_framedhistoryframes.html \
test_windowedhistoryframes.html \
$(NULL)
libs:: $(_TEST_FILES)

Просмотреть файл

@ -0,0 +1,152 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=602256
-->
<head>
<title>Test for Bug 602256</title>
</head>
<body onload="SimpleTest.executeSoon(run_test)">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
<div id="content">
<iframe id="iframe" src="data:text/html,<p%20id='text'>Start</p>"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 602256 **/
var testWin = window.opener ? window.opener : window.parent;
var SimpleTest = testWin.SimpleTest;
function is() { testWin.is.apply(testWin, arguments); }
var gFrame = null;
var gState = null;
window.addEventListener("popstate", function(aEvent) {
gState = aEvent.state;
}, false);
function waitForLoad(aCallback) {
function listener() {
gFrame.removeEventListener("load", listener, false);
SimpleTest.executeSoon(aCallback);
}
gFrame.addEventListener("load", listener, false);
}
function loadContent(aURL, aCallback) {
waitForLoad(aCallback);
gFrame.src = aURL;
}
function getURL() {
return gFrame.contentDocument.documentURI;
}
function getContent() {
return gFrame.contentDocument.getElementById("text").textContent;
}
var START = "data:text/html,<p%20id='text'>Start</p>";
var URL1 = "data:text/html,<p%20id='text'>Test1</p>";
var URL2 = "data:text/html,<p%20id='text'>Test2</p>";
function run_test() {
window.history.pushState("START", window.location);
gFrame = document.getElementById("iframe");
test_basic_inner_navigation();
}
function end_test() {
testWin.done();
}
function test_basic_inner_navigation() {
// Navigate the inner frame a few times
loadContent(URL1, function() {
is(getURL(), URL1, "URL should be correct");
is(getContent(), "Test1", "Page should be correct");
loadContent(URL2, function() {
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
// Test that history is working
waitForLoad(function() {
is(getURL(), URL1, "URL should be correct");
is(getContent(), "Test1", "Page should be correct");
waitForLoad(function() {
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
test_state_navigation();
});
window.history.forward();
});
window.history.back();
});
});
}
function test_state_navigation() {
window.history.pushState("STATE1", window.location);
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.pushState("STATE2", window.location);
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.back();
is(gState, "STATE1", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.forward();
is(gState, "STATE2", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.back();
window.history.back();
is(gState, "START", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
waitForLoad(function() {
is(getURL(), URL1, "URL should be correct");
is(getContent(), "Test1", "Page should be correct");
waitForLoad(function() {
is(gState, "START", "State should be correct");
is(getURL(), START, "URL should be correct");
is(getContent(), "Start", "Page should be correct");
end_test();
});
window.history.back();
is(gState, "START", "State should be correct");
});
window.history.back();
is(gState, "START", "State should be correct");
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,32 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=602256
-->
<head>
<title>Test for Bug 602256</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
<p id="display"></p>
<div id="content">
<iframe id="iframe" src="historyframes.html"></iframe>
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 602256 **/
SimpleTest.waitForExplicitFinish();
function done() {
SimpleTest.finish();
}
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,33 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=602256
-->
<head>
<title>Test for Bug 602256</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602256">Mozilla Bug 602256</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 602256 **/
SimpleTest.waitForExplicitFinish();
function done() {
subWin.close();
SimpleTest.finish();
}
var subWin = window.open("historyframes.html", "_blank");
</script>
</pre>
</body>
</html>