Align document.open() with overhauled spec.
This commit is contained in:
parent
cbc0c961db
commit
6b9ec91973
|
@ -11835,35 +11835,45 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
{
|
{
|
||||||
// Implements History.pushState and History.replaceState
|
// Implements History.pushState and History.replaceState
|
||||||
|
|
||||||
// Here's what we do, roughly in the order specified by HTML5:
|
// Here's what we do, roughly in the order specified by HTML5. The specific
|
||||||
// 1. Serialize aData using structured clone.
|
// steps we are executing are at
|
||||||
// 2. If the third argument is present,
|
// <https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate>
|
||||||
// a. Resolve the url, relative to the first script's base URL
|
// and
|
||||||
// b. If (a) fails, raise a SECURITY_ERR
|
// <https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps>.
|
||||||
// c. Compare the resulting absolute URL to the document's address. If
|
// This function basically implements #dom-history-pushstate and
|
||||||
// any part of the URLs difer other than the <path>, <query>, and
|
// UpdateURLAndHistory implements #url-and-history-update-steps.
|
||||||
// <fragment> components, raise a SECURITY_ERR and abort.
|
//
|
||||||
// 3. If !aReplace:
|
// A. Serialize aData using structured clone. This is #dom-history-pushstate
|
||||||
|
// step 5.
|
||||||
|
// B. If the third argument is present, #dom-history-pushstate step 7.
|
||||||
|
// 7.1. Resolve the url, relative to our document.
|
||||||
|
// 7.2. If (a) fails, raise a SECURITY_ERR
|
||||||
|
// 7.4. 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.
|
||||||
|
// C. If !aReplace, #url-and-history-update-steps steps 2.1-2.3:
|
||||||
// Remove from the session history all entries after the current entry,
|
// Remove from the session history all entries after the current entry,
|
||||||
// as we would after a regular navigation, and save the current
|
// as we would after a regular navigation, and save the current
|
||||||
// entry's scroll position (bug 590573).
|
// entry's scroll position (bug 590573).
|
||||||
// 4. As apropriate, either add a state object entry to the session history
|
// D. #url-and-history-update-steps step 2.4 or step 3. As apropriate,
|
||||||
// after the current entry with the following properties, or modify the
|
// either add a state object entry to the session history after the
|
||||||
// current session history entry to set
|
// current entry with the following properties, or modify the current
|
||||||
|
// session history entry to set
|
||||||
// a. cloned data as the state object,
|
// a. cloned data as the state object,
|
||||||
// b. if the third argument was present, the absolute URL found in
|
// b. if the third argument was present, the absolute URL found in
|
||||||
// step 2
|
// step 2
|
||||||
// Also clear the new history entry's POST data (see bug 580069).
|
// Also clear the new history entry's POST data (see bug 580069).
|
||||||
// 5. If aReplace is false (i.e. we're doing a pushState instead of a
|
// E. 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.
|
// replaceState), notify bfcache that we've navigated to a new page.
|
||||||
// 6. If the third argument is present, set the document's current address
|
// F. If the third argument is present, set the document's current address
|
||||||
// to the absolute URL found in step 2.
|
// to the absolute URL found in step B. This is
|
||||||
|
// #url-and-history-update-steps step 4.
|
||||||
//
|
//
|
||||||
// It's important that this function not run arbitrary scripts after step 1
|
// It's important that this function not run arbitrary scripts after step 1
|
||||||
// and before completing step 5. For example, if a script called
|
// and before completing step 5. For example, if a script called
|
||||||
// history.back() before we completed step 5, bfcache might destroy an
|
// history.back() before we completed step 5, bfcache might destroy an
|
||||||
// active content viewer. Since EvictOutOfRangeContentViewers at the end of
|
// active content viewer. Since EvictOutOfRangeContentViewers at the end of
|
||||||
// step 5 might run script, we can't just put a script blocker around the
|
// step E might run script, we can't just put a script blocker around the
|
||||||
// critical section.
|
// critical section.
|
||||||
//
|
//
|
||||||
// Note that we completely ignore the aTitle parameter.
|
// Note that we completely ignore the aTitle parameter.
|
||||||
|
@ -11883,7 +11893,9 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
nsCOMPtr<nsIDocument> document = GetDocument();
|
nsCOMPtr<nsIDocument> document = GetDocument();
|
||||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
// Step 1: Serialize aData using structured clone.
|
// Step A: Serialize aData using structured clone.
|
||||||
|
// https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
|
||||||
|
// step 5.
|
||||||
nsCOMPtr<nsIStructuredCloneContainer> scContainer;
|
nsCOMPtr<nsIStructuredCloneContainer> scContainer;
|
||||||
|
|
||||||
// scContainer->Init might cause arbitrary JS to run, and this code might
|
// scContainer->Init might cause arbitrary JS to run, and this code might
|
||||||
|
@ -11926,7 +11938,9 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
|
|
||||||
NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
|
NS_ENSURE_TRUE(scSize <= (uint32_t)maxStateObjSize, NS_ERROR_ILLEGAL_VALUE);
|
||||||
|
|
||||||
// Step 2: Resolve aURL
|
// Step B: Resolve aURL.
|
||||||
|
// https://html.spec.whatwg.org/multipage/history.html#dom-history-pushstate
|
||||||
|
// step 7.
|
||||||
bool equalURIs = true;
|
bool equalURIs = true;
|
||||||
nsCOMPtr<nsIURI> currentURI;
|
nsCOMPtr<nsIURI> currentURI;
|
||||||
if (sURIFixup && mCurrentURI) {
|
if (sURIFixup && mCurrentURI) {
|
||||||
|
@ -11935,12 +11949,11 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
} else {
|
} else {
|
||||||
currentURI = mCurrentURI;
|
currentURI = mCurrentURI;
|
||||||
}
|
}
|
||||||
nsCOMPtr<nsIURI> oldURI = currentURI;
|
|
||||||
nsCOMPtr<nsIURI> newURI;
|
nsCOMPtr<nsIURI> newURI;
|
||||||
if (aURL.Length() == 0) {
|
if (aURL.Length() == 0) {
|
||||||
newURI = currentURI;
|
newURI = currentURI;
|
||||||
} else {
|
} else {
|
||||||
// 2a: Resolve aURL relative to mURI
|
// 7.1: Resolve aURL relative to mURI
|
||||||
|
|
||||||
nsIURI* docBaseURI = document->GetDocBaseURI();
|
nsIURI* docBaseURI = document->GetDocBaseURI();
|
||||||
if (!docBaseURI) {
|
if (!docBaseURI) {
|
||||||
|
@ -11956,12 +11969,12 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
|
|
||||||
rv = NS_NewURI(getter_AddRefs(newURI), aURL, charset.get(), docBaseURI);
|
rv = NS_NewURI(getter_AddRefs(newURI), aURL, charset.get(), docBaseURI);
|
||||||
|
|
||||||
// 2b: If 2a fails, raise a SECURITY_ERR
|
// 7.2: If 7.1 fails, raise a SECURITY_ERR
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
return NS_ERROR_DOM_SECURITY_ERR;
|
return NS_ERROR_DOM_SECURITY_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2c: Same-origin check.
|
// 7.4 and 7.5: Same-origin check.
|
||||||
if (!nsContentUtils::URIIsLocalFile(newURI)) {
|
if (!nsContentUtils::URIIsLocalFile(newURI)) {
|
||||||
// In addition to checking that the security manager says that
|
// In addition to checking that the security manager says that
|
||||||
// the new URI has the same origin as our current URI, we also
|
// the new URI has the same origin as our current URI, we also
|
||||||
|
@ -12012,18 +12025,36 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
|
|
||||||
} // end of same-origin check
|
} // end of same-origin check
|
||||||
|
|
||||||
// Step 3: Create a new entry in the session history. This will erase
|
// Step 8: call "URL and history update steps"
|
||||||
// all SHEntries after the new entry and make this entry the current
|
rv = UpdateURLAndHistory(document, newURI, scContainer, aTitle, aReplace,
|
||||||
// one. This operation may modify mOSHE, which we need later, so we
|
currentURI, equalURIs);
|
||||||
// keep a reference here.
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
NS_ENSURE_TRUE(mOSHE, NS_ERROR_FAILURE);
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsDocShell::UpdateURLAndHistory(nsIDocument* aDocument, nsIURI* aNewURI,
|
||||||
|
nsIStructuredCloneContainer* aData,
|
||||||
|
const nsAString& aTitle, bool aReplace,
|
||||||
|
nsIURI* aCurrentURI, bool aEqualURIs)
|
||||||
|
{
|
||||||
|
// Implements
|
||||||
|
// https://html.spec.whatwg.org/multipage/history.html#url-and-history-update-steps
|
||||||
|
|
||||||
|
// Step 2, if aReplace is false: 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 || aReplace, NS_ERROR_FAILURE);
|
||||||
nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
|
nsCOMPtr<nsISHEntry> oldOSHE = mOSHE;
|
||||||
|
|
||||||
mLoadType = LOAD_PUSHSTATE;
|
mLoadType = LOAD_PUSHSTATE;
|
||||||
|
|
||||||
nsCOMPtr<nsISHEntry> newSHEntry;
|
nsCOMPtr<nsISHEntry> newSHEntry;
|
||||||
if (!aReplace) {
|
if (!aReplace) {
|
||||||
// Save the current scroll position (bug 590573).
|
// Step 2.
|
||||||
|
// Save the current scroll position (bug 590573). Step 2.3.
|
||||||
nscoord cx = 0, cy = 0;
|
nscoord cx = 0, cy = 0;
|
||||||
GetCurScrollPos(ScrollOrientation_X, &cx);
|
GetCurScrollPos(ScrollOrientation_X, &cx);
|
||||||
GetCurScrollPos(ScrollOrientation_Y, &cy);
|
GetCurScrollPos(ScrollOrientation_Y, &cy);
|
||||||
|
@ -12034,10 +12065,10 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
|
|
||||||
// Since we're not changing which page we have loaded, pass
|
// Since we're not changing which page we have loaded, pass
|
||||||
// true for aCloneChildren.
|
// true for aCloneChildren.
|
||||||
rv = AddToSessionHistory(newURI, nullptr,
|
nsresult rv = AddToSessionHistory(aNewURI, nullptr,
|
||||||
document->NodePrincipal(), // triggeringPrincipal
|
aDocument->NodePrincipal(), // triggeringPrincipal
|
||||||
nullptr, true,
|
nullptr, true,
|
||||||
getter_AddRefs(newSHEntry));
|
getter_AddRefs(newSHEntry));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
||||||
|
@ -12060,15 +12091,26 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
mOSHE = newSHEntry;
|
mOSHE = newSHEntry;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// Step 3.
|
||||||
newSHEntry = mOSHE;
|
newSHEntry = mOSHE;
|
||||||
newSHEntry->SetURI(newURI);
|
|
||||||
newSHEntry->SetOriginalURI(newURI);
|
// Since we're not changing which page we have loaded, pass
|
||||||
|
if (!newSHEntry) {
|
||||||
|
nsresult rv = AddToSessionHistory(
|
||||||
|
aNewURI, nullptr,
|
||||||
|
aDocument->NodePrincipal(), // triggeringPrincipal
|
||||||
|
nullptr, true, getter_AddRefs(newSHEntry));
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
mOSHE = newSHEntry;
|
||||||
|
}
|
||||||
|
newSHEntry->SetURI(aNewURI);
|
||||||
|
newSHEntry->SetOriginalURI(aNewURI);
|
||||||
newSHEntry->SetLoadReplace(false);
|
newSHEntry->SetLoadReplace(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 4: Modify new/original session history entry and clear its POST
|
// Step 2.4 and 3: Modify new/original session history entry and clear its
|
||||||
// data, if there is any.
|
// POST data, if there is any.
|
||||||
newSHEntry->SetStateData(scContainer);
|
newSHEntry->SetStateData(aData);
|
||||||
newSHEntry->SetPostData(nullptr);
|
newSHEntry->SetPostData(nullptr);
|
||||||
|
|
||||||
// If this push/replaceState changed the document's current URI and the new
|
// If this push/replaceState changed the document's current URI and the new
|
||||||
|
@ -12076,39 +12118,48 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
// SHEntry's URI was modified in this way by a push/replaceState call
|
// SHEntry's URI was modified in this way by a push/replaceState call
|
||||||
// set URIWasModified to true for the current SHEntry (bug 669671).
|
// set URIWasModified to true for the current SHEntry (bug 669671).
|
||||||
bool sameExceptHashes = true, oldURIWasModified = false;
|
bool sameExceptHashes = true, oldURIWasModified = false;
|
||||||
newURI->EqualsExceptRef(currentURI, &sameExceptHashes);
|
aNewURI->EqualsExceptRef(aCurrentURI, &sameExceptHashes);
|
||||||
oldOSHE->GetURIWasModified(&oldURIWasModified);
|
// mOSHE might be null on replace. Only check if we're not replacing.
|
||||||
|
if (oldOSHE) {
|
||||||
|
oldOSHE->GetURIWasModified(&oldURIWasModified);
|
||||||
|
}
|
||||||
newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
|
newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
|
||||||
|
|
||||||
// Step 5: If aReplace is false, indicating that we're doing a pushState
|
// Step E as described at the top of AddState: If aReplace is false,
|
||||||
// rather than a replaceState, notify bfcache that we've added a page to
|
// indicating that we're doing a pushState rather than a replaceState, notify
|
||||||
// the history so it can evict content viewers if appropriate. Otherwise
|
// bfcache that we've added a page to the history so it can evict content
|
||||||
// call ReplaceEntry so that we notify nsIHistoryListeners that an entry
|
// viewers if appropriate. Otherwise call ReplaceEntry so that we notify
|
||||||
// was replaced.
|
// nsIHistoryListeners that an entry was replaced. We may not have a root
|
||||||
|
// session history if this call is coming from a document.open() in a docshell
|
||||||
|
// subtree that disables session history.
|
||||||
nsCOMPtr<nsISHistory> rootSH;
|
nsCOMPtr<nsISHistory> rootSH;
|
||||||
GetRootSessionHistory(getter_AddRefs(rootSH));
|
GetRootSessionHistory(getter_AddRefs(rootSH));
|
||||||
NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
|
NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
|
||||||
|
|
||||||
nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
|
nsCOMPtr<nsISHistoryInternal> internalSH = do_QueryInterface(rootSH);
|
||||||
NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
|
NS_ENSURE_TRUE(internalSH, NS_ERROR_UNEXPECTED);
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
if (rootSH) {
|
||||||
|
if (!aReplace) {
|
||||||
|
int32_t curIndex = -1;
|
||||||
|
rv = rootSH->GetIndex(&curIndex);
|
||||||
|
if (NS_SUCCEEDED(rv) && curIndex > -1) {
|
||||||
|
internalSH->EvictOutOfRangeContentViewers(curIndex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
|
||||||
|
|
||||||
if (!aReplace) {
|
int32_t index = -1;
|
||||||
int32_t curIndex = -1;
|
rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
|
||||||
rv = rootSH->GetIndex(&curIndex);
|
if (NS_SUCCEEDED(rv) && index > -1) {
|
||||||
if (NS_SUCCEEDED(rv) && curIndex > -1) {
|
internalSH->ReplaceEntry(index, rootSHEntry);
|
||||||
internalSH->EvictOutOfRangeContentViewers(curIndex);
|
}
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nsCOMPtr<nsISHEntry> rootSHEntry = GetRootSHEntry(newSHEntry);
|
|
||||||
|
|
||||||
int32_t index = -1;
|
|
||||||
rv = rootSH->GetIndexOfEntry(rootSHEntry, &index);
|
|
||||||
if (NS_SUCCEEDED(rv) && index > -1) {
|
|
||||||
internalSH->ReplaceEntry(index, rootSHEntry);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 6: If the document's URI changed, update document's URI and update
|
// Step 4: If the document's URI changed, update document's URI and update
|
||||||
// global history.
|
// global history.
|
||||||
//
|
//
|
||||||
// We need to call FireOnLocationChange so that the browser's address bar
|
// We need to call FireOnLocationChange so that the browser's address bar
|
||||||
|
@ -12121,35 +12172,35 @@ nsDocShell::AddState(JS::Handle<JS::Value> aData, const nsAString& aTitle,
|
||||||
// notification is allowed only when we know docshell is not loading a new
|
// notification is allowed only when we know docshell is not loading a new
|
||||||
// document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
|
// document and it requires LOCATION_CHANGE_SAME_DOCUMENT flag. Otherwise,
|
||||||
// FireOnLocationChange(...) breaks security UI.
|
// FireOnLocationChange(...) breaks security UI.
|
||||||
if (!equalURIs) {
|
if (!aEqualURIs) {
|
||||||
document->SetDocumentURI(newURI);
|
aDocument->SetDocumentURI(aNewURI);
|
||||||
// We can't trust SetCurrentURI to do always fire locationchange events
|
// We can't trust SetCurrentURI to do always fire locationchange events
|
||||||
// when we expect it to, so we hack around that by doing it ourselves...
|
// when we expect it to, so we hack around that by doing it ourselves...
|
||||||
SetCurrentURI(newURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
|
SetCurrentURI(aNewURI, nullptr, false, LOCATION_CHANGE_SAME_DOCUMENT);
|
||||||
if (mLoadType != LOAD_ERROR_PAGE) {
|
if (mLoadType != LOAD_ERROR_PAGE) {
|
||||||
FireDummyOnLocationChange();
|
FireDummyOnLocationChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
AddURIVisit(newURI, oldURI, oldURI, 0);
|
AddURIVisit(aNewURI, aCurrentURI, aCurrentURI, 0);
|
||||||
|
|
||||||
// AddURIVisit doesn't set the title for the new URI in global history,
|
// AddURIVisit doesn't set the title for the new URI in global history,
|
||||||
// so do that here.
|
// so do that here.
|
||||||
if (mUseGlobalHistory && !UsePrivateBrowsing()) {
|
if (mUseGlobalHistory && !UsePrivateBrowsing()) {
|
||||||
nsCOMPtr<IHistory> history = services::GetHistoryService();
|
nsCOMPtr<IHistory> history = services::GetHistoryService();
|
||||||
if (history) {
|
if (history) {
|
||||||
history->SetURITitle(newURI, mTitle);
|
history->SetURITitle(aNewURI, mTitle);
|
||||||
} else if (mGlobalHistory) {
|
} else if (mGlobalHistory) {
|
||||||
mGlobalHistory->SetPageTitle(newURI, mTitle);
|
mGlobalHistory->SetPageTitle(aNewURI, mTitle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inform the favicon service that our old favicon applies to this new
|
// Inform the favicon service that our old favicon applies to this new
|
||||||
// URI.
|
// URI.
|
||||||
CopyFavicon(oldURI, newURI, document->NodePrincipal(), UsePrivateBrowsing());
|
CopyFavicon(aCurrentURI, aNewURI, aDocument->NodePrincipal(), UsePrivateBrowsing());
|
||||||
} else {
|
} else {
|
||||||
FireDummyOnLocationChange();
|
FireDummyOnLocationChange();
|
||||||
}
|
}
|
||||||
document->SetStateObject(scContainer);
|
aDocument->SetStateObject(aData);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ interface nsIChannel;
|
||||||
interface nsIContentViewer;
|
interface nsIContentViewer;
|
||||||
interface nsIDOMEventTarget;
|
interface nsIDOMEventTarget;
|
||||||
interface nsIDocShellLoadInfo;
|
interface nsIDocShellLoadInfo;
|
||||||
|
interface nsIDocument;
|
||||||
interface nsIEditor;
|
interface nsIEditor;
|
||||||
interface nsIEditingSession;
|
interface nsIEditingSession;
|
||||||
interface nsISimpleEnumerator;
|
interface nsISimpleEnumerator;
|
||||||
|
@ -35,6 +36,7 @@ interface nsISHEntry;
|
||||||
interface nsILayoutHistoryState;
|
interface nsILayoutHistoryState;
|
||||||
interface nsISecureBrowserUI;
|
interface nsISecureBrowserUI;
|
||||||
interface nsIScriptGlobalObject;
|
interface nsIScriptGlobalObject;
|
||||||
|
interface nsIStructuredCloneContainer;
|
||||||
interface nsIDOMStorage;
|
interface nsIDOMStorage;
|
||||||
interface nsIPrincipal;
|
interface nsIPrincipal;
|
||||||
interface nsIWebBrowserPrint;
|
interface nsIWebBrowserPrint;
|
||||||
|
@ -210,6 +212,30 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||||
void addState(in jsval aData, in DOMString aTitle,
|
void addState(in jsval aData, in DOMString aTitle,
|
||||||
in DOMString aURL, in boolean aReplace);
|
in DOMString aURL, in boolean aReplace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper for addState and document.open that does just the
|
||||||
|
* history-manipulation guts.
|
||||||
|
*
|
||||||
|
* Arguments the spec defines:
|
||||||
|
*
|
||||||
|
* @param aDocument the document we're manipulating. This will get the new URI.
|
||||||
|
* @param aNewURI the new URI.
|
||||||
|
* @param aData The serialized state data. May be null.
|
||||||
|
* @param aTitle The new title. May be empty.
|
||||||
|
* @param aReplace whether this should replace the exising SHEntry.
|
||||||
|
*
|
||||||
|
* Arguments we need internally because deriving them from the
|
||||||
|
* others is a bit complicated:
|
||||||
|
*
|
||||||
|
* @param aCurrentURI the current URI we're working with. Might be null.
|
||||||
|
* @param aEqualURIs whether the two URIs involved are equal.
|
||||||
|
*/
|
||||||
|
[nostdcall]
|
||||||
|
void updateURLAndHistory(in nsIDocument aDocument, in nsIURI aNewURI,
|
||||||
|
in nsIStructuredCloneContainer aData, in AString aTitle,
|
||||||
|
in boolean aReplace, in nsIURI aCurrentURI,
|
||||||
|
in boolean aEqualURIs);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a DocShellLoadInfo object that you can manipulate and then pass
|
* Creates a DocShellLoadInfo object that you can manipulate and then pass
|
||||||
* to loadURI.
|
* to loadURI.
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Bug 1379762</title>
|
|
||||||
</head>
|
|
||||||
<script type="text/just-data">
|
|
||||||
onunload = null; // enable bfcache
|
|
||||||
++opener.testCount;
|
|
||||||
onpageshow = function(e) {
|
|
||||||
opener.ok(!e.persisted, "Pageshow should not be coming from bfcache " + opener.testCount);
|
|
||||||
}
|
|
||||||
if (opener.testCount == 1) {
|
|
||||||
onload = function () {
|
|
||||||
setTimeout(function() {
|
|
||||||
document.write(testScript);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
} else if (opener.testCount == 2) {
|
|
||||||
// Do this async, just in case.
|
|
||||||
setTimeout(function() {
|
|
||||||
history.back();
|
|
||||||
}, 0);
|
|
||||||
} else if (opener.testCount == 3) {
|
|
||||||
// Do this async, just in case.
|
|
||||||
setTimeout(function() {
|
|
||||||
history.forward();
|
|
||||||
}, 0);
|
|
||||||
} else if (opener.testCount == 4) {
|
|
||||||
onload = function() {
|
|
||||||
opener.nextTest();
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script>
|
|
||||||
var data = document.querySelector("script[type='text/just-data']").textContent;
|
|
||||||
// Store the string that does all out work in a global variable, so we can
|
|
||||||
// get at it later.
|
|
||||||
var testScript = "<script>" + data + "</" + "script>";
|
|
||||||
document.write(testScript);
|
|
||||||
</script>
|
|
||||||
</html>
|
|
|
@ -1,27 +1,16 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<script>
|
<script>
|
||||||
function run() {
|
function start() {
|
||||||
|
var length = history.length;
|
||||||
document.open();
|
document.open();
|
||||||
document.write("<h5 id='dynamic'>document.written content</h5>");
|
document.write("<h5 id='dynamic'>document.written content</h5>");
|
||||||
document.close();
|
document.close();
|
||||||
window.history.go(-1);
|
opener.is(history.length, length,
|
||||||
|
"document.open/close should not change history");
|
||||||
|
opener.nextTest();
|
||||||
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
|
||||||
if (++opener.testCount == 1) {
|
|
||||||
setTimeout(run, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("pageshow",
|
|
||||||
function() {
|
|
||||||
++opener.file_document_write_1_loadCount;
|
|
||||||
if (opener.file_document_write_1_loadCount == 2) {
|
|
||||||
opener.setTimeout("isTestDynamic()", 0);
|
|
||||||
}
|
|
||||||
opener.ok(opener.file_document_write_1_loadCount <= 2);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="start();">
|
<body onload="start();">
|
||||||
|
|
|
@ -58,8 +58,8 @@ skip-if = (toolkit == 'android') || (!debug && (os == 'mac' || os == 'win')) # B
|
||||||
[test_reserved.html]
|
[test_reserved.html]
|
||||||
skip-if = (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s
|
skip-if = (toolkit == 'android') || (debug && e10s) #too slow on Android 4.3 aws only; bug 1030403; bug 1263213 for debug e10s
|
||||||
[test_sessionhistory.html]
|
[test_sessionhistory.html]
|
||||||
skip-if = toolkit == 'android' #RANDOM
|
skip-if = toolkit == 'android' #RANDOM on Android
|
||||||
support-files = file_bug1379762-1.html file_bug1379762-2.html
|
support-files = file_bug1379762-1.html
|
||||||
[test_sibling-matching-parent.html]
|
[test_sibling-matching-parent.html]
|
||||||
[test_sibling-off-domain.html]
|
[test_sibling-off-domain.html]
|
||||||
[test_triggeringprincipal_frame_nav.html]
|
[test_triggeringprincipal_frame_nav.html]
|
||||||
|
|
|
@ -33,7 +33,6 @@ var testFiles =
|
||||||
"file_scrollRestoration.html",
|
"file_scrollRestoration.html",
|
||||||
"file_bug1300461.html",
|
"file_bug1300461.html",
|
||||||
"file_bug1379762-1.html",
|
"file_bug1379762-1.html",
|
||||||
"file_bug1379762-2.html",
|
|
||||||
];
|
];
|
||||||
var testCount = 0; // Used by the test files.
|
var testCount = 0; // Used by the test files.
|
||||||
|
|
||||||
|
@ -51,15 +50,6 @@ function nextTest_() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Needed by file_document_write_1.html
|
|
||||||
window.file_document_write_1_loadCount = 0;
|
|
||||||
function isTestDynamic() {
|
|
||||||
var dyn = testWindow.document.getElementById("dynamic");
|
|
||||||
is(dyn, null, "Should have gone back to the static page!");
|
|
||||||
nextTest();
|
|
||||||
testWindow.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextTest() {
|
function nextTest() {
|
||||||
setTimeout(nextTest_, 0);
|
setTimeout(nextTest_, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a light-weight tree iterator for `for` loops when full iterator
|
||||||
|
* functionality isn't required.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_SimpleTreeIterator_h
|
||||||
|
#define mozilla_dom_SimpleTreeIterator_h
|
||||||
|
|
||||||
|
#include "nsINode.h"
|
||||||
|
#include "nsTArray.h"
|
||||||
|
#include "mozilla/dom/Element.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class SimpleTreeIterator {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Initialize an iterator with aRoot. After that it can be iterated with a
|
||||||
|
* range-based for loop. At the moment, that's the only supported form of use
|
||||||
|
* for this iterator.
|
||||||
|
*/
|
||||||
|
explicit SimpleTreeIterator(nsINode& aRoot)
|
||||||
|
: mCurrent(&aRoot)
|
||||||
|
{
|
||||||
|
mTree.AppendElement(&aRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic support for range-based for loops.
|
||||||
|
// This will modify the iterator as it goes.
|
||||||
|
SimpleTreeIterator& begin() { return *this; }
|
||||||
|
|
||||||
|
SimpleTreeIterator end() { return SimpleTreeIterator(); }
|
||||||
|
|
||||||
|
bool operator!=(const SimpleTreeIterator& aOther) {
|
||||||
|
return mCurrent != aOther.mCurrent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator++() { Next(); }
|
||||||
|
|
||||||
|
nsINode* operator*() { return mCurrent; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Constructor used only for end() to represent a drained iterator.
|
||||||
|
SimpleTreeIterator()
|
||||||
|
: mCurrent(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Next() {
|
||||||
|
MOZ_ASSERT(mCurrent, "Don't call Next() when we have no current node");
|
||||||
|
|
||||||
|
mCurrent = mCurrent->GetNextNode(mTree.LastElement());
|
||||||
|
}
|
||||||
|
|
||||||
|
// The current node.
|
||||||
|
nsINode* mCurrent;
|
||||||
|
|
||||||
|
// The DOM tree that we're inside of right now.
|
||||||
|
AutoTArray<nsINode*, 1> mTree;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_SimpleTreeIterator_h
|
|
@ -210,6 +210,7 @@ EXPORTS.mozilla.dom += [
|
||||||
'ScreenOrientation.h',
|
'ScreenOrientation.h',
|
||||||
'ScriptSettings.h',
|
'ScriptSettings.h',
|
||||||
'ShadowRoot.h',
|
'ShadowRoot.h',
|
||||||
|
'SimpleTreeIterator.h',
|
||||||
'StructuredCloneHolder.h',
|
'StructuredCloneHolder.h',
|
||||||
'StructuredCloneTags.h',
|
'StructuredCloneTags.h',
|
||||||
'StyleSheetList.h',
|
'StyleSheetList.h',
|
||||||
|
|
|
@ -1968,22 +1968,10 @@ nsDocument::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
nsDocument::DisconnectNodeTree() {
|
||||||
nsIPrincipal* aPrincipal)
|
|
||||||
{
|
|
||||||
NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
|
|
||||||
|
|
||||||
if (gDocumentLeakPRLog && MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
|
|
||||||
PR_LogPrint("DOCUMENT %p ResetToURI %s", this,
|
|
||||||
aURI->GetSpecOrDefault().get());
|
|
||||||
}
|
|
||||||
|
|
||||||
mSecurityInfo = nullptr;
|
|
||||||
|
|
||||||
mDocumentLoadGroup = nullptr;
|
|
||||||
|
|
||||||
// Delete references to sub-documents and kill the subdocument map,
|
// Delete references to sub-documents and kill the subdocument map,
|
||||||
// if any. It holds strong references
|
// if any. This is not strictly needed, but makes the node tree
|
||||||
|
// teardown a bit faster.
|
||||||
delete mSubDocuments;
|
delete mSubDocuments;
|
||||||
mSubDocuments = nullptr;
|
mSubDocuments = nullptr;
|
||||||
|
|
||||||
|
@ -2019,6 +2007,22 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||||
"After removing all children, there should be no root elem");
|
"After removing all children, there should be no root elem");
|
||||||
}
|
}
|
||||||
mInUnlinkOrDeletion = oldVal;
|
mInUnlinkOrDeletion = oldVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||||
|
nsIPrincipal* aPrincipal)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(aURI, "Null URI passed to ResetToURI");
|
||||||
|
|
||||||
|
if (gDocumentLeakPRLog && MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
|
||||||
|
PR_LogPrint("DOCUMENT %p ResetToURI %s", this,
|
||||||
|
aURI->GetSpecOrDefault().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
mSecurityInfo = nullptr;
|
||||||
|
|
||||||
|
mDocumentLoadGroup = nullptr;
|
||||||
|
|
||||||
// Reset our stylesheets
|
// Reset our stylesheets
|
||||||
ResetStylesheetsToURI(aURI);
|
ResetStylesheetsToURI(aURI);
|
||||||
|
@ -2029,6 +2033,8 @@ nsDocument::ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
|
||||||
mListenerManager = nullptr;
|
mListenerManager = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisconnectNodeTree();
|
||||||
|
|
||||||
// Release the stylesheets list.
|
// Release the stylesheets list.
|
||||||
mDOMStyleSheets = nullptr;
|
mDOMStyleSheets = nullptr;
|
||||||
|
|
||||||
|
@ -4506,18 +4512,6 @@ nsDocument::SetScriptGlobalObject(nsIScriptGlobalObject *aScriptGlobalObject)
|
||||||
mLayoutHistoryState = nullptr;
|
mLayoutHistoryState = nullptr;
|
||||||
SetScopeObject(aScriptGlobalObject);
|
SetScopeObject(aScriptGlobalObject);
|
||||||
mHasHadDefaultView = true;
|
mHasHadDefaultView = true;
|
||||||
#ifdef DEBUG
|
|
||||||
if (!mWillReparent) {
|
|
||||||
// We really shouldn't have a wrapper here but if we do we need to make sure
|
|
||||||
// it has the correct parent.
|
|
||||||
JSObject *obj = GetWrapperPreserveColor();
|
|
||||||
if (obj) {
|
|
||||||
JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
|
|
||||||
NS_ASSERTION(js::GetGlobalForObjectCrossCompartment(obj) == newScope,
|
|
||||||
"Wrong scope, this is really bad!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (mAllowDNSPrefetch) {
|
if (mAllowDNSPrefetch) {
|
||||||
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
||||||
|
@ -9077,7 +9071,8 @@ nsDocument::CloneDocHelper(nsDocument* clone) const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsDocument::SetReadyStateInternal(ReadyState rs)
|
nsDocument::SetReadyStateInternal(ReadyState rs,
|
||||||
|
bool updateTimingInformation)
|
||||||
{
|
{
|
||||||
mReadyState = rs;
|
mReadyState = rs;
|
||||||
if (rs == READYSTATE_UNINITIALIZED) {
|
if (rs == READYSTATE_UNINITIALIZED) {
|
||||||
|
@ -9086,7 +9081,12 @@ nsDocument::SetReadyStateInternal(ReadyState rs)
|
||||||
// transition undetectable by Web content.
|
// transition undetectable by Web content.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (mTiming) {
|
|
||||||
|
if (updateTimingInformation && READYSTATE_LOADING == rs) {
|
||||||
|
mLoadingTimeStamp = mozilla::TimeStamp::Now();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateTimingInformation && mTiming) {
|
||||||
switch (rs) {
|
switch (rs) {
|
||||||
case READYSTATE_LOADING:
|
case READYSTATE_LOADING:
|
||||||
mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
|
mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
|
||||||
|
@ -9102,10 +9102,6 @@ nsDocument::SetReadyStateInternal(ReadyState rs)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// At the time of loading start, we don't have timing object, record time.
|
|
||||||
if (READYSTATE_LOADING == rs) {
|
|
||||||
mLoadingTimeStamp = mozilla::TimeStamp::Now();
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<AsyncEventDispatcher> asyncDispatcher =
|
RefPtr<AsyncEventDispatcher> asyncDispatcher =
|
||||||
new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
|
new AsyncEventDispatcher(this, NS_LITERAL_STRING("readystatechange"),
|
||||||
|
|
|
@ -704,7 +704,11 @@ public:
|
||||||
virtual void BeginLoad() override;
|
virtual void BeginLoad() override;
|
||||||
virtual void EndLoad() override;
|
virtual void EndLoad() override;
|
||||||
|
|
||||||
virtual void SetReadyStateInternal(ReadyState rs) override;
|
// Set the readystate of the document. If updateTimingInformation is true,
|
||||||
|
// this will record relevant timestamps in the document's performance timing.
|
||||||
|
// Some consumers like document.open() don't want to do that.
|
||||||
|
virtual void SetReadyStateInternal(ReadyState rs,
|
||||||
|
bool updateTimingInformation = true) override;
|
||||||
|
|
||||||
virtual void ContentStateChanged(nsIContent* aContent,
|
virtual void ContentStateChanged(nsIContent* aContent,
|
||||||
mozilla::EventStates aStateMask)
|
mozilla::EventStates aStateMask)
|
||||||
|
@ -916,6 +920,14 @@ public:
|
||||||
UpdateFrameRequestCallbackSchedulingState();
|
UpdateFrameRequestCallbackSchedulingState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetLoadEventFiring(bool aFiring) override { mLoadEventFiring = aFiring; }
|
||||||
|
|
||||||
|
bool SkipLoadEventAfterClose() override {
|
||||||
|
bool skip = mSkipLoadEventAfterClose;
|
||||||
|
mSkipLoadEventAfterClose = false;
|
||||||
|
return skip;
|
||||||
|
}
|
||||||
|
|
||||||
virtual nsIDocument* GetTemplateContentsOwner() override;
|
virtual nsIDocument* GetTemplateContentsOwner() override;
|
||||||
|
|
||||||
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
|
NS_DECL_CYCLE_COLLECTION_SKIPPABLE_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsDocument,
|
||||||
|
@ -1255,6 +1267,11 @@ protected:
|
||||||
*/
|
*/
|
||||||
Element* GetTitleElement();
|
Element* GetTitleElement();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform tree disconnection needed by ResetToURI and document.open()
|
||||||
|
*/
|
||||||
|
void DisconnectNodeTree();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Get our title
|
// Get our title
|
||||||
virtual void GetTitle(nsString& aTitle) override;
|
virtual void GetTitle(nsString& aTitle) override;
|
||||||
|
@ -1458,6 +1475,20 @@ public:
|
||||||
// additional sheets and sheets from the nsStyleSheetService.
|
// additional sheets and sheets from the nsStyleSheetService.
|
||||||
bool mStyleSetFilled:1;
|
bool mStyleSetFilled:1;
|
||||||
|
|
||||||
|
// The HTML spec has a "iframe load in progress" flag, but that doesn't seem
|
||||||
|
// to have the right semantics. See <https://github.com/whatwg/html/issues/4292>.
|
||||||
|
// What we have instead is a flag that is set while the window's 'load' event is
|
||||||
|
// firing if this document is the window's document.
|
||||||
|
bool mLoadEventFiring : 1;
|
||||||
|
|
||||||
|
// The HTML spec has a "mute iframe load" flag, but that doesn't seem to have
|
||||||
|
// the right semantics. See <https://github.com/whatwg/html/issues/4292>.
|
||||||
|
// What we have instead is a flag that is set if completion of our document
|
||||||
|
// via document.close() should skip firing the load event. Note that this
|
||||||
|
// flag is only relevant for HTML documents, but lives here for reasons that
|
||||||
|
// are documented above on SkipLoadEventAfterClose().
|
||||||
|
bool mSkipLoadEventAfterClose : 1;
|
||||||
|
|
||||||
uint8_t mPendingFullscreenRequests;
|
uint8_t mPendingFullscreenRequests;
|
||||||
|
|
||||||
uint8_t mXMLDeclarationBits;
|
uint8_t mXMLDeclarationBits;
|
||||||
|
@ -1615,11 +1646,6 @@ private:
|
||||||
// Set to true when the document is possibly controlled by the ServiceWorker.
|
// Set to true when the document is possibly controlled by the ServiceWorker.
|
||||||
// Used to prevent multiple requests to ServiceWorkerManager.
|
// Used to prevent multiple requests to ServiceWorkerManager.
|
||||||
bool mMaybeServiceWorkerControlled;
|
bool mMaybeServiceWorkerControlled;
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
public:
|
|
||||||
bool mWillReparent;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class nsDocumentOnStack
|
class nsDocumentOnStack
|
||||||
|
|
|
@ -909,10 +909,6 @@ public:
|
||||||
*/
|
*/
|
||||||
nsresult GetSrcdocData(nsAString& aSrcdocData);
|
nsresult GetSrcdocData(nsAString& aSrcdocData);
|
||||||
|
|
||||||
bool DidDocumentOpen() {
|
|
||||||
return mDidDocumentOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
already_AddRefed<mozilla::dom::AnonymousContent>
|
already_AddRefed<mozilla::dom::AnonymousContent>
|
||||||
InsertAnonymousContent(mozilla::dom::Element& aElement,
|
InsertAnonymousContent(mozilla::dom::Element& aElement,
|
||||||
mozilla::ErrorResult& aError);
|
mozilla::ErrorResult& aError);
|
||||||
|
@ -1448,7 +1444,7 @@ public:
|
||||||
virtual void EndLoad() = 0;
|
virtual void EndLoad() = 0;
|
||||||
|
|
||||||
enum ReadyState { READYSTATE_UNINITIALIZED = 0, READYSTATE_LOADING = 1, READYSTATE_INTERACTIVE = 3, READYSTATE_COMPLETE = 4};
|
enum ReadyState { READYSTATE_UNINITIALIZED = 0, READYSTATE_LOADING = 1, READYSTATE_INTERACTIVE = 3, READYSTATE_COMPLETE = 4};
|
||||||
virtual void SetReadyStateInternal(ReadyState rs) = 0;
|
virtual void SetReadyStateInternal(ReadyState rs, bool updateTimingInformation = true) = 0;
|
||||||
ReadyState GetReadyStateEnum()
|
ReadyState GetReadyStateEnum()
|
||||||
{
|
{
|
||||||
return mReadyState;
|
return mReadyState;
|
||||||
|
@ -2186,6 +2182,19 @@ public:
|
||||||
mAllowXULXBL = eTriTrue;
|
mAllowXULXBL = eTriTrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag whether we're about to fire the window's load event for this document.
|
||||||
|
*/
|
||||||
|
virtual void SetLoadEventFiring(bool aFiring) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test whether we should be firing a load event for this document after a
|
||||||
|
* document.close().
|
||||||
|
* This method should only be called at the point when the load event is about
|
||||||
|
* to be fired, since it resets `skip`.
|
||||||
|
*/
|
||||||
|
virtual bool SkipLoadEventAfterClose() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the template content owner document that owns the content of
|
* Returns the template content owner document that owns the content of
|
||||||
* HTMLTemplateElement.
|
* HTMLTemplateElement.
|
||||||
|
@ -3146,11 +3155,6 @@ protected:
|
||||||
// Whether the document was created by a srcdoc iframe.
|
// Whether the document was created by a srcdoc iframe.
|
||||||
bool mIsSrcdocDocument : 1;
|
bool mIsSrcdocDocument : 1;
|
||||||
|
|
||||||
// Records whether we've done a document.open. If this is true, it's possible
|
|
||||||
// for nodes from this document to have outdated wrappers in their wrapper
|
|
||||||
// caches.
|
|
||||||
bool mDidDocumentOpen : 1;
|
|
||||||
|
|
||||||
// Whether this document has a display document and thus is considered to
|
// Whether this document has a display document and thus is considered to
|
||||||
// be a resource document. Normally this is the same as !!mDisplayDocument,
|
// be a resource document. Normally this is the same as !!mDisplayDocument,
|
||||||
// but mDisplayDocument is cleared during Unlink. mHasDisplayDocument is
|
// but mDisplayDocument is cleared during Unlink. mHasDisplayDocument is
|
||||||
|
|
|
@ -1549,27 +1549,6 @@ AdoptNodeIntoOwnerDoc(nsINode *aParent, nsINode *aNode)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static nsresult
|
|
||||||
CheckForOutdatedParent(nsINode* aParent, nsINode* aNode)
|
|
||||||
{
|
|
||||||
if (JSObject* existingObjUnrooted = aNode->GetWrapper()) {
|
|
||||||
JS::Rooted<JSObject*> existingObj(RootingCx(), existingObjUnrooted);
|
|
||||||
|
|
||||||
AutoJSContext cx;
|
|
||||||
nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject();
|
|
||||||
MOZ_ASSERT(global);
|
|
||||||
|
|
||||||
if (js::GetGlobalForObjectCrossCompartment(existingObj) !=
|
|
||||||
global->GetGlobalJSObject()) {
|
|
||||||
JSAutoCompartment ac(cx, existingObj);
|
|
||||||
nsresult rv = ReparentWrapper(cx, existingObj);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static nsresult
|
static nsresult
|
||||||
ReparentWrappersInSubtree(nsIContent* aRoot)
|
ReparentWrappersInSubtree(nsIContent* aRoot)
|
||||||
{
|
{
|
||||||
|
@ -1631,9 +1610,6 @@ nsINode::doInsertChildAt(nsIContent* aKid, uint32_t aIndex,
|
||||||
if (OwnerDoc() != aKid->OwnerDoc()) {
|
if (OwnerDoc() != aKid->OwnerDoc()) {
|
||||||
rv = AdoptNodeIntoOwnerDoc(this, aKid);
|
rv = AdoptNodeIntoOwnerDoc(this, aKid);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
} else if (OwnerDoc()->DidDocumentOpen()) {
|
|
||||||
rv = CheckForOutdatedParent(this, aKid);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t childCount = aChildArray.ChildCount();
|
uint32_t childCount = aChildArray.ChildCount();
|
||||||
|
@ -2481,11 +2457,6 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||||
if (aError.Failed()) {
|
if (aError.Failed()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
} else if (doc->DidDocumentOpen()) {
|
|
||||||
aError = CheckForOutdatedParent(this, aNewChild);
|
|
||||||
if (aError.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -113,19 +113,25 @@ var testFramesLoaded = function() {
|
||||||
|
|
||||||
// test that a document can be framed under a javascript: URL opened by the
|
// test that a document can be framed under a javascript: URL opened by the
|
||||||
// same site as the frame
|
// same site as the frame
|
||||||
|
// We can't set a load event listener before calling document.open/document.write, because those will remove such listeners. So we need to define a function that the new window will be able to call.
|
||||||
|
function frameInJSURILoaded(win) {
|
||||||
|
var test = win.document.getElementById("sameorigin3")
|
||||||
|
.contentDocument.getElementById("test");
|
||||||
|
ok(test != null, "frame under javascript: URL should have loaded.");
|
||||||
|
win.close();
|
||||||
|
|
||||||
|
// run last test
|
||||||
|
if (!isUnique) {
|
||||||
|
testFrameInDataURI();
|
||||||
|
} else {
|
||||||
|
testFrameNotLoadedInDataURI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var testFrameInJSURI = function() {
|
var testFrameInJSURI = function() {
|
||||||
var html = '<iframe id="sameorigin3" src="http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=sameorigin3&xfo=sameorigin"></iframe>';
|
var html = '<iframe id="sameorigin3" src="http://mochi.test:8888/tests/dom/base/test/file_x-frame-options_page.sjs?testid=sameorigin3&xfo=sameorigin"></iframe>';
|
||||||
var win = window.open();
|
var win = window.open();
|
||||||
win.onload = function() {
|
win.location.href = "javascript:document.open(); onload = opener.frameInJSURILoaded.bind(null, window); document.write('"+html+"');document.close();";
|
||||||
var test = win.document.getElementById("sameorigin3")
|
|
||||||
.contentDocument.getElementById("test");
|
|
||||||
ok(test != null, "frame under javascript: URL should have loaded.");
|
|
||||||
win.close();
|
|
||||||
|
|
||||||
// run last test
|
|
||||||
testFrameInDataURI();
|
|
||||||
}
|
|
||||||
win.location.href = "javascript:document.write('"+html+"');document.close();";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// test that a document can be framed under a data: URL opened by the
|
// test that a document can be framed under a data: URL opened by the
|
||||||
|
|
|
@ -514,8 +514,9 @@ private:
|
||||||
{
|
{
|
||||||
// NS_IF_RELEASE because we might have been unlinked before
|
// NS_IF_RELEASE because we might have been unlinked before
|
||||||
nsISupports* ptr = GetISupports();
|
nsISupports* ptr = GetISupports();
|
||||||
NS_IF_RELEASE(ptr);
|
// Clear mPtrBits before the release to prevent reentrance.
|
||||||
mPtrBits = 0;
|
mPtrBits = 0;
|
||||||
|
NS_IF_RELEASE(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uintptr_t mPtrBits;
|
uintptr_t mPtrBits;
|
||||||
|
|
|
@ -166,11 +166,11 @@ EventListenerManager::~EventListenerManager()
|
||||||
// XXX azakai: Is there any reason to not just call Disconnect
|
// XXX azakai: Is there any reason to not just call Disconnect
|
||||||
// from right here, if not previously called?
|
// from right here, if not previously called?
|
||||||
NS_ASSERTION(!mTarget, "didn't call Disconnect");
|
NS_ASSERTION(!mTarget, "didn't call Disconnect");
|
||||||
RemoveAllListeners();
|
RemoveAllListenersSilently();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
EventListenerManager::RemoveAllListeners()
|
EventListenerManager::RemoveAllListenersSilently()
|
||||||
{
|
{
|
||||||
if (mClearingListeners) {
|
if (mClearingListeners) {
|
||||||
return;
|
return;
|
||||||
|
@ -1329,7 +1329,7 @@ void
|
||||||
EventListenerManager::Disconnect()
|
EventListenerManager::Disconnect()
|
||||||
{
|
{
|
||||||
mTarget = nullptr;
|
mTarget = nullptr;
|
||||||
RemoveAllListeners();
|
RemoveAllListenersSilently();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1734,6 +1734,21 @@ EventListenerManager::IsApzAwareEvent(nsIAtom* aEvent)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EventListenerManager::RemoveAllListeners()
|
||||||
|
{
|
||||||
|
while (!mListeners.IsEmpty()) {
|
||||||
|
size_t idx = mListeners.Length() - 1;
|
||||||
|
nsCOMPtr<nsIAtom> type = mListeners.ElementAt(idx).mTypeAtom;
|
||||||
|
EventMessage message = mListeners.ElementAt(idx).mEventMessage;
|
||||||
|
mListeners.RemoveElementAt(idx);
|
||||||
|
NotifyEventListenerRemoved(type);
|
||||||
|
if (IsDeviceType(message)) {
|
||||||
|
DisableDevice(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
already_AddRefed<nsIScriptGlobalObject>
|
already_AddRefed<nsIScriptGlobalObject>
|
||||||
EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
|
EventListenerManager::GetScriptGlobalAndDocument(nsIDocument** aDoc)
|
||||||
{
|
{
|
||||||
|
|
|
@ -472,6 +472,12 @@ public:
|
||||||
bool IsApzAwareListener(Listener* aListener);
|
bool IsApzAwareListener(Listener* aListener);
|
||||||
bool IsApzAwareEvent(nsIAtom* aEvent);
|
bool IsApzAwareEvent(nsIAtom* aEvent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all event listeners from the event target this EventListenerManager
|
||||||
|
* is for.
|
||||||
|
*/
|
||||||
|
void RemoveAllListeners();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void HandleEventInternal(nsPresContext* aPresContext,
|
void HandleEventInternal(nsPresContext* aPresContext,
|
||||||
WidgetEvent* aEvent,
|
WidgetEvent* aEvent,
|
||||||
|
@ -604,7 +610,7 @@ protected:
|
||||||
const nsAString& aTypeString,
|
const nsAString& aTypeString,
|
||||||
const EventListenerFlags& aFlags,
|
const EventListenerFlags& aFlags,
|
||||||
bool aAllEvents = false);
|
bool aAllEvents = false);
|
||||||
void RemoveAllListeners();
|
void RemoveAllListenersSilently();
|
||||||
void NotifyEventListenerRemoved(nsIAtom* aUserType);
|
void NotifyEventListenerRemoved(nsIAtom* aUserType);
|
||||||
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
|
||||||
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "nsPrintfCString.h"
|
#include "nsPrintfCString.h"
|
||||||
#include "nsReadableUtils.h"
|
#include "nsReadableUtils.h"
|
||||||
#include "nsUnicharUtils.h"
|
#include "nsUnicharUtils.h"
|
||||||
|
#include "nsIDocumentLoader.h"
|
||||||
#include "nsIHTMLContentSink.h"
|
#include "nsIHTMLContentSink.h"
|
||||||
#include "nsIXMLContentSink.h"
|
#include "nsIXMLContentSink.h"
|
||||||
#include "nsHTMLParts.h"
|
#include "nsHTMLParts.h"
|
||||||
|
@ -84,6 +85,7 @@
|
||||||
|
|
||||||
#include "mozilla/dom/EncodingUtils.h"
|
#include "mozilla/dom/EncodingUtils.h"
|
||||||
#include "mozilla/dom/FallbackEncoding.h"
|
#include "mozilla/dom/FallbackEncoding.h"
|
||||||
|
#include "mozilla/EventListenerManager.h"
|
||||||
#include "mozilla/LoadInfo.h"
|
#include "mozilla/LoadInfo.h"
|
||||||
#include "nsIEditingSession.h"
|
#include "nsIEditingSession.h"
|
||||||
#include "nsIEditor.h"
|
#include "nsIEditor.h"
|
||||||
|
@ -107,12 +109,14 @@
|
||||||
#include "nsIImageDocument.h"
|
#include "nsIImageDocument.h"
|
||||||
#include "mozilla/dom/HTMLBodyElement.h"
|
#include "mozilla/dom/HTMLBodyElement.h"
|
||||||
#include "mozilla/dom/HTMLDocumentBinding.h"
|
#include "mozilla/dom/HTMLDocumentBinding.h"
|
||||||
|
#include "mozilla/dom/SimpleTreeIterator.h"
|
||||||
#include "nsCharsetSource.h"
|
#include "nsCharsetSource.h"
|
||||||
#include "nsIStringBundle.h"
|
#include "nsIStringBundle.h"
|
||||||
#include "nsDOMClassInfo.h"
|
#include "nsDOMClassInfo.h"
|
||||||
#include "nsFocusManager.h"
|
#include "nsFocusManager.h"
|
||||||
#include "nsIFrame.h"
|
#include "nsIFrame.h"
|
||||||
#include "nsIContent.h"
|
#include "nsIContent.h"
|
||||||
|
#include "nsIStructuredCloneContainer.h"
|
||||||
#include "nsLayoutStylesheetCache.h"
|
#include "nsLayoutStylesheetCache.h"
|
||||||
#include "mozilla/StyleSheet.h"
|
#include "mozilla/StyleSheet.h"
|
||||||
#include "mozilla/StyleSheetInlines.h"
|
#include "mozilla/StyleSheetInlines.h"
|
||||||
|
@ -842,6 +846,24 @@ nsHTMLDocument::EndLoad()
|
||||||
if (turnOnEditing) {
|
if (turnOnEditing) {
|
||||||
EditingStateChanged();
|
EditingStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!GetWindow()) {
|
||||||
|
// This is a document that's not in a window. For example, this could be an
|
||||||
|
// XMLHttpRequest responseXML document, or a document created via DOMParser
|
||||||
|
// or DOMImplementation. We don't reach this code normally for such
|
||||||
|
// documents (which is not obviously correct), but can reach it via
|
||||||
|
// document.open()/document.close().
|
||||||
|
//
|
||||||
|
// Such documents don't fire load events, but per spec should set their
|
||||||
|
// readyState to "complete" when parsing and all loading of subresources is
|
||||||
|
// done. Parsing is done now, and documents not in a window don't load
|
||||||
|
// subresources, so just go ahead and mark ourselves as complete.
|
||||||
|
SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE,
|
||||||
|
/* updateTimingInformation = */ false);
|
||||||
|
|
||||||
|
// Reset mSkipLoadEventAfterClose just in case.
|
||||||
|
mSkipLoadEventAfterClose = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1410,19 +1432,21 @@ already_AddRefed<nsIDocument>
|
||||||
nsHTMLDocument::Open(JSContext* cx,
|
nsHTMLDocument::Open(JSContext* cx,
|
||||||
const nsAString& aType,
|
const nsAString& aType,
|
||||||
const nsAString& aReplace,
|
const nsAString& aReplace,
|
||||||
ErrorResult& rv)
|
ErrorResult& aError)
|
||||||
{
|
{
|
||||||
// Implements the "When called with two arguments (or fewer)" steps here:
|
// Implements
|
||||||
// https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream
|
// <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
|
||||||
|
|
||||||
NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
|
NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast<nsIDOMHTMLDocument*>(this)),
|
||||||
"XOW should have caught this!");
|
"XOW should have caught this!");
|
||||||
|
|
||||||
|
// Step 1 - Throw if we're the wrong type of document.
|
||||||
if (!IsHTMLDocument() || mDisableDocWrite || !IsMasterDocument()) {
|
if (!IsHTMLDocument() || mDisableDocWrite || !IsMasterDocument()) {
|
||||||
// No calling document.open() on XHTML
|
aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set up the content type for insertion
|
||||||
nsAutoCString contentType;
|
nsAutoCString contentType;
|
||||||
contentType.AssignLiteral("text/html");
|
contentType.AssignLiteral("text/html");
|
||||||
|
|
||||||
|
@ -1435,51 +1459,7 @@ nsHTMLDocument::Open(JSContext* cx,
|
||||||
contentType.AssignLiteral("text/plain");
|
contentType.AssignLiteral("text/plain");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we already have a parser we ignore the document.open call.
|
// Step 3 - Get the entryDocument for security checks
|
||||||
if (mParser || mParserAborted) {
|
|
||||||
// The WHATWG spec says: "If the document has an active parser that isn't
|
|
||||||
// a script-created parser, and the insertion point associated with that
|
|
||||||
// parser's input stream is not undefined (that is, it does point to
|
|
||||||
// somewhere in the input stream), then the method does nothing. Abort
|
|
||||||
// these steps and return the Document object on which the method was
|
|
||||||
// invoked."
|
|
||||||
// Note that aborting a parser leaves the parser "active" with its
|
|
||||||
// insertion point "not undefined". We track this using mParserAborted,
|
|
||||||
// because aborting a parser nulls out mParser.
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// No calling document.open() without a script global object
|
|
||||||
if (!mScriptGlobalObject) {
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsPIDOMWindowOuter* outer = GetWindow();
|
|
||||||
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// check whether we're in the middle of unload. If so, ignore this call.
|
|
||||||
nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
|
|
||||||
if (!shell) {
|
|
||||||
// We won't be able to create a parser anyway.
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inUnload;
|
|
||||||
shell->GetIsInUnload(&inUnload);
|
|
||||||
if (inUnload) {
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: We want to use GetEntryDocument here because this document
|
|
||||||
// should inherit the security information of the document that's opening us,
|
|
||||||
// (since if it's secure, then it's presumably trusted).
|
|
||||||
nsCOMPtr<nsIDocument> callerDoc = GetEntryDocument();
|
nsCOMPtr<nsIDocument> callerDoc = GetEntryDocument();
|
||||||
if (!callerDoc) {
|
if (!callerDoc) {
|
||||||
// If we're called from C++ or in some other way without an originating
|
// If we're called from C++ or in some other way without an originating
|
||||||
|
@ -1489,67 +1469,39 @@ nsHTMLDocument::Open(JSContext* cx,
|
||||||
// change the principals of a document for security reasons we'll have to
|
// change the principals of a document for security reasons we'll have to
|
||||||
// refuse to go ahead with this call.
|
// refuse to go ahead with this call.
|
||||||
|
|
||||||
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Grab a reference to the calling documents security info (if any)
|
// Step 4 - Throw if we're not same-origin
|
||||||
// and URIs as they may be lost in the call to Reset().
|
if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
|
||||||
nsCOMPtr<nsISupports> securityInfo = callerDoc->GetSecurityInfo();
|
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
nsCOMPtr<nsIURI> uri = callerDoc->GetDocumentURI();
|
|
||||||
nsCOMPtr<nsIURI> baseURI = callerDoc->GetBaseURI();
|
|
||||||
nsCOMPtr<nsIPrincipal> callerPrincipal = callerDoc->NodePrincipal();
|
|
||||||
nsCOMPtr<nsIChannel> callerChannel = callerDoc->GetChannel();
|
|
||||||
|
|
||||||
// We're called from script. Make sure the script is from the same
|
|
||||||
// origin, not just that the caller can access the document. This is
|
|
||||||
// needed to keep document principals from ever changing, which is
|
|
||||||
// needed because of the way we use our XOW code, and is a sane
|
|
||||||
// thing to do anyways.
|
|
||||||
|
|
||||||
bool equals = false;
|
|
||||||
if (NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &equals)) ||
|
|
||||||
!equals) {
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
nsCOMPtr<nsIURI> callerDocURI = callerDoc->GetDocumentURI();
|
|
||||||
nsCOMPtr<nsIURI> thisURI = nsIDocument::GetDocumentURI();
|
|
||||||
printf("nsHTMLDocument::Open callerDoc %s this %s\n",
|
|
||||||
callerDocURI ? callerDocURI->GetSpecOrDefault().get() : "",
|
|
||||||
thisURI ? thisURI->GetSpecOrDefault().get() : "");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop current loads targeted at the window this document is in.
|
// Step 5 - If we have an active parser, abort with no-op
|
||||||
if (mScriptGlobalObject) {
|
if (mParser || mParserAborted) {
|
||||||
nsCOMPtr<nsIContentViewer> cv;
|
nsCOMPtr<nsIDocument> ret = this;
|
||||||
shell->GetContentViewer(getter_AddRefs(cv));
|
return ret.forget();
|
||||||
|
}
|
||||||
if (cv) {
|
|
||||||
bool okToUnload;
|
// Step 6 - Check if document.open() is called during unload
|
||||||
if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
|
nsCOMPtr<nsIDocShell> shell(mDocumentContainer);
|
||||||
// We don't want to unload, so stop here, but don't throw an
|
if (shell) {
|
||||||
// exception.
|
bool inUnload;
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
shell->GetIsInUnload(&inUnload);
|
||||||
return ret.forget();
|
if (inUnload) {
|
||||||
}
|
nsCOMPtr<nsIDocument> ret = this;
|
||||||
|
return ret.forget();
|
||||||
// Now double-check that our invariants still hold.
|
|
||||||
if (!mScriptGlobalObject) {
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
nsPIDOMWindowOuter* outer = GetWindow();
|
|
||||||
if (!outer || (GetInnerWindow() != outer->GetCurrentInnerWindow())) {
|
|
||||||
nsCOMPtr<nsIDocument> ret = this;
|
|
||||||
return ret.forget();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 7 - Stop existing navigation of our browsing context (and all
|
||||||
|
// other loads it's doing) if we're the active document of our browsing
|
||||||
|
// context. If there's no existing navigation, we don't want to stop
|
||||||
|
// anything.
|
||||||
|
if (shell && IsCurrentActiveDocument() &&
|
||||||
|
mScriptGlobalObject) {
|
||||||
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
|
nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(shell));
|
||||||
webnav->Stop(nsIWebNavigation::STOP_NETWORK);
|
webnav->Stop(nsIWebNavigation::STOP_NETWORK);
|
||||||
|
|
||||||
|
@ -1560,189 +1512,121 @@ nsHTMLDocument::Open(JSContext* cx,
|
||||||
EnsureOnloadBlocker();
|
EnsureOnloadBlocker();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The open occurred after the document finished loading.
|
// Step 8 - Clear all event listeners out of our DOM tree
|
||||||
// So we reset the document and then reinitialize it.
|
for (nsINode* node : SimpleTreeIterator(*this)) {
|
||||||
nsCOMPtr<nsIChannel> channel;
|
if (EventListenerManager* elm = node->GetExistingListenerManager()) {
|
||||||
nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
|
elm->RemoveAllListeners();
|
||||||
rv = NS_NewChannel(getter_AddRefs(channel),
|
|
||||||
uri,
|
|
||||||
callerDoc,
|
|
||||||
nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
|
|
||||||
nsIContentPolicy::TYPE_OTHER,
|
|
||||||
group);
|
|
||||||
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callerChannel) {
|
|
||||||
nsLoadFlags callerLoadFlags;
|
|
||||||
rv = callerChannel->GetLoadFlags(&callerLoadFlags);
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsLoadFlags loadFlags;
|
|
||||||
rv = channel->GetLoadFlags(&loadFlags);
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
loadFlags |= callerLoadFlags & nsIRequest::INHIBIT_PERSISTENT_CACHING;
|
|
||||||
|
|
||||||
rv = channel->SetLoadFlags(loadFlags);
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user has allowed mixed content on the rootDoc, then we should propogate it
|
|
||||||
// down to the new document channel.
|
|
||||||
bool rootHasSecureConnection = false;
|
|
||||||
bool allowMixedContent = false;
|
|
||||||
bool isDocShellRoot = false;
|
|
||||||
nsresult rvalue = shell->GetAllowMixedContentAndConnectionData(&rootHasSecureConnection, &allowMixedContent, &isDocShellRoot);
|
|
||||||
if (NS_SUCCEEDED(rvalue) && allowMixedContent && isDocShellRoot) {
|
|
||||||
shell->SetMixedContentChannel(channel);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Before we reset the doc notify the globalwindow of the change,
|
// Step 9 - Clear event listeners from our window, if we have one.
|
||||||
// but only if we still have a window (i.e. our window object the
|
//
|
||||||
// current inner window in our outer window).
|
// Note that we explicitly want the inner window, and only if we're its
|
||||||
|
// document. We want to do this (per spec) even when we're not the "active
|
||||||
// Hold onto ourselves on the offchance that we're down to one ref
|
// document", so we can't go through GetWindow(), because it might forward to
|
||||||
nsCOMPtr<nsIDocument> kungFuDeathGrip = this;
|
// the wrong inner.
|
||||||
|
if (nsPIDOMWindowInner* win = GetInnerWindow()) {
|
||||||
if (nsPIDOMWindowInner *window = GetInnerWindow()) {
|
if (win->GetExtantDoc() == this) {
|
||||||
// Remember the old scope in case the call to SetNewDocument changes it.
|
if (EventListenerManager* elm =
|
||||||
nsCOMPtr<nsIScriptGlobalObject> oldScope(do_QueryReferent(mScopeObject));
|
nsGlobalWindow::Cast(win)->GetExistingListenerManager()) {
|
||||||
|
elm->RemoveAllListeners();
|
||||||
#ifdef DEBUG
|
}
|
||||||
bool willReparent = mWillReparent;
|
|
||||||
mWillReparent = true;
|
|
||||||
|
|
||||||
nsDocument* templateContentsOwner =
|
|
||||||
static_cast<nsDocument*>(mTemplateContentsOwner.get());
|
|
||||||
|
|
||||||
if (templateContentsOwner) {
|
|
||||||
templateContentsOwner->mWillReparent = true;
|
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
|
||||||
// Per spec, we pass false here so that a new Window is created.
|
// Step 10 - Remove all of our DOM children without firing any mutation events.
|
||||||
rv = window->SetNewDocument(this, nullptr,
|
DisconnectNodeTree();
|
||||||
/* aForceReuseInnerWindow */ false);
|
|
||||||
if (rv.Failed()) {
|
// --- At this point our tree is clean and we can switch to the new URI ---
|
||||||
|
|
||||||
|
// Step 11 - If we're the current document in our docshell, do the
|
||||||
|
// equivalent of pushState() with the new URL we should have.
|
||||||
|
if (shell && IsCurrentActiveDocument()) {
|
||||||
|
nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
|
||||||
|
|
||||||
|
// UpdateURLAndHistory might do various member-setting, so make sure we're
|
||||||
|
// holding strong refs to all the refcounted args on the stack. We can
|
||||||
|
// assume that our caller is holding on to "this" already.
|
||||||
|
nsCOMPtr<nsIURI> currentURI = nsIDocument::GetDocumentURI();
|
||||||
|
bool equalURIs;
|
||||||
|
nsresult rv = currentURI->Equals(newURI, &equalURIs);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
aError.Throw(rv);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
|
||||||
|
rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, EmptyString(),
|
||||||
|
/* aReplace = */ true, currentURI,
|
||||||
|
equalURIs);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
aError.Throw(rv);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
// And use the security info of the caller document as well, since
|
||||||
if (templateContentsOwner) {
|
// it's the thing providing our data.
|
||||||
templateContentsOwner->mWillReparent = willReparent;
|
mSecurityInfo = callerDoc->GetSecurityInfo();
|
||||||
}
|
|
||||||
|
|
||||||
mWillReparent = willReparent;
|
// This is not mentioned in the spec, but that's probably a spec bug.
|
||||||
#endif
|
// See <https://github.com/whatwg/html/issues/4299>.
|
||||||
|
// Since our URL may be changing away from about:blank here, we really want
|
||||||
// Now make sure we're not flagged as the initial document anymore, now
|
// to unset this flag on any document.open(), since only about:blank can be
|
||||||
// that we've had stuff done to us. From now on, if anyone tries to
|
// an initial document.
|
||||||
// document.open() us, they get a new inner window.
|
|
||||||
SetIsInitialDocument(false);
|
SetIsInitialDocument(false);
|
||||||
|
|
||||||
nsCOMPtr<nsIScriptGlobalObject> newScope(do_QueryReferent(mScopeObject));
|
// And let our docloader know that it will need to track our load event.
|
||||||
JS::Rooted<JSObject*> wrapper(cx, GetWrapper());
|
nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
|
||||||
if (oldScope && newScope != oldScope && wrapper) {
|
}
|
||||||
JSAutoCompartment ac(cx, wrapper);
|
|
||||||
rv = mozilla::dom::ReparentWrapper(cx, wrapper);
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also reparent the template contents owner document
|
// Step 12
|
||||||
// because its global is set to the same as this document.
|
mSkipLoadEventAfterClose = mLoadEventFiring;
|
||||||
if (mTemplateContentsOwner) {
|
|
||||||
JS::Rooted<JSObject*> contentsOwnerWrapper(cx,
|
|
||||||
mTemplateContentsOwner->GetWrapper());
|
|
||||||
if (contentsOwnerWrapper) {
|
|
||||||
rv = mozilla::dom::ReparentWrapper(cx, contentsOwnerWrapper);
|
|
||||||
if (rv.Failed()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mDidDocumentOpen = true;
|
// Preliminary to steps 13-16. Set our ready state to uninitialized before
|
||||||
|
// we do anything else, so we can then proceed to later ready state levels.
|
||||||
|
SetReadyStateInternal(READYSTATE_UNINITIALIZED,
|
||||||
|
/* updateTimingInformation = */ false);
|
||||||
|
|
||||||
// Call Reset(), this will now do the full reset
|
// Step 13 - Set our compatibility mode to standards.
|
||||||
Reset(channel, group);
|
SetCompatibilityMode(eCompatibility_FullStandards);
|
||||||
if (baseURI) {
|
|
||||||
mDocumentBaseURI = baseURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the security info of the caller now that we're done
|
|
||||||
// resetting the document.
|
|
||||||
mSecurityInfo = securityInfo;
|
|
||||||
|
|
||||||
|
// Step 14 - Create a new parser associated with document.
|
||||||
|
// This also does step 16 implicitly.
|
||||||
mParserAborted = false;
|
mParserAborted = false;
|
||||||
mParser = nsHtml5Module::NewHtml5Parser();
|
mParser = nsHtml5Module::NewHtml5Parser();
|
||||||
nsHtml5Module::Initialize(mParser, this, uri, shell, channel);
|
nsHtml5Module::Initialize(mParser, this, nsIDocument::GetDocumentURI(), shell, nullptr);
|
||||||
if (mReferrerPolicySet) {
|
if (mReferrerPolicySet) {
|
||||||
// CSP may have set the referrer policy, so a speculative parser should
|
// CSP may have set the referrer policy, so a speculative parser should
|
||||||
// start with the new referrer policy.
|
// start with the new referrer policy.
|
||||||
nsHtml5TreeOpExecutor* executor = nullptr;
|
nsHtml5TreeOpExecutor* executor = nullptr;
|
||||||
executor = static_cast<nsHtml5TreeOpExecutor*> (mParser->GetContentSink());
|
executor = static_cast<nsHtml5TreeOpExecutor*>(mParser->GetContentSink());
|
||||||
if (executor && mReferrerPolicySet) {
|
if (executor && mReferrerPolicySet) {
|
||||||
executor->SetSpeculationReferrerPolicy(static_cast<ReferrerPolicy>(mReferrerPolicy));
|
executor->SetSpeculationReferrerPolicy(
|
||||||
|
static_cast<ReferrerPolicy>(mReferrerPolicy));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will be propagated to the parser when someone actually calls write()
|
if (shell) {
|
||||||
SetContentTypeInternal(contentType);
|
// Prepare the docshell and the document viewer for the impending
|
||||||
|
// out-of-band document.write()
|
||||||
|
shell->PrepareForNewContentModel();
|
||||||
|
|
||||||
// Prepare the docshell and the document viewer for the impending
|
nsCOMPtr<nsIContentViewer> cv;
|
||||||
// out of band document.write()
|
shell->GetContentViewer(getter_AddRefs(cv));
|
||||||
shell->PrepareForNewContentModel();
|
if (cv) {
|
||||||
|
cv->LoadStart(this);
|
||||||
// Now check whether we were opened with a "replace" argument. If
|
}
|
||||||
// so, we need to tell the docshell to not create a new history
|
|
||||||
// entry for this load. Otherwise, make sure that we're doing a normal load,
|
|
||||||
// not whatever type of load was previously done on this docshell.
|
|
||||||
shell->SetLoadType(aReplace.LowerCaseEqualsLiteral("replace") ?
|
|
||||||
LOAD_NORMAL_REPLACE : LOAD_NORMAL);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContentViewer> cv;
|
|
||||||
shell->GetContentViewer(getter_AddRefs(cv));
|
|
||||||
if (cv) {
|
|
||||||
cv->LoadStart(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a wyciwyg channel request into the document load group
|
// Step 15.
|
||||||
NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Open(): wyciwyg "
|
SetReadyStateInternal(nsIDocument::READYSTATE_LOADING,
|
||||||
"channel already exists!");
|
/* updateTimingInformation = */ false);
|
||||||
|
|
||||||
// In case the editor is listening and will see the new channel
|
// Step 16 happened with step 14 above.
|
||||||
// being added, make sure mWriteLevel is non-zero so that the editor
|
|
||||||
// knows that document.open/write/close() is being called on this
|
|
||||||
// document.
|
|
||||||
++mWriteLevel;
|
|
||||||
|
|
||||||
CreateAndAddWyciwygChannel();
|
// Step 17.
|
||||||
|
nsCOMPtr<nsIDocument> ret = this;
|
||||||
--mWriteLevel;
|
return ret.forget();
|
||||||
|
}
|
||||||
SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
|
|
||||||
|
|
||||||
// After changing everything around, make sure that the principal on the
|
|
||||||
// document's compartment exactly matches NodePrincipal().
|
|
||||||
DebugOnly<JSObject*> wrapper = GetWrapperPreserveColor();
|
|
||||||
MOZ_ASSERT_IF(wrapper,
|
|
||||||
JS_GetCompartmentPrincipals(js::GetObjectCompartment(wrapper)) ==
|
|
||||||
nsJSPrincipals::get(NodePrincipal()));
|
|
||||||
|
|
||||||
return kungFuDeathGrip.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsHTMLDocument::Clear()
|
nsHTMLDocument::Clear()
|
||||||
|
@ -1806,15 +1690,6 @@ nsHTMLDocument::Close(ErrorResult& rv)
|
||||||
if (GetShell()) {
|
if (GetShell()) {
|
||||||
FlushPendingNotifications(Flush_Layout);
|
FlushPendingNotifications(Flush_Layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Removing the wyciwygChannel here is wrong when document.close() is
|
|
||||||
// called from within the document itself. However, legacy requires the
|
|
||||||
// channel to be removed here. Otherwise, the load event never fires.
|
|
||||||
NS_ASSERTION(mWyciwygChannel, "nsHTMLDocument::Close(): Trying to remove "
|
|
||||||
"nonexistent wyciwyg channel!");
|
|
||||||
RemoveWyciwygChannel();
|
|
||||||
NS_ASSERTION(!mWyciwygChannel, "nsHTMLDocument::Close(): "
|
|
||||||
"nsIWyciwygChannel could not be removed!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -529,7 +529,6 @@ skip-if = toolkit == 'android' # plugins not supported
|
||||||
[test_bug196523.html]
|
[test_bug196523.html]
|
||||||
[test_bug199692.html]
|
[test_bug199692.html]
|
||||||
skip-if = toolkit == 'android' #bug 811644
|
skip-if = toolkit == 'android' #bug 811644
|
||||||
[test_bug172261.html]
|
|
||||||
[test_bug255820.html]
|
[test_bug255820.html]
|
||||||
[test_bug259332.html]
|
[test_bug259332.html]
|
||||||
[test_bug311681.html]
|
[test_bug311681.html]
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
https://bugzilla.mozilla.org/show_bug.cgi?id=172261
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<title>Test for Bug 172261</title>
|
|
||||||
<script type="text/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=172261">Mozilla Bug 172261</a>
|
|
||||||
<p id="display">
|
|
||||||
<iframe id="test"></iframe>
|
|
||||||
</p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script class="testbody" type="text/javascript">
|
|
||||||
/** Test for Bug 172261 **/
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
SimpleTest.requestFlakyTimeout("untriaged");
|
|
||||||
|
|
||||||
var callable = false;
|
|
||||||
function toggleCallable() { callable = true; }
|
|
||||||
|
|
||||||
var doTestInIframe = false;
|
|
||||||
|
|
||||||
// Shouldn't do history stuff from inside onload
|
|
||||||
addLoadEvent(function() { setTimeout(startTest, 10) });
|
|
||||||
|
|
||||||
function startTest() {
|
|
||||||
// First, create a dummy document. Use onunload handlers to make sure
|
|
||||||
// bfcache doesn't screw us up.
|
|
||||||
var doc = $("test").contentDocument;
|
|
||||||
|
|
||||||
doc.write("<html><body onunload=''>First</body></html>");
|
|
||||||
doc.close();
|
|
||||||
|
|
||||||
// Now write our test document
|
|
||||||
doc.write("<html><script>window.onerror = parent.onerror; if (parent.doTestInIframe) { parent.is(document.domain, parent.document.domain, 'Domains should match'); parent.toggleCallable(); } <" + "/script><body>Second</body></html>");
|
|
||||||
doc.close();
|
|
||||||
|
|
||||||
$("test").onload = goForward;
|
|
||||||
history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
function goForward() {
|
|
||||||
$("test").onload = doTest;
|
|
||||||
doTestInIframe = true;
|
|
||||||
history.forward();
|
|
||||||
}
|
|
||||||
|
|
||||||
function doTest() {
|
|
||||||
is($("test").contentDocument.domain, document.domain,
|
|
||||||
"Domains should match 2");
|
|
||||||
is($("test").contentDocument.location.href, location.href,
|
|
||||||
"Locations should match");
|
|
||||||
is(callable, true, "Subframe should be able to call us");
|
|
||||||
SimpleTest.finish();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ SimpleTest.waitForExplicitFinish();
|
||||||
is(document.characterSet, "UTF-8",
|
is(document.characterSet, "UTF-8",
|
||||||
"Unexpected character set for our document");
|
"Unexpected character set for our document");
|
||||||
|
|
||||||
var testsLeft = 4;
|
var testsLeft = 3;
|
||||||
|
|
||||||
function testFinished() {
|
function testFinished() {
|
||||||
--testsLeft;
|
--testsLeft;
|
||||||
|
@ -42,29 +42,11 @@ function charsetTestFinished(id, doc, charsetTarget) {
|
||||||
testFinished();
|
testFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
function f2Continue() {
|
|
||||||
// Commented out pending discussion at the WHATWG
|
|
||||||
// $("f2").
|
|
||||||
// setAttribute("onload",
|
|
||||||
// "charsetTestFinished('f2 reloaded', this.contentDocument, 'us-ascii');");
|
|
||||||
$("f2").
|
|
||||||
setAttribute("onload",
|
|
||||||
"testFinished();");
|
|
||||||
$("f2").contentWindow.location.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
function f3Continue() {
|
function f3Continue() {
|
||||||
var doc = $("f3").contentDocument;
|
var doc = $("f3").contentDocument;
|
||||||
is(doc.defaultView.getComputedStyle(doc.body, "").color, "rgb(0, 180, 0)",
|
is(doc.defaultView.getComputedStyle(doc.body, "").color, "rgb(0, 180, 0)",
|
||||||
"Wrong color before reload");
|
"Wrong color");
|
||||||
$("f3").
|
charsetTestFinished('f3', doc, "UTF-8");
|
||||||
setAttribute("onload",
|
|
||||||
'var doc = this.contentDocument; ' +
|
|
||||||
'is(doc.defaultView.getComputedStyle(doc.body, "").color, ' +
|
|
||||||
' "rgb(0, 180, 0)",' +
|
|
||||||
' "Wrong color after reload");' +
|
|
||||||
"charsetTestFinished('f1', this.contentDocument, 'UTF-8')");
|
|
||||||
$("f3").contentWindow.location.reload();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runTest() {
|
function runTest() {
|
||||||
|
@ -74,12 +56,7 @@ function runTest() {
|
||||||
doc.open();
|
doc.open();
|
||||||
doc.write('<html></html>');
|
doc.write('<html></html>');
|
||||||
doc.close();
|
doc.close();
|
||||||
is(doc.characterSet, "UTF-8",
|
charsetTestFinished("f1", doc, "UTF-8");
|
||||||
"Unexpected character set for first frame after write");
|
|
||||||
$("f1").
|
|
||||||
setAttribute("onload",
|
|
||||||
"charsetTestFinished('f1', this.contentDocument, 'UTF-8')");
|
|
||||||
$("f1").contentWindow.location.reload();
|
|
||||||
|
|
||||||
doc = $("f2").contentDocument;
|
doc = $("f2").contentDocument;
|
||||||
is(doc.characterSet, "UTF-8",
|
is(doc.characterSet, "UTF-8",
|
||||||
|
@ -96,12 +73,11 @@ function runTest() {
|
||||||
"Unexpected character set for second frame after write");
|
"Unexpected character set for second frame after write");
|
||||||
$("f2").
|
$("f2").
|
||||||
setAttribute("onload",
|
setAttribute("onload",
|
||||||
"charsetTestFinished('f2', this.contentDocument, 'UTF-8');" +
|
"charsetTestFinished('f2', this.contentDocument, 'UTF-8');");
|
||||||
"f2Continue()");
|
|
||||||
|
|
||||||
doc = $("f3").contentDocument;
|
doc = $("f3").contentDocument;
|
||||||
is(doc.characterSet, "UTF-8",
|
is(doc.characterSet, "UTF-8",
|
||||||
"Unexpected initial character set for first frame");
|
"Unexpected initial character set for third frame");
|
||||||
doc.open();
|
doc.open();
|
||||||
var str = '<html><head>';
|
var str = '<html><head>';
|
||||||
str += '<style>body { color: rgb(255, 0, 0) }</style>';
|
str += '<style>body { color: rgb(255, 0, 0) }</style>';
|
||||||
|
@ -111,7 +87,7 @@ function runTest() {
|
||||||
doc.write(str);
|
doc.write(str);
|
||||||
doc.close();
|
doc.close();
|
||||||
is(doc.characterSet, "UTF-8",
|
is(doc.characterSet, "UTF-8",
|
||||||
"Unexpected character set for first frame after write");
|
"Unexpected character set for third frame after write");
|
||||||
$("f3").setAttribute("onload", "f3Continue()");
|
$("f3").setAttribute("onload", "f3Continue()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ function messageReceiver(evt) {
|
||||||
is(testResult, "undefined", "Props on new window's child should go away when loading");
|
is(testResult, "undefined", "Props on new window's child should go away when loading");
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
is(testResult, "undefined", "Props on new window's child should go away when writing");
|
is(testResult, "6", "Props on new window's child should go away when writing");
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
is(testResult, "7", "Props on different-domain window opened from different-domain new window can stay");
|
is(testResult, "7", "Props on different-domain window opened from different-domain new window can stay");
|
||||||
|
|
|
@ -43,7 +43,7 @@ load 732870.html
|
||||||
load 751995.html
|
load 751995.html
|
||||||
load 761831.html
|
load 761831.html
|
||||||
asserts(0-1) load 752038.html # We may hit bug 645229 here.
|
asserts(0-1) load 752038.html # We may hit bug 645229 here.
|
||||||
asserts(1) load 753162.html # We hit bug 675518 or bug 680086 here.
|
load 753162.html
|
||||||
load 754311.html
|
load 754311.html
|
||||||
asserts(0-1) load 786142.html # We may hit bug 645229 here.
|
asserts(0-1) load 786142.html # We may hit bug 645229 here.
|
||||||
load 797583.html
|
load 797583.html
|
||||||
|
|
|
@ -998,6 +998,9 @@ nsDocumentViewer::LoadComplete(nsresult aStatus)
|
||||||
// will depend on whether it's cached!
|
// will depend on whether it's cached!
|
||||||
if(window &&
|
if(window &&
|
||||||
(NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
|
(NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
|
||||||
|
// If this code changes, the code in nsDocLoader::DocLoaderIsEmpty
|
||||||
|
// that fires load events for document.open() cases might need to
|
||||||
|
// be updated too.
|
||||||
nsEventStatus status = nsEventStatus_eIgnore;
|
nsEventStatus status = nsEventStatus_eIgnore;
|
||||||
WidgetEvent event(true, eLoad);
|
WidgetEvent event(true, eLoad);
|
||||||
event.mFlags.mBubbles = false;
|
event.mFlags.mBubbles = false;
|
||||||
|
@ -1063,7 +1066,9 @@ nsDocumentViewer::LoadComplete(nsresult aStatus)
|
||||||
MakeUnique<DocLoadingTimelineMarker>("document::Load"));
|
MakeUnique<DocLoadingTimelineMarker>("document::Load"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
d->SetLoadEventFiring(true);
|
||||||
EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
|
EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
|
||||||
|
d->SetLoadEventFiring(false);
|
||||||
if (timing) {
|
if (timing) {
|
||||||
timing->NotifyLoadEventEnd();
|
timing->NotifyLoadEventEnd();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ function boom() {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
var frameDoc = document.querySelector("iframe").contentDocument;
|
var frameDoc = document.querySelector("iframe").contentDocument;
|
||||||
frameDoc.write("3");
|
frameDoc.write("3");
|
||||||
frameDoc.defaultView.history.back();
|
|
||||||
requestAnimationFrame(function() {
|
requestAnimationFrame(function() {
|
||||||
popup.close();
|
popup.close();
|
||||||
ok(true, "Didn't crash");
|
ok(true, "Didn't crash");
|
||||||
|
|
|
@ -33,24 +33,30 @@ function textChildren(node) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var f, d;
|
||||||
|
|
||||||
function tick() {
|
function tick() {
|
||||||
runNumber++;
|
runNumber++;
|
||||||
var f = document.getElementsByTagName("iframe")[0];
|
f = document.getElementsByTagName("iframe")[0];
|
||||||
var d = f.contentDocument;
|
d = f.contentDocument;
|
||||||
|
|
||||||
if (runNumber == 1) {
|
if (runNumber == 1) {
|
||||||
d.open();
|
frames[1].setTimeout(`
|
||||||
f.addEventListener("load", tick);
|
var d = parent.d;
|
||||||
d.write("X");
|
var f = parent.f;
|
||||||
d.write("\u003cscript>document.write('Y');\u003c/script>");
|
d.open();
|
||||||
d.write("Z");
|
f.addEventListener("load", parent.tick);
|
||||||
d.close();
|
d.write("X");
|
||||||
|
d.write("\u003cscript>document.write('Y');\u003c/script>");
|
||||||
|
d.write("Z");
|
||||||
|
d.close();
|
||||||
|
`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (runNumber == 2) {
|
if (runNumber == 2) {
|
||||||
var text = textChildren(d.body);
|
var text = textChildren(d.body);
|
||||||
is(text, "XYZ", "Wrong text before reload.");
|
is(text, "ABC", "Wrong text before reload.");
|
||||||
f.contentWindow.location.reload();
|
f.contentWindow.location.reload();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -63,10 +69,20 @@ function tick() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We want to trigger a document.open/write with a different window as the
|
||||||
|
// entry global. Let's give that window a blob URL so we don't have to set up
|
||||||
|
// extra helper files.
|
||||||
|
var blob = new Blob(["ABC"], { type: "text/html" });
|
||||||
|
var blobURL = URL.createObjectURL(blob);
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</pre>
|
</pre>
|
||||||
<div id="content" style="display: none">
|
<div id="content" style="display: none">
|
||||||
<iframe></iframe>
|
<iframe></iframe>
|
||||||
|
<iframe></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll("iframe")[1].src = blobURL;
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
|
|
||||||
function afterNavigationTest()
|
function afterNavigationTest()
|
||||||
{
|
{
|
||||||
isSecurityState("broken", "security still broken after navigation");
|
isSecurityState("secure", "when we navigate back, we're loading our secure page again and not loading an insecure script, so our security state is secure");
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,8 @@
|
||||||
opener.pages.push(2);
|
opener.pages.push(2);
|
||||||
onload = function() {
|
onload = function() {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
document.write("<!doctype html>3<script>opener.pages.push(3); if(!opener.started) {opener.started = true; history.go(-1);} opener.start_test_wait();<\/script>");
|
document.write("<!doctype html>3<script>opener.pages.push(3); if(!opener.started) {opener.started = true; history.go(-1);}<\/script>");
|
||||||
document.close();
|
document.close();
|
||||||
if (opener.started) {
|
|
||||||
opener.start_test_wait();
|
|
||||||
}
|
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -11,12 +11,11 @@
|
||||||
function() {
|
function() {
|
||||||
check_result = t.step_func(
|
check_result = t.step_func(
|
||||||
function() {
|
function() {
|
||||||
if (pages.length < 4) {
|
if (pages.length < 3) {
|
||||||
setTimeout(check_result, 500);
|
setTimeout(check_result, 500);
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//The pass condition here is based on the idea that the spec is wrong and browsers are right
|
assert_array_equals(pages, [2, 3, 1], "Pages opened during history navigation");
|
||||||
assert_array_equals(pages, [2, 3, 2, 3], "Pages opened during history navigation");
|
|
||||||
t.done();
|
t.done();
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,11 +3,16 @@
|
||||||
<script>
|
<script>
|
||||||
function f() {
|
function f() {
|
||||||
opener.postMessage("original", "*");
|
opener.postMessage("original", "*");
|
||||||
|
if (opener.data.length >= 2) {
|
||||||
|
// If we proceed here, then our document.write will be racing with the
|
||||||
|
// setTimeout in our opener. Just stop.
|
||||||
|
return;
|
||||||
|
}
|
||||||
setTimeout(function () {
|
setTimeout(function () {
|
||||||
document.open();
|
document.open();
|
||||||
document.write("<!doctype html>2<script>opener.postMessage('written', '*');<\/script>");
|
document.write("<!doctype html>2<script>opener.postMessage('written', '*');<\/script>");
|
||||||
document.close();
|
document.close();
|
||||||
}), 100;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.onload = f
|
window.onload = f
|
||||||
|
|
|
@ -11,11 +11,11 @@ var data = [];
|
||||||
|
|
||||||
window.onmessage = t.step_func(function(e) {
|
window.onmessage = t.step_func(function(e) {
|
||||||
data.push(e.data);
|
data.push(e.data);
|
||||||
if (data.length < 3) {
|
if (data.length == 2) {
|
||||||
win.location.reload();
|
win.location.reload();
|
||||||
} else {
|
} else if (data.length >= 3) {
|
||||||
setTimeout(t.step_func(function() {
|
setTimeout(t.step_func(function() {
|
||||||
assert_array_equals(data, ["original", "written", "written"]);
|
assert_array_equals(data, ["original", "written", "original"]);
|
||||||
t.done();
|
t.done();
|
||||||
}), 500);
|
}), 500);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "nspr.h"
|
#include "nspr.h"
|
||||||
|
#include "mozilla/BasicEvents.h"
|
||||||
|
#include "mozilla/EventDispatcher.h"
|
||||||
#include "mozilla/Logging.h"
|
#include "mozilla/Logging.h"
|
||||||
|
|
||||||
#include "nsDocLoader.h"
|
#include "nsDocLoader.h"
|
||||||
|
@ -23,6 +25,7 @@
|
||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
|
|
||||||
#include "nsIDOMWindow.h"
|
#include "nsIDOMWindow.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
|
||||||
#include "nsIStringBundle.h"
|
#include "nsIStringBundle.h"
|
||||||
#include "nsIScriptSecurityManager.h"
|
#include "nsIScriptSecurityManager.h"
|
||||||
|
@ -36,7 +39,10 @@
|
||||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||||
|
|
||||||
using mozilla::DebugOnly;
|
using mozilla::DebugOnly;
|
||||||
|
using mozilla::eLoad;
|
||||||
|
using mozilla::EventDispatcher;
|
||||||
using mozilla::LogLevel;
|
using mozilla::LogLevel;
|
||||||
|
using mozilla::WidgetEvent;
|
||||||
|
|
||||||
static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
|
static NS_DEFINE_CID(kThisImplCID, NS_THIS_DOCLOADER_IMPL_CID);
|
||||||
|
|
||||||
|
@ -114,7 +120,8 @@ nsDocLoader::nsDocLoader()
|
||||||
mIsLoadingDocument(false),
|
mIsLoadingDocument(false),
|
||||||
mIsRestoringDocument(false),
|
mIsRestoringDocument(false),
|
||||||
mDontFlushLayout(false),
|
mDontFlushLayout(false),
|
||||||
mIsFlushingLayout(false)
|
mIsFlushingLayout(false),
|
||||||
|
mDocumentOpenedButNotLoaded(false)
|
||||||
{
|
{
|
||||||
ClearInternalProgress();
|
ClearInternalProgress();
|
||||||
|
|
||||||
|
@ -289,7 +296,7 @@ nsDocLoader::IsBusy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is this document loader busy? */
|
/* Is this document loader busy? */
|
||||||
if (!mIsLoadingDocument) {
|
if (!IsBlockingLoadEvent()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,6 +421,7 @@ nsDocLoader::OnStartRequest(nsIRequest *request, nsISupports *aCtxt)
|
||||||
if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
|
if (!mIsLoadingDocument && (loadFlags & nsIChannel::LOAD_DOCUMENT_URI)) {
|
||||||
bJustStartedLoading = true;
|
bJustStartedLoading = true;
|
||||||
mIsLoadingDocument = true;
|
mIsLoadingDocument = true;
|
||||||
|
mDocumentOpenedButNotLoaded = false;
|
||||||
ClearInternalProgress(); // only clear our progress if we are starting a new load....
|
ClearInternalProgress(); // only clear our progress if we are starting a new load....
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,10 +488,11 @@ nsDocLoader::OnStopRequest(nsIRequest *aRequest,
|
||||||
mLoadGroup->GetActiveCount(&count);
|
mLoadGroup->GetActiveCount(&count);
|
||||||
|
|
||||||
MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
|
MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
|
||||||
("DocLoader:%p: OnStopRequest[%p](%s) status=%x mIsLoadingDocument=%s, %u active URLs",
|
("DocLoader:%p: OnStopRequest[%p](%s) status=%x"
|
||||||
|
"mIsLoadingDocument=%s, mDocumentOpenedButNotLoaded=%s, %u active URLs",
|
||||||
this, aRequest, name.get(),
|
this, aRequest, name.get(),
|
||||||
aStatus, (mIsLoadingDocument ? "true" : "false"),
|
aStatus, (mIsLoadingDocument ? "true" : "false"),
|
||||||
count));
|
(mDocumentOpenedButNotLoaded ? "true" : "false"), count));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool bFireTransferring = false;
|
bool bFireTransferring = false;
|
||||||
|
@ -598,10 +607,9 @@ nsDocLoader::OnStopRequest(nsIRequest *aRequest,
|
||||||
RemoveRequestInfo(aRequest);
|
RemoveRequestInfo(aRequest);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Only fire the DocLoaderIsEmpty(...) if the document loader has initiated a
|
// Only fire the DocLoaderIsEmpty(...) if we may need to fire onload.
|
||||||
// load. This will handle removing the request from our hashtable as needed.
|
|
||||||
//
|
//
|
||||||
if (mIsLoadingDocument) {
|
if (IsBlockingLoadEvent()) {
|
||||||
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(static_cast<nsIRequestObserver*>(this));
|
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(static_cast<nsIRequestObserver*>(this));
|
||||||
bool doNotFlushLayout = false;
|
bool doNotFlushLayout = false;
|
||||||
if (ds) {
|
if (ds) {
|
||||||
|
@ -647,7 +655,7 @@ NS_IMETHODIMP nsDocLoader::GetDocumentChannel(nsIChannel ** aChannel)
|
||||||
|
|
||||||
void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
||||||
{
|
{
|
||||||
if (mIsLoadingDocument) {
|
if (IsBlockingLoadEvent()) {
|
||||||
/* In the unimagineably rude circumstance that onload event handlers
|
/* In the unimagineably rude circumstance that onload event handlers
|
||||||
triggered by this function actually kill the window ... ok, it's
|
triggered by this function actually kill the window ... ok, it's
|
||||||
not unimagineable; it's happened ... this deathgrip keeps this object
|
not unimagineable; it's happened ... this deathgrip keeps this object
|
||||||
|
@ -660,7 +668,9 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
|
NS_ASSERTION(!mIsFlushingLayout, "Someone screwed up");
|
||||||
NS_ASSERTION(mDocumentRequest, "No Document Request!");
|
// We may not have a document request if we are in a document.open() situation.
|
||||||
|
NS_ASSERTION(mDocumentRequest || mDocumentOpenedButNotLoaded,
|
||||||
|
"No Document Request!");
|
||||||
|
|
||||||
// The load group for this DocumentLoader is idle. Flush if we need to.
|
// The load group for this DocumentLoader is idle. Flush if we need to.
|
||||||
if (aFlushLayout && !mDontFlushLayout) {
|
if (aFlushLayout && !mDontFlushLayout) {
|
||||||
|
@ -687,9 +697,14 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
||||||
|
|
||||||
// And now check whether we're really busy; that might have changed with
|
// And now check whether we're really busy; that might have changed with
|
||||||
// the layout flush.
|
// the layout flush.
|
||||||
// Note, mDocumentRequest can be null if the flushing above re-entered this
|
//
|
||||||
// method.
|
// Note, mDocumentRequest can be null while mDocumentOpenedButNotLoaded is
|
||||||
if (!IsBusy() && mDocumentRequest) {
|
// false if the flushing above re-entered this method. Exit in that case.
|
||||||
|
if (IsBusy() || (!mDocumentRequest && !mDocumentOpenedButNotLoaded)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDocumentRequest) {
|
||||||
// Clear out our request info hash, now that our load really is done and
|
// Clear out our request info hash, now that our load really is done and
|
||||||
// we don't need it anymore to CalculateMaxProgress().
|
// we don't need it anymore to CalculateMaxProgress().
|
||||||
ClearInternalProgress();
|
ClearInternalProgress();
|
||||||
|
@ -715,7 +730,7 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
||||||
//
|
//
|
||||||
mLoadGroup->SetDefaultLoadRequest(nullptr);
|
mLoadGroup->SetDefaultLoadRequest(nullptr);
|
||||||
|
|
||||||
// Take a ref to our parent now so that we can call DocLoaderIsEmpty() on
|
// Take a ref to our parent now so that we can call ChildDoneWithOnload() on
|
||||||
// it even if our onload handler removes us from the docloader tree.
|
// it even if our onload handler removes us from the docloader tree.
|
||||||
RefPtr<nsDocLoader> parent = mParent;
|
RefPtr<nsDocLoader> parent = mParent;
|
||||||
|
|
||||||
|
@ -733,6 +748,67 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout)
|
||||||
parent->ChildDoneWithOnload(this);
|
parent->ChildDoneWithOnload(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(mDocumentOpenedButNotLoaded);
|
||||||
|
mDocumentOpenedButNotLoaded = false;
|
||||||
|
|
||||||
|
// Make sure we do the ChildEnteringOnload/ChildDoneWithOnload even if we
|
||||||
|
// plan to skip firing our own load event, because otherwise we might
|
||||||
|
// never end up firing our parent's load event.
|
||||||
|
RefPtr<nsDocLoader> parent = mParent;
|
||||||
|
if (!parent || parent->ChildEnteringOnload(this)) {
|
||||||
|
nsresult loadGroupStatus = NS_OK;
|
||||||
|
mLoadGroup->GetStatus(&loadGroupStatus);
|
||||||
|
// Make sure we're not canceling the loadgroup. If we are, then we should
|
||||||
|
// not fire a load event just like in the normal navigation case.
|
||||||
|
if (NS_SUCCEEDED(loadGroupStatus) ||
|
||||||
|
loadGroupStatus == NS_ERROR_PARSED_DATA_CACHED) {
|
||||||
|
// Can "doc" or "window" ever come back null here? Our state machine
|
||||||
|
// is complicated enough, so I wouldn't bet against it...
|
||||||
|
nsCOMPtr<nsIDocument> doc = do_GetInterface(GetAsSupports(this));
|
||||||
|
if (doc) {
|
||||||
|
doc->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE,
|
||||||
|
/* updateTimingInformation = */ false);
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
|
||||||
|
if (window && !doc->SkipLoadEventAfterClose()) {
|
||||||
|
MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
|
||||||
|
("DocLoader:%p: Firing load event for document.open\n",
|
||||||
|
this));
|
||||||
|
|
||||||
|
// This is a very cut-down version of
|
||||||
|
// nsDocumentViewer::LoadComplete that doesn't do various things
|
||||||
|
// that are not relevant here because this wasn't an actual
|
||||||
|
// navigation.
|
||||||
|
WidgetEvent event(true, eLoad);
|
||||||
|
event.mFlags.mBubbles = false;
|
||||||
|
event.mFlags.mCancelable = false;
|
||||||
|
// Dispatching to |window|, but using |document| as the target,
|
||||||
|
// per spec.
|
||||||
|
event.mTarget = doc;
|
||||||
|
nsEventStatus unused = nsEventStatus_eIgnore;
|
||||||
|
doc->SetLoadEventFiring(true);
|
||||||
|
EventDispatcher::Dispatch(window, nullptr, &event, nullptr,
|
||||||
|
&unused);
|
||||||
|
doc->SetLoadEventFiring(false);
|
||||||
|
|
||||||
|
// Now unsuppress painting on the presshell, if we
|
||||||
|
// haven't done that yet.
|
||||||
|
nsCOMPtr<nsIPresShell> shell = doc->GetShell();
|
||||||
|
if (shell && !shell->IsDestroying()) {
|
||||||
|
shell->UnsuppressPainting();
|
||||||
|
|
||||||
|
if (!shell->IsDestroying()) {
|
||||||
|
shell->LoadComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (parent) {
|
||||||
|
parent->ChildDoneWithOnload(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,8 @@ public:
|
||||||
unsigned long mNotifyMask;
|
unsigned long mNotifyMask;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void SetDocumentOpenedButNotLoaded() { mDocumentOpenedButNotLoaded = true; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~nsDocLoader();
|
virtual ~nsDocLoader();
|
||||||
|
|
||||||
|
@ -302,6 +304,15 @@ protected:
|
||||||
bool mIsFlushingLayout;
|
bool mIsFlushingLayout;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/**
|
||||||
|
* This flag indicates that the loader is waiting for completion of
|
||||||
|
* a document.open-triggered "document load". This is set when
|
||||||
|
* document.open() happens and sets up a new parser and cleared out
|
||||||
|
* when we go to fire our load event or end up with a new document
|
||||||
|
* channel.
|
||||||
|
*/
|
||||||
|
bool mDocumentOpenedButNotLoaded;
|
||||||
|
|
||||||
static const PLDHashTableOps sRequestInfoHashOps;
|
static const PLDHashTableOps sRequestInfoHashOps;
|
||||||
|
|
||||||
// A list of kids that are in the middle of their onload calls and will let
|
// A list of kids that are in the middle of their onload calls and will let
|
||||||
|
@ -324,10 +335,20 @@ private:
|
||||||
nsRequestInfo *GetRequestInfo(nsIRequest* aRequest);
|
nsRequestInfo *GetRequestInfo(nsIRequest* aRequest);
|
||||||
void ClearRequestInfoHash();
|
void ClearRequestInfoHash();
|
||||||
int64_t CalculateMaxProgress();
|
int64_t CalculateMaxProgress();
|
||||||
/// void DumpChannelInfo(void);
|
/// void DumpChannelInfo(void);
|
||||||
|
|
||||||
// used to clear our internal progress state between loads...
|
// used to clear our internal progress state between loads...
|
||||||
void ClearInternalProgress();
|
void ClearInternalProgress();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to test whether we might need to fire a load event. This
|
||||||
|
* can happen when we have a document load going on, or when we've
|
||||||
|
* had document.open() called and haven't fired the corresponding
|
||||||
|
* load event yet.
|
||||||
|
*/
|
||||||
|
bool IsBlockingLoadEvent() const {
|
||||||
|
return mIsLoadingDocument || mDocumentOpenedButNotLoaded;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader, NS_THIS_DOCLOADER_IMPL_CID)
|
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocLoader, NS_THIS_DOCLOADER_IMPL_CID)
|
||||||
|
|
Loading…
Reference in New Issue