Bug 1745638, allow stopping session history load to an iframe, r=peterv
The previous version had two issues: It revealed an issue in the old implementation which causes an assertion to fire. There was also a problem with mDocumentRequest, so the new approach tries to be less clever and just adds a new boolean member variable mCheckingSessionHistory, which GetIsAttemptingToNavigate() then uses. Differential Revision: https://phabricator.services.mozilla.com/D136041
This commit is contained in:
@@ -390,7 +390,8 @@ nsDocShell::nsDocShell(BrowsingContext* aBrowsingContext,
|
||||
mWillChangeProcess(false),
|
||||
mIsNavigating(false),
|
||||
mSuspendMediaWhenInactive(false),
|
||||
mForcedAutodetection(false) {
|
||||
mForcedAutodetection(false),
|
||||
mCheckingSessionHistory(false) {
|
||||
// If no outer window ID was provided, generate a new one.
|
||||
if (aContentWindowID == 0) {
|
||||
mContentWindowID = nsContentUtils::GenerateWindowId();
|
||||
@@ -788,6 +789,11 @@ nsresult nsDocShell::LoadURI(nsDocShellLoadState* aLoadState,
|
||||
("nsDocShell[%p]: loading %s with flags 0x%08x", this,
|
||||
aLoadState->URI()->GetSpecOrDefault().get(), aLoadState->LoadFlags()));
|
||||
|
||||
// Always clear mCheckingSessionHistory. MaybeHandleSubframeHistory uses it
|
||||
// internally when querying session history information from the parent
|
||||
// process.
|
||||
mCheckingSessionHistory = false;
|
||||
|
||||
if ((!aLoadState->LoadIsFromSessionHistory() &&
|
||||
!LOAD_TYPE_HAS_FLAGS(aLoadState->LoadType(),
|
||||
LOAD_FLAGS_REPLACE_HISTORY)) ||
|
||||
@@ -891,6 +897,83 @@ bool nsDocShell::IsLoadingFromSessionHistory() {
|
||||
return mActiveEntryIsLoadingFromSessionHistory;
|
||||
}
|
||||
|
||||
// StopDetector is modeled similarly to OnloadBlocker; it is a rather
|
||||
// dummy nsIRequest implementation which can be added to an nsILoadGroup to
|
||||
// detect Cancel calls.
|
||||
class StopDetector final : public nsIRequest {
|
||||
public:
|
||||
StopDetector() = default;
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUEST
|
||||
|
||||
bool Canceled() { return mCanceled; }
|
||||
|
||||
private:
|
||||
~StopDetector() = default;
|
||||
|
||||
bool mCanceled = false;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(StopDetector, nsIRequest)
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::GetName(nsACString& aResult) {
|
||||
aResult.AssignLiteral("about:stop-detector");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::IsPending(bool* aRetVal) {
|
||||
*aRetVal = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::GetStatus(nsresult* aStatus) {
|
||||
*aStatus = NS_OK;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::Cancel(nsresult aStatus) {
|
||||
mCanceled = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::Suspend(void) { return NS_OK; }
|
||||
NS_IMETHODIMP
|
||||
StopDetector::Resume(void) { return NS_OK; }
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::GetLoadGroup(nsILoadGroup** aLoadGroup) {
|
||||
*aLoadGroup = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::GetLoadFlags(nsLoadFlags* aLoadFlags) {
|
||||
*aLoadFlags = nsIRequest::LOAD_NORMAL;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
|
||||
return GetTRRModeImpl(aTRRMode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
|
||||
return SetTRRModeImpl(aTRRMode);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
StopDetector::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
|
||||
|
||||
bool nsDocShell::MaybeHandleSubframeHistory(
|
||||
nsDocShellLoadState* aLoadState, bool aContinueHandlingSubframeHistory) {
|
||||
// First, verify if this is a subframe.
|
||||
@@ -933,7 +1016,9 @@ bool nsDocShell::MaybeHandleSubframeHistory(
|
||||
!GetCreatedDynamically()) {
|
||||
if (XRE_IsContentProcess()) {
|
||||
dom::ContentChild* contentChild = dom::ContentChild::GetSingleton();
|
||||
if (contentChild) {
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
GetLoadGroup(getter_AddRefs(loadGroup));
|
||||
if (contentChild && loadGroup && !mCheckingSessionHistory) {
|
||||
RefPtr<Document> parentDoc = parentDS->GetDocument();
|
||||
parentDoc->BlockOnload();
|
||||
RefPtr<BrowsingContext> browsingContext = mBrowsingContext;
|
||||
@@ -941,11 +1026,34 @@ bool nsDocShell::MaybeHandleSubframeHistory(
|
||||
mBrowsingContext->GetCurrentLoadIdentifier();
|
||||
RefPtr<nsDocShellLoadState> loadState = aLoadState;
|
||||
bool isNavigating = mIsNavigating;
|
||||
RefPtr<StopDetector> stopDetector = new StopDetector();
|
||||
loadGroup->AddRequest(stopDetector, nullptr);
|
||||
// Need to set mCheckingSessionHistory so that
|
||||
// GetIsAttemptingToNavigate() returns true.
|
||||
mCheckingSessionHistory = true;
|
||||
|
||||
auto resolve =
|
||||
[currentLoadIdentifier, browsingContext, parentDoc, loadState,
|
||||
isNavigating](
|
||||
isNavigating, loadGroup, stopDetector](
|
||||
mozilla::Maybe<LoadingSessionHistoryInfo>&& aResult) {
|
||||
RefPtr<nsDocShell> docShell =
|
||||
static_cast<nsDocShell*>(browsingContext->GetDocShell());
|
||||
auto unblockParent = MakeScopeExit(
|
||||
[loadGroup, stopDetector, parentDoc, docShell]() {
|
||||
if (docShell) {
|
||||
docShell->mCheckingSessionHistory = false;
|
||||
}
|
||||
loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
|
||||
parentDoc->UnblockOnload(false);
|
||||
});
|
||||
|
||||
if (!docShell || !docShell->mCheckingSessionHistory) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (stopDetector->Canceled()) {
|
||||
return;
|
||||
}
|
||||
if (currentLoadIdentifier ==
|
||||
browsingContext->GetCurrentLoadIdentifier() &&
|
||||
aResult.isSome()) {
|
||||
@@ -954,16 +1062,20 @@ bool nsDocShell::MaybeHandleSubframeHistory(
|
||||
// history, index doesn't need to be updated.
|
||||
loadState->SetLoadIsFromSessionHistory(0, false);
|
||||
}
|
||||
RefPtr<nsDocShell> docShell =
|
||||
static_cast<nsDocShell*>(browsingContext->GetDocShell());
|
||||
if (docShell) {
|
||||
|
||||
// We got the results back from the parent process, call
|
||||
// LoadURI again with the possibly updated data.
|
||||
docShell->LoadURI(loadState, isNavigating, true);
|
||||
}
|
||||
parentDoc->UnblockOnload(false);
|
||||
};
|
||||
auto reject = [parentDoc](mozilla::ipc::ResponseRejectReason) {
|
||||
auto reject = [loadGroup, stopDetector, browsingContext,
|
||||
parentDoc](mozilla::ipc::ResponseRejectReason) {
|
||||
RefPtr<nsDocShell> docShell =
|
||||
static_cast<nsDocShell*>(browsingContext->GetDocShell());
|
||||
if (docShell) {
|
||||
docShell->mCheckingSessionHistory = false;
|
||||
}
|
||||
// In practise reject shouldn't be called ever.
|
||||
loadGroup->RemoveRequest(stopDetector, nullptr, NS_OK);
|
||||
parentDoc->UnblockOnload(false);
|
||||
};
|
||||
contentChild->SendGetLoadingSessionHistoryInfoFromParent(
|
||||
@@ -13412,7 +13524,7 @@ bool nsDocShell::GetIsAttemptingToNavigate() {
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return mCheckingSessionHistory;
|
||||
}
|
||||
|
||||
void nsDocShell::SetLoadingSessionHistoryInfo(
|
||||
|
||||
@@ -1356,6 +1356,12 @@ class nsDocShell final : public nsDocLoader,
|
||||
// Whether we have a pending encoding autodetection request from the
|
||||
// menu for all encodings.
|
||||
bool mForcedAutodetection : 1;
|
||||
|
||||
/*
|
||||
* Set to true if we're checking session history (in the parent process) for
|
||||
* a possible history load. Used only with iframes.
|
||||
*/
|
||||
bool mCheckingSessionHistory : 1;
|
||||
};
|
||||
|
||||
inline nsISupports* ToSupports(nsDocShell* aDocShell) {
|
||||
|
||||
15
docshell/test/navigation/file_bug1745638.html
Normal file
15
docshell/test/navigation/file_bug1745638.html
Normal file
@@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<script>
|
||||
function go() {
|
||||
var doc = document.getElementById("testFrame").contentWindow.document;
|
||||
doc.open();
|
||||
doc.close();
|
||||
doc.body.innerText = "passed";
|
||||
}
|
||||
</script>
|
||||
<body onload="setTimeout(opener.pageLoaded);">
|
||||
The iframe below should not be blank after a reload.<br>
|
||||
<iframe src="about:blank" id="testFrame"></iframe>
|
||||
<script>
|
||||
go();
|
||||
</script>
|
||||
@@ -98,6 +98,8 @@ support-files = file_bug1583110.html
|
||||
[test_bug1706090.html]
|
||||
support-files = file_bug1706090.html
|
||||
skip-if = fission # The test is currently for the old bfcache implementation
|
||||
[test_bug1745638.html]
|
||||
support-files = file_bug1745638.html
|
||||
[test_bug1747019.html]
|
||||
support-files =
|
||||
goback.html
|
||||
|
||||
40
docshell/test/navigation/test_bug1745638.html
Normal file
40
docshell/test/navigation/test_bug1745638.html
Normal file
@@ -0,0 +1,40 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>bug 1745638</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
|
||||
<script>
|
||||
// This test triggers an assertion in the old session history
|
||||
// implementation.
|
||||
SimpleTest.expectAssertions(0, 1);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
var testWindow;
|
||||
var loadCount = 0;
|
||||
function test() {
|
||||
testWindow = window.open("file_bug1745638.html");
|
||||
}
|
||||
|
||||
function pageLoaded() {
|
||||
++loadCount;
|
||||
is(testWindow.document.getElementById('testFrame').contentDocument.body.innerHTML,
|
||||
"passed",
|
||||
"Iframe's textual content should be 'passed'.");
|
||||
if (loadCount == 1) {
|
||||
testWindow.history.go(0);
|
||||
} else {
|
||||
testWindow.close();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="test()">
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user