WebKit Bugzilla
Attachment 358342 Details for
Bug 174980
: [Fetch API] Implement abortable fetch
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch for landing
bug-174980-20190104112139.patch (text/plain), 65.18 KB, created by
youenn fablet
on 2019-01-04 11:21:40 PST
(
hide
)
Description:
Patch for landing
Filename:
MIME Type:
Creator:
youenn fablet
Created:
2019-01-04 11:21:40 PST
Size:
65.18 KB
patch
obsolete
>Subversion Revision: 239617 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 9c6713e6cb81a05b27dd3a361c984d754082ef8b..528289df65c0681c24fdaab6b985033357ee1e4b 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,73 @@ >+2019-01-04 Youenn Fablet <youenn@apple.com> >+ >+ [Fetch API] Implement abortable fetch >+ https://bugs.webkit.org/show_bug.cgi?id=174980 >+ <rdar://problem/46861402> >+ >+ Reviewed by Chris Dumez. >+ >+ Add an AbortSignal to FetchRequest. >+ >+ Add support for AbortSignal algorithm. >+ The fetch request signal is added an algorithm to abort the fetch. >+ Update clone algorithm to let signal of the cloned request be following the origin request. >+ >+ Update ReadableStream error handling to return an exception instead of a string. >+ This allows passing an AbortError instead of a TypeError as previously done. >+ >+ Update FetchBodyOwner to store a loading error either as an exception or as a resource error. >+ The latter is used for passing the error from service worker back to the page. >+ The former is used to pass it to ReadableStream or body accessors. >+ >+ Covered by enabled tests. >+ >+ * Modules/cache/DOMCache.cpp: >+ (WebCore::DOMCache::put): >+ * Modules/fetch/FetchBody.cpp: >+ (WebCore::FetchBody::consumeAsStream): >+ (WebCore::FetchBody::loadingFailed): >+ * Modules/fetch/FetchBody.h: >+ * Modules/fetch/FetchBodyConsumer.cpp: >+ (WebCore::FetchBodyConsumer::loadingFailed): >+ * Modules/fetch/FetchBodyConsumer.h: >+ * Modules/fetch/FetchBodyOwner.cpp: >+ (WebCore::FetchBodyOwner::arrayBuffer): >+ (WebCore::FetchBodyOwner::blob): >+ (WebCore::FetchBodyOwner::cloneBody): >+ (WebCore::FetchBodyOwner::formData): >+ (WebCore::FetchBodyOwner::json): >+ (WebCore::FetchBodyOwner::text): >+ (WebCore::FetchBodyOwner::loadBlob): >+ (WebCore::FetchBodyOwner::blobLoadingFailed): >+ (WebCore::FetchBodyOwner::consumeBodyAsStream): >+ (WebCore::FetchBodyOwner::setLoadingError): >+ * Modules/fetch/FetchBodyOwner.h: >+ (WebCore::FetchBodyOwner::loadingError const): >+ (WebCore::FetchBodyOwner::loadingException const): >+ * Modules/fetch/FetchBodySource.cpp: >+ (WebCore::FetchBodySource::error): >+ * Modules/fetch/FetchBodySource.h: >+ * Modules/fetch/FetchRequest.cpp: >+ (WebCore::FetchRequest::initializeWith): >+ (WebCore::FetchRequest::clone): >+ * Modules/fetch/FetchRequest.h: >+ (WebCore::FetchRequest::FetchRequest): >+ * Modules/fetch/FetchRequest.idl: >+ * Modules/fetch/FetchRequestInit.h: >+ (WebCore::FetchRequestInit::hasMembers const): >+ * Modules/fetch/FetchRequestInit.idl: >+ * Modules/fetch/FetchResponse.cpp: >+ (WebCore::FetchResponse::clone): >+ (WebCore::FetchResponse::fetch): >+ (WebCore::FetchResponse::BodyLoader::didFail): >+ * Modules/fetch/FetchResponse.h: >+ * bindings/js/ReadableStreamDefaultController.h: >+ (WebCore::ReadableStreamDefaultController::error): >+ * dom/AbortSignal.cpp: >+ (WebCore::AbortSignal::abort): >+ (WebCore::AbortSignal::follow): >+ * dom/AbortSignal.h: >+ > 2019-01-04 Chris Fleizach <cfleizach@apple.com> > > AX: String check: "Rule" does not reflect the meaning of the <hr> html tag >diff --git a/Source/WebCore/Modules/cache/DOMCache.cpp b/Source/WebCore/Modules/cache/DOMCache.cpp >index 953a1adb46da1ca40164d5b2f3a64a9fafc76ea6..19ba1948bedba4a8358174df1c848fac0ee0e6fd 100644 >--- a/Source/WebCore/Modules/cache/DOMCache.cpp >+++ b/Source/WebCore/Modules/cache/DOMCache.cpp >@@ -321,8 +321,8 @@ void DOMCache::put(RequestInfo&& info, Ref<FetchResponse>&& response, DOMPromise > } > auto request = requestOrException.releaseReturnValue(); > >- if (response->loadingError()) { >- promise.reject(Exception { TypeError, response->loadingError()->localizedDescription() }); >+ if (auto exception = response->loadingException()) { >+ promise.reject(*exception); > return; > } > >diff --git a/Source/WebCore/Modules/fetch/FetchBody.cpp b/Source/WebCore/Modules/fetch/FetchBody.cpp >index da6d4d3687fa0dab9bc71242339c420f3cc7f049..bec22f47f407330f001c9c463c38b4a596dcacc9 100644 >--- a/Source/WebCore/Modules/fetch/FetchBody.cpp >+++ b/Source/WebCore/Modules/fetch/FetchBody.cpp >@@ -186,7 +186,7 @@ void FetchBody::consumeAsStream(FetchBodyOwner& owner, FetchBodySource& source) > owner.loadBlob(blobBody(), nullptr); > m_data = nullptr; > } else if (isFormData()) >- source.error("not implemented"_s); >+ source.error(Exception { NotSupportedError, "Not implemented"_s }); > else if (m_consumer.hasData()) > closeStream = source.enqueue(m_consumer.takeAsArrayBuffer()); > else >@@ -224,9 +224,9 @@ void FetchBody::consumeBlob(FetchBodyOwner& owner, Ref<DeferredPromise>&& promis > m_data = nullptr; > } > >-void FetchBody::loadingFailed() >+void FetchBody::loadingFailed(const Exception& exception) > { >- m_consumer.loadingFailed(); >+ m_consumer.loadingFailed(exception); > } > > void FetchBody::loadingSucceeded() >diff --git a/Source/WebCore/Modules/fetch/FetchBody.h b/Source/WebCore/Modules/fetch/FetchBody.h >index 816c63c98b15cefd904ecab9c703fa427700e146..a484f9d8f217e3994022965e1fc267c6c502a0c1 100644 >--- a/Source/WebCore/Modules/fetch/FetchBody.h >+++ b/Source/WebCore/Modules/fetch/FetchBody.h >@@ -60,7 +60,7 @@ public: > > WEBCORE_EXPORT static Optional<FetchBody> fromFormData(FormData&); > >- void loadingFailed(); >+ void loadingFailed(const Exception&); > void loadingSucceeded(); > > RefPtr<FormData> bodyAsFormData(ScriptExecutionContext&) const; >diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp >index 00f173edd289b43f87fba64feda614f99afab942..302dff60adafeff7631dfb89648b616818cb9cb6 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp >+++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.cpp >@@ -212,15 +212,15 @@ void FetchBodyConsumer::setSource(Ref<FetchBodySource>&& source) > } > } > >-void FetchBodyConsumer::loadingFailed() >+void FetchBodyConsumer::loadingFailed(const Exception& exception) > { > m_isLoading = false; > if (m_consumePromise) { >- m_consumePromise->reject(); >+ m_consumePromise->reject(exception); > m_consumePromise = nullptr; > } > if (m_source) { >- m_source->error("Loading failed"_s); >+ m_source->error(exception); > m_source = nullptr; > } > } >diff --git a/Source/WebCore/Modules/fetch/FetchBodyConsumer.h b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h >index b467db66eaafdea2dfcb9ffcf5dd883ea72129dc..1acfe0093e5085f93759c5057a7bfbde5e0fb907 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodyConsumer.h >+++ b/Source/WebCore/Modules/fetch/FetchBodyConsumer.h >@@ -66,7 +66,7 @@ public: > void resolve(Ref<DeferredPromise>&&, ReadableStream*); > void resolveWithData(Ref<DeferredPromise>&&, const unsigned char*, unsigned); > >- void loadingFailed(); >+ void loadingFailed(const Exception&); > void loadingSucceeded(); > > void setConsumePromise(Ref<DeferredPromise>&&); >diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp >index 85dc88bd7f6bf09a8d57f3cbd73d160c59b6fcbd..017a4f519b85226144a2d50b2be45e8fc4d62044 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp >+++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.cpp >@@ -99,6 +99,11 @@ bool FetchBodyOwner::isDisturbedOrLocked() const > > void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise) > { >+ if (auto exception = loadingException()) { >+ promise->reject(*exception); >+ return; >+ } >+ > if (isBodyNullOrOpaque()) { > fulfillPromiseWithArrayBuffer(WTFMove(promise), nullptr, 0); > return; >@@ -113,6 +118,11 @@ void FetchBodyOwner::arrayBuffer(Ref<DeferredPromise>&& promise) > > void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise) > { >+ if (auto exception = loadingException()) { >+ promise->reject(*exception); >+ return; >+ } >+ > if (isBodyNullOrOpaque()) { > promise->resolve<IDLInterface<Blob>>(Blob::create(Vector<uint8_t> { }, Blob::normalizedContentType(extractMIMETypeFromMediaType(m_contentType)))); > return; >@@ -127,6 +137,8 @@ void FetchBodyOwner::blob(Ref<DeferredPromise>&& promise) > > void FetchBodyOwner::cloneBody(FetchBodyOwner& owner) > { >+ m_loadingError = owner.m_loadingError; >+ > m_contentType = owner.m_contentType; > if (owner.isBodyNull()) > return; >@@ -161,6 +173,11 @@ void FetchBodyOwner::consumeOnceLoadingFinished(FetchBodyConsumer::Type type, Re > > void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise) > { >+ if (auto exception = loadingException()) { >+ promise->reject(*exception); >+ return; >+ } >+ > if (isBodyNullOrOpaque()) { > promise->reject(); > return; >@@ -175,6 +192,11 @@ void FetchBodyOwner::formData(Ref<DeferredPromise>&& promise) > > void FetchBodyOwner::json(Ref<DeferredPromise>&& promise) > { >+ if (auto exception = loadingException()) { >+ promise->reject(*exception); >+ return; >+ } >+ > if (isBodyNullOrOpaque()) { > promise->reject(SyntaxError); > return; >@@ -189,6 +211,11 @@ void FetchBodyOwner::json(Ref<DeferredPromise>&& promise) > > void FetchBodyOwner::text(Ref<DeferredPromise>&& promise) > { >+ if (auto exception = loadingException()) { >+ promise->reject(*exception); >+ return; >+ } >+ > if (isBodyNullOrOpaque()) { > promise->resolve<IDLDOMString>({ }); > return; >@@ -208,7 +235,7 @@ void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer) > ASSERT(!isBodyNull()); > > if (!scriptExecutionContext()) { >- m_body->loadingFailed(); >+ m_body->loadingFailed(Exception { TypeError, "Blob loading failed"_s}); > return; > } > >@@ -217,7 +244,7 @@ void FetchBodyOwner::loadBlob(const Blob& blob, FetchBodyConsumer* consumer) > > m_blobLoader->loader->start(*scriptExecutionContext(), blob); > if (!m_blobLoader->loader->isStarted()) { >- m_body->loadingFailed(); >+ m_body->loadingFailed(Exception { TypeError, "Blob loading failed"_s}); > m_blobLoader = WTF::nullopt; > return; > } >@@ -251,11 +278,11 @@ void FetchBodyOwner::blobLoadingFailed() > #if ENABLE(STREAMS_API) > if (m_readableStreamSource) { > if (!m_readableStreamSource->isCancelling()) >- m_readableStreamSource->error("Blob loading failed"_s); >+ m_readableStreamSource->error(Exception { TypeError, "Blob loading failed"_s}); > m_readableStreamSource = nullptr; > } else > #endif >- m_body->loadingFailed(); >+ m_body->loadingFailed(Exception { TypeError, "Blob loading failed"_s}); > > finishBlobLoading(); > } >@@ -318,9 +345,8 @@ void FetchBodyOwner::consumeBodyAsStream() > { > ASSERT(m_readableStreamSource); > >- if (m_loadingError) { >- auto errorMessage = m_loadingError->localizedDescription(); >- m_readableStreamSource->error(errorMessage.isEmpty() ? "Loading failed"_s : errorMessage); >+ if (auto exception = loadingException()) { >+ m_readableStreamSource->error(*exception); > return; > } > >@@ -329,4 +355,53 @@ void FetchBodyOwner::consumeBodyAsStream() > m_readableStreamSource = nullptr; > } > >+ResourceError FetchBodyOwner::loadingError() const >+{ >+ return WTF::switchOn(m_loadingError, [](const ResourceError& error) { >+ return ResourceError { error }; >+ }, [](const Exception& exception) { >+ return ResourceError { errorDomainWebKitInternal, 0, { }, exception.message() }; >+ }, [](auto&&) { >+ return ResourceError { }; >+ }); >+} >+ >+Optional<Exception> FetchBodyOwner::loadingException() const >+{ >+ return WTF::switchOn(m_loadingError, [](const ResourceError& error) { >+ return Exception { TypeError, error.localizedDescription().isEmpty() ? "Loading failed"_s : error.localizedDescription() }; >+ }, [](const Exception& exception) { >+ return Exception { exception }; >+ }, [](auto&&) -> Optional<Exception> { >+ return WTF::nullopt; >+ }); >+} >+ >+bool FetchBodyOwner::hasLoadingError() const >+{ >+ return WTF::switchOn(m_loadingError, [](const ResourceError&) { >+ return true; >+ }, [](const Exception&) { >+ return true; >+ }, [](auto&&) { >+ return false; >+ }); >+} >+ >+void FetchBodyOwner::setLoadingError(Exception&& exception) >+{ >+ if (hasLoadingError()) >+ return; >+ >+ m_loadingError = WTFMove(exception); >+} >+ >+void FetchBodyOwner::setLoadingError(ResourceError&& error) >+{ >+ if (hasLoadingError()) >+ return; >+ >+ m_loadingError = WTFMove(error); >+} >+ > } // namespace WebCore >diff --git a/Source/WebCore/Modules/fetch/FetchBodyOwner.h b/Source/WebCore/Modules/fetch/FetchBodyOwner.h >index 45cc67e6a8eae08b26e9d055f9e4d459d35f66e2..4872390ff868bf907441bf466113332aa9365aa7 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodyOwner.h >+++ b/Source/WebCore/Modules/fetch/FetchBodyOwner.h >@@ -66,6 +66,10 @@ public: > virtual void cancel() { } > #endif > >+ bool hasLoadingError() const; >+ ResourceError loadingError() const; >+ Optional<Exception> loadingException() const; >+ > protected: > const FetchBody& body() const { return *m_body; } > FetchBody& body() { return *m_body; } >@@ -88,6 +92,9 @@ protected: > void setBodyAsOpaque() { m_isBodyOpaque = true; } > bool isBodyOpaque() const { return m_isBodyOpaque; } > >+ void setLoadingError(Exception&&); >+ void setLoadingError(ResourceError&&); >+ > private: > // Blob loading routines > void blobChunk(const char*, size_t); >@@ -116,11 +123,12 @@ protected: > RefPtr<FetchBodySource> m_readableStreamSource; > #endif > Ref<FetchHeaders> m_headers; >- Optional<ResourceError> m_loadingError; > > private: > Optional<BlobLoader> m_blobLoader; > bool m_isBodyOpaque { false }; >+ >+ Variant<std::nullptr_t, Exception, ResourceError> m_loadingError; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/Modules/fetch/FetchBodySource.cpp b/Source/WebCore/Modules/fetch/FetchBodySource.cpp >index a28581485f9b0caaca65fc4d0ad9248d443c5217..6699ef4e44528548ebfa0ca4416f8cd5958c2295 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodySource.cpp >+++ b/Source/WebCore/Modules/fetch/FetchBodySource.cpp >@@ -88,7 +88,7 @@ void FetchBodySource::close() > m_bodyOwner = nullptr; > } > >-void FetchBodySource::error(const String& value) >+void FetchBodySource::error(const Exception& value) > { > controller().error(value); > clean(); >diff --git a/Source/WebCore/Modules/fetch/FetchBodySource.h b/Source/WebCore/Modules/fetch/FetchBodySource.h >index c10fe96e63ecddf21e3fbec2472eafc0de303cfc..f0e61afcb7e43f7f8bb27d1afe00308d95abd4b3 100644 >--- a/Source/WebCore/Modules/fetch/FetchBodySource.h >+++ b/Source/WebCore/Modules/fetch/FetchBodySource.h >@@ -44,7 +44,7 @@ public: > > bool enqueue(RefPtr<JSC::ArrayBuffer>&& chunk) { return controller().enqueue(WTFMove(chunk)); } > void close(); >- void error(const String&); >+ void error(const Exception&); > > bool isCancelling() const { return m_isCancelling; } > >diff --git a/Source/WebCore/Modules/fetch/FetchRequest.cpp b/Source/WebCore/Modules/fetch/FetchRequest.cpp >index 944451b86b9c5511f09e2574f97d83d26d05050d..c0790bc2a184f14604d3d44fc0f96ec172de9239 100644 >--- a/Source/WebCore/Modules/fetch/FetchRequest.cpp >+++ b/Source/WebCore/Modules/fetch/FetchRequest.cpp >@@ -159,6 +159,9 @@ ExceptionOr<void> FetchRequest::initializeWith(const String& url, Init&& init) > if (optionsResult.hasException()) > return optionsResult.releaseException(); > >+ if (init.signal && init.signal.value()) >+ m_signal->follow(*init.signal.value()); >+ > if (init.headers) { > auto fillResult = m_headers->fill(*init.headers); > if (fillResult.hasException()) >@@ -188,6 +191,12 @@ ExceptionOr<void> FetchRequest::initializeWith(FetchRequest& input, Init&& init) > if (optionsResult.hasException()) > return optionsResult.releaseException(); > >+ if (init.signal) { >+ if (init.signal.value()) >+ m_signal->follow(*init.signal.value()); >+ } else >+ m_signal->follow(input.m_signal); >+ > if (init.headers) { > auto fillResult = m_headers->fill(*init.headers); > if (fillResult.hasException()) >@@ -293,6 +302,7 @@ ExceptionOr<Ref<FetchRequest>> FetchRequest::clone(ScriptExecutionContext& conte > > auto clone = adoptRef(*new FetchRequest(context, WTF::nullopt, FetchHeaders::create(m_headers.get()), ResourceRequest { m_request }, FetchOptions { m_options}, String { m_referrer })); > clone->cloneBody(*this); >+ clone->m_signal->follow(m_signal); > return WTFMove(clone); > } > >diff --git a/Source/WebCore/Modules/fetch/FetchRequest.h b/Source/WebCore/Modules/fetch/FetchRequest.h >index ccdbf86ab3884c3cf98fb51f34990a5d40128b69..e85d1347cf0747c37f05c7b52420c7eab4aa1a2b 100644 >--- a/Source/WebCore/Modules/fetch/FetchRequest.h >+++ b/Source/WebCore/Modules/fetch/FetchRequest.h >@@ -28,6 +28,7 @@ > > #pragma once > >+#include "AbortSignal.h" > #include "ExceptionOr.h" > #include "FetchBodyOwner.h" > #include "FetchOptions.h" >@@ -68,6 +69,7 @@ public: > Cache cache() const { return m_options.cache; } > Redirect redirect() const { return m_options.redirect; } > bool keepalive() const { return m_options.keepAlive; }; >+ AbortSignal& signal() { return m_signal.get(); } > > const String& integrity() const { return m_options.integrity; } > >@@ -96,6 +98,7 @@ private: > FetchOptions m_options; > String m_referrer; > mutable String m_requestURL; >+ Ref<AbortSignal> m_signal; > }; > > inline FetchRequest::FetchRequest(ScriptExecutionContext& context, Optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceRequest&& request, FetchOptions&& options, String&& referrer) >@@ -103,6 +106,7 @@ inline FetchRequest::FetchRequest(ScriptExecutionContext& context, Optional<Fetc > , m_request(WTFMove(request)) > , m_options(WTFMove(options)) > , m_referrer(WTFMove(referrer)) >+ , m_signal(AbortSignal::create(context)) > { > updateContentType(); > } >diff --git a/Source/WebCore/Modules/fetch/FetchRequest.idl b/Source/WebCore/Modules/fetch/FetchRequest.idl >index 80a41f8fa96e420a58f2acc410ded218eee04745..dd2ec8f52cb05877ba48b3e2134540c0eab17e69 100644 >--- a/Source/WebCore/Modules/fetch/FetchRequest.idl >+++ b/Source/WebCore/Modules/fetch/FetchRequest.idl >@@ -55,6 +55,7 @@ typedef (Blob or BufferSource or DOMFormData or URLSearchParams or ReadableStrea > readonly attribute FetchRequestRedirect redirect; > readonly attribute DOMString integrity; > [EnabledAtRuntime=FetchAPIKeepAlive] readonly attribute boolean keepalive; >+ readonly attribute AbortSignal signal; > > [CallWith=ScriptExecutionContext, MayThrowException, NewObject] FetchRequest clone(); > }; >diff --git a/Source/WebCore/Modules/fetch/FetchRequestInit.h b/Source/WebCore/Modules/fetch/FetchRequestInit.h >index d2cb809564c21ae6ee7a48505cfdc6e6489939c2..a8d374b53060960c1472845f2394822027af9e37 100644 >--- a/Source/WebCore/Modules/fetch/FetchRequestInit.h >+++ b/Source/WebCore/Modules/fetch/FetchRequestInit.h >@@ -25,6 +25,7 @@ > > #pragma once > >+#include "AbortSignal.h" > #include "FetchBody.h" > #include "FetchHeaders.h" > #include "FetchOptions.h" >@@ -46,9 +47,10 @@ struct FetchRequestInit { > Optional<FetchOptions::Redirect> redirect; > String integrity; > Optional<bool> keepalive; >+ Optional<AbortSignal*> signal; > JSC::JSValue window; > >- bool hasMembers() const { return !method.isEmpty() || headers || body || !referrer.isEmpty() || referrerPolicy || mode || credentials || cache || redirect || !integrity.isEmpty() || keepalive || !window.isUndefined(); } >+ bool hasMembers() const { return !method.isEmpty() || headers || body || !referrer.isEmpty() || referrerPolicy || mode || credentials || cache || redirect || !integrity.isEmpty() || keepalive || !window.isUndefined() || signal; } > }; > > } >diff --git a/Source/WebCore/Modules/fetch/FetchRequestInit.idl b/Source/WebCore/Modules/fetch/FetchRequestInit.idl >index d007bddbf79b75ddf7ca2b219c7e4d40aa0a1813..cc7ca55e2c28a40567a49884bd3228c94162ed97 100644 >--- a/Source/WebCore/Modules/fetch/FetchRequestInit.idl >+++ b/Source/WebCore/Modules/fetch/FetchRequestInit.idl >@@ -39,5 +39,6 @@ dictionary FetchRequestInit { > FetchRequestRedirect redirect; > DOMString integrity; > boolean keepalive; >+ AbortSignal? signal; > any window; // can only be set to null > }; >diff --git a/Source/WebCore/Modules/fetch/FetchResponse.cpp b/Source/WebCore/Modules/fetch/FetchResponse.cpp >index 61fe682f6366c167a36ae5e1d41b7b95f94447f3..8dafc645025a02f036254aa31c1bda37ece29aed 100644 >--- a/Source/WebCore/Modules/fetch/FetchResponse.cpp >+++ b/Source/WebCore/Modules/fetch/FetchResponse.cpp >@@ -179,24 +179,62 @@ ExceptionOr<Ref<FetchResponse>> FetchResponse::clone(ScriptExecutionContext& con > m_internalResponse.setHTTPHeaderFields(HTTPHeaderMap { headers().internalHeaders() }); > > auto clone = FetchResponse::create(context, WTF::nullopt, headers().guard(), ResourceResponse { m_internalResponse }); >- clone->m_loadingError = m_loadingError; > clone->cloneBody(*this); > clone->m_opaqueLoadIdentifier = m_opaqueLoadIdentifier; > clone->m_bodySizeWithPadding = m_bodySizeWithPadding; > return WTFMove(clone); > } > >+void FetchResponse::addAbortSteps(Ref<AbortSignal>&& signal) >+{ >+ m_abortSignal = WTFMove(signal); >+ m_abortSignal->addAlgorithm([this, weakThis = makeWeakPtr(this)] { >+ // FIXME: Cancel request body if it is a stream. >+ if (!weakThis) >+ return; >+ >+ m_abortSignal = nullptr; >+ >+ setLoadingError(Exception { AbortError, "Fetch is aborted"_s }); >+ >+ if (m_bodyLoader) { >+ if (auto callback = m_bodyLoader->takeNotificationCallback()) >+ callback(Exception { AbortError, "Fetch is aborted"_s }); >+ } >+ >+ if (m_readableStreamSource) { >+ if (!m_readableStreamSource->isCancelling()) >+ m_readableStreamSource->error(*loadingException()); >+ m_readableStreamSource = nullptr; >+ } >+ if (m_body) >+ m_body->loadingFailed(*loadingException()); >+ >+ if (m_bodyLoader) { >+ m_bodyLoader->stop(); >+ m_bodyLoader = WTF::nullopt; >+ } >+ }); >+} >+ > void FetchResponse::fetch(ScriptExecutionContext& context, FetchRequest& request, NotificationCallback&& responseCallback) > { >+ if (request.signal().aborted()) { >+ responseCallback(Exception { AbortError, "Request signal is aborted"_s }); >+ // FIXME: Cancel request body if it is a stream. >+ return; >+ } >+ > if (request.hasReadableStreamBody()) { >- if (responseCallback) >- responseCallback(Exception { NotSupportedError, "ReadableStream uploading is not supported" }); >+ responseCallback(Exception { NotSupportedError, "ReadableStream uploading is not supported"_s }); > return; > } > auto response = adoptRef(*new FetchResponse(context, FetchBody { }, FetchHeaders::create(FetchHeaders::Guard::Immutable), { })); > > response->body().consumer().setAsLoading(); > >+ response->addAbortSteps(request.signal()); >+ > response->m_bodyLoader.emplace(response.get(), WTFMove(responseCallback)); > if (!response->m_bodyLoader->start(context, request)) > response->m_bodyLoader = WTF::nullopt; >@@ -245,7 +283,7 @@ void FetchResponse::BodyLoader::didFail(const ResourceError& error) > { > ASSERT(m_response.hasPendingActivity()); > >- m_response.m_loadingError = error; >+ m_response.setLoadingError(ResourceError { error }); > > if (auto responseCallback = WTFMove(m_responseCallback)) > responseCallback(Exception { TypeError, error.localizedDescription() }); >@@ -256,7 +294,7 @@ void FetchResponse::BodyLoader::didFail(const ResourceError& error) > #if ENABLE(STREAMS_API) > if (m_response.m_readableStreamSource) { > if (!m_response.m_readableStreamSource->isCancelling()) >- m_response.m_readableStreamSource->error(makeString("Loading failed: "_s, error.localizedDescription())); >+ m_response.m_readableStreamSource->error(*m_response.loadingException()); > m_response.m_readableStreamSource = nullptr; > } > #endif >diff --git a/Source/WebCore/Modules/fetch/FetchResponse.h b/Source/WebCore/Modules/fetch/FetchResponse.h >index 97f7d3b0b811dfe25d4261eefdf9ab38ce299638..9756d5c3d02527c5a3a27e26a6d619799628baaa 100644 >--- a/Source/WebCore/Modules/fetch/FetchResponse.h >+++ b/Source/WebCore/Modules/fetch/FetchResponse.h >@@ -33,6 +33,7 @@ > #include "ReadableStreamSink.h" > #include "ResourceResponse.h" > #include <JavaScriptCore/TypedArrays.h> >+#include <wtf/WeakPtr.h> > > namespace JSC { > class ExecState; >@@ -41,11 +42,12 @@ class JSValue; > > namespace WebCore { > >+class AbortSignal; > class FetchRequest; > struct ReadableStreamChunk; > class ReadableStreamSource; > >-class FetchResponse final : public FetchBodyOwner { >+class FetchResponse final : public FetchBodyOwner, public CanMakeWeakPtr<FetchResponse> { > public: > using Type = ResourceResponse::Type; > >@@ -107,8 +109,6 @@ public: > > void initializeOpaqueLoadIdentifierForTesting() { m_opaqueLoadIdentifier = 1; } > >- const Optional<ResourceError>& loadingError() const { return m_loadingError; } >- > const HTTPHeaderMap& internalResponseHeaders() const { return m_internalResponse.httpHeaderFields(); } > > private: >@@ -124,6 +124,8 @@ private: > void closeStream(); > #endif > >+ void addAbortSteps(Ref<AbortSignal>&&); >+ > class BodyLoader final : public FetchLoaderClient { > public: > BodyLoader(FetchResponse&, NotificationCallback&&); >@@ -137,6 +139,7 @@ private: > #if ENABLE(STREAMS_API) > RefPtr<SharedBuffer> startStreaming(); > #endif >+ NotificationCallback takeNotificationCallback() { return WTFMove(m_responseCallback); } > > private: > // FetchLoaderClient API >@@ -158,6 +161,7 @@ private: > // Opaque responses will padd their body size when used with Cache API. > uint64_t m_bodySizeWithPadding { 0 }; > uint64_t m_opaqueLoadIdentifier { 0 }; >+ RefPtr<AbortSignal> m_abortSignal; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/bindings/js/ReadableStreamDefaultController.h b/Source/WebCore/bindings/js/ReadableStreamDefaultController.h >index 484c8d81e7bc61aae396bac58ca6c234cbfd99ca..a7e4e7abfa925f07778b97bca58711a91f41ac24 100644 >--- a/Source/WebCore/bindings/js/ReadableStreamDefaultController.h >+++ b/Source/WebCore/bindings/js/ReadableStreamDefaultController.h >@@ -49,8 +49,7 @@ public: > > bool enqueue(RefPtr<JSC::ArrayBuffer>&&); > >- template<class ResolveResultType> >- void error(const ResolveResultType&); >+ void error(const Exception&); > > void close() { invoke(*globalObject().globalExec(), jsController(), "close", JSC::jsUndefined()); } > >@@ -104,12 +103,11 @@ inline bool ReadableStreamDefaultController::enqueue(RefPtr<JSC::ArrayBuffer>&& > return true; > } > >-template<> >-inline void ReadableStreamDefaultController::error<String>(const String& errorMessage) >+inline void ReadableStreamDefaultController::error(const Exception& exception) > { > JSC::ExecState& state = globalExec(); > JSC::JSLockHolder locker(&state); >- error(state, JSC::createTypeError(&state, errorMessage)); >+ error(state, createDOMException(&state, exception.code(), exception.message())); > } > > } // namespace WebCore >diff --git a/Source/WebCore/dom/AbortSignal.cpp b/Source/WebCore/dom/AbortSignal.cpp >index 512ecae3c56c9a16e52f25c0eeecb9cc0cfd8e8f..cd351fb8d12fe7a2347839954bdf8801c221a684 100644 >--- a/Source/WebCore/dom/AbortSignal.cpp >+++ b/Source/WebCore/dom/AbortSignal.cpp >@@ -37,7 +37,6 @@ Ref<AbortSignal> AbortSignal::create(ScriptExecutionContext& context) > return adoptRef(*new AbortSignal(context)); > } > >- > AbortSignal::AbortSignal(ScriptExecutionContext& context) > : ContextDestructionObserver(&context) > { >@@ -52,13 +51,32 @@ void AbortSignal::abort() > > // 2. Set signalâs aborted flag. > m_aborted = true; >- >- // 3. For each algorithm in signal's abort algorithms: run algorithm. >- // 4. Empty signal's abort algorithms. >- // FIXME: Add support for 'abort algorithms' - https://dom.spec.whatwg.org/#abortsignal-abort-algorithms >+ >+ auto protectedThis = makeRef(*this); >+ auto algorithms = WTFMove(m_algorithms); >+ for (auto& algorithm : algorithms) >+ algorithm(); > > // 5. Fire an event named abort at signal. > dispatchEvent(Event::create(eventNames().abortEvent, Event::CanBubble::No, Event::IsCancelable::No)); > } > >+// https://dom.spec.whatwg.org/#abortsignal-follow >+void AbortSignal::follow(AbortSignal& signal) >+{ >+ if (aborted()) >+ return; >+ >+ if (signal.aborted()) { >+ abort(); >+ return; >+ } >+ >+ signal.addAlgorithm([weakThis = makeWeakPtr(this)] { >+ if (!weakThis) >+ return; >+ weakThis->abort(); >+ }); >+} >+ > } >diff --git a/Source/WebCore/dom/AbortSignal.h b/Source/WebCore/dom/AbortSignal.h >index d3140acc2d8291454636a903c4be573981b1cbc1..a89be55f0b2e73d1206e13283dfb958800e6bfa1 100644 >--- a/Source/WebCore/dom/AbortSignal.h >+++ b/Source/WebCore/dom/AbortSignal.h >@@ -27,14 +27,16 @@ > > #include "ContextDestructionObserver.h" > #include "EventTarget.h" >+#include <wtf/Function.h> > #include <wtf/Ref.h> > #include <wtf/RefCounted.h> >+#include <wtf/WeakPtr.h> > > namespace WebCore { > > class ScriptExecutionContext; > >-class AbortSignal final : public RefCounted<AbortSignal>, public EventTargetWithInlineData, private ContextDestructionObserver { >+class AbortSignal final : public RefCounted<AbortSignal>, public EventTargetWithInlineData, public CanMakeWeakPtr<AbortSignal>, private ContextDestructionObserver { > public: > static Ref<AbortSignal> create(ScriptExecutionContext&); > >@@ -45,6 +47,11 @@ public: > using RefCounted::ref; > using RefCounted::deref; > >+ using Algorithm = WTF::Function<void()>; >+ void addAlgorithm(Algorithm&& algorithm) { m_algorithms.append(WTFMove(algorithm)); } >+ >+ void follow(AbortSignal&); >+ > private: > explicit AbortSignal(ScriptExecutionContext&); > >@@ -54,6 +61,7 @@ private: > void derefEventTarget() final { deref(); } > > bool m_aborted { false }; >+ Vector<Algorithm> m_algorithms; > }; > > } >diff --git a/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp b/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp >index 0e3649249827b14c35a485e95a481dcfa039a808..f62959d80da526c56e41c2dc7687ba316368f8e6 100644 >--- a/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp >+++ b/Source/WebCore/workers/service/context/ServiceWorkerFetch.cpp >@@ -53,8 +53,9 @@ static void processResponse(Ref<Client>&& client, Expected<Ref<FetchResponse>, R > > client->didReceiveResponse(response->resourceResponse()); > >- if (response->loadingError()) { >- client->didFail(*response->loadingError()); >+ auto loadingError = response->loadingError(); >+ if (!loadingError.isNull()) { >+ client->didFail(loadingError); > return; > } > >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 6c934d69ca102d357ac6e6648f09e4b7166575bb..e1a6f0d0d0d16c27415a52064109418baec8a584 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,13 @@ >+2019-01-04 Youenn Fablet <youenn@apple.com> >+ >+ [Fetch API] Implement abortable fetch >+ https://bugs.webkit.org/show_bug.cgi?id=174980 >+ <rdar://problem/46861402> >+ >+ Reviewed by Chris Dumez. >+ >+ * TestExpectations: Enable abort tests. >+ > 2019-01-04 Chris Fleizach <cfleizach@apple.com> > > AX: String check: "Rule" does not reflect the meaning of the <hr> html tag >diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog >index 6a43ef63a38c01ff956aa50713ad65484e05eff5..ce47ba8fcf13128e2c15b68bb509a55da53a28f8 100644 >--- a/LayoutTests/imported/w3c/ChangeLog >+++ b/LayoutTests/imported/w3c/ChangeLog >@@ -1,3 +1,22 @@ >+2019-01-04 Youenn Fablet <youenn@apple.com> >+ >+ [Fetch API] Implement abortable fetch >+ https://bugs.webkit.org/show_bug.cgi?id=174980 >+ <rdar://problem/46861402> >+ >+ Reviewed by Chris Dumez. >+ >+ Fixed tests to run in WebKit CI. >+ Also fixed a bug in a test where the fetch response body is not actually empty. >+ >+ * web-platform-tests/fetch/api/abort/cache.https-expected.txt: >+ * web-platform-tests/fetch/api/abort/general-serviceworker.https-expected.txt: >+ * web-platform-tests/fetch/api/abort/general.any-expected.txt: >+ * web-platform-tests/fetch/api/abort/general.any.js: >+ * web-platform-tests/fetch/api/abort/general.any.worker-expected.txt: >+ * web-platform-tests/fetch/api/abort/serviceworker-intercepted.https-expected.txt: >+ * web-platform-tests/fetch/api/response/response-consume-stream-expected.txt: >+ > 2019-01-02 Simon Fraser <simon.fraser@apple.com> > > Support css-color-4 rgb functions >diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations >index 23018bc2921d41600b98bb6cef164b9c74ad7665..4d5c642b9a2b70e5c26b0e6af498f4465f17876c 100644 >--- a/LayoutTests/TestExpectations >+++ b/LayoutTests/TestExpectations >@@ -211,7 +211,6 @@ imported/w3c/web-platform-tests/service-workers/service-worker/worker-client-id. > imported/w3c/web-platform-tests/cors/remote-origin.htm [ Skip ] > > # Skip service worker tests that are timing out. >-imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https.html [ Skip ] > imported/w3c/web-platform-tests/service-workers/service-worker/performance-timeline.https.html [ Skip ] > imported/w3c/web-platform-tests/service-workers/service-worker/respond-with-body-accessed-response.https.html [ Skip ] > imported/w3c/web-platform-tests/service-workers/service-worker/sandboxed-iframe-fetch-event.https.html [ Skip ] >@@ -383,10 +382,6 @@ webkit.org/b/189905 imported/w3c/web-platform-tests/resource-timing/resource_ini > webkit.org/b/189910 imported/w3c/web-platform-tests/resource-timing/resource_timing_store_and_clear_during_callback.html [ Pass Failure ] > webkit.org/b/190523 imported/w3c/web-platform-tests/resource-timing/resource_timing_cross_origin_redirect_chain.html [ Pass Failure ] > >-# The follow two tests change their output each run >-imported/w3c/web-platform-tests/fetch/api/abort/general.any.html [ Skip ] >-imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker.html [ Skip ] >- > # These tests time out > imported/w3c/web-platform-tests/fetch/api/request/destination/fetch-destination-no-load-event.https.html [ Skip ] > imported/w3c/web-platform-tests/fetch/api/request/destination/fetch-destination.https.html [ Skip ] >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/cache.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/cache.https-expected.txt >index 6a9770883c98c96ebef529e8c409f50c5a090f09..08a1867cba2b2ffcd1a6de6f348cbea402841966 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/cache.https-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/cache.https-expected.txt >@@ -1,4 +1,4 @@ > >-FAIL Signals are not stored in the cache API promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'cachedRequest.signal.aborted')" >-FAIL Signals are not stored in the cache API, even if they're already aborted promise_test: Unhandled rejection with value: object "TypeError: undefined is not an object (evaluating 'cachedRequest.signal.aborted')" >+PASS Signals are not stored in the cache API >+PASS Signals are not stored in the cache API, even if they're already aborted > >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https-expected.txt >index 9ee4d9c1b7352c0c4b1fa15db47840d7657e5deb..471bc341bfcdcf31c0149b15c89763da66f44999 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general-serviceworker.https-expected.txt >@@ -1,8 +1,6 @@ > >-Harness Error (TIMEOUT), message = null >- >-FAIL Aborting rejects with AbortError assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Aborting rejects with AbortError - no-cors assert_throws: function "function () { throw e }" threw object "TypeError: A server with the specified hostname could not be found." that is not a DOMException AbortError: property "code" is equal to undefined, expected 20 >+PASS Aborting rejects with AbortError >+PASS Aborting rejects with AbortError - no-cors > PASS TypeError from request constructor takes priority - RequestInit's window is not null > PASS TypeError from request constructor takes priority - Input URL is not valid > PASS TypeError from request constructor takes priority - Input URL has credentials >@@ -19,34 +17,34 @@ PASS TypeError from request constructor takes priority - Bad mode init parameter > PASS TypeError from request constructor takes priority - Bad credentials init parameter value > PASS TypeError from request constructor takes priority - Bad cache init parameter value > PASS TypeError from request constructor takes priority - Bad redirect init parameter value >-FAIL Request objects have a signal property assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object created from request object assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request overriding another assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal retained after unrelated properties are overridden by fetch assert_unreached: Should have rejected: undefined Reached unreachable code >+PASS Request objects have a signal property >+PASS Signal on request object >+PASS Signal on request object created from request object >+PASS Signal on request object created from request object, with signal on second request >+PASS Signal on request object created from request object, with signal on second request overriding another >+PASS Signal retained after unrelated properties are overridden by fetch > PASS Signal removed by setting to null >-FAIL Already aborted signal rejects immediately assert_unreached: Fetch must not resolve Reached unreachable code >+PASS Already aborted signal rejects immediately > PASS Request is still 'used' if signal is aborted before fetching >-FAIL response.arrayBuffer() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.blob() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.formData() rejects if already aborted assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >-FAIL response.json() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.text() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Already aborted signal does not make request assert_equals: Request hasn't been made to the server expected (object) null but got (string) "open" >-FAIL Already aborted signal can be used for many fetches assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal can be used to abort other fetches, even if another fetch succeeded before aborting assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Underlying connection is closed when aborting after receiving response promise_test: Unhandled rejection with value: object "Error: Timed out" >-FAIL Underlying connection is closed when aborting after receiving response - no-cors promise_test: Unhandled rejection with value: object "TypeError: A server with the specified hostname could not be found." >-TIMEOUT Fetch aborted & connection closed when aborted after calling response.arrayBuffer() Test timed out >-NOTRUN Fetch aborted & connection closed when aborted after calling response.blob() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.formData() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.json() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.text() >-NOTRUN Stream errors once aborted. Underlying connection closed. >-NOTRUN Stream errors once aborted, after reading. Underlying connection closed. >-NOTRUN Stream will not error if body is empty. It's closed with an empty queue before it errors. >-NOTRUN Readable stream synchronously cancels with AbortError if aborted before reading >-FAIL Signal state is cloned undefined is not an object (evaluating 'request.signal.aborted') >-FAIL Clone aborts with original controller undefined is not an object (evaluating 'request.signal.addEventListener') >+PASS response.arrayBuffer() rejects if already aborted >+PASS response.blob() rejects if already aborted >+PASS response.formData() rejects if already aborted >+PASS response.json() rejects if already aborted >+PASS response.text() rejects if already aborted >+PASS Already aborted signal does not make request >+PASS Already aborted signal can be used for many fetches >+PASS Signal can be used to abort other fetches, even if another fetch succeeded before aborting >+PASS Underlying connection is closed when aborting after receiving response >+PASS Underlying connection is closed when aborting after receiving response - no-cors >+PASS Fetch aborted & connection closed when aborted after calling response.arrayBuffer() >+PASS Fetch aborted & connection closed when aborted after calling response.blob() >+FAIL Fetch aborted & connection closed when aborted after calling response.formData() assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >+PASS Fetch aborted & connection closed when aborted after calling response.json() >+PASS Fetch aborted & connection closed when aborted after calling response.text() >+PASS Stream errors once aborted. Underlying connection closed. >+PASS Stream errors once aborted, after reading. Underlying connection closed. >+PASS Stream will not error if body is empty. It's closed with an empty queue before it errors. >+FAIL Readable stream synchronously cancels with AbortError if aborted before reading assert_true: Cancel called sync expected true got false >+PASS Signal state is cloned >+PASS Clone aborts with original controller > >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt >index e90ed02bce5c85a53769da66e92b9d9611878af1..e7fde2ecf08ca1d2a28df09d5bae10f21d819c94 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any-expected.txt >@@ -1,12 +1,7 @@ >-Blocked access to external URL http://www1.localhost:8800/fetch/api/resources/data.json >-CONSOLE MESSAGE: line 36: Fetch API cannot load http://www1.localhost:8800/fetch/api/resources/data.json due to access control checks. >-Blocked access to external URL http://www1.localhost:8800/fetch/api/resources/infinite-slow-response.py?stateKey=28d5c068-417e-4c81-a0cd-9b8c22aed3c1&abortKey=ef9a1b5a-7afd-4734-b145-f033788c0e6b >-CONSOLE MESSAGE: line 318: Fetch API cannot load http://www1.localhost:8800/fetch/api/resources/infinite-slow-response.py?stateKey=28d5c068-417e-4c81-a0cd-9b8c22aed3c1&abortKey=ef9a1b5a-7afd-4734-b145-f033788c0e6b due to access control checks. >+CONSOLE MESSAGE: Unhandled Promise Rejection: AbortError: Request signal is aborted > >-Harness Error (TIMEOUT), message = null >- >-FAIL Aborting rejects with AbortError assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Aborting rejects with AbortError - no-cors assert_throws: function "function () { throw e }" threw object "TypeError: Type error" that is not a DOMException AbortError: property "code" is equal to undefined, expected 20 >+PASS Aborting rejects with AbortError >+PASS Aborting rejects with AbortError - no-cors > PASS TypeError from request constructor takes priority - RequestInit's window is not null > PASS TypeError from request constructor takes priority - Input URL is not valid > PASS TypeError from request constructor takes priority - Input URL has credentials >@@ -15,7 +10,6 @@ PASS TypeError from request constructor takes priority - RequestInit's referrer > PASS TypeError from request constructor takes priority - RequestInit's method is invalid > PASS TypeError from request constructor takes priority - RequestInit's method is forbidden > PASS TypeError from request constructor takes priority - RequestInit's mode is no-cors and method is not simple >-PASS TypeError from request constructor takes priority - RequestInit's mode is no-cors and integrity is not empty > PASS TypeError from request constructor takes priority - RequestInit's cache mode is only-if-cached and mode is not same-origin > PASS TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode cors > PASS TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode no-cors >@@ -24,34 +18,34 @@ PASS TypeError from request constructor takes priority - Bad mode init parameter > PASS TypeError from request constructor takes priority - Bad credentials init parameter value > PASS TypeError from request constructor takes priority - Bad cache init parameter value > PASS TypeError from request constructor takes priority - Bad redirect init parameter value >-FAIL Request objects have a signal property assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object created from request object assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request overriding another assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal retained after unrelated properties are overridden by fetch assert_unreached: Should have rejected: undefined Reached unreachable code >+PASS Request objects have a signal property >+PASS Signal on request object >+PASS Signal on request object created from request object >+PASS Signal on request object created from request object, with signal on second request >+PASS Signal on request object created from request object, with signal on second request overriding another >+PASS Signal retained after unrelated properties are overridden by fetch > PASS Signal removed by setting to null >-FAIL Already aborted signal rejects immediately assert_unreached: Fetch must not resolve Reached unreachable code >+PASS Already aborted signal rejects immediately > PASS Request is still 'used' if signal is aborted before fetching >-FAIL response.arrayBuffer() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.blob() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.formData() rejects if already aborted assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >-FAIL response.json() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.text() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Already aborted signal does not make request assert_equals: Request hasn't been made to the server expected (object) null but got (string) "open" >-FAIL Already aborted signal can be used for many fetches assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal can be used to abort other fetches, even if another fetch succeeded before aborting assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Underlying connection is closed when aborting after receiving response promise_test: Unhandled rejection with value: object "Error: Timed out" >-FAIL Underlying connection is closed when aborting after receiving response - no-cors promise_test: Unhandled rejection with value: object "TypeError: Type error" >-TIMEOUT Fetch aborted & connection closed when aborted after calling response.arrayBuffer() Test timed out >-NOTRUN Fetch aborted & connection closed when aborted after calling response.blob() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.formData() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.json() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.text() >-NOTRUN Stream errors once aborted. Underlying connection closed. >-NOTRUN Stream errors once aborted, after reading. Underlying connection closed. >-NOTRUN Stream will not error if body is empty. It's closed with an empty queue before it errors. >-NOTRUN Readable stream synchronously cancels with AbortError if aborted before reading >-FAIL Signal state is cloned undefined is not an object (evaluating 'request.signal.aborted') >-FAIL Clone aborts with original controller undefined is not an object (evaluating 'request.signal.addEventListener') >+PASS response.arrayBuffer() rejects if already aborted >+PASS response.blob() rejects if already aborted >+PASS response.formData() rejects if already aborted >+PASS response.json() rejects if already aborted >+PASS response.text() rejects if already aborted >+PASS Already aborted signal does not make request >+PASS Already aborted signal can be used for many fetches >+PASS Signal can be used to abort other fetches, even if another fetch succeeded before aborting >+PASS Underlying connection is closed when aborting after receiving response >+PASS Underlying connection is closed when aborting after receiving response - no-cors >+PASS Fetch aborted & connection closed when aborted after calling response.arrayBuffer() >+PASS Fetch aborted & connection closed when aborted after calling response.blob() >+FAIL Fetch aborted & connection closed when aborted after calling response.formData() assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >+PASS Fetch aborted & connection closed when aborted after calling response.json() >+PASS Fetch aborted & connection closed when aborted after calling response.text() >+PASS Stream errors once aborted. Underlying connection closed. >+PASS Stream errors once aborted, after reading. Underlying connection closed. >+PASS Stream will not error if body is empty. It's closed with an empty queue before it errors. >+FAIL Readable stream synchronously cancels with AbortError if aborted before reading assert_true: Cancel called sync expected true got false >+PASS Signal state is cloned >+PASS Clone aborts with original controller > >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.js b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.js >index eb5979756fe8ad27698c1c43103652113c2fb337..2b3641b998de8f193c5b134f69f8816e3d2b5691 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.js >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.js >@@ -1,4 +1,5 @@ > // META: script=/common/utils.js >+// META: script=/common/get-host-info.sub.js > // META: script=../request/request-error.js > > const BODY_METHODS = ['arrayBuffer', 'blob', 'formData', 'json', 'text']; >@@ -15,6 +16,9 @@ function abortRequests() { > ); > } > >+const hostInfo = get_host_info(); >+const urlHostname = hostInfo.REMOTE_HOST; >+ > promise_test(async t => { > const controller = new AbortController(); > const signal = controller.signal; >@@ -31,7 +35,7 @@ promise_test(async t => { > controller.abort(); > > const url = new URL('../resources/data.json', location); >- url.hostname = 'www1.' + url.hostname; >+ url.hostname = urlHostname; > > const fetchPromise = fetch(url, { > signal, >@@ -314,7 +318,7 @@ promise_test(async t => { > requestAbortKeys.push(abortKey); > > const url = new URL(`../resources/infinite-slow-response.py?stateKey=${stateKey}&abortKey=${abortKey}`, location); >- url.hostname = 'www1.' + url.hostname; >+ url.hostname = urlHostname; > > await fetch(url, { > signal, >@@ -322,7 +326,7 @@ promise_test(async t => { > }); > > const stashTakeURL = new URL(`../resources/stash-take.py?key=${stateKey}`, location); >- stashTakeURL.hostname = 'www1.' + stashTakeURL.hostname; >+ stashTakeURL.hostname = urlHostname; > > const beforeAbortResult = await fetch(stashTakeURL).then(r => r.json()); > assert_equals(beforeAbortResult, "open", "Connection is open"); >@@ -440,7 +444,7 @@ promise_test(async t => { > const controller = new AbortController(); > const signal = controller.signal; > >- const response = await fetch(`../resources/empty.txt`, { signal }); >+ const response = await fetch(`../resources/method.py`, { signal }); > > // Read whole response to ensure close signal has sent. > await response.clone().text(); >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt >index 25eee9fcda9fb800556e51122f7e2db1b97e86f1..471bc341bfcdcf31c0149b15c89763da66f44999 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/general.any.worker-expected.txt >@@ -1,10 +1,6 @@ >-Blocked access to external URL http://www1.localhost:8800/fetch/api/resources/data.json >-Blocked access to external URL http://www1.localhost:8800/fetch/api/resources/infinite-slow-response.py?stateKey=7471d98e-1bdb-4254-a315-9489c98b8f59&abortKey=4c631f17-2786-4b07-84f4-0a5760d28f1e > >-Harness Error (TIMEOUT), message = null >- >-FAIL Aborting rejects with AbortError assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Aborting rejects with AbortError - no-cors assert_throws: function "function () { throw e }" threw object "TypeError: Type error" that is not a DOMException AbortError: property "code" is equal to undefined, expected 20 >+PASS Aborting rejects with AbortError >+PASS Aborting rejects with AbortError - no-cors > PASS TypeError from request constructor takes priority - RequestInit's window is not null > PASS TypeError from request constructor takes priority - Input URL is not valid > PASS TypeError from request constructor takes priority - Input URL has credentials >@@ -13,7 +9,6 @@ PASS TypeError from request constructor takes priority - RequestInit's referrer > PASS TypeError from request constructor takes priority - RequestInit's method is invalid > PASS TypeError from request constructor takes priority - RequestInit's method is forbidden > PASS TypeError from request constructor takes priority - RequestInit's mode is no-cors and method is not simple >-PASS TypeError from request constructor takes priority - RequestInit's mode is no-cors and integrity is not empty > PASS TypeError from request constructor takes priority - RequestInit's cache mode is only-if-cached and mode is not same-origin > PASS TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode cors > PASS TypeError from request constructor takes priority - Request with cache mode: only-if-cached and fetch mode no-cors >@@ -22,34 +17,34 @@ PASS TypeError from request constructor takes priority - Bad mode init parameter > PASS TypeError from request constructor takes priority - Bad credentials init parameter value > PASS TypeError from request constructor takes priority - Bad cache init parameter value > PASS TypeError from request constructor takes priority - Bad redirect init parameter value >-FAIL Request objects have a signal property assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object assert_true: Signal member is present & truthy expected true got false >-FAIL Signal on request object created from request object assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal on request object created from request object, with signal on second request overriding another assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal retained after unrelated properties are overridden by fetch assert_unreached: Should have rejected: undefined Reached unreachable code >+PASS Request objects have a signal property >+PASS Signal on request object >+PASS Signal on request object created from request object >+PASS Signal on request object created from request object, with signal on second request >+PASS Signal on request object created from request object, with signal on second request overriding another >+PASS Signal retained after unrelated properties are overridden by fetch > PASS Signal removed by setting to null >-FAIL Already aborted signal rejects immediately assert_unreached: Fetch must not resolve Reached unreachable code >+PASS Already aborted signal rejects immediately > PASS Request is still 'used' if signal is aborted before fetching >-FAIL response.arrayBuffer() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.blob() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.formData() rejects if already aborted assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >-FAIL response.json() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.text() rejects if already aborted assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Already aborted signal does not make request assert_equals: Request hasn't been made to the server expected (object) null but got (string) "open" >-FAIL Already aborted signal can be used for many fetches assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Signal can be used to abort other fetches, even if another fetch succeeded before aborting assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL Underlying connection is closed when aborting after receiving response promise_test: Unhandled rejection with value: object "Error: Timed out" >-FAIL Underlying connection is closed when aborting after receiving response - no-cors promise_test: Unhandled rejection with value: object "TypeError: Type error" >-TIMEOUT Fetch aborted & connection closed when aborted after calling response.arrayBuffer() Test timed out >-NOTRUN Fetch aborted & connection closed when aborted after calling response.blob() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.formData() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.json() >-NOTRUN Fetch aborted & connection closed when aborted after calling response.text() >-NOTRUN Stream errors once aborted. Underlying connection closed. >-NOTRUN Stream errors once aborted, after reading. Underlying connection closed. >-NOTRUN Stream will not error if body is empty. It's closed with an empty queue before it errors. >-NOTRUN Readable stream synchronously cancels with AbortError if aborted before reading >-FAIL Signal state is cloned undefined is not an object (evaluating 'request.signal.aborted') >-FAIL Clone aborts with original controller undefined is not an object (evaluating 'request.signal.addEventListener') >+PASS response.arrayBuffer() rejects if already aborted >+PASS response.blob() rejects if already aborted >+PASS response.formData() rejects if already aborted >+PASS response.json() rejects if already aborted >+PASS response.text() rejects if already aborted >+PASS Already aborted signal does not make request >+PASS Already aborted signal can be used for many fetches >+PASS Signal can be used to abort other fetches, even if another fetch succeeded before aborting >+PASS Underlying connection is closed when aborting after receiving response >+PASS Underlying connection is closed when aborting after receiving response - no-cors >+PASS Fetch aborted & connection closed when aborted after calling response.arrayBuffer() >+PASS Fetch aborted & connection closed when aborted after calling response.blob() >+FAIL Fetch aborted & connection closed when aborted after calling response.formData() assert_throws: function "function () { throw e }" threw object "NotSupportedError: The operation is not supported." that is not a DOMException AbortError: property "code" is equal to 9, expected 20 >+PASS Fetch aborted & connection closed when aborted after calling response.json() >+PASS Fetch aborted & connection closed when aborted after calling response.text() >+PASS Stream errors once aborted. Underlying connection closed. >+PASS Stream errors once aborted, after reading. Underlying connection closed. >+PASS Stream will not error if body is empty. It's closed with an empty queue before it errors. >+FAIL Readable stream synchronously cancels with AbortError if aborted before reading assert_true: Cancel called sync expected true got false >+PASS Signal state is cloned >+PASS Clone aborts with original controller > >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https-expected.txt >index 114cf8a0cd9e5f313e2986a43a2a86597e1f8785..37d876380564c7bc77417266a9ad90f68c291514 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/abort/serviceworker-intercepted.https-expected.txt >@@ -1,9 +1,10 @@ > >-FAIL Already aborted request does not land in service worker assert_unreached: Should have rejected: undefined Reached unreachable code >-FAIL response.arrayBuffer() rejects if already aborted promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >-FAIL response.blob() rejects if already aborted promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >-FAIL response.formData() rejects if already aborted promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >-FAIL response.json() rejects if already aborted promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >-FAIL response.text() rejects if already aborted promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >-FAIL Stream errors once aborted. promise_test: Unhandled rejection with value: object "Error: wait_for_state must be passed a ServiceWorker" >+ >+PASS Already aborted request does not land in service worker >+PASS response.arrayBuffer() rejects if already aborted >+PASS response.blob() rejects if already aborted >+PASS response.formData() rejects if already aborted >+PASS response.json() rejects if already aborted >+PASS response.text() rejects if already aborted >+FAIL Stream errors once aborted. assert_unreached: Should have rejected: undefined Reached unreachable code > >diff --git a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt >index d48ac1b38aeee508b29dc2aae20e9ce62eab5f11..8e7976d6d6c3304364dc90a9a24d37e67f6e8455 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/fetch/api/response/response-consume-stream-expected.txt >@@ -5,7 +5,7 @@ PASS Read blob response's body as readableStream > PASS Read text response's body as readableStream > PASS Read URLSearchParams response's body as readableStream > PASS Read array buffer response's body as readableStream >-FAIL Read form data response's body as readableStream promise_test: Unhandled rejection with value: object "TypeError: not implemented" >+FAIL Read form data response's body as readableStream promise_test: Unhandled rejection with value: object "NotSupportedError: Not implemented" > PASS Getting an error Response stream > PASS Getting a redirect Response stream >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 174980
:
357950
|
357951
|
357952
|
357959
|
357966
|
358267
|
358291
|
358340
|
358342
|
358354
|
358374