mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 1512388 - Add loading cross domain iframes in background r=smaug
This patch adds the ability to load cross domain iframes in the background to make the top level documents finish earlier. This is an experiment feature that we'll keep it disabled by default. Differential Revision: https://phabricator.services.mozilla.com/D24938 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
d62d8572d3
commit
36865676e1
@ -224,6 +224,10 @@
|
||||
# include "nsIWebBrowserPrint.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
# include "ProfilerMarkerPayload.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::net;
|
||||
@ -8111,6 +8115,10 @@ nsresult nsDocShell::RestoreFromHistory() {
|
||||
nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
|
||||
nsIRequest* aRequest,
|
||||
nsIStreamListener** aContentHandler) {
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
ResetToFirstLoad();
|
||||
}
|
||||
|
||||
*aContentHandler = nullptr;
|
||||
|
||||
if (!mTreeOwner || mIsBeingDestroyed) {
|
||||
@ -8278,8 +8286,49 @@ nsresult nsDocShell::CreateContentViewer(const nsACString& aContentType,
|
||||
aOpenedChannel->SetNotificationCallbacks(this);
|
||||
}
|
||||
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
if ((!mContentViewer || GetDocument()->IsInitialDocument()) && IsFrame()) {
|
||||
// At this point, we know we just created a new iframe document based on the
|
||||
// response from the server, and we check if it's a cross-domain iframe
|
||||
|
||||
RefPtr<Document> newDoc = viewer->GetDocument();
|
||||
|
||||
RefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
nsCOMPtr<nsIPrincipal> parentPrincipal = parent->GetDocument()->NodePrincipal();
|
||||
nsCOMPtr<nsIPrincipal> thisPrincipal = newDoc->NodePrincipal();
|
||||
|
||||
SiteIdentifier parentSite;
|
||||
SiteIdentifier thisSite;
|
||||
|
||||
nsresult rv = BasePrincipal::Cast(parentPrincipal)->GetSiteIdentifier(parentSite);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = BasePrincipal::Cast(thisPrincipal)->GetSiteIdentifier(thisSite);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!parentSite.Equals(thisSite)) {
|
||||
#ifdef MOZ_GECKO_PROFILER
|
||||
nsCOMPtr<nsIURI> prinURI;
|
||||
thisPrincipal->GetURI(getter_AddRefs(prinURI));
|
||||
nsPrintfCString marker("Iframe loaded in background: %s", prinURI->GetSpecOrDefault().get());
|
||||
TimeStamp now = TimeStamp::Now();
|
||||
profiler_add_text_marker("Background Iframe", marker, JS::ProfilingCategoryPair::DOM, now, now, Nothing(), Nothing());
|
||||
#endif
|
||||
SetBackgroundLoadIframe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_ENSURE_SUCCESS(Embed(viewer, "", nullptr), NS_ERROR_FAILURE);
|
||||
|
||||
if (TreatAsBackgroundLoad()) {
|
||||
nsCOMPtr<nsIRunnable> triggerParentCheckDocShell = NewRunnableMethod(
|
||||
"nsDocShell::TriggerParentCheckDocShellIsEmpty", this,
|
||||
&nsDocShell::TriggerParentCheckDocShellIsEmpty);
|
||||
nsresult rv = NS_DispatchToCurrentThread(triggerParentCheckDocShell);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
mSavedRefreshURIList = nullptr;
|
||||
mSavingOldViewer = false;
|
||||
mEODForCurrentDocument = false;
|
||||
|
@ -270,6 +270,16 @@ class nsDocShell final : public nsDocLoader,
|
||||
LOCATION_CHANGE_SAME_DOCUMENT);
|
||||
}
|
||||
|
||||
// This function is created exclusively for dom.background_loading_iframe is set.
|
||||
// As soon as the current DocShell knows itself can be treated as background loading,
|
||||
// it triggers the parent docshell to see if the parent document can fire load event earlier.
|
||||
void TriggerParentCheckDocShellIsEmpty() {
|
||||
RefPtr<nsDocShell> parent = GetParentDocshell();
|
||||
if (parent) {
|
||||
parent->DocLoaderIsEmpty(true);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult HistoryEntryRemoved(int32_t aIndex);
|
||||
|
||||
// Notify Scroll observers when an async panning/zooming transform
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/dom/TabGroup.h"
|
||||
#include "mozilla/AbstractThread.h"
|
||||
#include "mozilla/PerformanceUtils.h"
|
||||
#include "mozilla/ThrottledEventQueue.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsIDocShell.h"
|
||||
@ -65,6 +66,10 @@ DocGroup::~DocGroup() {
|
||||
}
|
||||
|
||||
mTabGroup->mDocGroups.RemoveEntry(mKey);
|
||||
|
||||
if (mIframePostMessageQueue) {
|
||||
FlushIframePostMessageQueue();
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<PerformanceInfoPromise> DocGroup::ReportPerformanceInfo() {
|
||||
@ -188,6 +193,61 @@ void DocGroup::SignalSlotChange(HTMLSlotElement& aSlot) {
|
||||
sPendingDocGroups->AppendElement(this);
|
||||
}
|
||||
|
||||
bool DocGroup::TryToLoadIframesInBackground() {
|
||||
return
|
||||
StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
|
||||
StaticPrefs::dom_cross_origin_iframes_loaded_in_background();
|
||||
}
|
||||
|
||||
nsresult DocGroup::QueueIframePostMessages(
|
||||
already_AddRefed<nsIRunnable>&& aRunnable,
|
||||
uint64_t aWindowId) {
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
if (!mIframePostMessageQueue) {
|
||||
nsCOMPtr<nsISerialEventTarget> target = GetMainThreadSerialEventTarget();
|
||||
mIframePostMessageQueue = ThrottledEventQueue::Create(
|
||||
target, "Background Loading Iframe PostMessage Queue",
|
||||
nsIRunnablePriority::PRIORITY_DEFERRED_TIMERS);
|
||||
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
}
|
||||
|
||||
// Ensure the queue is disabled. Unlike the postMessageEvent queue in
|
||||
// TabGroup, this postMessage queue should always be paused, because if
|
||||
// we leave it open, the postMessage may get dispatched to an unloaded
|
||||
// iframe
|
||||
MOZ_ASSERT(mIframePostMessageQueue);
|
||||
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
||||
|
||||
mIframesUsedPostMessageQueue.PutEntry(aWindowId);
|
||||
|
||||
mIframePostMessageQueue->Dispatch(std::move(aRunnable),
|
||||
NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
void DocGroup::TryFlushIframePostMessages(uint64_t aWindowId) {
|
||||
if (DocGroup::TryToLoadIframesInBackground()) {
|
||||
mIframesUsedPostMessageQueue.RemoveEntry(aWindowId);
|
||||
if (mIframePostMessageQueue &&
|
||||
mIframesUsedPostMessageQueue.IsEmpty()) {
|
||||
MOZ_ASSERT(mIframePostMessageQueue->IsPaused());
|
||||
nsresult rv = mIframePostMessageQueue->SetIsPaused(true);
|
||||
MOZ_ALWAYS_SUCCEEDS(rv);
|
||||
FlushIframePostMessageQueue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DocGroup::FlushIframePostMessageQueue() {
|
||||
nsCOMPtr<nsIRunnable> event;
|
||||
while ((event = mIframePostMessageQueue->GetEvent())) {
|
||||
Dispatch(TaskCategory::Other, event.forget());
|
||||
}
|
||||
}
|
||||
|
||||
void DocGroup::MoveSignalSlotListTo(nsTArray<RefPtr<HTMLSlotElement>>& aDest) {
|
||||
aDest.SetCapacity(aDest.Length() + mSignalSlotList.Length());
|
||||
for (RefPtr<HTMLSlotElement>& slot : mSignalSlotList) {
|
||||
|
@ -105,16 +105,28 @@ class DocGroup final {
|
||||
// Returns true if any of its documents are active but not in the bfcache.
|
||||
bool IsActive() const;
|
||||
|
||||
nsresult QueueIframePostMessages(
|
||||
already_AddRefed<nsIRunnable>&& aRunnable,
|
||||
uint64_t aWindowId);
|
||||
|
||||
void TryFlushIframePostMessages(uint64_t aWindowId);
|
||||
|
||||
static bool TryToLoadIframesInBackground();
|
||||
|
||||
private:
|
||||
DocGroup(TabGroup* aTabGroup, const nsACString& aKey);
|
||||
~DocGroup();
|
||||
|
||||
void FlushIframePostMessageQueue();
|
||||
nsCString mKey;
|
||||
RefPtr<TabGroup> mTabGroup;
|
||||
nsTArray<Document*> mDocuments;
|
||||
RefPtr<mozilla::dom::CustomElementReactionsStack> mReactionsStack;
|
||||
nsTArray<RefPtr<HTMLSlotElement>> mSignalSlotList;
|
||||
RefPtr<mozilla::PerformanceCounter> mPerformanceCounter;
|
||||
|
||||
RefPtr<mozilla::ThrottledEventQueue> mIframePostMessageQueue;
|
||||
nsTHashtable<nsUint64HashKey> mIframesUsedPostMessageQueue;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -1937,12 +1937,20 @@ void nsGlobalWindowInner::FireFrameLoadEvent(bool aIsTrusted) {
|
||||
event.mFlags.mBubbles = false;
|
||||
event.mFlags.mCancelable = false;
|
||||
|
||||
// Most of the time we could get a pres context to pass in here,
|
||||
// but not always (i.e. if this window is not shown there won't
|
||||
// be a pres context available). Since we're not firing a GUI
|
||||
// event we don't need a pres context anyway so we just pass
|
||||
// null as the pres context all the time here.
|
||||
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
|
||||
if (mozilla::dom::DocGroup::TryToLoadIframesInBackground()) {
|
||||
nsDocShell* ds = nsDocShell::Cast(GetDocShell());
|
||||
|
||||
if (ds && !ds->HasFakeOnLoadDispatched()) {
|
||||
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
|
||||
}
|
||||
} else {
|
||||
// Most of the time we could get a pres context to pass in here,
|
||||
// but not always (i.e. if this window is not shown there won't
|
||||
// be a pres context available). Since we're not firing a GUI
|
||||
// event we don't need a pres context anyway so we just pass
|
||||
// null as the pres context all the time here.
|
||||
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2429,6 +2429,15 @@ void nsGlobalWindowOuter::DetachFromDocShell() {
|
||||
// later. Meanwhile, keep our weak reference to the script object
|
||||
// so that it can be retrieved later (until it is finalized by the JS GC).
|
||||
|
||||
if (mDoc && DocGroup::TryToLoadIframesInBackground()) {
|
||||
DocGroup* docGroup = GetDocGroup();
|
||||
RefPtr<nsIDocShell> docShell = GetDocShell();
|
||||
RefPtr<nsDocShell> dShell = nsDocShell::Cast(docShell);
|
||||
if (dShell){
|
||||
docGroup->TryFlushIframePostMessages(dShell->GetOuterWindowID());
|
||||
}
|
||||
}
|
||||
|
||||
// Call FreeInnerObjects on all inner windows, not just the current
|
||||
// one, since some could be held by WindowStateHolder objects that
|
||||
// are GC-owned.
|
||||
@ -6064,14 +6073,40 @@ void nsGlobalWindowOuter::PostMessageMozOuter(JSContext* aCx,
|
||||
return;
|
||||
}
|
||||
|
||||
if (StaticPrefs::dom_separate_event_queue_for_post_message_enabled()) {
|
||||
if (mDoc) {
|
||||
Document* doc = mDoc->GetTopLevelContentDocument();
|
||||
if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
|
||||
// As long as the top level is loading, we can dispatch events to the
|
||||
// queue because the queue will be flushed eventually
|
||||
mozilla::dom::TabGroup* tabGroup = TabGroup();
|
||||
aError = tabGroup->QueuePostMessageEvent(event.forget());
|
||||
|
||||
if (mDoc &&
|
||||
StaticPrefs::dom_separate_event_queue_for_post_message_enabled() &&
|
||||
!DocGroup::TryToLoadIframesInBackground()) {
|
||||
Document* doc = mDoc->GetTopLevelContentDocument();
|
||||
if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
|
||||
// As long as the top level is loading, we can dispatch events to the
|
||||
// queue because the queue will be flushed eventually
|
||||
mozilla::dom::TabGroup* tabGroup = TabGroup();
|
||||
aError = tabGroup->QueuePostMessageEvent(event.forget());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mDoc && DocGroup::TryToLoadIframesInBackground()) {
|
||||
|
||||
RefPtr<nsIDocShell> docShell = GetDocShell();
|
||||
RefPtr<nsDocShell> dShell = nsDocShell::Cast(docShell);
|
||||
|
||||
// PostMessage that are added to the tabGroup are the ones that
|
||||
// can be flushed when the top level document is loaded
|
||||
if (dShell) {
|
||||
if (!dShell->TreatAsBackgroundLoad()) {
|
||||
Document* doc = mDoc->GetTopLevelContentDocument();
|
||||
if (doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
|
||||
// As long as the top level is loading, we can dispatch events to the
|
||||
// queue because the queue will be flushed eventually
|
||||
mozilla::dom::TabGroup* tabGroup = TabGroup();
|
||||
aError = tabGroup->QueuePostMessageEvent(event.forget());
|
||||
return;
|
||||
}
|
||||
} else if (mDoc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE) {
|
||||
mozilla::dom::DocGroup* docGroup = GetDocGroup();
|
||||
aError = docGroup->QueueIframePostMessages(event.forget(), dShell->GetOuterWindowID());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ class nsWindowSizes;
|
||||
namespace mozilla {
|
||||
class AbstractThread;
|
||||
class DOMEventTargetHelper;
|
||||
class ThrottledEventQueue;
|
||||
namespace dom {
|
||||
class BarProp;
|
||||
class BrowsingContext;
|
||||
@ -540,6 +541,7 @@ class nsGlobalWindowOuter final : public mozilla::dom::EventTarget,
|
||||
|
||||
nsresult GetPrompter(nsIPrompt** aPrompt) override;
|
||||
|
||||
RefPtr<mozilla::ThrottledEventQueue> mPostMessageEventQueue;
|
||||
protected:
|
||||
nsPIDOMWindowOuter* GetOpenerWindowOuter();
|
||||
// Initializes the mWasOffline member variable
|
||||
|
@ -0,0 +1,19 @@
|
||||
<html>
|
||||
<body>
|
||||
<script>
|
||||
var onloadFiredAt;
|
||||
|
||||
let start = performance.now();
|
||||
while (performance.now() - start < 5000);
|
||||
|
||||
window.onload = function() {
|
||||
onloadFiredAt = performance.now();
|
||||
}
|
||||
|
||||
window.addEventListener('message', function(event) {
|
||||
window.parent.postMessage(onloadFiredAt, "*");
|
||||
})
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -10,6 +10,7 @@ support-files =
|
||||
iframe1_location_setters.html
|
||||
iframe2_location_setters.html
|
||||
iframe3_location_setters.html
|
||||
file_test_background_loading_iframes.html
|
||||
|
||||
[test_crossdomainprops.html]
|
||||
[test_innerWidthHeight_script.html]
|
||||
@ -21,3 +22,4 @@ support-files =
|
||||
[test_setting_document.domain_idn.html]
|
||||
[test_setting_document.domain_to_shortened_ipaddr.html]
|
||||
[test_separate_post_message_queue.html]
|
||||
[test_background_loading_iframes.html]
|
||||
|
@ -0,0 +1,67 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for background loading iframes</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="text/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var myLoadTime;
|
||||
var receivedPostMessage = [];
|
||||
|
||||
window.addEventListener('message', function(event) {
|
||||
receivedPostMessage.push(parseInt(event.data));
|
||||
if (receivedPostMessage.length == 3) {
|
||||
if (!myLoadTime){
|
||||
ok(false, "Child iframes are loaded earlier than the parent document");
|
||||
} else {
|
||||
receivedPostMessage.forEach(function(iframeLoadTime, index) {
|
||||
ok(iframeLoadTime, "Iframe load time should not be null");
|
||||
ok(iframeLoadTime >= myLoadTime, "Parent document should be loaded earlier than child iframes");
|
||||
})
|
||||
}
|
||||
SimpleTest.finish();
|
||||
}
|
||||
})
|
||||
|
||||
window.onload = function() {
|
||||
myLoadTime = performance.now();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({"set":[["dom.background_loading_iframe", true]]}, function () {
|
||||
var iframe1 = document.createElement("iframe");
|
||||
var iframe2 = document.createElement("iframe");
|
||||
var iframe3 = document.createElement("iframe");
|
||||
|
||||
iframe1.src = "http://example.org:80/tests/dom/tests/mochitest/dom-level0/file_test_background_loading_iframes.html";
|
||||
iframe2.src = "http://example.org:80/tests/dom/tests/mochitest/dom-level0/file_test_background_loading_iframes.html";
|
||||
iframe3.src = "http://example.org:80/tests/dom/tests/mochitest/dom-level0/file_test_background_loading_iframes.html";
|
||||
|
||||
iframe1.onload = function() {
|
||||
iframe1.contentWindow.postMessage("test", "*");
|
||||
}
|
||||
|
||||
iframe2.onload = function() {
|
||||
iframe2.contentWindow.postMessage("test", "*");
|
||||
}
|
||||
|
||||
iframe3.onload = function() {
|
||||
iframe3.contentWindow.postMessage("test", "*");
|
||||
}
|
||||
|
||||
document.body.append(iframe1);
|
||||
document.body.append(iframe2);
|
||||
document.body.append(iframe3);
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -78,7 +78,7 @@
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsStyleSheetService.h"
|
||||
#include "nsILoadContext.h"
|
||||
|
||||
#include "mozilla/ThrottledEventQueue.h"
|
||||
#include "nsIPrompt.h"
|
||||
#include "imgIContainer.h" // image animation mode constants
|
||||
|
||||
@ -1101,14 +1101,68 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) {
|
||||
docShell, MakeUnique<DocLoadingTimelineMarker>("document::Load"));
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
|
||||
RefPtr<DocGroup> docGroup = mDocument->GetDocGroup();
|
||||
// It is possible that the parent document's load event fires earlier than
|
||||
// childs' load event, and in this case we need to fire some artificial
|
||||
// load events to make the parent thinks the load events for child has
|
||||
// been done
|
||||
if (innerWindow && DocGroup::TryToLoadIframesInBackground()) {
|
||||
nsTArray<nsCOMPtr<nsIDocShell>> docShells;
|
||||
nsCOMPtr<nsIDocShell> container(mContainer);
|
||||
if (container) {
|
||||
int32_t count;
|
||||
container->GetChildCount(&count);
|
||||
// We first find all background loading iframes that need to
|
||||
// fire artificial load events, and instead of firing them as
|
||||
// soon as we find them, we store them in an array, to prevent
|
||||
// us from skipping some events.
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
nsCOMPtr<nsIDocShellTreeItem> child;
|
||||
container->GetChildAt(i, getter_AddRefs(child));
|
||||
nsCOMPtr<nsIDocShell> childIDocShell = do_QueryInterface(child);
|
||||
RefPtr<nsDocShell> docShell = nsDocShell::Cast(childIDocShell);
|
||||
if (docShell && docShell->TreatAsBackgroundLoad() &&
|
||||
docShell->GetDocument()->GetReadyStateEnum() <
|
||||
Document::READYSTATE_COMPLETE) {
|
||||
docShells.AppendElement(childIDocShell);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-iterate the stored docShells to fire artificial load events
|
||||
for (size_t i = 0; i < docShells.Length(); ++i) {
|
||||
RefPtr<nsDocShell> docShell = nsDocShell::Cast(docShells[i]);
|
||||
if (docShell && docShell->TreatAsBackgroundLoad() &&
|
||||
docShell->GetDocument()->GetReadyStateEnum() <
|
||||
Document::READYSTATE_COMPLETE) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetEvent event(true, eLoad);
|
||||
event.mFlags.mBubbles = false;
|
||||
event.mFlags.mCancelable = false;
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> win = docShell->GetWindow();
|
||||
nsCOMPtr<Element> element = win->GetFrameElementInternal();
|
||||
|
||||
docShell->SetFakeOnLoadDispatched();
|
||||
EventDispatcher::Dispatch(element, nullptr, &event, nullptr, &status);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d->SetLoadEventFiring(true);
|
||||
EventDispatcher::Dispatch(window, mPresContext, &event, nullptr, &status);
|
||||
d->SetLoadEventFiring(false);
|
||||
|
||||
RefPtr<nsDocShell> dShell = nsDocShell::Cast(docShell);
|
||||
if (dShell->TreatAsBackgroundLoad()) {
|
||||
docGroup->TryFlushIframePostMessages(dShell->GetOuterWindowID());
|
||||
}
|
||||
|
||||
if (timing) {
|
||||
timing->NotifyLoadEventEnd();
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner* innerWindow = window->GetCurrentInnerWindow();
|
||||
if (innerWindow) {
|
||||
innerWindow->QueuePerformanceNavigationTiming();
|
||||
}
|
||||
@ -1156,7 +1210,6 @@ nsDocumentViewer::LoadComplete(nsresult aStatus) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release the JS bytecode cache from its wait on the load event, and
|
||||
// potentially dispatch the encoding of the bytecode.
|
||||
if (mDocument && mDocument->ScriptLoader()) {
|
||||
|
@ -673,6 +673,15 @@ VARCACHE_PREF(
|
||||
bool, true
|
||||
)
|
||||
|
||||
// When this pref is set, parent documents may consider child iframes've loaded
|
||||
// while they are still loading
|
||||
VARCACHE_PREF(
|
||||
Live,
|
||||
"dom.cross_origin_iframes_loaded_in_background",
|
||||
dom_cross_origin_iframes_loaded_in_background,
|
||||
bool, false
|
||||
)
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Prefs starting with "browser."
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -39,11 +39,13 @@
|
||||
#include "nsISocketTransport.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocGroup.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsILoadURIDelegate.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "mozilla/ThrottledEventQueue.h"
|
||||
using namespace mozilla;
|
||||
using mozilla::DebugOnly;
|
||||
using mozilla::eLoad;
|
||||
@ -117,6 +119,9 @@ nsDocLoader::nsDocLoader()
|
||||
mIsRestoringDocument(false),
|
||||
mDontFlushLayout(false),
|
||||
mIsFlushingLayout(false),
|
||||
mTreatAsBackgroundLoad(false),
|
||||
mHasFakeOnLoadDispatched(false),
|
||||
mIsReadyToHandlePostMessage(false),
|
||||
mDocumentOpenedButNotLoaded(false) {
|
||||
ClearInternalProgress();
|
||||
|
||||
@ -255,6 +260,14 @@ nsDocLoader::Stop(void) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool nsDocLoader::TreatAsBackgroundLoad() {
|
||||
return mTreatAsBackgroundLoad;
|
||||
}
|
||||
|
||||
void nsDocLoader::SetBackgroundLoadIframe() {
|
||||
mTreatAsBackgroundLoad = true;
|
||||
}
|
||||
|
||||
bool nsDocLoader::IsBusy() {
|
||||
nsresult rv;
|
||||
|
||||
@ -291,6 +304,11 @@ bool nsDocLoader::IsBusy() {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
nsIDocumentLoader* loader = ChildAt(i);
|
||||
|
||||
// If 'dom.cross_origin_iframes_loaded_in_background' is set, the parent
|
||||
// document treats cross domain iframes as background loading frame
|
||||
if (loader && static_cast<nsDocLoader *>(loader)->TreatAsBackgroundLoad()) {
|
||||
continue;
|
||||
}
|
||||
// This is a safe cast, because we only put nsDocLoader objects into the
|
||||
// array
|
||||
if (loader && static_cast<nsDocLoader*>(loader)->IsBusy()) return true;
|
||||
@ -739,34 +757,38 @@ void nsDocLoader::DocLoaderIsEmpty(bool aFlushLayout) {
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window = doc->GetWindow();
|
||||
if (window && !doc->SkipLoadEventAfterClose()) {
|
||||
MOZ_LOG(gDocLoaderLog, LogLevel::Debug,
|
||||
("DocLoader:%p: Firing load event for document.open\n",
|
||||
this));
|
||||
if (!mozilla::dom::DocGroup::TryToLoadIframesInBackground() ||
|
||||
(mozilla::dom::DocGroup::TryToLoadIframesInBackground() &&
|
||||
!HasFakeOnLoadDispatched())) {
|
||||
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);
|
||||
// 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.
|
||||
RefPtr<PresShell> presShell = doc->GetPresShell();
|
||||
if (presShell && !presShell->IsDestroying()) {
|
||||
presShell->UnsuppressPainting();
|
||||
// Now unsuppress painting on the presshell, if we
|
||||
// haven't done that yet.
|
||||
RefPtr<PresShell> presShell = doc->GetPresShell();
|
||||
if (presShell && !presShell->IsDestroying()) {
|
||||
presShell->UnsuppressPainting();
|
||||
|
||||
if (!presShell->IsDestroying()) {
|
||||
presShell->LoadComplete();
|
||||
if (!presShell->IsDestroying()) {
|
||||
presShell->LoadComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +123,18 @@ class nsDocLoader : public nsIDocumentLoader,
|
||||
|
||||
void SetDocumentOpenedButNotLoaded() { mDocumentOpenedButNotLoaded = true; }
|
||||
|
||||
bool TreatAsBackgroundLoad();
|
||||
|
||||
void SetFakeOnLoadDispatched(){ mHasFakeOnLoadDispatched = true; };
|
||||
|
||||
bool HasFakeOnLoadDispatched(){ return mHasFakeOnLoadDispatched; };
|
||||
|
||||
void ResetToFirstLoad() {
|
||||
mHasFakeOnLoadDispatched = false;
|
||||
mIsReadyToHandlePostMessage = false;
|
||||
mTreatAsBackgroundLoad = false;
|
||||
};
|
||||
|
||||
protected:
|
||||
virtual ~nsDocLoader();
|
||||
|
||||
@ -130,6 +142,8 @@ class nsDocLoader : public nsIDocumentLoader,
|
||||
|
||||
bool IsBusy();
|
||||
|
||||
void SetBackgroundLoadIframe();
|
||||
|
||||
void Destroy();
|
||||
virtual void DestroyChildren();
|
||||
|
||||
@ -203,6 +217,12 @@ class nsDocLoader : public nsIDocumentLoader,
|
||||
DocLoaderIsEmpty(true);
|
||||
}
|
||||
|
||||
// DocLoaderIsEmpty should be called whenever the docloader may be empty.
|
||||
// This method is idempotent and does nothing if the docloader is not in
|
||||
// fact empty. This method _does_ make sure that layout is flushed if our
|
||||
// loadgroup has no active requests before checking for "real" emptiness if
|
||||
// aFlushLayout is true.
|
||||
void DocLoaderIsEmpty(bool aFlushLayout);
|
||||
protected:
|
||||
struct nsStatusInfo : public mozilla::LinkedListElement<nsStatusInfo> {
|
||||
nsString mStatusMessage;
|
||||
@ -297,7 +317,12 @@ class nsDocLoader : public nsIDocumentLoader,
|
||||
flushing. */
|
||||
bool mIsFlushingLayout;
|
||||
|
||||
bool mTreatAsBackgroundLoad;
|
||||
|
||||
private:
|
||||
bool mHasFakeOnLoadDispatched;
|
||||
|
||||
bool mIsReadyToHandlePostMessage;
|
||||
/**
|
||||
* This flag indicates that the loader is waiting for completion of
|
||||
* a document.open-triggered "document load". This is set when
|
||||
@ -315,13 +340,6 @@ class nsDocLoader : public nsIDocumentLoader,
|
||||
// loadgroup) unless this is empty.
|
||||
nsCOMArray<nsIDocumentLoader> mChildrenInOnload;
|
||||
|
||||
// DocLoaderIsEmpty should be called whenever the docloader may be empty.
|
||||
// This method is idempotent and does nothing if the docloader is not in
|
||||
// fact empty. This method _does_ make sure that layout is flushed if our
|
||||
// loadgroup has no active requests before checking for "real" emptiness if
|
||||
// aFlushLayout is true.
|
||||
void DocLoaderIsEmpty(bool aFlushLayout);
|
||||
|
||||
int64_t GetMaxTotalProgress();
|
||||
|
||||
nsresult AddRequestInfo(nsIRequest* aRequest);
|
||||
|
Loading…
Reference in New Issue
Block a user