Bug 1563587, Make history.back/forward/go asynchronous, r=farre

The main part of the change is the change to ChildSHistory - make it possible to have Go() to be called asynchronously
and also let one to cancel pending history navigations. History object (window.history) can then use either the sync or
async Go(), depending on the dom.window.history.async pref.

LoadDelegate, which is used by GeckoView, needs special handling, since
it spins event loop nestedly. With session history loads and same-document loads we can just
bypass it.
To deal with same-document case, MaybeHandleSameDocumentNavigation is split to IsSameDocumentNavigation,
which collects relevant information about the request and returns true if same-document navigation should happen,
and then later HandleSameDocumentNavigation uses that information to trigger the navigation.
SameDocumentNavigationState is used to pass the information around.

referrer-policy-test-case.sub.js is buggy causing tests to pass only on Firefox with sync history API.

nested-context-navigations-iframe.html.ini is added because of https://bugzilla.mozilla.org/show_bug.cgi?id=1572932

Differential Revision: https://phabricator.services.mozilla.com/D41199

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Olli Pettay 2019-08-13 17:11:35 +00:00
Родитель 7f699ce0f7
Коммит 924f5f1a55
33 изменённых файлов: 319 добавлений и 198 удалений

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

@ -32,6 +32,9 @@ add_task(async function purgeHistoryTest() {
content.history.pushState({}, "");
content.history.pushState({}, "");
content.history.back();
await new Promise(function(r) {
setTimeout(r);
});
let newHistory = content.history.length;
Assert.equal(startHistory, 1, "Initial SHistory size");
Assert.equal(newHistory, 3, "New SHistory size");

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

@ -21,6 +21,7 @@
#include "mozilla/BasePrincipal.h"
#include "mozilla/Casting.h"
#include "mozilla/Components.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Encoding.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/HTMLEditor.h"
@ -8933,70 +8934,65 @@ nsresult nsDocShell::PerformRetargeting(nsDocShellLoadState* aLoadState,
return rv;
}
nsresult nsDocShell::MaybeHandleSameDocumentNavigation(
nsDocShellLoadState* aLoadState, bool* aWasSameDocument) {
bool nsDocShell::IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
SameDocumentNavigationState& aState) {
MOZ_ASSERT(aLoadState);
MOZ_ASSERT(aWasSameDocument);
*aWasSameDocument = false;
if (!(aLoadState->LoadType() == LOAD_NORMAL ||
aLoadState->LoadType() == LOAD_STOP_CONTENT ||
LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
LOAD_FLAGS_REPLACE_HISTORY) ||
aLoadState->LoadType() == LOAD_HISTORY ||
aLoadState->LoadType() == LOAD_LINK)) {
return NS_OK;
return false;
}
nsresult rv;
nsCOMPtr<nsIURI> currentURI = mCurrentURI;
nsAutoCString curHash, newHash;
bool curURIHasRef = false, newURIHasRef = false;
nsresult rvURINew = aLoadState->URI()->GetRef(newHash);
nsresult rvURINew = aLoadState->URI()->GetRef(aState.mNewHash);
if (NS_SUCCEEDED(rvURINew)) {
rvURINew = aLoadState->URI()->GetHasRef(&newURIHasRef);
rvURINew = aLoadState->URI()->GetHasRef(&aState.mNewURIHasRef);
}
bool sameExceptHashes = false;
if (currentURI && NS_SUCCEEDED(rvURINew)) {
nsresult rvURIOld = currentURI->GetRef(curHash);
nsresult rvURIOld = currentURI->GetRef(aState.mCurrentHash);
if (NS_SUCCEEDED(rvURIOld)) {
rvURIOld = currentURI->GetHasRef(&curURIHasRef);
rvURIOld = currentURI->GetHasRef(&aState.mCurrentURIHasRef);
}
if (NS_SUCCEEDED(rvURIOld)) {
if (NS_FAILED(currentURI->EqualsExceptRef(aLoadState->URI(),
&sameExceptHashes))) {
sameExceptHashes = false;
&aState.mSameExceptHashes))) {
aState.mSameExceptHashes = false;
}
}
}
if (!sameExceptHashes && sURIFixup && currentURI && NS_SUCCEEDED(rvURINew)) {
if (!aState.mSameExceptHashes && sURIFixup && currentURI &&
NS_SUCCEEDED(rvURINew)) {
// Maybe aLoadState->URI() came from the exposable form of currentURI?
nsCOMPtr<nsIURI> currentExposableURI;
rv = sURIFixup->CreateExposableURI(currentURI,
getter_AddRefs(currentExposableURI));
NS_ENSURE_SUCCESS(rv, rv);
nsresult rvURIOld = currentExposableURI->GetRef(curHash);
DebugOnly<nsresult> rv = sURIFixup->CreateExposableURI(
currentURI, getter_AddRefs(currentExposableURI));
MOZ_ASSERT(NS_SUCCEEDED(rv), "CreateExposableURI should not fail, ever!");
nsresult rvURIOld = currentExposableURI->GetRef(aState.mCurrentHash);
if (NS_SUCCEEDED(rvURIOld)) {
rvURIOld = currentExposableURI->GetHasRef(&curURIHasRef);
rvURIOld = currentExposableURI->GetHasRef(&aState.mCurrentURIHasRef);
}
if (NS_SUCCEEDED(rvURIOld)) {
if (NS_FAILED(currentExposableURI->EqualsExceptRef(aLoadState->URI(),
&sameExceptHashes))) {
sameExceptHashes = false;
if (NS_FAILED(currentExposableURI->EqualsExceptRef(
aLoadState->URI(), &aState.mSameExceptHashes))) {
aState.mSameExceptHashes = false;
}
}
}
bool historyNavBetweenSameDoc = false;
if (mOSHE && aLoadState->SHEntry()) {
// We're doing a history load.
mOSHE->SharesDocumentWith(aLoadState->SHEntry(), &historyNavBetweenSameDoc);
mOSHE->SharesDocumentWith(aLoadState->SHEntry(),
&aState.mHistoryNavBetweenSameDoc);
#ifdef DEBUG
if (historyNavBetweenSameDoc) {
if (aState.mHistoryNavBetweenSameDoc) {
nsCOMPtr<nsIInputStream> currentPostData = mOSHE->GetPostData();
NS_ASSERTION(currentPostData == aLoadState->PostDataStream(),
"Different POST data for entries for the same page?");
@ -9019,13 +9015,21 @@ nsresult nsDocShell::MaybeHandleSameDocumentNavigation(
// that history.go(0) and the like trigger full refreshes, rather than
// same document navigations.
bool doSameDocumentNavigation =
(historyNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
(aState.mHistoryNavBetweenSameDoc && mOSHE != aLoadState->SHEntry()) ||
(!aLoadState->SHEntry() && !aLoadState->PostDataStream() &&
sameExceptHashes && newURIHasRef);
aState.mSameExceptHashes && aState.mNewURIHasRef);
if (!doSameDocumentNavigation) {
return NS_OK;
}
return doSameDocumentNavigation;
}
nsresult nsDocShell::HandleSameDocumentNavigation(
nsDocShellLoadState* aLoadState, SameDocumentNavigationState& aState) {
#ifdef DEBUG
SameDocumentNavigationState state;
MOZ_ASSERT(IsSameDocumentNavigation(aLoadState, state));
#endif
nsCOMPtr<nsIURI> currentURI = mCurrentURI;
// Save the position of the scrollers.
nsPoint scrollPos = GetCurScrollPos();
@ -9184,8 +9188,8 @@ nsresult nsDocShell::MaybeHandleSameDocumentNavigation(
// arguments it receives. But even if we don't end up scrolling,
// ScrollToAnchor performs other important tasks, such as informing
// the presShell that we have a new hash. See bug 680257.
rv = ScrollToAnchor(curURIHasRef, newURIHasRef, newHash,
aLoadState->LoadType());
nsresult rv = ScrollToAnchor(aState.mCurrentURIHasRef, aState.mNewURIHasRef,
aState.mNewHash, aLoadState->LoadType());
NS_ENSURE_SUCCESS(rv, rv);
/* restore previous position of scroller(s), if we're moving
@ -9209,10 +9213,11 @@ nsresult nsDocShell::MaybeHandleSameDocumentNavigation(
// reference to avoid null derefs. See bug 914521.
if (win) {
// Fire a hashchange event URIs differ, and only in their hashes.
bool doHashchange = sameExceptHashes && (curURIHasRef != newURIHasRef ||
!curHash.Equals(newHash));
bool doHashchange = aState.mSameExceptHashes &&
(aState.mCurrentURIHasRef != aState.mNewURIHasRef ||
!aState.mCurrentHash.Equals(aState.mNewHash));
if (historyNavBetweenSameDoc || doHashchange) {
if (aState.mHistoryNavBetweenSameDoc || doHashchange) {
win->DispatchSyncPopState();
}
@ -9227,7 +9232,6 @@ nsresult nsDocShell::MaybeHandleSameDocumentNavigation(
}
}
*aWasSameDocument = true;
return NS_OK;
}
@ -9275,14 +9279,22 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
// If we don't have a target, we're loading into ourselves, and our load
// delegate may want to intercept that load.
bool handled;
rv = MaybeHandleLoadDelegate(
aLoadState, nsIBrowserDOMWindow::OPEN_CURRENTWINDOW, &handled);
if (NS_FAILED(rv)) {
return rv;
}
if (handled) {
return NS_OK;
SameDocumentNavigationState sameDocumentNavigationState;
bool sameDocument =
IsSameDocumentNavigation(aLoadState, sameDocumentNavigationState);
// LoadDelegate has already had chance to delegate loads which ended up to
// session history, so no need to re-delegate here, and we don't want fragment
// navigations to go through load delegate.
if (!sameDocument && !(aLoadState->LoadType() & LOAD_CMD_HISTORY)) {
bool handled;
rv = MaybeHandleLoadDelegate(
aLoadState, nsIBrowserDOMWindow::OPEN_CURRENTWINDOW, &handled);
if (NS_FAILED(rv)) {
return rv;
}
if (handled) {
return NS_OK;
}
}
// If a source docshell has been passed, check to see if we are sandboxed
@ -9391,10 +9403,9 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
// See if this is actually a load between two history entries for the same
// document. If the process fails, or if we successfully navigate within the
// same document, return.
bool wasSameDocument;
rv = MaybeHandleSameDocumentNavigation(aLoadState, &wasSameDocument);
if (NS_FAILED(rv) || wasSameDocument) {
return rv;
if (sameDocument) {
return HandleSameDocumentNavigation(aLoadState,
sameDocumentNavigationState);
}
// mContentViewer->PermitUnload can destroy |this| docShell, which
@ -9601,6 +9612,18 @@ nsresult nsDocShell::InternalLoad(nsDocShellLoadState* aLoadState,
// If we have a saved content viewer in history, restore and show it now.
if (aLoadState->SHEntry() && (mLoadType & LOAD_CMD_HISTORY)) {
// https://html.spec.whatwg.org/#history-traversal:
// To traverse the history
// "If entry has a different Document object than the current entry, then
// run the following substeps: Remove any tasks queued by the history
// traversal task source..."
// Same document object case was handled already above with
// HandleSameDocumentNavigation call.
RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
if (shistory) {
shistory->RemovePendingHistoryNavigations();
}
// It's possible that the previous viewer of mContentViewer is the
// viewer that will end up in aLoadState->SHEntry() when it gets closed. If
// that's the case, we need to go ahead and force it into its shentry so we
@ -11261,6 +11284,14 @@ nsresult nsDocShell::UpdateURLAndHistory(Document* aDocument, nsIURI* aNewURI,
nsCOMPtr<nsISHEntry> newSHEntry;
if (!aReplace) {
// Step 2.
// Step 2.2, "Remove any tasks queued by the history traversal task
// source..."
RefPtr<ChildSHistory> shistory = GetRootSessionHistory();
if (shistory) {
shistory->RemovePendingHistoryNavigations();
}
// Save the current scroll position (bug 590573). Step 2.3.
nsPoint scrollPos = GetCurScrollPos();
mOSHE->SetScrollPosition(scrollPos.x, scrollPos.y);

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

@ -1034,12 +1034,25 @@ class nsDocShell final : public nsDocLoader,
nsresult MaybeHandleLoadDelegate(nsDocShellLoadState* aLoadState,
uint32_t aWindowType, bool* aDidHandleLoad);
// Check to see if we're loading a prior history entry in the same document.
// If so, handle the scrolling or other action required instead of continuing
// with new document navigation.
struct SameDocumentNavigationState {
nsAutoCString mCurrentHash;
nsAutoCString mNewHash;
bool mCurrentURIHasRef = false;
bool mNewURIHasRef = false;
bool mSameExceptHashes = false;
bool mHistoryNavBetweenSameDoc = false;
};
// Check to see if we're loading a prior history entry or doing a fragment
// navigation in the same document.
bool IsSameDocumentNavigation(nsDocShellLoadState* aLoadState,
SameDocumentNavigationState& aState);
// ... If so, handle the scrolling or other action required instead of
// continuing with new document navigation.
MOZ_CAN_RUN_SCRIPT
nsresult MaybeHandleSameDocumentNavigation(nsDocShellLoadState* aLoadState,
bool* aWasSameDocument);
nsresult HandleSameDocumentNavigation(nsDocShellLoadState* aLoadState,
SameDocumentNavigationState& aState);
private: // data members
static nsIURIFixup* sURIFixup;

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

@ -53,6 +53,21 @@ void ChildSHistory::Go(int32_t aOffset, ErrorResult& aRv) {
aRv = mHistory->GotoIndex(index.value());
}
void ChildSHistory::AsyncGo(int32_t aOffset) {
if (!CanGo(aOffset)) {
return;
}
RefPtr<PendingAsyncHistoryNavigation> asyncNav =
new PendingAsyncHistoryNavigation(this, aOffset);
mPendingNavigations.insertBack(asyncNav);
NS_DispatchToCurrentThread(asyncNav.forget());
}
void ChildSHistory::RemovePendingHistoryNavigations() {
mPendingNavigations.clear();
}
void ChildSHistory::EvictLocalContentViewers() {
mHistory->EvictAllContentViewers();
}

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

@ -23,6 +23,8 @@
#include "nsCOMPtr.h"
#include "mozilla/ErrorResult.h"
#include "nsWrapperCache.h"
#include "nsThreadUtils.h"
#include "mozilla/LinkedList.h"
class nsSHistory;
class nsDocShell;
@ -62,6 +64,9 @@ class ChildSHistory : public nsISupports, public nsWrapperCache {
*/
bool CanGo(int32_t aOffset);
void Go(int32_t aOffset, ErrorResult& aRv);
void AsyncGo(int32_t aOffset);
void RemovePendingHistoryNavigations();
/**
* Evicts all content viewers within the current process.
@ -75,8 +80,31 @@ class ChildSHistory : public nsISupports, public nsWrapperCache {
private:
virtual ~ChildSHistory();
class PendingAsyncHistoryNavigation
: public Runnable,
public mozilla::LinkedListElement<PendingAsyncHistoryNavigation> {
public:
PendingAsyncHistoryNavigation(ChildSHistory* aHistory, int32_t aOffset)
: Runnable("PendingAsyncHistoryNavigation"),
mHistory(aHistory),
mOffset(aOffset) {}
NS_IMETHOD Run() override {
if (isInList()) {
remove();
mHistory->Go(mOffset, IgnoreErrors());
}
return NS_OK;
}
private:
RefPtr<ChildSHistory> mHistory;
int32_t mOffset;
};
RefPtr<nsDocShell> mDocShell;
RefPtr<nsSHistory> mHistory;
mozilla::LinkedList<PendingAsyncHistoryNavigation> mPendingNavigations;
};
} // namespace dom

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

@ -67,6 +67,11 @@ function end_test() {
testWin.done();
}
var gTestContinuation = null;
function continueAsync() {
setTimeout(function() { gTestContinuation.next(); })
}
function test_basic_inner_navigation() {
// Navigate the inner frame a few times
loadContent(URL1, function() {
@ -86,7 +91,8 @@ function test_basic_inner_navigation() {
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
test_state_navigation();
gTestContinuation = test_state_navigation();
gTestContinuation.next();
});
window.history.forward();
});
@ -95,7 +101,7 @@ function test_basic_inner_navigation() {
});
}
function test_state_navigation() {
function* test_state_navigation() {
window.location.hash = "STATE1";
is(getURL(), URL2, "URL should be correct");
@ -107,19 +113,27 @@ function test_state_navigation() {
is(getContent(), "Test2", "Page should be correct");
window.history.back();
continueAsync();
yield;
is(gState(), "STATE1", "State should be correct after going back");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.forward();
continueAsync();
yield;
is(gState(), "STATE2", "State should be correct after going forward");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.back();
continueAsync();
yield;
window.history.back();
continueAsync();
yield;
is(gState(), "START", "State should be correct");
is(getURL(), URL2, "URL should be correct");
@ -143,6 +157,8 @@ function test_state_navigation() {
});
window.history.back();
continueAsync();
yield;
is(gState(), "START", "State should be correct");
}
</script>

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

@ -124,6 +124,10 @@ function pageLoad() {
}
}
function continueAsync() {
setTimeout(function() { gTestContinuation.next(); })
}
function* testBody() {
is(popup.scrollY, 0, "test 1");
popup.scroll(0, 100);
@ -133,16 +137,24 @@ function* testBody() {
popup.scroll(0, 200); // set state-2's position to 200
popup.history.back();
continueAsync();
yield;
is(Math.round(popup.scrollY), 100, "test 3");
popup.scroll(0, 150); // set original page's position to 150
popup.history.forward();
continueAsync();
yield;
is(Math.round(popup.scrollY), 200, "test 4");
popup.history.back();
continueAsync();
yield;
is(Math.round(popup.scrollY), 150, "test 5");
popup.history.forward();
continueAsync();
yield;
is(Math.round(popup.scrollY), 200, "test 6");
// At this point, the history looks like:
@ -187,8 +199,12 @@ function* testBody() {
is(Math.round(popup.scrollY), 200, "test 8");
popup.history.back();
continueAsync();
yield;
is(Math.round(popup.scrollY), 150, "test 9");
popup.history.forward();
continueAsync();
yield;
is(Math.round(popup.scrollY), 200, "test 10");

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

@ -17,23 +17,38 @@ SimpleTest.waitForExplicitFinish();
var popup = window.open("file_bug680257.html");
var gTestContinuation = null;
function continueAsync() {
setTimeout(function() { gTestContinuation.next(); })
}
// The popup will call into popupLoaded() once it loads.
function popupLoaded() {
// runTests() needs to be called from outside popupLoaded's onload handler.
// Otherwise, the navigations we do in runTests won't create new SHEntries.
SimpleTest.executeSoon(runTests);
SimpleTest.executeSoon(function() {
if (!gTestContinuation) {
gTestContinuation = runTests();
}
gTestContinuation.next();
});
}
function runTests() {
function* runTests() {
checkPopupLinkStyle(false, "Initial");
popup.location.hash = "a";
checkPopupLinkStyle(true, "After setting hash");
popup.history.back();
continueAsync();
yield;
checkPopupLinkStyle(false, "After going back");
popup.history.forward();
continueAsync();
yield;
checkPopupLinkStyle(true, "After going forward");
popup.close();

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

@ -40,7 +40,6 @@
opener.is(shistory.index, 0, "check history index");
opener.ok(webNav.canGoForward, "check canGoForward");
window.history.forward();
opener.is(shistory.legacySHistory.requestedIndex, 1, "check requestedIndex");
},
function() {
opener.is(shistory.count, 2, "check history length");

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

@ -70,6 +70,10 @@
history.pushState({ state: "state2" }, "state2");
window.scrollTo(0, 0);
history.back();
setTimeout(test);
break;
}
case 7: {
opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled back to the state1's position");
opener.is(history.state.state, "state1", "Unexpected state.");
@ -79,6 +83,10 @@
history.pushState({ state: "state4" }, "state4");
window.scrollTo(0, 0);
history.back();
setTimeout(test);
break;
}
case 8: {
opener.is(Math.round(window.scrollY), 0, "Shouldn't have scrolled back to the state3's position");
opener.is(history.state.state, "state3", "Unexpected state.");
@ -87,8 +95,16 @@
document.getElementById("bottom").scrollIntoView();
opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled to 'bottom'.");
history.back();
setTimeout(test);
break;
}
case 9: {
window.scrollTo(0, 0);
history.forward();
setTimeout(test);
break;
}
case 10: {
opener.isnot(Math.round(window.scrollY), 0, "Should have scrolled back to the state5's position");
var ifr = document.createElement("iframe");
@ -97,12 +113,12 @@
ifr.onload = test;
break;
}
case 7: {
case 11: {
oldHistoryObject = SpecialPowers.wrap(event.target).contentWindow.history;
event.target.src = "about:blank";
break;
}
case 8: {
case 12: {
try {
oldHistoryObject.scrollRestoration;
opener.ok(false, "Should have thrown an exception.");

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

@ -18,8 +18,8 @@
#include "nsContentUtils.h"
#include "nsISHistory.h"
#include "mozilla/dom/Location.h"
#include "mozilla/Preferences.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticPrefs_dom.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -159,7 +159,11 @@ void nsHistory::Go(int32_t aDelta, ErrorResult& aRv) {
// Ignore the return value from Go(), since returning errors from Go() can
// lead to exceptions and a possible leak of history length
session_history->Go(aDelta, IgnoreErrors());
if (StaticPrefs::dom_window_history_async()) {
session_history->AsyncGo(aDelta);
} else {
session_history->Go(aDelta, IgnoreErrors());
}
}
void nsHistory::Back(ErrorResult& aRv) {
@ -177,7 +181,11 @@ void nsHistory::Back(ErrorResult& aRv) {
return;
}
sHistory->Go(-1, IgnoreErrors());
if (StaticPrefs::dom_window_history_async()) {
sHistory->AsyncGo(-1);
} else {
sHistory->Go(-1, IgnoreErrors());
}
}
void nsHistory::Forward(ErrorResult& aRv) {
@ -195,7 +203,11 @@ void nsHistory::Forward(ErrorResult& aRv) {
return;
}
sHistory->Go(1, IgnoreErrors());
if (StaticPrefs::dom_window_history_async()) {
sHistory->AsyncGo(1);
} else {
sHistory->Go(1, IgnoreErrors());
}
}
void nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData,

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

@ -83,6 +83,11 @@ function continue_test() {
}
}
var gTestContinuation = null;
function continueAsync() {
setTimeout(function() { gTestContinuation.next(); })
}
function* test_basic_inner_navigation() {
// Navigate the inner frame a few times
yield loadContent(URL1);
@ -118,25 +123,35 @@ function* test_state_navigation() {
is(getContent(), "Test2", "Page should be correct");
window.history.back();
continueAsync();
yield;
is(gState, "STATE1", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.forward();
continueAsync();
yield;
is(gState, "STATE2", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.back();
continueAsync();
yield;
window.history.back();
continueAsync();
yield;
is(gState, "START", "State should be correct");
is(getURL(), URL2, "URL should be correct");
is(getContent(), "Test2", "Page should be correct");
window.history.back();
continueAsync();
yield;
is(gState, "START", "State should be correct");
yield waitForLoad();
@ -144,6 +159,8 @@ function* test_state_navigation() {
is(getContent(), "Test1", "Page should be correct");
window.history.back();
continueAsync();
yield;
is(gState, "START", "State should be correct after going back twice");
yield waitForLoad();

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

@ -2380,6 +2380,11 @@
value: true
mirror: always
- name: dom.window.history.async
type: bool
value: true
mirror: always
# Enable the "noreferrer" feature argument for window.open()
- name: dom.window.open.noreferrer.enabled
type: bool

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

@ -1,8 +0,0 @@
[scroll-restoration-fragment-scrolling-cross-origin.html]
expected:
if (os == "android") and e10s: ERROR
[Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation]
expected:
if (os == "android") and e10s: TIMEOUT
FAIL

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

@ -5,9 +5,6 @@
[pushState must not be allowed to create cross-origin URLs (data:URI)]
expected: FAIL
[pushState must remove any tasks queued by the history traversal task source]
expected: FAIL
[history.state should be a separate clone of the object, not a reference to the object passed to the event handler]
expected: FAIL

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

@ -5,12 +5,6 @@
[replaceState must not be allowed to create cross-origin URLs (data:URI)]
expected: FAIL
[replaceState must not remove any tasks queued by the history traversal task source]
expected: FAIL
[.go must queue a task with the history traversal task source (run asynchronously)]
expected: FAIL
[history.state should be a separate clone of the object, not a reference to the object passed to the event handler]
expected: FAIL

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

@ -1,4 +0,0 @@
[004.html]
[.go commands should be queued until the thread has ended]
expected: FAIL

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

@ -1,5 +0,0 @@
[combination_history_004.html]
expected: TIMEOUT
[After calling of back method, check length]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[combination_history_005.html]
expected: TIMEOUT
[After calling of forward method, check length]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[combination_history_006.html]
expected: TIMEOUT
[After calling of go method, check length]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[combination_history_007.html]
expected: TIMEOUT
[After calling of back and pushState method, check length]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[history_back.html]
expected: TIMEOUT
[history back]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[history_forward.html]
expected: TIMEOUT
[history forward]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[history_go_minus.html]
expected: TIMEOUT
[history go minus]
expected: TIMEOUT

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

@ -1,5 +0,0 @@
[history_go_plus.html]
expected: TIMEOUT
[history go plus]
expected: TIMEOUT

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

@ -1,4 +0,0 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

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

@ -1,4 +0,0 @@
[traverse_the_history_2.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

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

@ -1,4 +0,0 @@
[traverse_the_history_3.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

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

@ -1,4 +0,0 @@
[traverse_the_history_4.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

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

@ -1,4 +0,0 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

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

@ -0,0 +1,5 @@
[nested-context-navigations-iframe.html]
[Test that iframe navigations are not observable by the parent, even after history navigations by the parent]
expected: FAIL
[Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent]
expected: FAIL

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

@ -11,57 +11,63 @@
}
</style>
<div id="log"></div>
<iframe></iframe>
<script>
'use strict';
// The test does the following navigation steps for iframe
// 1. load page-with-fragment.html#fragment
// 2. load blank1
// 3. go back to page-with-fragment.html
async_test(function(t) {
var iframe = document.querySelector('iframe');
var hostInfo = get_host_info();
var basePath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
var localURL = hostInfo.HTTP_ORIGIN + basePath + '/resources/page-with-fragment.html#fragment';
var remoteURL = hostInfo.HTTP_REMOTE_ORIGIN + basePath + "/resources/blank1.html"
var next;
function frameOnload() {
if (next) {
next();
dump("next \n");
} else {
dump("no next \n");
// The test does the following navigation steps for iframe
// 1. load page-with-fragment.html#fragment
// 2. load blank1
// 3. go back to page-with-fragment.html
async_test(function(t) {
var iframe = document.querySelector('iframe');
var hostInfo = get_host_info();
var basePath = location.pathname.substring(0, location.pathname.lastIndexOf('/'));
var localURL = hostInfo.HTTP_ORIGIN + basePath + '/resources/page-with-fragment.html#fragment';
var remoteURL = hostInfo.HTTP_REMOTE_ORIGIN + basePath + "/resources/blank1.html"
var steps = [
function() {
iframe.src = 'resources/page-with-fragment.html#fragment';
}, function() {
assert_equals(iframe.contentWindow.location.href, localURL, 'should be on page-with-fragment page');
// wait one animation frame to ensure layout is run and fragment scrolling is complete
iframe.contentWindow.requestAnimationFrame(function() {
assert_equals(iframe.contentWindow.scrollY, 800, 'should scroll to fragment');
var steps = [
function() {
assert_equals(iframe.contentWindow.location.href, localURL, 'should be on page-with-fragment page');
// wait one animation frame to ensure layout is run and fragment scrolling is complete
iframe.contentWindow.requestAnimationFrame(function() {
assert_approx_equals(iframe.contentWindow.scrollY, 800, 5, 'should scroll to fragment');
iframe.contentWindow.history.scrollRestoration = 'manual';
assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
setTimeout(next, 0);
iframe.contentWindow.history.scrollRestoration = 'manual';
assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual');
setTimeout(next, 0);
});
}, function() {
// navigate to a new page from a different origin
iframe.src = remoteURL;
}, function() {
// going back causes the iframe to traverse back
history.back();
}, function() {
// coming back from history, scrollRestoration should be set to manual and respected
assert_equals(iframe.contentWindow.location.href, localURL, 'should be back on page-with-fragment page');
iframe.contentWindow.requestAnimationFrame(t.step_func_done(function() {
assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
assert_equals(iframe.contentWindow.scrollX, 0, 'should not scroll to fragment');
assert_equals(iframe.contentWindow.scrollY, 0, 'should not scroll to fragment');
}));
}
];
var stepCount = 0;
next = t.step_func(function() {
steps[stepCount++]();
});
}, function() {
// navigate to a new page from a different origin
iframe.src = remoteURL;
}, function() {
// going back causes the iframe to traverse back
history.back();
}, function() {
// coming back from history, scrollRestoration should be set to manual and respected
assert_equals(iframe.contentWindow.location.href, localURL, 'should be back on page-with-fragment page');
iframe.contentWindow.requestAnimationFrame(t.step_func_done(function() {
assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
assert_equals(iframe.contentWindow.scrollX, 0, 'should not scroll to fragment');
assert_equals(iframe.contentWindow.scrollY, 0, 'should not scroll to fragment');
}));
}
];
var stepCount = 0;
var next = t.step_func(function() {
steps[stepCount++]();
});
iframe.onload = next;
next();
}, 'Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation');
next();
}, 'Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation');
}
}
</script>
<iframe src="resources/page-with-fragment.html#fragment" onload="frameOnload()"></iframe>

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

@ -129,6 +129,11 @@ function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
const expectedReferrer =
referrerUrlResolver[scenario.referrer_url](currentURL);
function asyncResolve(result) {
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result), 0);
});}
// Request in the top-level document.
promise_test(_ => {
return invokeRequest(subresource, [])
@ -146,7 +151,7 @@ function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
subresource.url += "&-1";
return invokeRequest(subresource, [])
.then(result => checkResult(location.href, result))
.finally(_ => history.back());
.then(_ => history.back()).then(asyncResolve);
}, "`Referer` header with length < 4k is not stripped to an origin.");
promise_test(_ => {
@ -157,7 +162,7 @@ function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
subresource.url += "&0";
return invokeRequest(subresource, [])
.then(result => checkResult(expectedReferrer, result))
.finally(_ => history.back());
.then(_ => history.back()).then(asyncResolve);
}, "`Referer` header with length == 4k is not stripped to an origin.");
promise_test(_ => {
@ -168,7 +173,7 @@ function ReferrerPolicyTestCase(scenario, testDescription, sanityChecker) {
subresource.url += "&+1";
return invokeRequest(subresource, [])
.then(result => checkResult(originString, result))
.finally(_ => history.back());
.then(_ => history.back()).then(asyncResolve);
}, "`Referer` header with length > 4k is stripped to an origin.");
}