Bug 500328 - Implement History.pushState(), History.replaceState() methods. r=smaug, r=zpao, sr=sicking
This commit is contained in:
@@ -109,6 +109,7 @@
|
||||
#include "nsIURIClassifier.h"
|
||||
#include "nsIOfflineCacheUpdate.h"
|
||||
#include "nsCPrefetchService.h"
|
||||
#include "nsJSON.h"
|
||||
|
||||
// we want to explore making the document own the load group
|
||||
// so we can associate the document URI with the load group.
|
||||
@@ -655,6 +656,8 @@ DispatchPings(nsIContent *content, nsIURI *referrer)
|
||||
ForEachPing(content, SendPing, &info);
|
||||
}
|
||||
|
||||
static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
|
||||
|
||||
//*****************************************************************************
|
||||
//*** nsDocShell: Object Management
|
||||
//*****************************************************************************
|
||||
@@ -863,6 +866,14 @@ NS_IMETHODIMP nsDocShell::GetInterface(const nsIID & aIID, void **aSink)
|
||||
mContentViewer->GetDOMDocument((nsIDOMDocument **) aSink);
|
||||
return *aSink ? NS_OK : NS_NOINTERFACE;
|
||||
}
|
||||
else if (aIID.Equals(NS_GET_IID(nsIDocument)) &&
|
||||
NS_SUCCEEDED(EnsureContentViewer())) {
|
||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
||||
if (!domDoc)
|
||||
return NS_NOINTERFACE;
|
||||
return domDoc->QueryInterface(aIID, aSink);
|
||||
}
|
||||
else if (aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer))) {
|
||||
*aSink = nsnull;
|
||||
|
||||
@@ -1196,7 +1207,7 @@ nsDocShell::LoadURI(nsIURI * aURI,
|
||||
// The parent was loaded normally. In this case, this *brand new* child really shouldn't
|
||||
// have a SHEntry. If it does, it could be because the parent is replacing an
|
||||
// existing frame with a new frame, in the onLoadHandler. We don't want this
|
||||
// url to get into session history. Clear off shEntry, and set laod type to
|
||||
// url to get into session history. Clear off shEntry, and set load type to
|
||||
// LOAD_BYPASS_HISTORY.
|
||||
PRBool inOnLoadHandler=PR_FALSE;
|
||||
parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
|
||||
@@ -3226,11 +3237,11 @@ nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
|
||||
PRInt32 aChildOffset)
|
||||
PRInt32 aChildOffset, PRUint32 loadType)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
if (mLSHE) {
|
||||
if (mLSHE && loadType != LOAD_PUSHSTATE) {
|
||||
/* You get here if you are currently building a
|
||||
* hierarchy ie.,you just visited a frameset page
|
||||
*/
|
||||
@@ -3285,7 +3296,8 @@ nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
|
||||
nsCOMPtr<nsIDocShellHistory> parent =
|
||||
do_QueryInterface(GetAsSupports(mParent), &rv);
|
||||
if (parent) {
|
||||
rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset);
|
||||
rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset,
|
||||
loadType);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
@@ -3313,7 +3325,7 @@ nsDocShell::DoAddChildSHEntry(nsISHEntry* aNewEntry, PRInt32 aChildOffset)
|
||||
nsCOMPtr<nsIDocShellHistory> parent =
|
||||
do_QueryInterface(GetAsSupports(mParent), &rv);
|
||||
if (parent) {
|
||||
rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset);
|
||||
rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset, mLoadType);
|
||||
}
|
||||
|
||||
|
||||
@@ -3457,7 +3469,6 @@ NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
|
||||
|
||||
}
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::LoadURI(const PRUnichar * aURI,
|
||||
PRUint32 aLoadFlags,
|
||||
@@ -3846,6 +3857,13 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
|
||||
mFailedURI = aURI;
|
||||
mFailedLoadType = mLoadType;
|
||||
|
||||
if (mLSHE) {
|
||||
// If we don't give mLSHE a new doc identifier here, when we go back or
|
||||
// forward to another SHEntry with the same doc identifier, the error
|
||||
// page will persist.
|
||||
mLSHE->SetUniqueDocIdentifier();
|
||||
}
|
||||
|
||||
nsCAutoString url;
|
||||
nsCAutoString charset;
|
||||
if (aURI)
|
||||
@@ -5714,13 +5732,13 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress,
|
||||
// We're done with the URI classifier for this channel
|
||||
mClassifier = nsnull;
|
||||
|
||||
//
|
||||
// Notify the ContentViewer that the Document has finished loading...
|
||||
//
|
||||
// This will cause any OnLoad(...) handlers to fire, if it is a HTML
|
||||
// document...
|
||||
//
|
||||
// Notify the ContentViewer that the Document has finished loading. This
|
||||
// will cause any OnLoad(...) and PopState(...) handlers to fire.
|
||||
if (!mEODForCurrentDocument && mContentViewer) {
|
||||
// Set the pending state object which will be returned to the page in
|
||||
// the popstate event.
|
||||
SetDocPendingStateObj(mLSHE);
|
||||
|
||||
mIsExecutingOnLoadHandler = PR_TRUE;
|
||||
mContentViewer->LoadComplete(aStatus);
|
||||
mIsExecutingOnLoadHandler = PR_FALSE;
|
||||
@@ -7368,6 +7386,26 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::SetDocPendingStateObj(nsISHEntry *shEntry)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||
|
||||
nsAutoString stateData;
|
||||
if (shEntry) {
|
||||
rv = shEntry->GetStateData(stateData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// if shEntry is null, we just set the pending state object to the
|
||||
// empty string.
|
||||
}
|
||||
|
||||
document->SetPendingStateObject(stateData);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::CheckLoadingPermissions()
|
||||
@@ -7844,27 +7882,50 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((aLoadType == LOAD_NORMAL ||
|
||||
aLoadType == LOAD_STOP_CONTENT ||
|
||||
LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
|
||||
aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_LINK) && allowScroll) {
|
||||
|
||||
if (aLoadType == LOAD_NORMAL ||
|
||||
aLoadType == LOAD_STOP_CONTENT ||
|
||||
LOAD_TYPE_HAS_FLAGS(aLoadType, LOAD_FLAGS_REPLACE_HISTORY) ||
|
||||
aLoadType == LOAD_HISTORY ||
|
||||
aLoadType == LOAD_LINK) {
|
||||
|
||||
PRBool wasAnchor = PR_FALSE;
|
||||
PRBool doHashchange = PR_FALSE;
|
||||
nscoord cx, cy;
|
||||
NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx, &cy,
|
||||
&doHashchange),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
if (wasAnchor) {
|
||||
if (allowScroll) {
|
||||
NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType, &cx,
|
||||
&cy, &doHashchange),
|
||||
NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
// If this is a history load, aSHEntry will have document identifier X
|
||||
// if it was created as a result of a History.pushState() from a
|
||||
// SHEntry with doc ident X, or if it was created by changing the hash
|
||||
// of the URI corresponding to a SHEntry with doc ident X.
|
||||
PRBool sameDocIdent = PR_FALSE;
|
||||
if (mOSHE && aSHEntry) {
|
||||
PRUint64 ourDocIdent, otherDocIdent;
|
||||
mOSHE->GetDocIdentifier(&ourDocIdent);
|
||||
aSHEntry->GetDocIdentifier(&otherDocIdent);
|
||||
sameDocIdent = (ourDocIdent == otherDocIdent);
|
||||
}
|
||||
|
||||
// Do a short-circuited load if the new URI differs from the current
|
||||
// URI only in the hash, or if the two entries belong to the same
|
||||
// document and don't point to the same object.
|
||||
//
|
||||
// (If we didn't check that the SHEntries are different objects,
|
||||
// history.go(0) would short-circuit instead of triggering a true
|
||||
// load, and we wouldn't dispatch an onload event to the page.)
|
||||
if (wasAnchor || (sameDocIdent && (mOSHE != aSHEntry))) {
|
||||
mLoadType = aLoadType;
|
||||
mURIResultedInDocument = PR_TRUE;
|
||||
|
||||
/* we need to assign mLSHE to aSHEntry right here, so that on History loads,
|
||||
* SetCurrentURI() called from OnNewURI() will send proper
|
||||
/* we need to assign mLSHE to aSHEntry right here, so that on History loads,
|
||||
* SetCurrentURI() called from OnNewURI() will send proper
|
||||
* onLocationChange() notifications to the browser to update
|
||||
* back/forward buttons.
|
||||
* back/forward buttons.
|
||||
*/
|
||||
SetHistoryEntry(&mLSHE, aSHEntry);
|
||||
|
||||
@@ -7877,10 +7938,11 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
mOSHE->GetOwner(getter_AddRefs(owner));
|
||||
}
|
||||
OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE);
|
||||
|
||||
nsCOMPtr<nsIInputStream> postData;
|
||||
PRUint32 pageIdent = PR_UINT32_MAX;
|
||||
nsCOMPtr<nsISupports> cacheKey;
|
||||
|
||||
|
||||
if (mOSHE) {
|
||||
/* save current position of scroller(s) (bug 59774) */
|
||||
mOSHE->SetScrollPosition(cx, cy);
|
||||
@@ -7895,8 +7957,19 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
mOSHE->GetPageIdentifier(&pageIdent);
|
||||
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
|
||||
}
|
||||
|
||||
if (mLSHE && wasAnchor) {
|
||||
// If it's an anchor load, set mLSHE's doc identifier to
|
||||
// mOSHE's doc identifier -- These are the same documents,
|
||||
// as far as HTML5 is concerned.
|
||||
PRUint64 docIdent;
|
||||
rv = mOSHE->GetDocIdentifier(&docIdent);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mLSHE->SetDocIdentifier(docIdent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Assign mOSHE to mLSHE. This will either be a new entry created
|
||||
* by OnNewURI() for normal loads or aSHEntry for history loads.
|
||||
*/
|
||||
@@ -7913,7 +7986,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
// cache first
|
||||
if (cacheKey)
|
||||
mOSHE->SetCacheKey(cacheKey);
|
||||
|
||||
|
||||
// Propagate our page ident to the new mOSHE so that
|
||||
// we'll know it just differed by a scroll on the page.
|
||||
if (pageIdent != PR_UINT32_MAX)
|
||||
@@ -7949,12 +8022,27 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||
shEntry->SetTitle(mTitle);
|
||||
}
|
||||
|
||||
if (doHashchange) {
|
||||
nsCOMPtr<nsPIDOMWindow> window =
|
||||
do_QueryInterface(mScriptGlobal);
|
||||
if (sameDocIdent) {
|
||||
// Set the doc's URI according to the new history entry's URI
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
mOSHE->GetURI(getter_AddRefs(newURI));
|
||||
NS_ENSURE_TRUE(newURI, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsIDocument> doc =
|
||||
do_GetInterface(GetAsSupports(this));
|
||||
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
||||
|
||||
if (window)
|
||||
window->DispatchSyncHashchange();
|
||||
doc->SetDocumentURI(newURI);
|
||||
}
|
||||
|
||||
SetDocPendingStateObj(mOSHE);
|
||||
|
||||
// Dispatch the popstate and hashchange events, as appropriate
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
|
||||
if (window) {
|
||||
window->DispatchSyncPopState();
|
||||
|
||||
if (doHashchange)
|
||||
window->DispatchSyncHashchange();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -8837,6 +8925,14 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
|
||||
if (uploadChannel) {
|
||||
uploadChannel->GetUploadStream(getter_AddRefs(inputStream));
|
||||
}
|
||||
|
||||
// If the response status indicates an error, unlink this session
|
||||
// history entry from any entries sharing its doc ident.
|
||||
PRUint32 responseStatus;
|
||||
nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
|
||||
if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
|
||||
mLSHE->SetUniqueDocIdentifier();
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Create SH Entry (mLSHE) only if there is a SessionHistory object (mSessionHistory) in
|
||||
@@ -8858,7 +8954,7 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
|
||||
aLoadType & LOAD_CMD_RELOAD)
|
||||
updateHistory = PR_FALSE;
|
||||
|
||||
// Check if the url to be loaded is the same as the one already loaded.
|
||||
// Check if the url to be loaded is the same as the one already loaded.
|
||||
if (mCurrentURI)
|
||||
aURI->Equals(mCurrentURI, &equalUri);
|
||||
|
||||
@@ -8895,7 +8991,6 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
|
||||
SetHistoryEntry(&mLSHE, mOSHE);
|
||||
}
|
||||
|
||||
|
||||
/* If the user pressed shift-reload, cache will create a new cache key
|
||||
* for the page. Save the new cacheKey in Session History.
|
||||
* see bug 90098
|
||||
@@ -8985,7 +9080,283 @@ nsDocShell::SetReferrerURI(nsIURI * aURI)
|
||||
|
||||
//*****************************************************************************
|
||||
// nsDocShell: Session History
|
||||
//*****************************************************************************
|
||||
//*****************************************************************************
|
||||
|
||||
nsresult
|
||||
nsDocShell::StringifyJSValVariant(nsIVariant *aData, nsAString &aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
aResult.Truncate();
|
||||
|
||||
// First, try to extract a jsval from the variant |aData|. This works only
|
||||
// if the variant implements GetAsJSVal.
|
||||
jsval jsData;
|
||||
rv = aData->GetAsJSVal(&jsData);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
|
||||
|
||||
// Now get the JSContext associated with the current document.
|
||||
// First get the current document.
|
||||
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||
|
||||
// Get the JSContext from the document, like we do in
|
||||
// nsContentUtils::GetContextFromDocument().
|
||||
nsIScriptGlobalObject *sgo = document->GetScopeObject();
|
||||
NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
|
||||
|
||||
nsIScriptContext *scx = sgo->GetContext();
|
||||
NS_ENSURE_TRUE(scx, NS_ERROR_FAILURE);
|
||||
|
||||
JSContext *cx = (JSContext *)scx->GetNativeContext();
|
||||
|
||||
// If our json call triggers a JS-to-C++ call, we want that call to use cx
|
||||
// as the context. So we push cx onto the context stack.
|
||||
nsCOMPtr<nsIJSContextStack> contextStack =
|
||||
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
contextStack->Push(cx);
|
||||
|
||||
nsCOMPtr<nsIJSON> json = do_GetService("@mozilla.org/dom/json;1");
|
||||
if(json) {
|
||||
// Do the encoding
|
||||
rv = json->EncodeFromJSVal(&jsData, cx, aResult);
|
||||
}
|
||||
else {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Always pop the stack!
|
||||
contextStack->Pop(&cx);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
|
||||
const nsAString& aURL, PRBool aReplace)
|
||||
{
|
||||
// Implements History.pushState and History.replaceState
|
||||
|
||||
// Here's what we do, roughly in the order specified by HTML5:
|
||||
// 1. Serialize aData to JSON.
|
||||
// 2. If the third argument is present,
|
||||
// a. Resolve the url, relative to the first script's base URL
|
||||
// b. If (a) fails, raise a SECURITY_ERR
|
||||
// c. Compare the resulting absolute URL to the document's address. If
|
||||
// any part of the URLs difer other than the <path>, <query>, and
|
||||
// <fragment> components, raise a SECURITY_ERR and abort.
|
||||
// 3. If !aReplace:
|
||||
// Remove from the session history all entries after the current entry,
|
||||
// as we would after a regular navigation.
|
||||
// 4. As apropriate, either add a state object entry to the session history
|
||||
// after the current entry with the following properties, or modify the
|
||||
// current session history entry to set
|
||||
// a. cloned data as the state object,
|
||||
// b. the given title as the title, and,
|
||||
// c. if the third argument was present, the absolute URL found in
|
||||
// step 2
|
||||
// 5. If aReplace is false (i.e. we're doing a pushState instead of a
|
||||
// replaceState), notify bfcache that we've navigated to a new page.
|
||||
// 6. If the third argument is present, set the document's current address
|
||||
// to the absolute URL found in step 2.
|
||||
//
|
||||
// It's important that this function not run arbitrary scripts after step 1
|
||||
// and before completing step 5. For example, if a script called
|
||||
// history.back() before we completed step 5, bfcache might destroy an
|
||||
// active content viewer. Since EvictContentViewers at the end of step 5
|
||||
// might run script, we can't just put a script blocker around the critical
|
||||
// section.
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIDocument> document = do_GetInterface(GetAsSupports(this));
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||
|
||||
mLoadType = LOAD_PUSHSTATE;
|
||||
|
||||
// Step 1: Clone aData by getting its JSON representation
|
||||
nsString dataStr;
|
||||
rv = StringifyJSValVariant(aData, dataStr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Check that the state object isn't too long.
|
||||
// Default max length: 640k chars.
|
||||
PRInt32 maxStateObjSize = 0xA0000;
|
||||
if (mPrefs) {
|
||||
mPrefs->GetIntPref("browser.history.maxStateObjectSize",
|
||||
&maxStateObjSize);
|
||||
}
|
||||
if (maxStateObjSize < 0)
|
||||
maxStateObjSize = 0;
|
||||
NS_ENSURE_TRUE(dataStr.Length() <= (PRUint32)maxStateObjSize,
|
||||
NS_ERROR_ILLEGAL_VALUE);
|
||||
|
||||
// Step 2: Resolve aURL
|
||||
PRBool equalURIs = PR_TRUE;
|
||||
nsCOMPtr<nsIURI> oldURI = mCurrentURI;
|
||||
nsCOMPtr<nsIURI> newURI;
|
||||
if (aURL.Length() == 0) {
|
||||
newURI = mCurrentURI;
|
||||
}
|
||||
else {
|
||||
// 2a: Resolve aURL relative to mURI
|
||||
|
||||
nsIURI* docBaseURI = document->GetBaseURI();
|
||||
if (!docBaseURI)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCAutoString spec;
|
||||
docBaseURI->GetSpec(spec);
|
||||
|
||||
nsCAutoString charset;
|
||||
rv = docBaseURI->GetOriginCharset(charset);
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(newURI), aURL,
|
||||
charset.get(), docBaseURI);
|
||||
|
||||
// 2b: If 2a fails, raise a SECURITY_ERR
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
// 2c: Same-origin check.
|
||||
if (!URIIsLocalFile(newURI)) {
|
||||
// In addition to checking that the security manager says that
|
||||
// the new URI has the same origin as our current URI, we also
|
||||
// check that the two URIs have the same userpass. (The
|
||||
// security manager says that |http://foo.com| and
|
||||
// |http://me@foo.com| have the same origin.) mCurrentURI
|
||||
// won't contain the password part of the userpass, so this
|
||||
// means that it's never valid to specify a password in a
|
||||
// pushState or replaceState URI.
|
||||
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(secMan, NS_ERROR_FAILURE);
|
||||
|
||||
// It's very important that we check that newURI is of the same
|
||||
// origin as mCurrentURI, not docBaseURI, because a page can
|
||||
// set docBaseURI arbitrarily to any domain.
|
||||
nsCAutoString currentUserPass, newUserPass;
|
||||
NS_ENSURE_SUCCESS(mCurrentURI->GetUserPass(currentUserPass),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_SUCCESS(newURI->GetUserPass(newUserPass),
|
||||
NS_ERROR_FAILURE);
|
||||
if (NS_FAILED(secMan->CheckSameOriginURI(mCurrentURI,
|
||||
newURI, PR_TRUE)) ||
|
||||
!currentUserPass.Equals(newUserPass)) {
|
||||
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// It's a file:// URI
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> docScriptObj =
|
||||
do_QueryInterface(document);
|
||||
|
||||
if (!docScriptObj) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = docScriptObj->GetPrincipal();
|
||||
|
||||
if (!principal ||
|
||||
NS_FAILED(principal->CheckMayLoad(newURI, PR_TRUE))) {
|
||||
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentURI->Equals(newURI, &equalURIs);
|
||||
|
||||
} // end of same-origin check
|
||||
|
||||
nsCOMPtr<nsISHistory> sessionHistory = mSessionHistory;
|
||||
if (!sessionHistory) {
|
||||
// Get the handle to SH from the root docshell
|
||||
GetRootSessionHistory(getter_AddRefs(sessionHistory));
|
||||
}
|
||||
NS_ENSURE_TRUE(sessionHistory, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsISHistoryInternal> shInternal =
|
||||
do_QueryInterface(sessionHistory, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Step 3: Create a new entry in the session history; this will erase
|
||||
// all SHEntries after the new entry and make this entry the current
|
||||
// one. This operation may modify mOSHE, which we need later, so we
|
||||
// keep a reference here.
|
||||
NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
|
||||
nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
|
||||
|
||||
nsCOMPtr<nsISHEntry> newSHEntry;
|
||||
if (!aReplace) {
|
||||
rv = AddToSessionHistory(newURI, nsnull, nsnull,
|
||||
getter_AddRefs(newSHEntry));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
||||
|
||||
// Set the new SHEntry's document identifier, if we can.
|
||||
PRUint64 ourDocIdent;
|
||||
NS_ENSURE_SUCCESS(oldOSHE->GetDocIdentifier(&ourDocIdent),
|
||||
NS_ERROR_FAILURE);
|
||||
NS_ENSURE_SUCCESS(newSHEntry->SetDocIdentifier(ourDocIdent),
|
||||
NS_ERROR_FAILURE);
|
||||
|
||||
// AddToSessionHistory may not modify mOSHE. In case it doesn't,
|
||||
// we'll just set mOSHE here.
|
||||
mOSHE = newSHEntry;
|
||||
|
||||
} else {
|
||||
newSHEntry = mOSHE;
|
||||
newSHEntry->SetURI(newURI);
|
||||
}
|
||||
|
||||
// Step 4: Modify new/original session history entry
|
||||
newSHEntry->SetStateData(dataStr);
|
||||
|
||||
// Step 5: If aReplace is false, indicating that we're doing a pushState
|
||||
// rather than a replaceState, notify bfcache that we've added a page to
|
||||
// the history so it can evict content viewers if appropriate.
|
||||
if (!aReplace) {
|
||||
nsCOMPtr<nsISHistory> rootSH;
|
||||
GetRootSessionHistory(getter_AddRefs(rootSH));
|
||||
NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsISHistoryInternal> internalSH =
|
||||
do_QueryInterface(rootSH);
|
||||
NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
|
||||
|
||||
PRInt32 curIndex = -1;
|
||||
rv = rootSH->GetIndex(&curIndex);
|
||||
if (NS_SUCCEEDED(rv) && curIndex > -1) {
|
||||
internalSH->EvictContentViewers(curIndex - 1, curIndex);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: If the document's URI changed, update document's URI and update
|
||||
// global history
|
||||
if (!equalURIs) {
|
||||
SetCurrentURI(newURI, nsnull, PR_TRUE);
|
||||
document->SetDocumentURI(newURI);
|
||||
|
||||
AddToGlobalHistory(newURI, PR_FALSE, oldURI);
|
||||
}
|
||||
|
||||
// Try to set the title of the current history element
|
||||
if (mOSHE)
|
||||
mOSHE->SetTitle(aTitle);
|
||||
|
||||
// We need this to ensure that the back button is enabled after a
|
||||
// pushState, if it wasn't already enabled.
|
||||
FireOnLocationChange(this, nsnull, mCurrentURI);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
PRBool
|
||||
nsDocShell::ShouldAddToSessionHistory(nsIURI * aURI)
|
||||
{
|
||||
@@ -9428,7 +9799,6 @@ nsDocShell::CloneAndReplace(nsISHEntry *aSrcEntry,
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
nsDocShell::SwapHistoryEntries(nsISHEntry *aOldEntry, nsISHEntry *aNewEntry)
|
||||
{
|
||||
@@ -9679,9 +10049,6 @@ nsresult
|
||||
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
|
||||
nsIChannel * aChannel)
|
||||
{
|
||||
if (mItemType != typeContent || !mGlobalHistory)
|
||||
return NS_OK;
|
||||
|
||||
// If this is a POST request, we do not want to include this in global
|
||||
// history, so return early.
|
||||
nsCOMPtr<nsIHttpChannel> hchan(do_QueryInterface(aChannel));
|
||||
@@ -9692,16 +10059,26 @@ nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> referrer;
|
||||
if (aChannel)
|
||||
NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
|
||||
|
||||
return AddToGlobalHistory(aURI, aRedirect, referrer);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
|
||||
nsIURI * aReferrer)
|
||||
{
|
||||
if (mItemType != typeContent || !mGlobalHistory)
|
||||
return NS_OK;
|
||||
|
||||
PRBool visited;
|
||||
nsresult rv = mGlobalHistory->IsVisited(aURI, &visited);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> referrer;
|
||||
if (aChannel)
|
||||
NS_GetReferrerFromChannel(aChannel, getter_AddRefs(referrer));
|
||||
|
||||
rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), referrer);
|
||||
rv = mGlobalHistory->AddURI(aURI, aRedirect, !IsFrame(), aReferrer);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
@@ -9714,6 +10091,7 @@ nsDocShell::AddToGlobalHistory(nsIURI * aURI, PRBool aRedirect,
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user