Bug 1120501 P2 Move Cache Add/AddAll logic to child process. r=ehsan,smaug

This commit is contained in:
Ben Kelly
2015-04-29 08:59:43 -07:00
parent c9822afc96
commit 1e4ce069d9
20 changed files with 503 additions and 895 deletions

View File

@@ -197,6 +197,7 @@ DOMInterfaces = {
},
'Cache': {
'implicitJSContext': [ 'add', 'addAll' ],
'nativeType': 'mozilla::dom::cache::Cache',
},

View File

@@ -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<CacheRequestResponse>& 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<InternalHeaders> 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<InternalHeaders> cachedRequestHeaders =
TypeUtils::ToInternalHeaders(cachedRequest.headers());
nsRefPtr<InternalHeaders> cachedResponseHeaders =
TypeUtils::ToInternalHeaders(cachedResponse.headers());
nsAutoTArray<nsCString, 16> 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);
}

View File

@@ -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();

335
dom/cache/Cache.cpp vendored
View File

@@ -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<nsRefPtr<Request>>&& 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<JS::Value> aValue) override
{
NS_ASSERT_OWNINGTHREAD(FetchHandler);
// Stop holding the worker alive when we leave this method.
nsRefPtr<Feature> 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<nsRefPtr<Response>>.
nsAutoTArray<nsRefPtr<Response>, 256> responseList;
responseList.SetCapacity(mRequestList.Length());
if (NS_WARN_IF(!JS_IsArrayObject(aCx, aValue))) {
Fail();
return;
}
JS::Rooted<JSObject*> 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<JS::Value> value(aCx);
if (NS_WARN_IF(!JS_GetElement(aCx, obj, i, &value))) {
Fail();
return;
}
if (NS_WARN_IF(!value.isObject())) {
Fail();
return;
}
JS::Rooted<JSObject*> responseObj(aCx, &value.toObject());
nsRefPtr<Response> 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<Promise> 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<JS::Value> aValue) override
{
NS_ASSERT_OWNINGTHREAD(FetchHandler);
Fail();
}
private:
~FetchHandler()
{
}
void
Fail()
{
ErrorResult rv;
rv.ThrowTypeError(MSG_FETCH_FAILED);
mPromise->MaybeReject(rv);
}
nsRefPtr<Feature> mFeature;
nsRefPtr<Cache> mCache;
nsTArray<nsRefPtr<Request>> mRequestList;
nsRefPtr<Promise> 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<RequestOrUSVString>& 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<RequestOrUSVString>& aRequest,
}
already_AddRefed<Promise>
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<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
MOZ_ASSERT(!global.Failed());
nsTArray<nsRefPtr<Request>> requestList(1);
nsRefPtr<Request> 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<Promise>
Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
Cache::AddAll(JSContext* aContext,
const Sequence<OwningRequestOrUSVString>& 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 = Promise::Create(mGlobal, aRv);
if (!promise) {
nsTArray<nsRefPtr<Request>> 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;
}
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
} else {
requestOrString.SetAsUSVString().Rebind(
aRequestList[i].GetAsUSVString().Data(),
aRequestList[i].GetAsUSVString().Length());
}
AutoChildOpArgs args(this, CacheAddAllArgs());
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
if (!IsValidPutRequestMethod(aRequests[i], aRv)) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequests[i], ReadBody,
aRv);
nsRefPtr<Request> 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<Promise>
@@ -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<RequestOrUSVString>& 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<Promise>
Cache::AddAll(const GlobalObject& aGlobal,
nsTArray<nsRefPtr<Request>>&& aRequestList, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
// If there is no work to do, then resolve immediately
if (aRequestList.IsEmpty()) {
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
nsAutoTArray<nsRefPtr<Promise>, 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<Promise> fetch = FetchRequest(mGlobal, requestOrString,
RequestInit(), aRv);
if (aRv.Failed()) {
return nullptr;
}
fetchList.AppendElement(Move(fetch));
}
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (aRv.Failed()) {
return nullptr;
}
nsRefPtr<FetchHandler> handler = new FetchHandler(mActor->GetFeature(), this,
Move(aRequestList), promise);
nsRefPtr<Promise> fetchPromise = Promise::All(aGlobal, fetchList, aRv);
if (aRv.Failed()) {
return nullptr;
}
fetchPromise->AppendNativeHandler(handler);
return promise.forget();
}
already_AddRefed<Promise>
Cache::PutAll(const nsTArray<nsRefPtr<Request>>& aRequestList,
const nsTArray<nsRefPtr<Response>>& 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<InternalRequest> 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

18
dom/cache/Cache.h vendored
View File

@@ -50,11 +50,12 @@ public:
MatchAll(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv);
already_AddRefed<Promise>
Add(const RequestOrUSVString& aRequest, ErrorResult& aRv);
already_AddRefed<Promise>
AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
Add(JSContext* aContext, const RequestOrUSVString& aRequest,
ErrorResult& aRv);
already_AddRefed<Promise>
AddAll(JSContext* aContext,
const Sequence<OwningRequestOrUSVString>& aRequests, ErrorResult& aRv);
already_AddRefed<Promise>
Put(const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv);
already_AddRefed<Promise>
@@ -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<Promise>
ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv);
already_AddRefed<Promise>
AddAll(const GlobalObject& aGlobal, nsTArray<nsRefPtr<Request>>&& aRequestList,
ErrorResult& aRv);
already_AddRefed<Promise>
PutAll(const nsTArray<nsRefPtr<Request>>& aRequestList,
const nsTArray<nsRefPtr<Response>>& aResponseList,
ErrorResult& aRv);
nsCOMPtr<nsIGlobalObject> mGlobal;
CacheChild* mActor;

