Bug 754029 - Navigating from a new script tag does not add a session history entry. r=smaug

This commit is contained in:
Oonishi Atsushi 2012-07-27 17:51:52 -04:00
parent 1b3e9b3054
commit 0fe829b2cd
2 changed files with 68 additions and 50 deletions

View File

@ -1391,16 +1391,6 @@ nsDocShell::LoadURI(nsIURI * aURI,
}
} // parent
} //parentDS
else {
// This is the root docshell. If we got here while
// executing an onLoad Handler,this load will not go
// into session history.
bool inOnLoadHandler=false;
GetIsExecutingOnLoadHandler(&inOnLoadHandler);
if (inOnLoadHandler) {
loadType = LOAD_NORMAL_REPLACE;
}
}
} // !shEntry
if (shEntry) {
@ -6293,7 +6283,7 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
// Notify the ContentViewer that the Document has finished loading. This
// will cause any OnLoad(...) and PopState(...) handlers to fire.
// will cause any OnLoad(...) handlers to fire.
if (!mEODForCurrentDocument && mContentViewer) {
mIsExecutingOnLoadHandler = true;
mContentViewer->LoadComplete(aStatus);

View File

@ -38,6 +38,7 @@
#include "nsJSUtils.h"
#include "jsfriendapi.h"
#include "nsContentUtils.h"
#include "nsEventStateManager.h"
static nsresult
GetContextFromStack(nsIJSContextStack *aStack, JSContext **aContext)
@ -530,8 +531,71 @@ nsLocation::SetHref(const nsAString& aHref)
if (NS_FAILED(GetContextFromStack(stack, &cx)))
return NS_ERROR_FAILURE;
// According to HTML5 spec, |location.href = ...| must act as if
// it were |location.replace(...)| by the time the page load finishes.
//
// http://www.w3.org/TR/2011/WD-html5-20110113/history.html#location
//
// > The href attribute must return the current address of the
// > associated Document object, as an absolute URL.
// >
// > On setting, if the Location object's associated Document
// > object has completely loaded, then the user agent must act
// > as if the assign() method had been called with the new value
// > as its argument. Otherwise, the user agent must act as if
// > the replace() method had been called with the new value as its
// > argument.
//
// Note: The spec says the condition is "Document object has completely
// loaded", but that may break some websites. If the user was
// willing to move from one page to another, and was able to do
// so, we should not overwrite the session history entry even
// if the loading has not finished yet.
//
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=17041
//
// See bug 39938, bug 72197, bug 178729 and bug 754029.
// About other browsers:
// http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2010-July/027372.html
bool replace = false;
if (!nsEventStateManager::IsHandlingUserInput()) {
// "completely loaded" is defined at:
//
// http://www.w3.org/TR/2012/WD-html5-20120329/the-end.html#completely-loaded
//
// > 7. document readiness to "complete", and fire "load".
// >
// > 8. "pageshow"
// >
// > 9. ApplicationCache
// >
// > 10. Print in the pending list.
// >
// > 12. Queue a task to mark the Document as completely loaded.
//
// Since Gecko doesn't (yet) have a flag corresponding to no. "12.
// ... completely loaded", here the logic is a little tricky.
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
nsCOMPtr<nsIDocument> document(do_GetInterface(docShell));
if (document) {
replace =
nsIDocument::READYSTATE_COMPLETE != document->GetReadyStateEnum();
// nsIDocShell::isExecutingOnLoadHandler is true while
// the document is handling "load", "pageshow",
// "readystatechange" for "complete" and "beforeprint"/"afterprint".
//
// Maybe this API property needs a better name.
if (!replace) {
docShell->GetIsExecutingOnLoadHandler(&replace);
}
}
}
if (cx) {
rv = SetHrefWithContext(cx, aHref, false);
rv = SetHrefWithContext(cx, aHref, replace);
} else {
rv = GetHref(oldHref);
@ -541,7 +605,7 @@ nsLocation::SetHref(const nsAString& aHref)
rv = NS_NewURI(getter_AddRefs(oldUri), oldHref);
if (oldUri) {
rv = SetHrefWithBase(aHref, oldUri, false);
rv = SetHrefWithBase(aHref, oldUri, replace);
}
}
}
@ -572,8 +636,6 @@ nsLocation::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
nsresult result;
nsCOMPtr<nsIURI> newUri;
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mDocShell));
nsCAutoString docCharset;
if (NS_SUCCEEDED(GetDocumentCharacterSetForURI(aHref, docCharset)))
result = NS_NewURI(getter_AddRefs(newUri), aHref, docCharset.get(), aBase);
@ -581,41 +643,7 @@ nsLocation::SetHrefWithBase(const nsAString& aHref, nsIURI* aBase,
result = NS_NewURI(getter_AddRefs(newUri), aHref, nsnull, aBase);
if (newUri) {
/* Check with the scriptContext if it is currently processing a script tag.
* If so, this must be a <script> tag with a location.href in it.
* we want to do a replace load, in such a situation.
* In other cases, for example if a event handler or a JS timer
* had a location.href in it, we want to do a normal load,
* so that the new url will be appended to Session History.
* This solution is tricky. Hopefully it isn't going to bite
* anywhere else. This is part of solution for bug # 39938, 72197
*
*/
bool inScriptTag=false;
// Get JSContext from stack.
nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1", &result));
if (stack) {
JSContext *cx;
result = GetContextFromStack(stack, &cx);
if (cx) {
nsIScriptContext *scriptContext =
nsJSUtils::GetDynamicScriptContext(cx);
if (scriptContext) {
if (scriptContext->GetProcessingScriptTag()) {
// Now check to make sure that the script is running in our window,
// since we only want to replace if the location is set by a
// <script> tag in the same window. See bug 178729.
nsCOMPtr<nsIScriptGlobalObject> ourGlobal(do_GetInterface(docShell));
inScriptTag = (ourGlobal == scriptContext->GetGlobalObject());
}
}
} //cx
} // stack
return SetURI(newUri, aReplace || inScriptTag);
return SetURI(newUri, aReplace);
}
return result;