Bug 1538364 - Fire the load/loadend/final readyState events for XHR later during page load (after page load event if possible), r=baku

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Olli Pettay 2019-03-26 20:04:47 +00:00
Родитель ffd99e653e
Коммит 9ca69947d4
6 изменённых файлов: 105 добавлений и 11 удалений

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

@ -2457,10 +2457,21 @@ void nsPIDOMWindowInner::SetAudioCapture(bool aCapture) {
}
}
void nsPIDOMWindowInner::SetActiveLoadingState(bool aIsLoading) /* const? */ {
void nsPIDOMWindowInner::SetActiveLoadingState(bool aIsLoading) {
if (!nsGlobalWindowInner::Cast(this)->IsChromeWindow()) {
mTimeoutManager->SetLoading(aIsLoading);
}
if (!aIsLoading) {
for (uint32_t i = 0; i < mAfterLoadRunners.Length(); ++i) {
NS_DispatchToCurrentThread(mAfterLoadRunners[i].forget());
}
mAfterLoadRunners.Clear();
}
}
void nsPIDOMWindowInner::AddAfterLoadRunner(nsIRunnable* aRunner) {
mAfterLoadRunners.AppendElement(aRunner);
}
// nsISpeechSynthesisGetter

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

@ -33,6 +33,7 @@ class nsICSSDeclaration;
class nsIDocShell;
class nsDocShellLoadState;
class nsIPrincipal;
class nsIRunnable;
class nsIScriptTimeoutHandler;
class nsISerialEventTarget;
class nsIURI;
@ -175,6 +176,7 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
// Note: not related to IsLoading. Set to false if there's an error, etc.
void SetActiveLoadingState(bool aIsActiveLoading);
void AddAfterLoadRunner(nsIRunnable* aRunner);
bool AddAudioContext(mozilla::dom::AudioContext* aAudioContext);
void RemoveAudioContext(mozilla::dom::AudioContext* aAudioContext);
@ -664,6 +666,8 @@ class nsPIDOMWindowInner : public mozIDOMWindow {
// This will be non-null during the full lifetime of the window, initialized
// during SetNewDocument, and cleared during FreeInnerObjects.
RefPtr<mozilla::dom::WindowGlobalChild> mWindowGlobalChild;
nsTArray<nsCOMPtr<nsIRunnable>> mAfterLoadRunners;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)

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

@ -43,7 +43,7 @@ already_AddRefed<XMLHttpRequest> XMLHttpRequest::Constructor(
}
RefPtr<XMLHttpRequestMainThread> req = new XMLHttpRequestMainThread();
req->Construct(principal->GetPrincipal(), global, cookieSettings);
req->Construct(principal->GetPrincipal(), global, cookieSettings, false);
req->InitParameters(aParams.mMozAnon, aParams.mMozSystem);
return req.forget();
}

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

