зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1706347
- Check mOOPChildrenLoading when deciding to put page in BFCache with Fission enabled. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D117199
This commit is contained in:
Родитель
e72d41676c
Коммит
f78596c95a
|
@ -2549,11 +2549,25 @@ void CanonicalBrowsingContext::CloneDocumentTreeInto(
|
|||
[source = MaybeDiscardedBrowsingContext{aSource},
|
||||
data = std::move(aPrintData)](
|
||||
BrowserParent* aBp) -> RefPtr<GenericNonExclusivePromise> {
|
||||
RefPtr<BrowserBridgeParent> bridge =
|
||||
aBp->GetBrowserBridgeParent();
|
||||
return aBp->SendCloneDocumentTreeIntoSelf(source, data)
|
||||
->Then(
|
||||
GetMainThreadSerialEventTarget(), __func__,
|
||||
[](BrowserParent::CloneDocumentTreeIntoSelfPromise::
|
||||
ResolveOrRejectValue&& aValue) {
|
||||
[bridge](
|
||||
BrowserParent::CloneDocumentTreeIntoSelfPromise::
|
||||
ResolveOrRejectValue&& aValue) {
|
||||
// We're cloning a remote iframe, so we created a
|
||||
// BrowserBridge which makes us register an OOP load
|
||||
// (see Document::OOPChildLoadStarted), even though
|
||||
// this isn't a real load. We call
|
||||
// SendMaybeFireEmbedderLoadEvents here so that we do
|
||||
// register the end of the load (see
|
||||
// Document::OOPChildLoadDone).
|
||||
if (bridge) {
|
||||
Unused << bridge->SendMaybeFireEmbedderLoadEvents(
|
||||
EmbedderElementEventType::NoEvent);
|
||||
}
|
||||
if (aValue.IsResolve() && aValue.ResolveValue()) {
|
||||
return GenericNonExclusivePromise::CreateAndResolve(
|
||||
true, __func__);
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body>
|
||||
<iframe id="inner" src="iframe_slow_onload_inner.html">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,19 @@
|
|||
<html>
|
||||
<head>
|
||||
<script>
|
||||
function load() {
|
||||
// Let the test page know that it can try to navigate.
|
||||
top.postMessage("onload", "*");
|
||||
// We're starting an infinite loop, but we need to time out after a
|
||||
// while, or the loop will keep running until shutdown.
|
||||
let t0 = performance.now();
|
||||
while (performance.now() - t0 < 5000) {
|
||||
document.getElementById("output").innerText = Math.random();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload="load()">
|
||||
<p id="output"></p>
|
||||
</body>
|
||||
</html>
|
|
@ -106,6 +106,8 @@ support-files = file_bug1536471.html
|
|||
support-files =
|
||||
file_blockBFCache.html
|
||||
slow.sjs
|
||||
iframe_slow_onload.html
|
||||
iframe_slow_onload_inner.html
|
||||
[test_child.html]
|
||||
[test_docshell_gotoindex.html]
|
||||
support-files = file_docshell_gotoindex.html
|
||||
|
|
|
@ -98,6 +98,30 @@ const blockBFCacheTests = [
|
|||
},
|
||||
];
|
||||
|
||||
if (SpecialPowers.Services.appinfo.sessionHistoryInParent) {
|
||||
blockBFCacheTests.push({
|
||||
name: "Loading OOP iframe",
|
||||
test: () => {
|
||||
return new Promise((resolve) => {
|
||||
const el = document.body.appendChild(document.createElement("iframe"));
|
||||
el.id = "frame";
|
||||
addEventListener("message", ({ data }) => {
|
||||
if (data == "onload") {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
el.src = "https://example.com/tests/docshell/test/navigation/iframe_slow_onload.html";
|
||||
});
|
||||
},
|
||||
waitForDone: () => {
|
||||
SimpleTest.requestFlakyTimeout("Test has a loop in an onload handler that runs for 5000ms, we need to make sure the loop is done before moving to the next test.");
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 5000);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const dontBlockBFCacheTests = [
|
||||
{
|
||||
name: "getUserMedia",
|
||||
|
@ -158,7 +182,7 @@ function promisePageShowNotFromBFCache(e) {
|
|||
}
|
||||
|
||||
function runTests(testArray, shouldBlockBFCache) {
|
||||
for (const { name, prefs = {}, test } of testArray) {
|
||||
for (const { name, prefs = {}, test, waitForDone } of testArray) {
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv(prefs, async function() {
|
||||
// Load a mostly blank page that we can communicate with over
|
||||
|
@ -189,6 +213,15 @@ function runTests(testArray, shouldBlockBFCache) {
|
|||
let result = await goneForward;
|
||||
ok(result, `Page ${shouldBlockBFCache ? "should" : "should not"} have been blocked from going into the BFCache (${name})`);
|
||||
|
||||
// If the test will keep running after navigation, then we need to make
|
||||
// sure it's completely done before moving to the next test, to avoid
|
||||
// interfering with any following tests. If waitForDone is defined then
|
||||
// it'll return a Promise that we can use to wait for the end of the
|
||||
// test.
|
||||
if (waitForDone) {
|
||||
await waitForDone();
|
||||
}
|
||||
|
||||
bc.postMessage({ message: "close" });
|
||||
|
||||
SpecialPowers.popPrefEnv();
|
||||
|
|
|
@ -17351,4 +17351,37 @@ void Document::UnregisterFromMemoryReportingForDataDocument() {
|
|||
}
|
||||
}
|
||||
}
|
||||
void Document::OOPChildLoadStarted(BrowserBridgeChild* aChild) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading.Contains(aChild));
|
||||
mOOPChildrenLoading.AppendElement(aChild);
|
||||
if (mOOPChildrenLoading.Length() == 1) {
|
||||
// Let's block unload so that we're blocked from going into the BFCache
|
||||
// until the child has actually notified us that it has done loading.
|
||||
BlockOnload();
|
||||
}
|
||||
}
|
||||
|
||||
void Document::OOPChildLoadDone(BrowserBridgeChild* aChild) {
|
||||
// aChild will not be in the list if nsDocLoader::Stop() was called, since
|
||||
// that clears mOOPChildrenLoading. It also dispatches the 'load' event,
|
||||
// so we don't need to call DocLoaderIsEmpty in that case.
|
||||
if (mOOPChildrenLoading.RemoveElement(aChild)) {
|
||||
if (mOOPChildrenLoading.IsEmpty()) {
|
||||
UnblockOnload(false);
|
||||
}
|
||||
RefPtr<nsDocLoader> docLoader(mDocumentContainer);
|
||||
if (docLoader) {
|
||||
docLoader->OOPChildrenLoadingIsEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Document::ClearOOPChildrenLoading() {
|
||||
nsTArray<const BrowserBridgeChild*> oopChildrenLoading;
|
||||
mOOPChildrenLoading.SwapElements(oopChildrenLoading);
|
||||
if (!oopChildrenLoading.IsEmpty()) {
|
||||
UnblockOnload(false);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
|
|
@ -220,6 +220,7 @@ class AnonymousContent;
|
|||
class Attr;
|
||||
class XULBroadcastManager;
|
||||
class XULPersist;
|
||||
class BrowserBridgeChild;
|
||||
class ChromeObserver;
|
||||
class ClientInfo;
|
||||
class ClientState;
|
||||
|
@ -4023,6 +4024,20 @@ class Document : public nsINode,
|
|||
|
||||
static bool IsValidDomain(nsIURI* aOrigHost, nsIURI* aNewURI);
|
||||
|
||||
// Inform a parent document that a BrowserBridgeChild has been created for
|
||||
// an OOP sub-document.
|
||||
// (This is the OOP counterpart to nsDocLoader::ChildEnteringOnload)
|
||||
void OOPChildLoadStarted(BrowserBridgeChild* aChild);
|
||||
|
||||
// Inform a parent document that the BrowserBridgeChild for one of its
|
||||
// OOP sub-documents is done calling its onload handler.
|
||||
// (This is the OOP counterpart to nsDocLoader::ChildDoneWithOnload)
|
||||
void OOPChildLoadDone(BrowserBridgeChild* aChild);
|
||||
|
||||
void ClearOOPChildrenLoading();
|
||||
|
||||
bool HasOOPChildrenLoading() { return !mOOPChildrenLoading.IsEmpty(); }
|
||||
|
||||
protected:
|
||||
// Returns the WindowContext for the document that we will contribute
|
||||
// page use counters to.
|
||||
|
@ -5257,6 +5272,10 @@ class Document : public nsINode,
|
|||
// Accumulate page load metrics
|
||||
void AccumulatePageLoadTelemetry();
|
||||
|
||||
// The OOP counterpart to nsDocLoader::mChildrenInOnload.
|
||||
// Not holding strong refs here since we don't actually use the BBCs.
|
||||
nsTArray<const BrowserBridgeChild*> mOOPChildrenLoading;
|
||||
|
||||
public:
|
||||
// Needs to be public because the bindings code pokes at it.
|
||||
JS::ExpandoAndGeneration mExpandoAndGeneration;
|
||||
|
|
|
@ -43,14 +43,11 @@ already_AddRefed<BrowserBridgeHost> BrowserBridgeChild::FinishInit(
|
|||
mFrameLoader = aFrameLoader;
|
||||
|
||||
RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
|
||||
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(owner->GetOwnerGlobal());
|
||||
MOZ_DIAGNOSTIC_ASSERT(docShell);
|
||||
|
||||
nsDocShell::Cast(docShell)->OOPChildLoadStarted(this);
|
||||
Document* doc = owner->OwnerDoc();
|
||||
doc->OOPChildLoadStarted(this);
|
||||
|
||||
#if defined(ACCESSIBILITY)
|
||||
if (a11y::DocAccessible* docAcc =
|
||||
a11y::GetExistingDocAccessible(owner->OwnerDoc())) {
|
||||
if (a11y::DocAccessible* docAcc = a11y::GetExistingDocAccessible(doc)) {
|
||||
if (a11y::LocalAccessible* ownerAcc = docAcc->GetAccessible(owner)) {
|
||||
if (a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc()) {
|
||||
outerAcc->SendEmbedderAccessible(this);
|
||||
|
@ -230,9 +227,9 @@ void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
|
|||
void BrowserBridgeChild::UnblockOwnerDocsLoadEvent() {
|
||||
if (!mHadInitialLoad) {
|
||||
mHadInitialLoad = true;
|
||||
if (auto* docShell =
|
||||
nsDocShell::Cast(mBrowsingContext->GetParent()->GetDocShell())) {
|
||||
docShell->OOPChildLoadDone(this);
|
||||
|
||||
if (Document* doc = mBrowsingContext->GetParent()->GetExtantDocument()) {
|
||||
doc->OOPChildLoadDone(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -266,7 +266,11 @@ nsDocLoader::Stop(void) {
|
|||
// after this, since mDocumentRequest will be null after the
|
||||
// DocLoaderIsEmpty() call.
|
||||
mChildrenInOnload.Clear();
|
||||
mOOPChildrenLoading.Clear();
|
||||
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(GetAsSupports(this));
|
||||
Document* doc = ds ? ds->GetExtantDocument() : nullptr;
|
||||
if (doc) {
|
||||
doc->ClearOOPChildrenLoading();
|
||||
}
|
||||
|
||||
// Make sure to call DocLoaderIsEmpty now so that we reset mDocumentRequest,
|
||||
// etc, as needed. We could be getting into here from a subframe onload, in
|
||||
|
@ -306,7 +310,9 @@ bool nsDocLoader::IsBusy() {
|
|||
// 3. It's currently flushing layout in DocLoaderIsEmpty().
|
||||
//
|
||||
|
||||
if (!mChildrenInOnload.IsEmpty() || !mOOPChildrenLoading.IsEmpty() ||
|
||||
nsCOMPtr<nsIDocShell> ds = do_QueryInterface(GetAsSupports(this));
|
||||
Document* doc = ds ? ds->GetExtantDocument() : nullptr;
|
||||
if (!mChildrenInOnload.IsEmpty() || (doc && doc->HasOOPChildrenLoading()) ||
|
||||
mIsFlushingLayout) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class BrowserBridgeChild;
|
||||
class BrowsingContext;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -55,8 +54,6 @@ class nsDocLoader : public nsIDocumentLoader,
|
|||
public nsIChannelEventSink,
|
||||
public nsISupportsPriority {
|
||||
public:
|
||||
using BrowserBridgeChild = mozilla::dom::BrowserBridgeChild;
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_THIS_DOCLOADER_IMPL_CID)
|
||||
|
||||
nsDocLoader() : nsDocLoader(false) {}
|
||||
|
@ -136,28 +133,10 @@ class nsDocLoader : public nsIDocumentLoader,
|
|||
mTreatAsBackgroundLoad = false;
|
||||
};
|
||||
|
||||
// Inform a parent docloader that a BrowserBridgeChild has been created for
|
||||
// an OOP sub-document.
|
||||
// (This is the OOP counterpart to ChildEnteringOnload below.)
|
||||
void OOPChildLoadStarted(BrowserBridgeChild* aChild) {
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading.Contains(aChild));
|
||||
mOOPChildrenLoading.AppendElement(aChild);
|
||||
}
|
||||
|
||||
// Inform a parent docloader that the BrowserBridgeChild for one of its
|
||||
// OOP sub-documents is done calling its onload handler.
|
||||
// (This is the OOP counterpart to ChildDoneWithOnload below.)
|
||||
void OOPChildLoadDone(BrowserBridgeChild* aChild) {
|
||||
// aChild will not be in the list if nsDocLoader::Stop() was called, since
|
||||
// that clears mOOPChildrenLoading. It also dispatches the 'load' event,
|
||||
// so we don't need to call DocLoaderIsEmpty in that case.
|
||||
if (mOOPChildrenLoading.RemoveElement(aChild)) {
|
||||
DocLoaderIsEmpty(true);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ChildCount() const { return mChildList.Length(); }
|
||||
|
||||
void OOPChildrenLoadingIsEmpty() { DocLoaderIsEmpty(true); }
|
||||
|
||||
protected:
|
||||
explicit nsDocLoader(bool aNotifyAboutBackgroundRequests);
|
||||
virtual ~nsDocLoader();
|
||||
|
@ -374,10 +353,6 @@ class nsDocLoader : public nsIDocumentLoader,
|
|||
// loadgroup) unless this is empty.
|
||||
nsCOMArray<nsIDocumentLoader> mChildrenInOnload;
|
||||
|
||||
// The OOP counterpart to mChildrenInOnload.
|
||||
// Not holding strong refs here since we don't actually use the BBCs.
|
||||
nsTArray<const BrowserBridgeChild*> mOOPChildrenLoading;
|
||||
|
||||
int64_t GetMaxTotalProgress();
|
||||
|
||||
nsresult AddRequestInfo(nsIRequest* aRequest);
|
||||
|
|
Загрузка…
Ссылка в новой задаче