View File

@@ -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);

View File

@@ -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<CacheRequest>& list = args.requestList();
nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
for (uint32_t i = 0; i < list.Length(); ++i) {
requestStreamList.AppendElement(DeserializeCacheStream(list[i].body()));
}
nsRefPtr<FetchPut> 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<nsIInputStream>
CacheOpParent::DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid)
{

View File

@@ -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<SavedRequest>& aSavedRequestList,
StreamList* aStreamList) override;
// FetchPut::Listener methods
virtual void
OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) override;
// utility methods
already_AddRefed<nsIInputStream>
DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid);
@@ -75,7 +69,6 @@ private:
const CacheOpArgs mOpArgs;
nsRefPtr<Manager> mManager;
nsRefPtr<PrincipalVerifier> mVerifier;
nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
NS_DECL_OWNINGTHREAD
};

View File

@@ -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)

View File

@@ -432,8 +432,7 @@ CacheStorage::MaybeRunPendingRequests()
nsAutoPtr<Entry> 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);

View File

@@ -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;

483
dom/cache/FetchPut.cpp vendored
View File

@@ -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<FetchPut> 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<nsIThread> initiatingThread(mFetchPut->mInitiatingThread);
nsCOMPtr<nsIRunnable> runnable =
NS_NewNonOwningRunnableMethod(mFetchPut.forget().take(), &FetchPut::Release);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
initiatingThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL)));
}
}
protected:
virtual ~FetchObserver() { }
private:
nsRefPtr<FetchPut> mFetchPut;
nsRefPtr<InternalResponse> mInternalResponse;
};
// static
nsresult
FetchPut::Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& 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<FetchPut> 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<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& 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<nsIRunnable> 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> managerId = mManager->GetManagerId();
nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
mPendingCount = mStateList.Length();
nsCOMPtr<nsILoadGroup> 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> 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<InternalRequest> 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> 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<CacheRequestResponse, 16> putList;
nsAutoTArray<nsCOMPtr<nsIInputStream>, 16> requestStreamList;
nsAutoTArray<nsCOMPtr<nsIInputStream>, 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<CacheRequestResponse>& 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<InternalHeaders> 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<InternalHeaders> cachedRequestHeaders =
ToInternalHeaders(cachedRequest.headers());
nsRefPtr<InternalHeaders> cachedResponseHeaders =
ToInternalHeaders(cachedResponse.headers());
nsAutoTArray<nsCString, 16> 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<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& 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<FetchPut> 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

123
dom/cache/FetchPut.h vendored
View File

@@ -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 <utility>
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<Request>, nsRefPtr<Response>> PutPair;
class Listener
{
public:
virtual void
OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) = 0;
};
static nsresult
Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
FetchPut** aFetchPutOut);
void ClearListener();
private:
class Runnable;
class FetchObserver;
friend class FetchObserver;
struct State
{
CacheRequest mCacheRequest;
nsCOMPtr<nsIInputStream> mRequestStream;
nsRefPtr<FetchObserver> mFetchObserver;
CacheResponse mCacheResponse;
nsCOMPtr<nsIInputStream> mResponseStream;
nsRefPtr<Request> mRequest;
nsRefPtr<Response> mResponse;
};
FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& 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<CacheRequestResponse>& aPutList);
virtual void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& 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<Manager> mManager;
const CacheId mCacheId;
nsCOMPtr<nsIThread> mInitiatingThread;
nsTArray<State> mStateList;
uint32_t mPendingCount;
ErrorResult mResult;
nsCOMPtr<nsIRunnable> mRunnable;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::cache::FetchPut)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_FetchPut_h

View File

@@ -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) {

3
dom/cache/Manager.h vendored
View File

@@ -18,6 +18,9 @@ class nsIInputStream;
class nsIThread;
namespace mozilla {
class ErrorResult;
namespace dom {
namespace cache {

View File

@@ -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<nsIURLParser> 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<HeadersEntry>& 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<InternalRequest>
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<InternalHeaders> headers = aIn->Headers();
@@ -465,6 +374,73 @@ TypeUtils::ToInternalHeaders(const nsTArray<HeadersEntry>& 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<nsIURLParser> 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)

17
dom/cache/TypeUtils.h vendored
View File

@@ -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<HeadersEntry>& aHeadersEntryList,
HeadersGuardEnum aGuard = HeadersGuardEnum::None);
static void
ProcessURL(nsAString& aUrl, bool* aSchemeValidOut,
nsAString* aUrlWithoutQueryOut, ErrorResult& aRv);
private:
void
CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,

2
dom/cache/moz.build vendored
View File

@@ -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',

View File

@@ -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);
});

View File

@@ -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<Response>