@ -235,6 +235,8 @@ XMLHttpRequestMainThread::XMLHttpRequestMainThread()
}
XMLHttpRequestMainThread::~XMLHttpRequestMainThread() {
DisconnectDoneNotifier();
mFlagDeleted = true;
if ((mState == XMLHttpRequest_Binding::OPENED && mFlagSend) ||
@ -975,6 +977,7 @@ void XMLHttpRequestMainThread::Abort(ErrorResult& aRv) {
void XMLHttpRequestMainThread::AbortInternal(ErrorResult& aRv) {
mFlagAborted = true;
DisconnectDoneNotifier();
// Step 1
TerminateOngoingFetch();
@ -1456,6 +1459,7 @@ nsresult XMLHttpRequestMainThread::Open(const nsACString& aMethod,
// Step 11
// timeouts are handled without a flag
DisconnectDoneNotifier();
mFlagSend = false;
mRequestMethod.Assign(method);
mRequestURL = parsedURL;
@ -1691,7 +1695,7 @@ void XMLHttpRequestMainThread::LocalFileToBlobCompleted(Blob* aBlob) {
mBlobStorage = nullptr;
NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty");
ChangeStateToDone();
ChangeStateToDone(mFlagSyncLooping);
}
NS_IMETHODIMP
@ -2096,7 +2100,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
// If we were just reading a blob URL, we're already done
if (status == NS_ERROR_FILE_ALREADY_EXISTS && mResponseBlob) {
ChangeStateToDone();
ChangeStateToDone(mFlagSyncLooping);
return NS_OK;
}
@ -2182,6 +2186,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
mChannelEventSink = nullptr;
mProgressEventSink = nullptr;
bool wasSync = mFlagSyncLooping;
mFlagSyncLooping = false;
mRequestSentTime = 0;
@ -2210,7 +2215,7 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
// We postpone the 'done' until the creation of the Blob is completed.
if (!waitingForBlobCreation) {
ChangeStateToDone();
ChangeStateToDone(wasSync);
}
return NS_OK;
@ -2238,14 +2243,14 @@ XMLHttpRequestMainThread::OnStopRequest(nsIRequest* request, nsresult status) {
mErrorParsingXML = true;
mResponseXML = nullptr;
}
ChangeStateToDone();
ChangeStateToDone(wasSync);
return NS_OK;
}
void XMLHttpRequestMainThread::OnBodyParseEnd() {
mFlagParseBody = false;
mParseEndListener = nullptr;
ChangeStateToDone();
ChangeStateToDone(mFlagSyncLooping);
}
void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
@ -2258,8 +2263,48 @@ void XMLHttpRequestMainThread::MatchCharsetAndDecoderToResponseDocument() {
mDecoder = mResponseXML->GetDocumentCharacterSet()->NewDecoder();
}
}
void XMLHttpRequestMainThread::DisconnectDoneNotifier() {
if (mDelayedDoneNotifier) {
mDelayedDoneNotifier->Disconnect();
mDelayedDoneNotifier = nullptr;
}
}
void XMLHttpRequestMainThread::ChangeStateToDone() {
void XMLHttpRequestMainThread::ChangeStateToDone(bool aWasSync) {
DisconnectDoneNotifier();
if (!mForWorker && !aWasSync && mChannel) {
// If the top level page is loading, try to postpone the handling of the
// final events.
nsLoadFlags loadFlags = 0;
mChannel->GetLoadFlags(&loadFlags);
if (loadFlags & nsIRequest::LOAD_BACKGROUND) {
nsPIDOMWindowInner* owner = GetOwner();
Document* doc = owner ? owner->GetExtantDoc() : nullptr;
doc = doc ? doc->GetTopLevelContentDocument() : nullptr;
if (doc &&
(doc->GetReadyStateEnum() > Document::READYSTATE_UNINITIALIZED &&
doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE)) {
nsPIDOMWindowInner* topWin = doc->GetInnerWindow();
if (topWin) {
MOZ_ASSERT(!mDelayedDoneNotifier);
RefPtr<XMLHttpRequestDoneNotifier> notifier =
new XMLHttpRequestDoneNotifier(this);
mDelayedDoneNotifier = notifier;
topWin->AddAfterLoadRunner(notifier);
NS_DispatchToCurrentThreadQueue(notifier.forget(), 5000,
EventQueuePriority::Idle);
return;
}
}
}
}
ChangeStateToDoneInternal();
}
void XMLHttpRequestMainThread::ChangeStateToDoneInternal() {
DisconnectDoneNotifier();
StopProgressEventTimer();
MOZ_ASSERT(!mFlagParseBody,
@ -3594,7 +3639,7 @@ void XMLHttpRequestMainThread::BlobStoreCompleted(
mResponseBlob = aBlob;
mBlobStorage = nullptr;
ChangeStateToDone();
ChangeStateToDone(mFlagSyncLooping);
}
NS_IMETHODIMP

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

@ -157,6 +157,7 @@ class RequestHeaders {
};
class nsXHRParseEndListener;
class XMLHttpRequestDoneNotifier;
// Make sure that any non-DOM interfaces added here are also added to
// nsXMLHttpRequestXPCOMifier.
@ -171,6 +172,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
public MutableBlobStorageCallback {
friend class nsXHRParseEndListener;
friend class nsXMLHttpRequestXPCOMifier;
friend class XMLHttpRequestDoneNotifier;
public:
enum class ProgressEventType : uint8_t {
@ -199,7 +201,8 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
XMLHttpRequestMainThread();
void Construct(nsIPrincipal* aPrincipal, nsIGlobalObject* aGlobalObject,
nsICookieSettings* aCookieSettings, nsIURI* aBaseURI = nullptr,
nsICookieSettings* aCookieSettings, bool aForWorker,
nsIURI* aBaseURI = nullptr,
nsILoadGroup* aLoadGroup = nullptr,
PerformanceStorage* aPerformanceStorage = nullptr,
nsICSPEventListener* aCSPEventListener = nullptr) {
@ -209,6 +212,7 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
mBaseURI = aBaseURI;
mLoadGroup = aLoadGroup;
mCookieSettings = aCookieSettings;
mForWorker = aForWorker;
mPerformanceStorage = aPerformanceStorage;
mCSPEventListener = aCSPEventListener;
}
@ -458,7 +462,8 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
bool InUploadPhase() const;
void OnBodyParseEnd();
void ChangeStateToDone();
void ChangeStateToDone(bool aWasSync);
void ChangeStateToDoneInternal();
void StartProgressEventTimer();
void StopProgressEventTimer();
@ -604,6 +609,9 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
uint16_t mState;
// If true, this object is used by the worker's XMLHttpRequest.
bool mForWorker;
bool mFlagSynchronous;
bool mFlagAborted;
bool mFlagParseBody;
@ -714,6 +722,9 @@ class XMLHttpRequestMainThread final : public XMLHttpRequest,
// Our parse-end listener, if we are parsing.
RefPtr<nsXHRParseEndListener> mParseEndListener;
RefPtr<XMLHttpRequestDoneNotifier> mDelayedDoneNotifier;
void DisconnectDoneNotifier();
static bool sDontWarnAboutSyncXHR;
};
@ -770,6 +781,28 @@ class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
RefPtr<XMLHttpRequestMainThread> mXHR;
};
class XMLHttpRequestDoneNotifier : public Runnable
{
public:
explicit XMLHttpRequestDoneNotifier(XMLHttpRequestMainThread* aXHR)
: Runnable("XMLHttpRequestDoneNotifier"), mXHR(aXHR) {}
NS_IMETHOD Run() override {
if (mXHR) {
RefPtr<XMLHttpRequestMainThread> xhr = mXHR;
mXHR = nullptr;
xhr->ChangeStateToDoneInternal();
}
return NS_OK;
}
void Disconnect() { mXHR = nullptr; }
private:
XMLHttpRequestMainThread* mXHR;
};
class nsXHRParseEndListener : public nsIDOMEventListener {
public:
NS_DECL_ISUPPORTS

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

@ -780,6 +780,7 @@ bool Proxy::Init() {
mXHR->Construct(mWorkerPrivate->GetPrincipal(),
ownerWindow ? ownerWindow->AsGlobal() : nullptr,
mWorkerPrivate->CookieSettings(),
true,
mWorkerPrivate->GetBaseURI(), mWorkerPrivate->GetLoadGroup(),
mWorkerPrivate->GetPerformanceStorage(),
mWorkerPrivate->CSPEventListener());