diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 806e117553a5..e148072ed668 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -197,6 +197,7 @@ DOMInterfaces = { }, 'Cache': { + 'implicitJSContext': [ 'add', 'addAll' ], 'nativeType': 'mozilla::dom::cache::Cache', }, diff --git a/dom/cache/AutoUtils.cpp b/dom/cache/AutoUtils.cpp index ff75acf9fc8d..95a67d27f8d9 100644 --- a/dom/cache/AutoUtils.cpp +++ b/dom/cache/AutoUtils.cpp @@ -7,6 +7,8 @@ #include "mozilla/dom/cache/AutoUtils.h" #include "mozilla/unused.h" +#include "mozilla/dom/InternalHeaders.h" +#include "mozilla/dom/InternalRequest.h" #include "mozilla/dom/cache/CacheParent.h" #include "mozilla/dom/cache/CachePushStreamChild.h" #include "mozilla/dom/cache/CacheStreamControlParent.h" @@ -17,6 +19,8 @@ #include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/FileDescriptorSetParent.h" #include "mozilla/ipc/PBackgroundParent.h" +#include "nsCRT.h" +#include "nsHttp.h" namespace { @@ -168,15 +172,6 @@ AutoChildOpArgs::~AutoChildOpArgs() CleanupChild(args.requestOrVoid().get_CacheRequest().body(), action); break; } - case CacheOpArgs::TCacheAddAllArgs: - { - CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs(); - auto& list = args.requestList(); - for (uint32_t i = 0; i < list.Length(); ++i) { - CleanupChild(list[i].body(), action); - } - break; - } case CacheOpArgs::TCachePutAllArgs: { CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs(); @@ -216,8 +211,7 @@ AutoChildOpArgs::~AutoChildOpArgs() void AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, - ReferrerAction aReferrerAction, SchemeAction aSchemeAction, - ErrorResult& aRv) + SchemeAction aSchemeAction, ErrorResult& aRv) { MOZ_ASSERT(!mSent); @@ -226,7 +220,7 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, { CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs(); mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction, - aReferrerAction, aSchemeAction, aRv); + aSchemeAction, aRv); break; } case CacheOpArgs::TCacheMatchAllArgs: @@ -235,35 +229,14 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t); args.requestOrVoid() = CacheRequest(); mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(), - aRequest, aBodyAction, aReferrerAction, - aSchemeAction, aRv); - break; - } - case CacheOpArgs::TCacheAddAllArgs: - { - CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs(); - - // The FileDescriptorSetChild asserts in its destructor that all fds have - // been removed. The copy constructor, however, simply duplicates the - // fds without removing any. This means each temporary and copy must be - // explicitly cleaned up. - // - // Avoid a lot of this hassle by making sure we only create one here. On - // error we remove it. - CacheRequest& request = *args.requestList().AppendElement(); - - mTypeUtils->ToCacheRequest(request, aRequest, aBodyAction, - aReferrerAction, aSchemeAction, aRv); - if (aRv.Failed()) { - args.requestList().RemoveElementAt(args.requestList().Length() - 1); - } + aRequest, aBodyAction, aSchemeAction, aRv); break; } case CacheOpArgs::TCacheDeleteArgs: { CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs(); mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction, - aReferrerAction, aSchemeAction, aRv); + aSchemeAction, aRv); break; } case CacheOpArgs::TCacheKeysArgs: @@ -272,15 +245,14 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t); args.requestOrVoid() = CacheRequest(); mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(), - aRequest, aBodyAction, aReferrerAction, - aSchemeAction, aRv); + aRequest, aBodyAction, aSchemeAction, aRv); break; } case CacheOpArgs::TStorageMatchArgs: { StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs(); mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction, - aReferrerAction, aSchemeAction, aRv); + aSchemeAction, aRv); break; } default: @@ -288,10 +260,112 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, } } +namespace { + +bool +MatchInPutList(InternalRequest* aRequest, + const nsTArray& aPutList) +{ + MOZ_ASSERT(aRequest); + + // This method implements the SW spec QueryCache algorithm against an + // in memory array of Request/Response objects. This essentially the + // same algorithm that is implemented in DBSchema.cpp. Unfortunately + // we cannot unify them because when operating against the real database + // we don't want to load all request/response objects into memory. + + // Note, we can skip the check for a invalid request method because + // Cache should only call into here with a GET or HEAD. +#ifdef DEBUG + nsAutoCString method; + aRequest->GetMethod(method); + MOZ_ASSERT(method.LowerCaseEqualsLiteral("get") || + method.LowerCaseEqualsLiteral("head")); +#endif + + nsRefPtr requestHeaders = aRequest->Headers(); + + for (uint32_t i = 0; i < aPutList.Length(); ++i) { + const CacheRequest& cachedRequest = aPutList[i].request(); + const CacheResponse& cachedResponse = aPutList[i].response(); + + nsAutoCString url; + aRequest->GetURL(url); + + // If the URLs don't match, then just skip to the next entry. + if (NS_ConvertUTF8toUTF16(url) != cachedRequest.url()) { + continue; + } + + nsRefPtr cachedRequestHeaders = + TypeUtils::ToInternalHeaders(cachedRequest.headers()); + + nsRefPtr cachedResponseHeaders = + TypeUtils::ToInternalHeaders(cachedResponse.headers()); + + nsAutoTArray varyHeaders; + ErrorResult rv; + cachedResponseHeaders->GetAll(NS_LITERAL_CSTRING("vary"), varyHeaders, rv); + MOZ_ALWAYS_TRUE(!rv.Failed()); + + // Assume the vary headers match until we find a conflict + bool varyHeadersMatch = true; + + for (uint32_t j = 0; j < varyHeaders.Length(); ++j) { + // Extract the header names inside the Vary header value. + nsAutoCString varyValue(varyHeaders[j]); + char* rawBuffer = varyValue.BeginWriting(); + char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer); + bool bailOut = false; + for (; token; + token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) { + nsDependentCString header(token); + MOZ_ASSERT(!header.EqualsLiteral("*"), + "We should have already caught this in " + "TypeUtils::ToPCacheResponseWithoutBody()"); + + ErrorResult headerRv; + nsAutoCString value; + requestHeaders->Get(header, value, headerRv); + if (NS_WARN_IF(headerRv.Failed())) { + headerRv.SuppressException(); + MOZ_ASSERT(value.IsEmpty()); + } + + nsAutoCString cachedValue; + cachedRequestHeaders->Get(header, value, headerRv); + if (NS_WARN_IF(headerRv.Failed())) { + headerRv.SuppressException(); + MOZ_ASSERT(cachedValue.IsEmpty()); + } + + if (value != cachedValue) { + varyHeadersMatch = false; + bailOut = true; + break; + } + } + + if (bailOut) { + break; + } + } + + // URL was equal and all vary headers match! + if (varyHeadersMatch) { + return true; + } + } + + return false; +} + +} // anonymous namespace + void AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, - ReferrerAction aReferrerAction, SchemeAction aSchemeAction, - Response& aResponse, ErrorResult& aRv) + SchemeAction aSchemeAction, Response& aResponse, + ErrorResult& aRv) { MOZ_ASSERT(!mSent); @@ -300,6 +374,14 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, { CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs(); + // Throw an error if a request/response pair would mask another + // request/response pair in the same PutAll operation. This is + // step 2.3.2.3 from the "Batch Cache Operations" spec algorithm. + if (MatchInPutList(aRequest, args.requestResponseList())) { + aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); + return; + } + // The FileDescriptorSetChild asserts in its destructor that all fds have // been removed. The copy constructor, however, simply duplicates the // fds without removing any. This means each temporary and copy must be @@ -312,7 +394,7 @@ AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction, pair.response().body() = void_t(); mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction, - aReferrerAction, aSchemeAction, aRv); + aSchemeAction, aRv); if (!aRv.Failed()) { mTypeUtils->ToCacheResponse(pair.response(), aResponse, aRv); } diff --git a/dom/cache/AutoUtils.h b/dom/cache/AutoUtils.h index 69864591d526..2d9eec6b0854 100644 --- a/dom/cache/AutoUtils.h +++ b/dom/cache/AutoUtils.h @@ -47,18 +47,15 @@ class MOZ_STACK_CLASS AutoChildOpArgs final { public: typedef TypeUtils::BodyAction BodyAction; - typedef TypeUtils::ReferrerAction ReferrerAction; typedef TypeUtils::SchemeAction SchemeAction; AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs); ~AutoChildOpArgs(); void Add(InternalRequest* aRequest, BodyAction aBodyAction, - ReferrerAction aReferrerAction, SchemeAction aSchemeAction, - ErrorResult& aRv); + SchemeAction aSchemeAction, ErrorResult& aRv); void Add(InternalRequest* aRequest, BodyAction aBodyAction, - ReferrerAction aReferrerAction, SchemeAction aSchemeAction, - Response& aResponse, ErrorResult& aRv); + SchemeAction aSchemeAction, Response& aResponse, ErrorResult& aRv); const CacheOpArgs& SendAsOpArgs(); diff --git a/dom/cache/Cache.cpp b/dom/cache/Cache.cpp index 7a69510bb432..b1a1d7125285 100644 --- a/dom/cache/Cache.cpp +++ b/dom/cache/Cache.cpp @@ -9,12 +9,14 @@ #include "mozilla/dom/Headers.h" #include "mozilla/dom/InternalResponse.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/PromiseNativeHandler.h" #include "mozilla/dom/Response.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/CacheBinding.h" #include "mozilla/dom/cache/AutoUtils.h" #include "mozilla/dom/cache/CacheChild.h" #include "mozilla/dom/cache/CachePushStreamChild.h" +#include "mozilla/dom/cache/Feature.h" #include "mozilla/dom/cache/ReadStream.h" #include "mozilla/ErrorResult.h" #include "mozilla/Preferences.h" @@ -22,30 +24,53 @@ #include "nsIGlobalObject.h" #include "nsNetUtil.h" +namespace mozilla { +namespace dom { +namespace cache { + +using mozilla::dom::workers::GetCurrentThreadWorkerPrivate; +using mozilla::dom::workers::WorkerPrivate; + namespace { -using mozilla::ErrorResult; -using mozilla::dom::MSG_INVALID_REQUEST_METHOD; -using mozilla::dom::OwningRequestOrUSVString; -using mozilla::dom::Request; -using mozilla::dom::RequestOrUSVString; +bool +IsValidPutRequestURL(const nsAString& aUrl, ErrorResult& aRv) +{ + bool validScheme = false; + + // make a copy because ProcessURL strips the fragmet + nsAutoString url(aUrl); + + TypeUtils::ProcessURL(url, &validScheme, nullptr, aRv); + if (aRv.Failed()) { + return false; + } + + if (!validScheme) { + NS_NAMED_LITERAL_STRING(label, "Request"); + aRv.ThrowTypeError(MSG_INVALID_URL_SCHEME, &label, &url); + return false; + } + + return true; +} static bool IsValidPutRequestMethod(const Request& aRequest, ErrorResult& aRv) { nsAutoCString method; aRequest.GetMethod(method); - bool valid = method.LowerCaseEqualsLiteral("get"); - if (!valid) { + if (!method.LowerCaseEqualsLiteral("get")) { NS_ConvertASCIItoUTF16 label(method); aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label); + return false; } - return valid; + + return true; } static bool -IsValidPutRequestMethod(const RequestOrUSVString& aRequest, - ErrorResult& aRv) +IsValidPutRequestMethod(const RequestOrUSVString& aRequest, ErrorResult& aRv) { // If the provided request is a string URL, then it will default to // a valid http method automatically. @@ -55,26 +80,132 @@ IsValidPutRequestMethod(const RequestOrUSVString& aRequest, return IsValidPutRequestMethod(aRequest.GetAsRequest(), aRv); } -static bool -IsValidPutRequestMethod(const OwningRequestOrUSVString& aRequest, - ErrorResult& aRv) -{ - if (!aRequest.IsRequest()) { - return true; - } - return IsValidPutRequestMethod(*aRequest.GetAsRequest().get(), aRv); -} - } // anonymous namespace -namespace mozilla { -namespace dom { -namespace cache { +// Helper class to wait for Add()/AddAll() fetch requests to complete and +// then perform a PutAll() with the responses. This class holds a Feature +// to keep the Worker thread alive. This is mainly to ensure that Add/AddAll +// act the same as other Cache operations that directly create a CacheOpChild +// actor. +class Cache::FetchHandler final : public PromiseNativeHandler +{ +public: + FetchHandler(Feature* aFeature, Cache* aCache, + nsTArray>&& aRequestList, Promise* aPromise) + : mFeature(aFeature) + , mCache(aCache) + , mRequestList(Move(aRequestList)) + , mPromise(aPromise) + { + MOZ_ASSERT_IF(!NS_IsMainThread(), mFeature); + MOZ_ASSERT(mCache); + MOZ_ASSERT(mPromise); + } -using mozilla::ErrorResult; -using mozilla::unused; -using mozilla::dom::workers::GetCurrentThreadWorkerPrivate; -using mozilla::dom::workers::WorkerPrivate; + virtual void + ResolvedCallback(JSContext* aCx, JS::Handle aValue) override + { + NS_ASSERT_OWNINGTHREAD(FetchHandler); + + // Stop holding the worker alive when we leave this method. + nsRefPtr feature; + feature.swap(mFeature); + + // Promise::All() passed an array of fetch() Promises should give us + // an Array of Response objects. The following code unwraps these + // JS values back to an nsTArray>. + + nsAutoTArray, 256> responseList; + responseList.SetCapacity(mRequestList.Length()); + + if (NS_WARN_IF(!JS_IsArrayObject(aCx, aValue))) { + Fail(); + return; + } + + JS::Rooted obj(aCx, &aValue.toObject()); + + uint32_t length; + if (NS_WARN_IF(!JS_GetArrayLength(aCx, obj, &length))) { + Fail(); + return; + } + + for (uint32_t i = 0; i < length; ++i) { + JS::Rooted value(aCx); + + if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &value))) { + Fail(); + return; + } + + if (NS_WARN_IF(!value.isObject())) { + Fail(); + return; + } + + JS::Rooted responseObj(aCx, &value.toObject()); + + nsRefPtr response; + nsresult rv = UNWRAP_OBJECT(Response, responseObj, response); + if (NS_WARN_IF(NS_FAILED(rv))) { + Fail(); + return; + } + + if (NS_WARN_IF(response->Type() == ResponseType::Error)) { + Fail(); + return; + } + + responseList.AppendElement(Move(response)); + } + + MOZ_ASSERT(mRequestList.Length() == responseList.Length()); + + // Now store the unwrapped Response list in the Cache. + ErrorResult result; + nsRefPtr put = mCache->PutAll(mRequestList, responseList, result); + if (NS_WARN_IF(result.Failed())) { + // TODO: abort the fetch requests we have running (bug 1157434) + mPromise->MaybeReject(result); + return; + } + + // Chain the Cache::Put() promise to the original promise returned to + // the content script. + mPromise->MaybeResolve(put); + } + + virtual void + RejectedCallback(JSContext* aCx, JS::Handle aValue) override + { + NS_ASSERT_OWNINGTHREAD(FetchHandler); + Fail(); + } + +private: + ~FetchHandler() + { + } + + void + Fail() + { + ErrorResult rv; + rv.ThrowTypeError(MSG_FETCH_FAILED); + mPromise->MaybeReject(rv); + } + + nsRefPtr mFeature; + nsRefPtr mCache; + nsTArray> mRequestList; + nsRefPtr mPromise; + + NS_DECL_ISUPPORTS +}; + +NS_IMPL_ISUPPORTS0(Cache::FetchHandler) NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::Cache); NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache); @@ -110,7 +241,7 @@ Cache::Match(const RequestOrUSVString& aRequest, AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params)); - args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv); + args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } @@ -136,7 +267,7 @@ Cache::MatchAll(const Optional& aRequest, return nullptr; } - args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv); + args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); if (aRv.Failed()) { return nullptr; } @@ -146,66 +277,72 @@ Cache::MatchAll(const Optional& aRequest, } already_AddRefed -Cache::Add(const RequestOrUSVString& aRequest, ErrorResult& aRv) +Cache::Add(JSContext* aContext, const RequestOrUSVString& aRequest, + ErrorResult& aRv) { - MOZ_ASSERT(mActor); - if (!IsValidPutRequestMethod(aRequest, aRv)) { return nullptr; } - nsRefPtr ir = ToInternalRequest(aRequest, ReadBody, aRv); + GlobalObject global(aContext, mGlobal->GetGlobalJSObject()); + MOZ_ASSERT(!global.Failed()); + + nsTArray> requestList(1); + nsRefPtr request = Request::Constructor(global, aRequest, + RequestInit(), aRv); if (aRv.Failed()) { return nullptr; } - AutoChildOpArgs args(this, CacheAddAllArgs()); - - args.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv); - if (aRv.Failed()) { + nsAutoString url; + request->GetUrl(url); + if (!IsValidPutRequestURL(url, aRv)) { return nullptr; } - return ExecuteOp(args, aRv); + requestList.AppendElement(Move(request)); + return AddAll(global, Move(requestList), aRv); } already_AddRefed -Cache::AddAll(const Sequence& aRequests, +Cache::AddAll(JSContext* aContext, + const Sequence& aRequestList, ErrorResult& aRv) { - MOZ_ASSERT(mActor); + GlobalObject global(aContext, mGlobal->GetGlobalJSObject()); + MOZ_ASSERT(!global.Failed()); - // If there is no work to do, then resolve immediately - if (aRequests.IsEmpty()) { - nsRefPtr promise = Promise::Create(mGlobal, aRv); - if (!promise) { - return nullptr; + nsTArray> requestList(aRequestList.Length()); + for (uint32_t i = 0; i < aRequestList.Length(); ++i) { + RequestOrUSVString requestOrString; + + if (aRequestList[i].IsRequest()) { + requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest(); + if (!IsValidPutRequestMethod(requestOrString.GetAsRequest(), aRv)) { + return nullptr; + } + } else { + requestOrString.SetAsUSVString().Rebind( + aRequestList[i].GetAsUSVString().Data(), + aRequestList[i].GetAsUSVString().Length()); } - promise->MaybeResolve(JS::UndefinedHandleValue); - return promise.forget(); - } - - AutoChildOpArgs args(this, CacheAddAllArgs()); - - for (uint32_t i = 0; i < aRequests.Length(); ++i) { - if (!IsValidPutRequestMethod(aRequests[i], aRv)) { - return nullptr; - } - - nsRefPtr ir = ToInternalRequest(aRequests[i], ReadBody, - aRv); + nsRefPtr request = Request::Constructor(global, requestOrString, + RequestInit(), aRv); if (aRv.Failed()) { return nullptr; } - args.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv); - if (aRv.Failed()) { + nsAutoString url; + request->GetUrl(url); + if (!IsValidPutRequestURL(url, aRv)) { return nullptr; } + + requestList.AppendElement(Move(request)); } - return ExecuteOp(args, aRv); + return AddAll(global, Move(requestList), aRv); } already_AddRefed @@ -225,7 +362,7 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse, AutoChildOpArgs args(this, CachePutAllArgs()); - args.Add(ir, ReadBody, PassThroughReferrer, TypeErrorOnInvalidScheme, + args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, aResponse, aRv); if (aRv.Failed()) { return nullptr; @@ -250,7 +387,7 @@ Cache::Delete(const RequestOrUSVString& aRequest, AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params)); - args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv); + args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); if (aRv.Failed()) { return nullptr; } @@ -276,7 +413,7 @@ Cache::Keys(const Optional& aRequest, return nullptr; } - args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv); + args.Add(ir, IgnoreBody, IgnoreInvalidScheme, aRv); if (aRv.Failed()) { return nullptr; } @@ -375,6 +512,80 @@ Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv) return promise.forget(); } +already_AddRefed +Cache::AddAll(const GlobalObject& aGlobal, + nsTArray>&& aRequestList, ErrorResult& aRv) +{ + MOZ_ASSERT(mActor); + + // If there is no work to do, then resolve immediately + if (aRequestList.IsEmpty()) { + nsRefPtr promise = Promise::Create(mGlobal, aRv); + if (!promise) { + return nullptr; + } + + promise->MaybeResolve(JS::UndefinedHandleValue); + return promise.forget(); + } + + nsAutoTArray, 256> fetchList; + fetchList.SetCapacity(aRequestList.Length()); + + // Begin fetching each request in parallel. For now, if an error occurs just + // abandon our previous fetch calls. In theory we could cancel them in the + // future once fetch supports it. + + for (uint32_t i = 0; i < aRequestList.Length(); ++i) { + RequestOrUSVString requestOrString; + requestOrString.SetAsRequest() = aRequestList[i]; + nsRefPtr fetch = FetchRequest(mGlobal, requestOrString, + RequestInit(), aRv); + if (aRv.Failed()) { + return nullptr; + } + + fetchList.AppendElement(Move(fetch)); + } + + nsRefPtr promise = Promise::Create(mGlobal, aRv); + if (aRv.Failed()) { + return nullptr; + } + + nsRefPtr handler = new FetchHandler(mActor->GetFeature(), this, + Move(aRequestList), promise); + + nsRefPtr fetchPromise = Promise::All(aGlobal, fetchList, aRv); + if (aRv.Failed()) { + return nullptr; + } + fetchPromise->AppendNativeHandler(handler); + + return promise.forget(); +} + +already_AddRefed +Cache::PutAll(const nsTArray>& aRequestList, + const nsTArray>& aResponseList, + ErrorResult& aRv) +{ + MOZ_ASSERT(mActor); + MOZ_ASSERT(aRequestList.Length() == aResponseList.Length()); + + AutoChildOpArgs args(this, CachePutAllArgs()); + + for (uint32_t i = 0; i < aRequestList.Length(); ++i) { + nsRefPtr ir = aRequestList[i]->GetInternalRequest(); + args.Add(ir, ReadBody, TypeErrorOnInvalidScheme, *aResponseList[i], aRv); + if (aRv.Failed()) { + return nullptr; + } + } + + return ExecuteOp(args, aRv); +} + } // namespace cache } // namespace dom } // namespace mozilla diff --git a/dom/cache/Cache.h b/dom/cache/Cache.h index 7656c539af30..f5275ac9a398 100644 --- a/dom/cache/Cache.h +++ b/dom/cache/Cache.h @@ -50,10 +50,11 @@ public: MatchAll(const Optional& aRequest, const CacheQueryOptions& aOptions, ErrorResult& aRv); already_AddRefed - Add(const RequestOrUSVString& aRequest, ErrorResult& aRv); + Add(JSContext* aContext, const RequestOrUSVString& aRequest, + ErrorResult& aRv); already_AddRefed - AddAll(const Sequence& aRequests, - ErrorResult& aRv); + AddAll(JSContext* aContext, + const Sequence& aRequests, ErrorResult& aRv); already_AddRefed Put(const RequestOrUSVString& aRequest, Response& aResponse, ErrorResult& aRv); @@ -85,6 +86,8 @@ public: CreatePushStream(nsIAsyncInputStream* aStream) override; private: + class FetchHandler; + ~Cache(); // Called when we're destroyed or CCed. @@ -93,6 +96,15 @@ private: already_AddRefed ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv); + already_AddRefed + AddAll(const GlobalObject& aGlobal, nsTArray>&& aRequestList, + ErrorResult& aRv); + + already_AddRefed + PutAll(const nsTArray>& aRequestList, + const nsTArray>& aResponseList, + ErrorResult& aRv); + nsCOMPtr mGlobal; CacheChild* mActor; diff --git a/dom/cache/CacheOpChild.cpp b/dom/cache/CacheOpChild.cpp index 7d892afa1ac0..9eafef41198a 100644 --- a/dom/cache/CacheOpChild.cpp +++ b/dom/cache/CacheOpChild.cpp @@ -116,7 +116,6 @@ CacheOpChild::Recv__delete__(const ErrorResult& aRv, HandleResponseList(aResult.get_CacheMatchAllResult().responseList()); break; } - case CacheOpResult::TCacheAddAllResult: case CacheOpResult::TCachePutAllResult: { mPromise->MaybeResolve(JS::UndefinedHandleValue); diff --git a/dom/cache/CacheOpParent.cpp b/dom/cache/CacheOpParent.cpp index 5e658302ca7e..c7e2edc91009 100644 --- a/dom/cache/CacheOpParent.cpp +++ b/dom/cache/CacheOpParent.cpp @@ -72,30 +72,6 @@ CacheOpParent::Execute(Manager* aManager) mManager = aManager; - // Handle add/addAll op with a FetchPut object - if (mOpArgs.type() == CacheOpArgs::TCacheAddAllArgs) { - MOZ_ASSERT(mCacheId != INVALID_CACHE_ID); - - const CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs(); - const nsTArray& list = args.requestList(); - - nsAutoTArray, 256> requestStreamList; - for (uint32_t i = 0; i < list.Length(); ++i) { - requestStreamList.AppendElement(DeserializeCacheStream(list[i].body())); - } - - nsRefPtr fetchPut; - nsresult rv = FetchPut::Create(this, mManager, mCacheId, list, - requestStreamList, getter_AddRefs(fetchPut)); - if (NS_WARN_IF(NS_FAILED(rv))) { - OnOpComplete(ErrorResult(rv), CacheAddAllResult()); - return; - } - - mFetchPutList.AppendElement(fetchPut.forget()); - return; - } - // Handle put op if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) { MOZ_ASSERT(mCacheId != INVALID_CACHE_ID); @@ -151,11 +127,6 @@ CacheOpParent::ActorDestroy(ActorDestroyReason aReason) mVerifier = nullptr; } - for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) { - mFetchPutList[i]->ClearListener(); - } - mFetchPutList.Clear(); - if (mManager) { mManager->RemoveListener(this); mManager = nullptr; @@ -222,18 +193,6 @@ CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult, unused << Send__delete__(this, aRv, result.SendAsOpResult()); } -void -CacheOpParent::OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) -{ - NS_ASSERT_OWNINGTHREAD(CacheOpParent); - MOZ_ASSERT(aFetchPut); - - aFetchPut->ClearListener(); - MOZ_ALWAYS_TRUE(mFetchPutList.RemoveElement(aFetchPut)); - - OnOpComplete(Move(aRv), CacheAddAllResult()); -} - already_AddRefed CacheOpParent::DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid) { diff --git a/dom/cache/CacheOpParent.h b/dom/cache/CacheOpParent.h index 6b6679e29e76..ef350b8b1fad 100644 --- a/dom/cache/CacheOpParent.h +++ b/dom/cache/CacheOpParent.h @@ -7,7 +7,6 @@ #ifndef mozilla_dom_cache_CacheOpParent_h #define mozilla_dom_cache_CacheOpParent_h -#include "mozilla/dom/cache/FetchPut.h" #include "mozilla/dom/cache/Manager.h" #include "mozilla/dom/cache/PCacheOpParent.h" #include "mozilla/dom/cache/PrincipalVerifier.h" @@ -23,7 +22,6 @@ namespace cache { class CacheOpParent final : public PCacheOpParent , public PrincipalVerifier::Listener , public Manager::Listener - , public FetchPut::Listener { // to allow use of convenience overrides using Manager::Listener::OnOpComplete; @@ -61,10 +59,6 @@ private: const nsTArray& aSavedRequestList, StreamList* aStreamList) override; - // FetchPut::Listener methods - virtual void - OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) override; - // utility methods already_AddRefed DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid); @@ -75,7 +69,6 @@ private: const CacheOpArgs mOpArgs; nsRefPtr mManager; nsRefPtr mVerifier; - nsTArray> mFetchPutList; NS_DECL_OWNINGTHREAD }; diff --git a/dom/cache/CacheParent.cpp b/dom/cache/CacheParent.cpp index 15ca59b2a494..2cca17d5ed1a 100644 --- a/dom/cache/CacheParent.cpp +++ b/dom/cache/CacheParent.cpp @@ -49,7 +49,6 @@ CacheParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs) { if (aOpArgs.type() != CacheOpArgs::TCacheMatchArgs && aOpArgs.type() != CacheOpArgs::TCacheMatchAllArgs && - aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs && aOpArgs.type() != CacheOpArgs::TCachePutAllArgs && aOpArgs.type() != CacheOpArgs::TCacheDeleteArgs && aOpArgs.type() != CacheOpArgs::TCacheKeysArgs) diff --git a/dom/cache/CacheStorage.cpp b/dom/cache/CacheStorage.cpp index 988279f23850..2680e4d32db4 100644 --- a/dom/cache/CacheStorage.cpp +++ b/dom/cache/CacheStorage.cpp @@ -432,8 +432,7 @@ CacheStorage::MaybeRunPendingRequests() nsAutoPtr entry(mPendingRequests[i].forget()); AutoChildOpArgs args(this, entry->mArgs); if (entry->mRequest) { - args.Add(entry->mRequest, IgnoreBody, PassThroughReferrer, - IgnoreInvalidScheme, rv); + args.Add(entry->mRequest, IgnoreBody, IgnoreInvalidScheme, rv); } if (rv.Failed()) { entry->mPromise->MaybeReject(rv); diff --git a/dom/cache/CacheTypes.ipdlh b/dom/cache/CacheTypes.ipdlh index 4947389a464d..43f8a38b5dee 100644 --- a/dom/cache/CacheTypes.ipdlh +++ b/dom/cache/CacheTypes.ipdlh @@ -108,11 +108,6 @@ struct CacheMatchAllArgs CacheQueryParams params; }; -struct CacheAddAllArgs -{ - CacheRequest[] requestList; -}; - struct CachePutAllArgs { CacheRequestResponse[] requestResponseList; @@ -159,7 +154,6 @@ union CacheOpArgs { CacheMatchArgs; CacheMatchAllArgs; - CacheAddAllArgs; CachePutAllArgs; CacheDeleteArgs; CacheKeysArgs; @@ -180,10 +174,6 @@ struct CacheMatchAllResult CacheResponse[] responseList; }; -struct CacheAddAllResult -{ -}; - struct CachePutAllResult { }; @@ -228,7 +218,6 @@ union CacheOpResult void_t; CacheMatchResult; CacheMatchAllResult; - CacheAddAllResult; CachePutAllResult; CacheDeleteResult; CacheKeysResult; diff --git a/dom/cache/FetchPut.cpp b/dom/cache/FetchPut.cpp deleted file mode 100644 index 0e03589e9f99..000000000000 --- a/dom/cache/FetchPut.cpp +++ /dev/null @@ -1,483 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/cache/FetchPut.h" - -#include "mozilla/dom/Fetch.h" -#include "mozilla/dom/FetchDriver.h" -#include "mozilla/dom/Headers.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/PromiseNativeHandler.h" -#include "mozilla/dom/Request.h" -#include "mozilla/dom/Response.h" -#include "mozilla/dom/ResponseBinding.h" -#include "mozilla/dom/UnionTypes.h" -#include "mozilla/dom/cache/ManagerId.h" -#include "nsContentUtils.h" -#include "nsNetUtil.h" -#include "nsThreadUtils.h" -#include "nsCRT.h" -#include "nsHttp.h" - -namespace mozilla { -namespace dom { -namespace cache { - -class FetchPut::Runnable final : public nsRunnable -{ -public: - explicit Runnable(FetchPut* aFetchPut) - : mFetchPut(aFetchPut) - { - MOZ_ASSERT(mFetchPut); - } - - NS_IMETHOD Run() override - { - if (NS_IsMainThread()) - { - mFetchPut->DoFetchOnMainThread(); - return NS_OK; - } - - MOZ_ASSERT(mFetchPut->mInitiatingThread == NS_GetCurrentThread()); - - mFetchPut->DoPutOnWorkerThread(); - - // The FetchPut object must ultimately be freed on the worker thread, - // so make sure we release our reference here. The runnable may end - // up getting deleted on the main thread. - mFetchPut = nullptr; - - return NS_OK; - } - -private: - nsRefPtr mFetchPut; -}; - -class FetchPut::FetchObserver final : public FetchDriverObserver -{ -public: - explicit FetchObserver(FetchPut* aFetchPut) - : mFetchPut(aFetchPut) - { - } - - virtual void OnResponseAvailable(InternalResponse* aResponse) override - { - MOZ_ASSERT(!mInternalResponse); - mInternalResponse = aResponse; - } - - virtual void OnResponseEnd() override - { - mFetchPut->FetchComplete(this, mInternalResponse); - if (mFetchPut->mInitiatingThread == NS_GetCurrentThread()) { - mFetchPut = nullptr; - } else { - nsCOMPtr initiatingThread(mFetchPut->mInitiatingThread); - nsCOMPtr runnable = - NS_NewNonOwningRunnableMethod(mFetchPut.forget().take(), &FetchPut::Release); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED( - initiatingThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL))); - } - } - -protected: - virtual ~FetchObserver() { } - -private: - nsRefPtr mFetchPut; - nsRefPtr mInternalResponse; -}; - -// static -nsresult -FetchPut::Create(Listener* aListener, Manager* aManager, CacheId aCacheId, - const nsTArray& aRequests, - const nsTArray>& aRequestStreams, - FetchPut** aFetchPutOut) -{ - MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length()); - - // The FetchDriver requires that all requests have a referrer already set. -#ifdef DEBUG - for (uint32_t i = 0; i < aRequests.Length(); ++i) { - if (aRequests[i].referrer() == EmptyString()) { - return NS_ERROR_UNEXPECTED; - } - } -#endif - - nsRefPtr ref = new FetchPut(aListener, aManager, aCacheId, - aRequests, aRequestStreams); - - nsresult rv = ref->DispatchToMainThread(); - if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - ref.forget(aFetchPutOut); - - return NS_OK; -} - -void -FetchPut::ClearListener() -{ - MOZ_ASSERT(mListener); - mListener = nullptr; -} - -FetchPut::FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId, - const nsTArray& aRequests, - const nsTArray>& aRequestStreams) - : mListener(aListener) - , mManager(aManager) - , mCacheId(aCacheId) - , mInitiatingThread(NS_GetCurrentThread()) - , mStateList(aRequests.Length()) - , mPendingCount(0) -{ - MOZ_ASSERT(mListener); - MOZ_ASSERT(mManager); - MOZ_ASSERT(aRequests.Length() == aRequestStreams.Length()); - - for (uint32_t i = 0; i < aRequests.Length(); ++i) { - State* s = mStateList.AppendElement(); - s->mCacheRequest = aRequests[i]; - s->mRequestStream = aRequestStreams[i]; - } - - mManager->AddRefCacheId(mCacheId); -} - -FetchPut::~FetchPut() -{ - MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread()); - MOZ_ASSERT(!mListener); - mManager->RemoveListener(this); - mManager->ReleaseCacheId(mCacheId); - mResult.SuppressException(); // XXXbz should we really be ending up here with - // a failed mResult we never reported to anyone? -} - -nsresult -FetchPut::DispatchToMainThread() -{ - MOZ_ASSERT(!mRunnable); - - nsCOMPtr runnable = new Runnable(this); - - nsresult rv = NS_DispatchToMainThread(runnable, nsIThread::DISPATCH_NORMAL); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - MOZ_ASSERT(!mRunnable); - mRunnable = runnable.forget(); - - return NS_OK; -} - -void -FetchPut::DispatchToInitiatingThread() -{ - MOZ_ASSERT(mRunnable); - - nsresult rv = mInitiatingThread->Dispatch(mRunnable, - nsIThread::DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - MOZ_CRASH("Failed to dispatch to worker thread after fetch completion."); - } - - mRunnable = nullptr; -} - -void -FetchPut::DoFetchOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - nsRefPtr managerId = mManager->GetManagerId(); - nsCOMPtr principal = managerId->Principal(); - mPendingCount = mStateList.Length(); - - nsCOMPtr loadGroup; - nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal); - if (NS_WARN_IF(NS_FAILED(rv))) { - MaybeSetError(ErrorResult(rv)); - MaybeCompleteOnMainThread(); - return; - } - - for (uint32_t i = 0; i < mStateList.Length(); ++i) { - nsRefPtr internalRequest = - ToInternalRequest(mStateList[i].mCacheRequest); - - // If there is a stream we must clone it so that its still available - // to store in the cache later; - if (mStateList[i].mRequestStream) { - internalRequest->SetBody(mStateList[i].mRequestStream); - nsRefPtr clone = internalRequest->Clone(); - - // The copy construction clone above can change the source stream, - // so get it back out to use when we put this in the cache. - internalRequest->GetBody(getter_AddRefs(mStateList[i].mRequestStream)); - - internalRequest = clone; - } - - nsRefPtr fetchDriver = new FetchDriver(internalRequest, - principal, - loadGroup); - - mStateList[i].mFetchObserver = new FetchObserver(this); - rv = fetchDriver->Fetch(mStateList[i].mFetchObserver); - if (NS_WARN_IF(NS_FAILED(rv))) { - MaybeSetError(ErrorResult(rv)); - mStateList[i].mFetchObserver = nullptr; - mPendingCount -= 1; - continue; - } - } - - // If they all failed, then we might need to complete main thread immediately - MaybeCompleteOnMainThread(); -} - -void -FetchPut::FetchComplete(FetchObserver* aObserver, - InternalResponse* aInternalResponse) -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (aInternalResponse->IsError() && !mResult.Failed()) { - MaybeSetError(ErrorResult(NS_ERROR_FAILURE)); - } - - for (uint32_t i = 0; i < mStateList.Length(); ++i) { - if (mStateList[i].mFetchObserver == aObserver) { - ErrorResult rv; - ToCacheResponseWithoutBody(mStateList[i].mCacheResponse, - *aInternalResponse, rv); - if (rv.Failed()) { - MaybeSetError(Move(rv)); - } else { - aInternalResponse->GetBody(getter_AddRefs(mStateList[i].mResponseStream)); - } - mStateList[i].mFetchObserver = nullptr; - MOZ_ASSERT(mPendingCount > 0); - mPendingCount -= 1; - MaybeCompleteOnMainThread(); - return; - } - } - - MOZ_ASSERT_UNREACHABLE("Should never get called by unknown fetch observer."); -} - -void -FetchPut::MaybeCompleteOnMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - - if (mPendingCount > 0) { - return; - } - - DispatchToInitiatingThread(); -} - -void -FetchPut::DoPutOnWorkerThread() -{ - MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread()); - - if (mResult.Failed()) { - MaybeNotifyListener(); - return; - } - - // These allocate ~4.5k combined on the stack - nsAutoTArray putList; - nsAutoTArray, 16> requestStreamList; - nsAutoTArray, 16> responseStreamList; - - putList.SetCapacity(mStateList.Length()); - requestStreamList.SetCapacity(mStateList.Length()); - responseStreamList.SetCapacity(mStateList.Length()); - - for (uint32_t i = 0; i < mStateList.Length(); ++i) { - // The spec requires us to catch if content tries to insert a set of - // requests that would overwrite each other. - if (MatchInPutList(mStateList[i].mCacheRequest, putList)) { - MaybeSetError(ErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR)); - MaybeNotifyListener(); - return; - } - - CacheRequestResponse* entry = putList.AppendElement(); - entry->request() = mStateList[i].mCacheRequest; - entry->response() = mStateList[i].mCacheResponse; - requestStreamList.AppendElement(mStateList[i].mRequestStream.forget()); - responseStreamList.AppendElement(mStateList[i].mResponseStream.forget()); - } - mStateList.Clear(); - - mManager->ExecutePutAll(this, mCacheId, putList, requestStreamList, - responseStreamList); -} - -// static -bool -FetchPut::MatchInPutList(const CacheRequest& aRequest, - const nsTArray& aPutList) -{ - // This method implements the SW spec QueryCache algorithm against an - // in memory array of Request/Response objects. This essentially the - // same algorithm that is implemented in DBSchema.cpp. Unfortunately - // we cannot unify them because when operating against the real database - // we don't want to load all request/response objects into memory. - - if (!aRequest.method().LowerCaseEqualsLiteral("get") && - !aRequest.method().LowerCaseEqualsLiteral("head")) { - return false; - } - - nsRefPtr requestHeaders = - ToInternalHeaders(aRequest.headers()); - - for (uint32_t i = 0; i < aPutList.Length(); ++i) { - const CacheRequest& cachedRequest = aPutList[i].request(); - const CacheResponse& cachedResponse = aPutList[i].response(); - - // If the URLs don't match, then just skip to the next entry. - if (aRequest.url() != cachedRequest.url()) { - continue; - } - - nsRefPtr cachedRequestHeaders = - ToInternalHeaders(cachedRequest.headers()); - - nsRefPtr cachedResponseHeaders = - ToInternalHeaders(cachedResponse.headers()); - - nsAutoTArray varyHeaders; - ErrorResult rv; - cachedResponseHeaders->GetAll(NS_LITERAL_CSTRING("vary"), varyHeaders, rv); - MOZ_ALWAYS_TRUE(!rv.Failed()); - - // Assume the vary headers match until we find a conflict - bool varyHeadersMatch = true; - - for (uint32_t j = 0; j < varyHeaders.Length(); ++j) { - // Extract the header names inside the Vary header value. - nsAutoCString varyValue(varyHeaders[j]); - char* rawBuffer = varyValue.BeginWriting(); - char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer); - bool bailOut = false; - for (; token; - token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) { - nsDependentCString header(token); - MOZ_ASSERT(!header.EqualsLiteral("*"), - "We should have already caught this in " - "TypeUtils::ToPCacheResponseWithoutBody()"); - - ErrorResult headerRv; - nsAutoCString value; - requestHeaders->Get(header, value, headerRv); - if (NS_WARN_IF(headerRv.Failed())) { - headerRv.SuppressException(); - MOZ_ASSERT(value.IsEmpty()); - } - - nsAutoCString cachedValue; - cachedRequestHeaders->Get(header, value, headerRv); - if (NS_WARN_IF(headerRv.Failed())) { - headerRv.SuppressException(); - MOZ_ASSERT(cachedValue.IsEmpty()); - } - - if (value != cachedValue) { - varyHeadersMatch = false; - bailOut = true; - break; - } - } - - if (bailOut) { - break; - } - } - - // URL was equal and all vary headers match! - if (varyHeadersMatch) { - return true; - } - } - - return false; -} - -void -FetchPut::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult, - CacheId aOpenedCacheId, - const nsTArray& aSavedResponseList, - const nsTArray& aSavedRequestList, - StreamList* aStreamList) -{ - MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread()); - MOZ_ASSERT(aResult.type() == CacheOpResult::TCachePutAllResult); - MaybeSetError(Move(aRv)); - MaybeNotifyListener(); -} - -void -FetchPut::MaybeSetError(ErrorResult&& aRv) -{ - if (mResult.Failed() || !aRv.Failed()) { - return; - } - mResult = Move(aRv); -} - -void -FetchPut::MaybeNotifyListener() -{ - MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread()); - if (!mListener) { - return; - } - // CacheParent::OnFetchPut can lead to the destruction of |this| when the - // object is removed from CacheParent::mFetchPutList, so make sure that - // doesn't happen until this method returns. - nsRefPtr kungFuDeathGrip(this); - mListener->OnFetchPut(this, Move(mResult)); -} - -nsIGlobalObject* -FetchPut::GetGlobalObject() const -{ - MOZ_CRASH("No global object in parent-size FetchPut operation!"); -} - -#ifdef DEBUG -void -FetchPut::AssertOwningThread() const -{ - MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread()); -} -#endif - -CachePushStreamChild* -FetchPut::CreatePushStream(nsIAsyncInputStream* aStream) -{ - MOZ_CRASH("FetchPut should never create a push stream!"); -} - -} // namespace cache -} // namespace dom -} // namespace mozilla diff --git a/dom/cache/FetchPut.h b/dom/cache/FetchPut.h deleted file mode 100644 index 2f6b3bd56203..000000000000 --- a/dom/cache/FetchPut.h +++ /dev/null @@ -1,123 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_cache_FetchPut_h -#define mozilla_dom_cache_FetchPut_h - -#include "mozilla/AlreadyAddRefed.h" -#include "mozilla/Attributes.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/cache/Manager.h" -#include "mozilla/dom/cache/CacheTypes.h" -#include "mozilla/dom/cache/Types.h" -#include "mozilla/dom/cache/TypeUtils.h" -#include "nsRefPtr.h" -#include "nsTArray.h" -#include - -class nsIInputStream; -class nsIRunnable; -class nsIThread; - -namespace mozilla { -namespace dom { - -class Request; -class Response; - -namespace cache { - -class FetchPut final : public Manager::Listener - , public TypeUtils -{ -public: - typedef std::pair, nsRefPtr> PutPair; - - class Listener - { - public: - virtual void - OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) = 0; - }; - - static nsresult - Create(Listener* aListener, Manager* aManager, CacheId aCacheId, - const nsTArray& aRequests, - const nsTArray>& aRequestStreams, - FetchPut** aFetchPutOut); - - void ClearListener(); - -private: - class Runnable; - class FetchObserver; - friend class FetchObserver; - struct State - { - CacheRequest mCacheRequest; - nsCOMPtr mRequestStream; - nsRefPtr mFetchObserver; - CacheResponse mCacheResponse; - nsCOMPtr mResponseStream; - - nsRefPtr mRequest; - nsRefPtr mResponse; - }; - - FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId, - const nsTArray& aRequests, - const nsTArray>& aRequestStreams); - ~FetchPut(); - - nsresult DispatchToMainThread(); - void DispatchToInitiatingThread(); - - void DoFetchOnMainThread(); - void FetchComplete(FetchObserver* aObserver, - InternalResponse* aInternalResponse); - void MaybeCompleteOnMainThread(); - - void DoPutOnWorkerThread(); - static bool MatchInPutList(const CacheRequest& aRequest, - const nsTArray& aPutList); - - virtual void - OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult, - CacheId aOpenedCacheId, - const nsTArray& aSavedResponseList, - const nsTArray& aSavedRequestList, - StreamList* aStreamList) override; - - void MaybeSetError(ErrorResult&& aRv); - void MaybeNotifyListener(); - - // TypeUtils methods - virtual nsIGlobalObject* GetGlobalObject() const override; -#ifdef DEBUG - virtual void AssertOwningThread() const override; -#endif - - virtual CachePushStreamChild* - CreatePushStream(nsIAsyncInputStream* aStream) override; - - Listener* mListener; - nsRefPtr mManager; - const CacheId mCacheId; - nsCOMPtr mInitiatingThread; - nsTArray mStateList; - uint32_t mPendingCount; - ErrorResult mResult; - nsCOMPtr mRunnable; - -public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::cache::FetchPut) -}; - -} // namespace cache -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_cache_FetchPut_h diff --git a/dom/cache/Manager.cpp b/dom/cache/Manager.cpp index 60e5d9c13cfe..6cddb043aba2 100644 --- a/dom/cache/Manager.cpp +++ b/dom/cache/Manager.cpp @@ -1621,7 +1621,6 @@ Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId, { NS_ASSERT_OWNINGTHREAD(Manager); MOZ_ASSERT(aListener); - MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs); MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs); if (mState == Closing) { diff --git a/dom/cache/Manager.h b/dom/cache/Manager.h index e27d2c09e7fd..5bfd0e3e99d6 100644 --- a/dom/cache/Manager.h +++ b/dom/cache/Manager.h @@ -18,6 +18,9 @@ class nsIInputStream; class nsIThread; namespace mozilla { + +class ErrorResult; + namespace dom { namespace cache { diff --git a/dom/cache/TypeUtils.cpp b/dom/cache/TypeUtils.cpp index 81ad6f761de1..42161412e329 100644 --- a/dom/cache/TypeUtils.cpp +++ b/dom/cache/TypeUtils.cpp @@ -15,7 +15,6 @@ #include "mozilla/dom/cache/CacheTypes.h" #include "mozilla/dom/cache/ReadStream.h" #include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/FileDescriptorSetChild.h" #include "mozilla/ipc/PBackgroundChild.h" #include "mozilla/ipc/PFileDescriptorSetChild.h" #include "mozilla/ipc/InputStreamUtils.h" @@ -30,85 +29,16 @@ #include "nsCRT.h" #include "nsHttp.h" -namespace { +namespace mozilla { +namespace dom { +namespace cache { -using mozilla::ErrorResult; -using mozilla::unused; -using mozilla::void_t; -using mozilla::dom::InternalHeaders; -using mozilla::dom::cache::CacheReadStream; -using mozilla::dom::cache::HeadersEntry; using mozilla::ipc::BackgroundChild; using mozilla::ipc::FileDescriptor; using mozilla::ipc::PBackgroundChild; using mozilla::ipc::PFileDescriptorSetChild; -// Utility function to remove the fragment from a URL, check its scheme, and optionally -// provide a URL without the query. We're not using nsIURL or URL to do this because -// they require going to the main thread. -static void -ProcessURL(nsAString& aUrl, bool* aSchemeValidOut, - nsAString* aUrlWithoutQueryOut, ErrorResult& aRv) -{ - NS_ConvertUTF16toUTF8 flatURL(aUrl); - const char* url = flatURL.get(); - - // off the main thread URL parsing using nsStdURLParser. - nsCOMPtr urlParser = new nsStdURLParser(); - - uint32_t pathPos; - int32_t pathLen; - uint32_t schemePos; - int32_t schemeLen; - aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen, - nullptr, nullptr, // ignore authority - &pathPos, &pathLen); - if (NS_WARN_IF(aRv.Failed())) { return; } - - if (aSchemeValidOut) { - nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen)); - *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") || - scheme.LowerCaseEqualsLiteral("https") || - scheme.LowerCaseEqualsLiteral("app"); - } - - uint32_t queryPos; - int32_t queryLen; - uint32_t refPos; - int32_t refLen; - - aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos, - nullptr, nullptr, // ignore filepath - &queryPos, &queryLen, - &refPos, &refLen); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - // TODO: Remove this once Request/Response properly strip the fragment (bug 1110476) - if (refLen >= 0) { - // ParsePath gives us ref position relative to the start of the path - refPos += pathPos; - - aUrl = Substring(aUrl, 0, refPos - 1); - } - - if (!aUrlWithoutQueryOut) { - return; - } - - if (queryLen < 0) { - *aUrlWithoutQueryOut = aUrl; - return; - } - - // ParsePath gives us query position relative to the start of the path - queryPos += pathPos; - - // We want everything before the query sine we already removed the trailing - // fragment - *aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1); -} +namespace { static bool HasVaryStar(mozilla::dom::InternalHeaders* aHeaders) @@ -174,18 +104,6 @@ ToHeadersEntryList(nsTArray& aOut, InternalHeaders* aHeaders) } // anonymous namespace -namespace mozilla { -namespace dom { -namespace cache { - -using mozilla::ipc::BackgroundChild; -using mozilla::ipc::FileDescriptor; -using mozilla::ipc::FileDescriptorSetChild; -using mozilla::ipc::PFileDescriptorSetChild; -using mozilla::ipc::PBackgroundChild; -using mozilla::ipc::OptionalFileDescriptorSet; - - already_AddRefed TypeUtils::ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction, ErrorResult& aRv) @@ -225,9 +143,8 @@ TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn, void TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn, - BodyAction aBodyAction, - ReferrerAction aReferrerAction, - SchemeAction aSchemeAction, ErrorResult& aRv) + BodyAction aBodyAction, SchemeAction aSchemeAction, + ErrorResult& aRv) { MOZ_ASSERT(aIn); @@ -249,16 +166,8 @@ TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn, aRv.ThrowTypeError(MSG_INVALID_URL_SCHEME, &label, &aOut.url()); return; } - - if (aSchemeAction == NetworkErrorOnInvalidScheme) { - aRv.Throw(NS_ERROR_DOM_NETWORK_ERR); - return; - } } - if (aReferrerAction == ExpandReferrer) { - UpdateRequestReferrer(GetGlobalObject(), aIn); - } aIn->GetReferrer(aOut.referrer()); nsRefPtr headers = aIn->Headers(); @@ -465,6 +374,73 @@ TypeUtils::ToInternalHeaders(const nsTArray& aHeadersEntryList, return ref.forget(); } +// Utility function to remove the fragment from a URL, check its scheme, and optionally +// provide a URL without the query. We're not using nsIURL or URL to do this because +// they require going to the main thread. +// static +void +TypeUtils::ProcessURL(nsAString& aUrl, bool* aSchemeValidOut, + nsAString* aUrlWithoutQueryOut, ErrorResult& aRv) +{ + NS_ConvertUTF16toUTF8 flatURL(aUrl); + const char* url = flatURL.get(); + + // off the main thread URL parsing using nsStdURLParser. + nsCOMPtr urlParser = new nsStdURLParser(); + + uint32_t pathPos; + int32_t pathLen; + uint32_t schemePos; + int32_t schemeLen; + aRv = urlParser->ParseURL(url, flatURL.Length(), &schemePos, &schemeLen, + nullptr, nullptr, // ignore authority + &pathPos, &pathLen); + if (NS_WARN_IF(aRv.Failed())) { return; } + + if (aSchemeValidOut) { + nsAutoCString scheme(Substring(flatURL, schemePos, schemeLen)); + *aSchemeValidOut = scheme.LowerCaseEqualsLiteral("http") || + scheme.LowerCaseEqualsLiteral("https"); + } + + uint32_t queryPos; + int32_t queryLen; + uint32_t refPos; + int32_t refLen; + + aRv = urlParser->ParsePath(url + pathPos, flatURL.Length() - pathPos, + nullptr, nullptr, // ignore filepath + &queryPos, &queryLen, + &refPos, &refLen); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + // TODO: Remove this once Request/Response properly strip the fragment (bug 1110476) + if (refLen >= 0) { + // ParsePath gives us ref position relative to the start of the path + refPos += pathPos; + + aUrl = Substring(aUrl, 0, refPos - 1); + } + + if (!aUrlWithoutQueryOut) { + return; + } + + if (queryLen < 0) { + *aUrlWithoutQueryOut = aUrl; + return; + } + + // ParsePath gives us query position relative to the start of the path + queryPos += pathPos; + + // We want everything before the query sine we already removed the trailing + // fragment + *aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1); +} + void TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction, ErrorResult& aRv) diff --git a/dom/cache/TypeUtils.h b/dom/cache/TypeUtils.h index ea2f971394e9..ca675e262c47 100644 --- a/dom/cache/TypeUtils.h +++ b/dom/cache/TypeUtils.h @@ -46,17 +46,10 @@ public: ReadBody }; - enum ReferrerAction - { - PassThroughReferrer, - ExpandReferrer - }; - enum SchemeAction { IgnoreInvalidScheme, - TypeErrorOnInvalidScheme, - NetworkErrorOnInvalidScheme + TypeErrorOnInvalidScheme }; ~TypeUtils() { } @@ -80,8 +73,8 @@ public: void ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn, - BodyAction aBodyAction, ReferrerAction aReferrerAction, - SchemeAction aSchemeAction, ErrorResult& aRv); + BodyAction aBodyAction, SchemeAction aSchemeAction, + ErrorResult& aRv); void ToCacheResponseWithoutBody(CacheResponse& aOut, InternalResponse& aIn, @@ -107,6 +100,10 @@ public: ToInternalHeaders(const nsTArray& aHeadersEntryList, HeadersGuardEnum aGuard = HeadersGuardEnum::None); + static void + ProcessURL(nsAString& aUrl, bool* aSchemeValidOut, + nsAString* aUrlWithoutQueryOut, ErrorResult& aRv); + private: void CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction, diff --git a/dom/cache/moz.build b/dom/cache/moz.build index cd7514fb649e..5862c86d3986 100644 --- a/dom/cache/moz.build +++ b/dom/cache/moz.build @@ -25,7 +25,6 @@ EXPORTS.mozilla.dom.cache += [ 'DBAction.h', 'DBSchema.h', 'Feature.h', - 'FetchPut.h', 'FileUtils.h', 'IPCUtils.h', 'Manager.h', @@ -61,7 +60,6 @@ UNIFIED_SOURCES += [ 'DBAction.cpp', 'DBSchema.cpp', 'Feature.cpp', - 'FetchPut.cpp', 'FileUtils.cpp', 'Manager.cpp', 'ManagerId.cpp', diff --git a/dom/cache/test/mochitest/test_cache_add.js b/dom/cache/test/mochitest/test_cache_add.js index e1fc264acea9..cee4ebd817db 100644 --- a/dom/cache/test/mochitest/test_cache_add.js +++ b/dom/cache/test/mochitest/test_cache_add.js @@ -10,10 +10,10 @@ caches.open(name).then(function(openCache) { cache = openCache; return cache.add('ftp://example.com/invalid' + context); }).catch(function (err) { - is(err.name, 'NetworkError', 'add() should throw NetworkError for invalid scheme'); + is(err.name, 'TypeError', 'add() should throw TypeError for invalid scheme'); return cache.addAll(['http://example.com/valid' + context, 'ftp://example.com/invalid' + context]); }).catch(function (err) { - is(err.name, 'NetworkError', 'addAll() should throw NetworkError for invalid scheme'); + is(err.name, 'TypeError', 'addAll() should throw TypeError for invalid scheme'); var promiseList = urlList.map(function(url) { return cache.match(url); }); diff --git a/dom/fetch/Response.h b/dom/fetch/Response.h index af75446bcacd..3453ff7d448c 100644 --- a/dom/fetch/Response.h +++ b/dom/fetch/Response.h @@ -12,13 +12,13 @@ #include "mozilla/dom/Fetch.h" #include "mozilla/dom/ResponseBinding.h" +#include "InternalHeaders.h" #include "InternalResponse.h" namespace mozilla { namespace dom { class Headers; -class InternalHeaders; class Response final : public nsISupports , public FetchBody