Bug 940273 - Part 4 - Initial implementation of Service Worker Cache. r=ehsan,baku,janv

This commit is contained in:
Ben Kelly 2015-03-02 14:20:00 +01:00
parent bad713c66b
commit e0b9075c14
71 changed files with 12907 additions and 2 deletions

View File

@ -176,6 +176,14 @@ DOMInterfaces = {
'resultNotAddRefed': ['element'],
},
'Cache': {
'nativeType': 'mozilla::dom::cache::Cache',
},
'CacheStorage': {
'nativeType': 'mozilla::dom::cache::CacheStorage',
},
'CameraCapabilities': {
'nativeType': 'mozilla::dom::CameraCapabilities',
'headerFile': 'DOMCameraCapabilities.h'

View File

@ -71,4 +71,5 @@ MSG_DEF(MSG_INVALID_ZOOMANDPAN_VALUE_ERROR, 0, JSEXN_RANGEERR, "Invalid zoom and
MSG_DEF(MSG_INVALID_TRANSFORM_ANGLE_ERROR, 0, JSEXN_RANGEERR, "Invalid transform angle.")
MSG_DEF(MSG_INVALID_RESPONSE_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid response status code.")
MSG_DEF(MSG_INVALID_REDIRECT_STATUSCODE_ERROR, 0, JSEXN_RANGEERR, "Invalid redirect status code.")
MSG_DEF(MSG_INVALID_URL_SCHEME, 2, JSEXN_TYPEERR, "{0} URL {1} must be either http:// or https://.")
MSG_DEF(MSG_RESPONSE_URL_IS_NULL, 0, JSEXN_TYPEERR, "Cannot set Response.finalURL when Response.url is null.")

40
dom/cache/Action.cpp vendored Normal file
View File

@ -0,0 +1,40 @@
/* -*- 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/Action.h"
namespace mozilla {
namespace dom {
namespace cache {
NS_IMPL_ISUPPORTS0(mozilla::dom::cache::Action::Resolver);
void
Action::CancelOnInitiatingThread()
{
NS_ASSERT_OWNINGTHREAD(Action);
MOZ_ASSERT(!mCanceled);
mCanceled = true;
}
Action::Action()
: mCanceled(false)
{
}
Action::~Action()
{
}
bool
Action::IsCanceled() const
{
return mCanceled;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

88
dom/cache/Action.h vendored Normal file
View File

@ -0,0 +1,88 @@
/* -*- 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_Action_h
#define mozilla_dom_cache_Action_h
#include "mozilla/Atomics.h"
#include "mozilla/dom/cache/Types.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
namespace cache {
class Action
{
public:
class Resolver : public nsISupports
{
protected:
// virtual because deleted through base class pointer
virtual ~Resolver() { }
public:
// Note: Action must drop Resolver ref after calling Resolve()!
// Note: Must be called on the same thread used to execute
// Action::RunOnTarget().
virtual void Resolve(nsresult aRv) = 0;
// We must use ISUPPORTS for our refcounting here because sub-classes also
// want to inherit interfaces like nsIRunnable.
NS_DECL_THREADSAFE_ISUPPORTS
};
// Execute operations on the target thread. Once complete call
// Resolver::Resolve(). This can be done sync or async.
// Note: Action should hold Resolver ref until its ready to call Resolve().
// Note: The "target" thread is determined when the Action is scheduled on
// Context. The Action should not assume any particular thread is used.
virtual void RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo) = 0;
// Called on initiating thread when the Action is canceled. The Action is
// responsible for calling Resolver::Resolve() as normal; either with a
// normal error code or NS_ERROR_ABORT. If CancelOnInitiatingThread() is
// called after Resolve() has already occurred, then the cancel can be
// ignored.
//
// Cancellation is a best effort to stop processing as soon as possible, but
// does not guarantee the Action will not run.
//
// Default implementation sets an internal cancellation flag that can be
// queried with IsCanceled().
virtual void CancelOnInitiatingThread();
// Executed on the initiating thread and is passed the nsresult given to
// Resolver::Resolve().
virtual void CompleteOnInitiatingThread(nsresult aRv) { }
// Executed on the initiating thread. If this Action will operate on the
// given cache ID then override this to return true.
virtual bool MatchesCacheId(CacheId aCacheId) const { return false; }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::Action)
NS_DECL_OWNINGTHREAD
protected:
Action();
// virtual because deleted through base class pointer
virtual ~Action();
// Check if this Action has been canceled. May be called from any thread,
// but typically used from the target thread.
bool IsCanceled() const;
private:
// Accessible from any thread.
Atomic<bool> mCanceled;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Action_h

53
dom/cache/ActorChild.cpp vendored Normal file
View File

@ -0,0 +1,53 @@
/* -*- 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/ActorChild.h"
#include "mozilla/dom/cache/Feature.h"
namespace mozilla {
namespace dom {
namespace cache {
void
ActorChild::SetFeature(Feature* aFeature)
{
MOZ_ASSERT(!mFeature);
mFeature = aFeature;
if (mFeature) {
mFeature->AddActor(this);
}
}
void
ActorChild::RemoveFeature()
{
if (mFeature) {
mFeature->RemoveActor(this);
mFeature = nullptr;
}
}
Feature*
ActorChild::GetFeature() const
{
return mFeature;
}
bool
ActorChild::FeatureNotified() const
{
return mFeature && mFeature->Notified();
}
ActorChild::~ActorChild()
{
MOZ_ASSERT(!mFeature);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

47
dom/cache/ActorChild.h vendored Normal file
View File

@ -0,0 +1,47 @@
/* -*- 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_ActioChild_h
#define mozilla_dom_cache_ActioChild_h
#include "nsRefPtr.h"
namespace mozilla {
namespace dom {
namespace cache {
class Feature;
class ActorChild
{
public:
virtual void
StartDestroy() = 0;
void
SetFeature(Feature* aFeature);
void
RemoveFeature();
Feature*
GetFeature() const;
bool
FeatureNotified() const;
protected:
~ActorChild();
private:
nsRefPtr<Feature> mFeature;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_ActioChild_h

65
dom/cache/ActorUtils.h vendored Normal file
View File

@ -0,0 +1,65 @@
/* -*- 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_ActorUtils_h
#define mozilla_dom_cache_ActorUtils_h
#include "mozilla/dom/cache/Types.h"
namespace mozilla {
namespace ipc {
class PBackgroundParent;
class PrincipalInfo;
}
namespace dom {
namespace cache {
class PCacheChild;
class PCacheParent;
class PCacheStreamControlChild;
class PCacheStreamControlParent;
class PCacheStorageChild;
class PCacheStorageParent;
// Factory methods for use in ipc/glue methods. Implemented in individual actor
// cpp files.
PCacheChild*
AllocPCacheChild();
void
DeallocPCacheChild(PCacheChild* aActor);
void
DeallocPCacheParent(PCacheParent* aActor);
PCacheStreamControlChild*
AllocPCacheStreamControlChild();
void
DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor);
void
DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor);
PCacheStorageParent*
AllocPCacheStorageParent(mozilla::ipc::PBackgroundParent* aManagingActor,
Namespace aNamespace,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
void
DeallocPCacheStorageChild(PCacheStorageChild* aActor);
void
DeallocPCacheStorageParent(PCacheStorageParent* aActor);
} // namesapce cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_ActorUtils_h

438
dom/cache/AutoUtils.cpp vendored Normal file
View File

@ -0,0 +1,438 @@
/* -*- 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/AutoUtils.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
#include "mozilla/dom/cache/StreamList.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/PBackgroundParent.h"
namespace {
using mozilla::unused;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::dom::cache::PCacheReadStreamOrVoid;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::FileDescriptorSetChild;
using mozilla::ipc::FileDescriptorSetParent;
using mozilla::ipc::OptionalFileDescriptorSet;
enum CleanupAction
{
ForgetFds,
DeleteFds
};
void
CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
{
if (aReadStream.fds().type() !=
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
return;
}
nsAutoTArray<FileDescriptor, 4> fds;
FileDescriptorSetChild* fdSetActor =
static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
MOZ_ASSERT(fdSetActor);
if (aAction == DeleteFds) {
unused << fdSetActor->Send__delete__(fdSetActor);
}
// FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
// unconditionally forget them here. The fds themselves are auto-closed in
// ~FileDescriptor since they originated in this process.
fdSetActor->ForgetFileDescriptors(fds);
}
void
CleanupChildFds(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
return;
}
CleanupChildFds(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
}
void
CleanupParentFds(PCacheReadStream& aReadStream, CleanupAction aAction)
{
if (aReadStream.fds().type() !=
OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
return;
}
nsAutoTArray<FileDescriptor, 4> fds;
FileDescriptorSetParent* fdSetActor =
static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
MOZ_ASSERT(fdSetActor);
if (aAction == DeleteFds) {
unused << fdSetActor->Send__delete__(fdSetActor);
}
// FileDescriptorSet doesn't clear its fds in its ActorDestroy, so we
// unconditionally forget them here. The fds themselves are auto-closed in
// ~FileDescriptor since they originated in this process.
fdSetActor->ForgetFileDescriptors(fds);
}
void
CleanupParentFds(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
return;
}
CleanupParentFds(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
}
} // anonymous namespace
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::ipc::PBackgroundParent;
AutoChildBase::AutoChildBase(TypeUtils* aTypeUtils)
: mTypeUtils(aTypeUtils)
, mSent(false)
{
MOZ_ASSERT(mTypeUtils);
}
AutoChildBase::~AutoChildBase()
{
}
// --------------------------------------------
AutoChildRequest::AutoChildRequest(TypeUtils* aTypeUtils)
: AutoChildBase(aTypeUtils)
{
mRequestOrVoid = void_t();
}
AutoChildRequest::~AutoChildRequest()
{
if (mRequestOrVoid.type() != PCacheRequestOrVoid::TPCacheRequest) {
return;
}
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupChildFds(mRequestOrVoid.get_PCacheRequest().body(), action);
}
void
AutoChildRequest::Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
ErrorResult& aRv)
{
MOZ_ASSERT(!mSent);
MOZ_ASSERT(mRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t);
mRequestOrVoid = PCacheRequest();
mTypeUtils->ToPCacheRequest(mRequestOrVoid.get_PCacheRequest(), aRequest,
aBodyAction, aReferrerAction, aSchemeAction, aRv);
}
const PCacheRequest&
AutoChildRequest::SendAsRequest()
{
MOZ_ASSERT(mRequestOrVoid.type() == PCacheRequestOrVoid::TPCacheRequest);
return mRequestOrVoid.get_PCacheRequest();
}
const PCacheRequestOrVoid&
AutoChildRequest::SendAsRequestOrVoid()
{
return mRequestOrVoid;
}
// --------------------------------------------
AutoChildRequestList::AutoChildRequestList(TypeUtils* aTypeUtils,
uint32_t aCapacity)
: AutoChildBase(aTypeUtils)
{
mRequestList.SetCapacity(aCapacity);
}
AutoChildRequestList::~AutoChildRequestList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
CleanupChildFds(mRequestList[i].body(), action);
}
}
void
AutoChildRequestList::Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
{
MOZ_ASSERT(!mSent);
// 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.
PCacheRequest* request = mRequestList.AppendElement();
mTypeUtils->ToPCacheRequest(*request, aRequest, aBodyAction, aReferrerAction,
aSchemeAction, aRv);
if (aRv.Failed()) {
mRequestList.RemoveElementAt(mRequestList.Length() - 1);
}
}
const nsTArray<PCacheRequest>&
AutoChildRequestList::SendAsRequestList()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mRequestList;
}
// --------------------------------------------
AutoChildRequestResponse::AutoChildRequestResponse(TypeUtils* aTypeUtils)
: AutoChildBase(aTypeUtils)
{
// Default IPC-generated constructor does not initialize these correctly
// and we check them later when cleaning up.
mRequestResponse.request().body() = void_t();
mRequestResponse.response().body() = void_t();
}
AutoChildRequestResponse::~AutoChildRequestResponse()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupChildFds(mRequestResponse.request().body(), action);
CleanupChildFds(mRequestResponse.response().body(), action);
}
void
AutoChildRequestResponse::Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
{
MOZ_ASSERT(!mSent);
mTypeUtils->ToPCacheRequest(mRequestResponse.request(), aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
}
void
AutoChildRequestResponse::Add(Response& aResponse, ErrorResult& aRv)
{
MOZ_ASSERT(!mSent);
mTypeUtils->ToPCacheResponse(mRequestResponse.response(), aResponse, aRv);
}
const CacheRequestResponse&
AutoChildRequestResponse::SendAsRequestResponse()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mRequestResponse;
}
// --------------------------------------------
AutoParentBase::AutoParentBase(PBackgroundParent* aManager)
: mManager(aManager)
, mStreamControl(nullptr)
, mSent(false)
{
MOZ_ASSERT(mManager);
}
AutoParentBase::~AutoParentBase()
{
if (!mSent && mStreamControl) {
unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
}
}
void
AutoParentBase::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
PCacheReadStream* aReadStreamOut)
{
MOZ_ASSERT(aStreamList);
MOZ_ASSERT(aReadStreamOut);
MOZ_ASSERT(!mSent);
nsCOMPtr<nsIInputStream> stream = aStreamList->Extract(aId);
MOZ_ASSERT(stream);
if (!mStreamControl) {
mStreamControl = static_cast<CacheStreamControlParent*>(
mManager->SendPCacheStreamControlConstructor(new CacheStreamControlParent()));
// If this failed, then the child process is gone. Warn and allow actor
// cleanup to proceed as normal.
if (!mStreamControl) {
NS_WARNING("Cache failed to create stream control actor.");
return;
}
}
aStreamList->SetStreamControl(mStreamControl);
nsRefPtr<ReadStream> readStream = ReadStream::Create(mStreamControl,
aId, stream);
readStream->Serialize(aReadStreamOut);
}
// --------------------------------------------
AutoParentRequestList::AutoParentRequestList(PBackgroundParent* aManager,
uint32_t aCapacity)
: AutoParentBase(aManager)
{
mRequestList.SetCapacity(aCapacity);
}
AutoParentRequestList::~AutoParentRequestList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
CleanupParentFds(mRequestList[i].body(), action);
}
}
void
AutoParentRequestList::Add(const SavedRequest& aSavedRequest,
StreamList* aStreamList)
{
MOZ_ASSERT(!mSent);
mRequestList.AppendElement(aSavedRequest.mValue);
PCacheRequest& request = mRequestList.LastElement();
if (!aSavedRequest.mHasBodyId) {
request.body() = void_t();
return;
}
request.body() = PCacheReadStream();
SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
&request.body().get_PCacheReadStream());
}
const nsTArray<PCacheRequest>&
AutoParentRequestList::SendAsRequestList()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mRequestList;
}
// --------------------------------------------
AutoParentResponseList::AutoParentResponseList(PBackgroundParent* aManager,
uint32_t aCapacity)
: AutoParentBase(aManager)
{
mResponseList.SetCapacity(aCapacity);
}
AutoParentResponseList::~AutoParentResponseList()
{
CleanupAction action = mSent ? ForgetFds : DeleteFds;
for (uint32_t i = 0; i < mResponseList.Length(); ++i) {
CleanupParentFds(mResponseList[i].body(), action);
}
}
void
AutoParentResponseList::Add(const SavedResponse& aSavedResponse,
StreamList* aStreamList)
{
MOZ_ASSERT(!mSent);
mResponseList.AppendElement(aSavedResponse.mValue);
PCacheResponse& response = mResponseList.LastElement();
if (!aSavedResponse.mHasBodyId) {
response.body() = void_t();
return;
}
response.body() = PCacheReadStream();
SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
&response.body().get_PCacheReadStream());
}
const nsTArray<PCacheResponse>&
AutoParentResponseList::SendAsResponseList()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mResponseList;
}
// --------------------------------------------
AutoParentResponseOrVoid::AutoParentResponseOrVoid(ipc::PBackgroundParent* aManager)
: AutoParentBase(aManager)
{
mResponseOrVoid = void_t();
}
AutoParentResponseOrVoid::~AutoParentResponseOrVoid()
{
if (mResponseOrVoid.type() != PCacheResponseOrVoid::TPCacheResponse) {
return;
}
CleanupAction action = mSent ? ForgetFds : DeleteFds;
CleanupParentFds(mResponseOrVoid.get_PCacheResponse().body(), action);
}
void
AutoParentResponseOrVoid::Add(const SavedResponse& aSavedResponse,
StreamList* aStreamList)
{
MOZ_ASSERT(!mSent);
mResponseOrVoid = aSavedResponse.mValue;
PCacheResponse& response = mResponseOrVoid.get_PCacheResponse();
if (!aSavedResponse.mHasBodyId) {
response.body() = void_t();
return;
}
response.body() = PCacheReadStream();
SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
&response.body().get_PCacheReadStream());
}
const PCacheResponseOrVoid&
AutoParentResponseOrVoid::SendAsResponseOrVoid()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mResponseOrVoid;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

176
dom/cache/AutoUtils.h vendored Normal file
View File

@ -0,0 +1,176 @@
/* -*- 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_AutoUtils_h
#define mozilla_dom_cache_AutoUtils_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "nsTArray.h"
struct nsID;
namespace mozilla {
class ErrorResult;
namespace ipc {
class PBackgroundParent;
}
namespace dom {
class InternalRequest;
class OwningRequestOrUSVString;
class RequestOrUSVString;
template<typename T> class Optional;
namespace cache {
class CacheStreamControlParent;
struct SavedRequest;
struct SavedResponse;
class StreamList;
// A collection of RAII-style helper classes to ensure that IPC
// FileDescriptorSet actors are properly cleaned up. The user of these actors
// must manually either Forget() the Fds or Send__delete__() the actor
// depending on if the descriptors were actually sent.
//
// Note, these should only be used when *sending* streams across IPC. The
// deserialization case is handled by creating a ReadStream object.
class MOZ_STACK_CLASS AutoChildBase
{
protected:
typedef TypeUtils::BodyAction BodyAction;
typedef TypeUtils::ReferrerAction ReferrerAction;
typedef TypeUtils::SchemeAction SchemeAction;
AutoChildBase(TypeUtils* aTypeUtils);
virtual ~AutoChildBase() = 0;
TypeUtils* mTypeUtils;
bool mSent;
};
class MOZ_STACK_CLASS AutoChildRequest MOZ_FINAL : public AutoChildBase
{
public:
explicit AutoChildRequest(TypeUtils* aTypeUtils);
~AutoChildRequest();
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
ErrorResult& aRv);
const PCacheRequest& SendAsRequest();
const PCacheRequestOrVoid& SendAsRequestOrVoid();
private:
PCacheRequestOrVoid mRequestOrVoid;
};
class MOZ_STACK_CLASS AutoChildRequestList MOZ_FINAL : public AutoChildBase
{
public:
AutoChildRequestList(TypeUtils* aTypeUtils, uint32_t aCapacity);
~AutoChildRequestList();
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
ErrorResult& aRv);
const nsTArray<PCacheRequest>& SendAsRequestList();
private:
// Allocates ~5k inline in the stack-only class
nsAutoTArray<PCacheRequest, 32> mRequestList;
};
class MOZ_STACK_CLASS AutoChildRequestResponse MOZ_FINAL : public AutoChildBase
{
public:
explicit AutoChildRequestResponse(TypeUtils* aTypeUtils);
~AutoChildRequestResponse();
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
ErrorResult& aRv);
void Add(Response& aResponse, ErrorResult& aRv);
const CacheRequestResponse& SendAsRequestResponse();
private:
CacheRequestResponse mRequestResponse;
};
class MOZ_STACK_CLASS AutoParentBase
{
protected:
explicit AutoParentBase(mozilla::ipc::PBackgroundParent* aManager);
virtual ~AutoParentBase() = 0;
void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
PCacheReadStream* aReadStreamOut);
mozilla::ipc::PBackgroundParent* mManager;
CacheStreamControlParent* mStreamControl;
bool mSent;
};
class MOZ_STACK_CLASS AutoParentRequestList MOZ_FINAL : public AutoParentBase
{
public:
AutoParentRequestList(mozilla::ipc::PBackgroundParent* aManager,
uint32_t aCapacity);
~AutoParentRequestList();
void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
const nsTArray<PCacheRequest>& SendAsRequestList();
private:
// Allocates ~5k inline in the stack-only class
nsAutoTArray<PCacheRequest, 32> mRequestList;
};
class MOZ_STACK_CLASS AutoParentResponseList MOZ_FINAL : public AutoParentBase
{
public:
AutoParentResponseList(mozilla::ipc::PBackgroundParent* aManager,
uint32_t aCapacity);
~AutoParentResponseList();
void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
const nsTArray<PCacheResponse>& SendAsResponseList();
private:
// Allocates ~4k inline in the stack-only class
nsAutoTArray<PCacheResponse, 32> mResponseList;
};
class MOZ_STACK_CLASS AutoParentResponseOrVoid MOZ_FINAL : public AutoParentBase
{
public:
explicit AutoParentResponseOrVoid(mozilla::ipc::PBackgroundParent* aManager);
~AutoParentResponseOrVoid();
void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
const PCacheResponseOrVoid& SendAsResponseOrVoid();
private:
PCacheResponseOrVoid mResponseOrVoid;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_AutoUtils_h

586
dom/cache/Cache.cpp vendored Normal file
View File

@ -0,0 +1,586 @@
/* -*- 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/Cache.h"
#include "mozilla/dom/Headers.h"
#include "mozilla/dom/InternalResponse.h"
#include "mozilla/dom/Promise.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/ReadStream.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "nsIGlobalObject.h"
#include "nsNetUtil.h"
namespace {
using mozilla::ErrorResult;
using mozilla::dom::MSG_INVALID_REQUEST_METHOD;
using mozilla::dom::OwningRequestOrUSVString;
using mozilla::dom::Request;
using mozilla::dom::RequestOrUSVString;
static bool
IsValidPutRequestMethod(const Request& aRequest, ErrorResult& aRv)
{
nsAutoCString method;
aRequest.GetMethod(method);
bool valid = method.LowerCaseEqualsLiteral("get");
if (!valid) {
NS_ConvertASCIItoUTF16 label(method);
aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
}
return valid;
}
static bool
IsValidPutRequestMethod(const RequestOrUSVString& aRequest,
ErrorResult& aRv)
{
// If the provided request is a string URL, then it will default to
// a valid http method automatically.
if (!aRequest.IsRequest()) {
return true;
}
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 {
using mozilla::ErrorResult;
using mozilla::unused;
using mozilla::dom::workers::GetCurrentThreadWorkerPrivate;
using mozilla::dom::workers::WorkerPrivate;
NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::Cache);
NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::Cache);
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Cache, mGlobal, mRequestPromises)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
: mGlobal(aGlobal)
, mActor(aActor)
{
MOZ_ASSERT(mGlobal);
MOZ_ASSERT(mActor);
mActor->SetListener(this);
}
already_AddRefed<Promise>
Cache::Match(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
AutoChildRequest request(this);
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
PCacheQueryParams params;
ToPCacheQueryParams(params, aOptions);
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendMatch(requestId, request.SendAsRequest(), params);
return promise.forget();
}
already_AddRefed<Promise>
Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
AutoChildRequest request(this);
if (aRequest.WasPassed()) {
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
}
PCacheQueryParams params;
ToPCacheQueryParams(params, aOptions);
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendMatchAll(requestId, request.SendAsRequestOrVoid(),
params);
return promise.forget();
}
already_AddRefed<Promise>
Cache::Add(const RequestOrUSVString& aRequest, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
if (!IsValidPutRequestMethod(aRequest, aRv)) {
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
AutoChildRequestList requests(this, 1);
requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendAddAll(requestId, requests.SendAsRequestList());
return promise.forget();
}
already_AddRefed<Promise>
Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
// If there is no work to do, then resolve immediately
if (aRequests.IsEmpty()) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
AutoChildRequestList requests(this, aRequests.Length());
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
if (!IsValidPutRequestMethod(aRequests[i], aRv)) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequests[i], ReadBody,
aRv);
if (aRv.Failed()) {
return nullptr;
}
requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme,
aRv);
if (aRv.Failed()) {
return nullptr;
}
}
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendAddAll(requestId, requests.SendAsRequestList());
return promise.forget();
}
already_AddRefed<Promise>
Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
if (!IsValidPutRequestMethod(aRequest, aRv)) {
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, ReadBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
AutoChildRequestResponse put(this);
put.Add(ir, ReadBody, PassThroughReferrer, TypeErrorOnInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
put.Add(aResponse, aRv);
if (aRv.Failed()) {
return nullptr;
}
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendPut(requestId, put.SendAsRequestResponse());
return promise.forget();
}
already_AddRefed<Promise>
Cache::Delete(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
AutoChildRequest request(this);
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
PCacheQueryParams params;
ToPCacheQueryParams(params, aOptions);
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendDelete(requestId, request.SendAsRequest(), params);
return promise.forget();
}
already_AddRefed<Promise>
Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
AutoChildRequest request(this);
if (aRequest.WasPassed()) {
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) {
return nullptr;
}
}
PCacheQueryParams params;
ToPCacheQueryParams(params, aOptions);
RequestId requestId = AddRequestPromise(promise, aRv);
unused << mActor->SendKeys(requestId, request.SendAsRequestOrVoid(), params);
return promise.forget();
}
// static
bool
Cache::PrefEnabled(JSContext* aCx, JSObject* aObj)
{
using mozilla::dom::workers::WorkerPrivate;
using mozilla::dom::workers::GetWorkerPrivateFromContext;
// If we're on the main thread, then check the pref directly.
if (NS_IsMainThread()) {
bool enabled = false;
Preferences::GetBool("dom.caches.enabled", &enabled);
return enabled;
}
// Otherwise check the pref via the work private helper
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
if (!workerPrivate) {
return false;
}
return workerPrivate->DOMCachesEnabled();
}
nsISupports*
Cache::GetParentObject() const
{
return mGlobal;
}
JSObject*
Cache::WrapObject(JSContext* aContext)
{
return CacheBinding::Wrap(aContext, this);
}
void
Cache::DestroyInternal(CacheChild* aActor)
{
MOZ_ASSERT(mActor);
MOZ_ASSERT(mActor == aActor);
mActor->ClearListener();
mActor = nullptr;
}
void
Cache::RecvMatchResponse(RequestId aRequestId, nsresult aRv,
const PCacheResponseOrVoid& aResponse)
{
// Convert the response immediately if its present. This ensures that
// any stream actors are cleaned up, even if we error out below.
nsRefPtr<Response> response;
if (aResponse.type() == PCacheResponseOrVoid::TPCacheResponse) {
response = ToResponse(aResponse);
}
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
if (!response) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return;
}
promise->MaybeResolve(response);
}
void
Cache::RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<PCacheResponse>& aResponses)
{
// Convert responses immediately. This ensures that any stream actors are
// cleaned up, even if we error out below.
nsAutoTArray<nsRefPtr<Response>, 256> responses;
responses.SetCapacity(aResponses.Length());
for (uint32_t i = 0; i < aResponses.Length(); ++i) {
nsRefPtr<Response> response = ToResponse(aResponses[i]);
responses.AppendElement(response.forget());
}
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(responses);
}
void
Cache::RecvAddAllResponse(RequestId aRequestId, nsresult aRv)
{
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(JS::UndefinedHandleValue);
}
void
Cache::RecvPutResponse(RequestId aRequestId, nsresult aRv)
{
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(JS::UndefinedHandleValue);
}
void
Cache::RecvDeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess)
{
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(aSuccess);
}
void
Cache::RecvKeysResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<PCacheRequest>& aRequests)
{
// Convert requests immediately. This ensures that any stream actors are
// cleaned up, even if we error out below.
nsAutoTArray<nsRefPtr<Request>, 256> requests;
requests.SetCapacity(aRequests.Length());
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
nsRefPtr<Request> request = ToRequest(aRequests[i]);
requests.AppendElement(request.forget());
}
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(requests);
}
nsIGlobalObject*
Cache::GetGlobalObject() const
{
return mGlobal;
}
#ifdef DEBUG
void
Cache::AssertOwningThread() const
{
NS_ASSERT_OWNINGTHREAD(Cache);
}
#endif
void
Cache::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
// Do nothing. The Promise will automatically drop the ref to us after
// calling the callback. This is what we want as we only registered in order
// to be held alive via the Promise handle.
}
void
Cache::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
// Do nothing. The Promise will automatically drop the ref to us after
// calling the callback. This is what we want as we only registered in order
// to be held alive via the Promise handle.
}
Cache::~Cache()
{
if (mActor) {
mActor->StartDestroy();
// DestroyInternal() is called synchronously by StartDestroy(). So we
// should have already cleared the mActor.
MOZ_ASSERT(!mActor);
}
}
RequestId
Cache::AddRequestPromise(Promise* aPromise, ErrorResult& aRv)
{
MOZ_ASSERT(aPromise);
MOZ_ASSERT(!mRequestPromises.Contains(aPromise));
// Register ourself as a promise handler so that the promise will hold us
// alive. This allows the client code to drop the ref to the Cache
// object and just keep their promise. This is fairly common in promise
// chaining code.
aPromise->AppendNativeHandler(this);
mRequestPromises.AppendElement(aPromise);
// (Ab)use the promise pointer as our request ID. This is a fast, thread-safe
// way to get a unique ID for the promise to be resolved later.
return reinterpret_cast<RequestId>(aPromise);
}
already_AddRefed<Promise>
Cache::RemoveRequestPromise(RequestId aRequestId)
{
MOZ_ASSERT(aRequestId != INVALID_REQUEST_ID);
for (uint32_t i = 0; i < mRequestPromises.Length(); ++i) {
nsRefPtr<Promise>& promise = mRequestPromises.ElementAt(i);
// To be safe, only cast promise pointers to our integer RequestId
// type and never cast an integer to a pointer.
if (aRequestId == reinterpret_cast<RequestId>(promise.get())) {
nsRefPtr<Promise> ref;
ref.swap(promise);
mRequestPromises.RemoveElementAt(i);
return ref.forget();
}
}
MOZ_ASSERT_UNREACHABLE("Received response without a matching promise!");
return nullptr;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

133
dom/cache/Cache.h vendored Normal file
View File

@ -0,0 +1,133 @@
/* -*- 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_Cache_h
#define mozilla_dom_cache_Cache_h
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsWrapperCache.h"
class nsIGlobalObject;
namespace mozilla {
class ErrorResult;
namespace ipc {
class IProtocol;
}
namespace dom {
class OwningRequestOrUSVString;
class Promise;
struct CacheQueryOptions;
class RequestOrUSVString;
class Response;
template<typename T> class Optional;
template<typename T> class Sequence;
namespace cache {
class CacheChild;
class PCacheRequest;
class PCacheRequestOrVoid;
class PCacheResponse;
class PCacheResponseOrVoid;
class PCacheStreamControlChild;
class Cache MOZ_FINAL : public PromiseNativeHandler
, public nsWrapperCache
, public TypeUtils
{
public:
Cache(nsIGlobalObject* aGlobal, CacheChild* aActor);
// webidl interface methods
already_AddRefed<Promise>
Match(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
ErrorResult& aRv);
already_AddRefed<Promise>
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,
ErrorResult& aRv);
already_AddRefed<Promise>
Put(const RequestOrUSVString& aRequest, Response& aResponse,
ErrorResult& aRv);
already_AddRefed<Promise>
Delete(const RequestOrUSVString& aRequest, const CacheQueryOptions& aOptions,
ErrorResult& aRv);
already_AddRefed<Promise>
Keys(const Optional<RequestOrUSVString>& aRequest,
const CacheQueryOptions& aParams, ErrorResult& aRv);
// binding methods
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
nsISupports* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aContext) MOZ_OVERRIDE;
// Called when CacheChild actor is being destroyed
void DestroyInternal(CacheChild* aActor);
// methods forwarded from CacheChild
void RecvMatchResponse(RequestId aRequestId, nsresult aRv,
const PCacheResponseOrVoid& aResponse);
void RecvMatchAllResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<PCacheResponse>& aResponses);
void RecvAddAllResponse(RequestId aRequestId, nsresult aRv);
void RecvPutResponse(RequestId aRequestId, nsresult aRv);
void RecvDeleteResponse(RequestId aRequestId, nsresult aRv,
bool aSuccess);
void RecvKeysResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<PCacheRequest>& aRequests);
// TypeUtils methods
virtual nsIGlobalObject*
GetGlobalObject() const MOZ_OVERRIDE;
#ifdef DEBUG
virtual void AssertOwningThread() const MOZ_OVERRIDE;
#endif
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
private:
~Cache();
// TODO: Replace with actor-per-request model during refactor (bug 1110485)
RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
nsCOMPtr<nsIGlobalObject> mGlobal;
CacheChild* mActor;
nsTArray<nsRefPtr<Promise>> mRequestPromises;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Cache)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_Cache_h

187
dom/cache/CacheChild.cpp vendored Normal file
View File

@ -0,0 +1,187 @@
/* -*- 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/CacheChild.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/StreamUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
// Declared in ActorUtils.h
PCacheChild*
AllocPCacheChild()
{
return new CacheChild();
}
// Declared in ActorUtils.h
void
DeallocPCacheChild(PCacheChild* aActor)
{
delete aActor;
}
CacheChild::CacheChild()
: mListener(nullptr)
{
MOZ_COUNT_CTOR(cache::CacheChild);
}
CacheChild::~CacheChild()
{
MOZ_COUNT_DTOR(cache::CacheChild);
NS_ASSERT_OWNINGTHREAD(CacheChild);
MOZ_ASSERT(!mListener);
}
void
CacheChild::SetListener(Cache* aListener)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
MOZ_ASSERT(!mListener);
mListener = aListener;
MOZ_ASSERT(mListener);
}
void
CacheChild::ClearListener()
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
MOZ_ASSERT(mListener);
mListener = nullptr;
}
void
CacheChild::StartDestroy()
{
nsRefPtr<Cache> listener = mListener;
// StartDestroy() can get called from either Cache or the Feature.
// Theoretically we can get double called if the right race happens. Handle
// that by just ignoring the second StartDestroy() call.
if (!listener) {
return;
}
listener->DestroyInternal(this);
// Cache listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
// Start actor destruction from parent process
unused << SendTeardown();
}
void
CacheChild::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
nsRefPtr<Cache> listener = mListener;
if (listener) {
listener->DestroyInternal(this);
// Cache listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
}
RemoveFeature();
}
bool
CacheChild::RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
AddFeatureToStreamChild(aResponse, GetFeature());
nsRefPtr<Cache> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aResponse);
return true;
}
listener->RecvMatchResponse(requestId, aRv, aResponse);
return true;
}
bool
CacheChild::RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheResponse>&& aResponses)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
AddFeatureToStreamChild(aResponses, GetFeature());
nsRefPtr<Cache> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aResponses);
return true;
}
listener->RecvMatchAllResponse(requestId, aRv, aResponses);
return true;
}
bool
CacheChild::RecvAddAllResponse(const RequestId& requestId, const nsresult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
nsRefPtr<Cache> listener = mListener;
if (listener) {
listener->RecvAddAllResponse(requestId, aRv);
}
return true;
}
bool
CacheChild::RecvPutResponse(const RequestId& aRequestId, const nsresult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
nsRefPtr<Cache> listener = mListener;
if (listener) {
listener->RecvPutResponse(aRequestId, aRv);
}
return true;
}
bool
CacheChild::RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
const bool& result)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
nsRefPtr<Cache> listener = mListener;
if (listener) {
listener->RecvDeleteResponse(requestId, aRv, result);
}
return true;
}
bool
CacheChild::RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheRequest>&& aRequests)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
AddFeatureToStreamChild(aRequests, GetFeature());
nsRefPtr<Cache> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aRequests);
return true;
}
listener->RecvKeysResponse(requestId, aRv, aRequests);
return true;
}
} // namespace cache
} // namespace dom
} // namesapce mozilla

75
dom/cache/CacheChild.h vendored Normal file
View File

@ -0,0 +1,75 @@
/* -*- 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_CacheChild_h
#define mozilla_dom_cache_CacheChild_h
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/PCacheChild.h"
namespace mozilla {
namespace dom {
namespace cache {
class Cache;
class CacheChild MOZ_FINAL : public PCacheChild
, public ActorChild
{
public:
CacheChild();
~CacheChild();
void SetListener(Cache* aListener);
// Must be called by the associated Cache listener in its ActorDestroy()
// method. Also, Cache must Send__delete__() the actor in its destructor to
// trigger ActorDestroy() if it has not been called yet.
void ClearListener();
// ActorChild methods
// Synchronously call ActorDestroy on our Cache listener and then start the
// actor destruction asynchronously from the parent-side.
virtual void StartDestroy() MOZ_OVERRIDE;
private:
// PCacheChild methods
virtual void
ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool
RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse) MOZ_OVERRIDE;
virtual bool
RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheResponse>&& responses) MOZ_OVERRIDE;
virtual bool
RecvAddAllResponse(const RequestId& requestId,
const nsresult& aRv) MOZ_OVERRIDE;
virtual bool
RecvPutResponse(const RequestId& aRequestId,
const nsresult& aRv) MOZ_OVERRIDE;
virtual bool
RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
const bool& result) MOZ_OVERRIDE;
virtual bool
RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheRequest>&& requests) MOZ_OVERRIDE;
// Use a weak ref so actor does not hold DOM object alive past content use.
// The Cache object must call ClearListener() to null this before its
// destroyed.
Cache* MOZ_NON_OWNING_REF mListener;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheChild_h

24
dom/cache/CacheInitData.ipdlh vendored Normal file
View File

@ -0,0 +1,24 @@
/* 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 PBackgroundSharedTypes;
using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
namespace mozilla {
namespace dom {
namespace cache {
// Data needed to initialize a CacheStorage or Cache backend. Don't put
// this with the other types in PCacheTypes.ipdlh since we want to import
// it into PBackground.ipdl.
struct CacheInitData
{
Namespace namespaceEnum;
PrincipalInfo principalInfo;
};
} // namespace cache
} // namespace dom
} // namespace mozilla

291
dom/cache/CacheParent.cpp vendored Normal file
View File

@ -0,0 +1,291 @@
/* -*- 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/CacheParent.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
#include "mozilla/dom/cache/StreamList.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/PFileDescriptorSetParent.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::ipc::FileDescriptorSetParent;
using mozilla::ipc::PFileDescriptorSetParent;
// Declared in ActorUtils.h
void
DeallocPCacheParent(PCacheParent* aActor)
{
delete aActor;
}
CacheParent::CacheParent(cache::Manager* aManager, CacheId aCacheId)
: mManager(aManager)
, mCacheId(aCacheId)
{
MOZ_COUNT_CTOR(cache::CacheParent);
MOZ_ASSERT(mManager);
mManager->AddRefCacheId(mCacheId);
}
CacheParent::~CacheParent()
{
MOZ_COUNT_DTOR(cache::CacheParent);
MOZ_ASSERT(!mManager);
MOZ_ASSERT(mFetchPutList.IsEmpty());
}
void
CacheParent::ActorDestroy(ActorDestroyReason aReason)
{
MOZ_ASSERT(mManager);
for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
mFetchPutList[i]->ClearListener();
}
mFetchPutList.Clear();
mManager->RemoveListener(this);
mManager->ReleaseCacheId(mCacheId);
mManager = nullptr;
}
bool
CacheParent::RecvTeardown()
{
if (!Send__delete__(this)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send delete.");
}
return true;
}
bool
CacheParent::RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams)
{
MOZ_ASSERT(mManager);
mManager->CacheMatch(this, aRequestId, mCacheId, aRequest,
aParams);
return true;
}
bool
CacheParent::RecvMatchAll(const RequestId& aRequestId,
const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams)
{
MOZ_ASSERT(mManager);
mManager->CacheMatchAll(this, aRequestId, mCacheId, aRequest, aParams);
return true;
}
bool
CacheParent::RecvAddAll(const RequestId& aRequestId,
nsTArray<PCacheRequest>&& aRequests)
{
nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreams;
requestStreams.SetCapacity(aRequests.Length());
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
requestStreams.AppendElement(DeserializeCacheStream(aRequests[i].body()));
}
nsRefPtr<FetchPut> fetchPut;
nsresult rv = FetchPut::Create(this, mManager, aRequestId, mCacheId,
aRequests, requestStreams,
getter_AddRefs(fetchPut));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendAddAllResponse(aRequestId, rv)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send AddAll response.");
}
return true;
}
mFetchPutList.AppendElement(fetchPut.forget());
return true;
}
bool
CacheParent::RecvPut(const RequestId& aRequestId,
const CacheRequestResponse& aPut)
{
MOZ_ASSERT(mManager);
nsAutoTArray<CacheRequestResponse, 1> putList;
putList.AppendElement(aPut);
nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> requestStreamList;
nsAutoTArray<nsCOMPtr<nsIInputStream>, 1> responseStreamList;
requestStreamList.AppendElement(
DeserializeCacheStream(aPut.request().body()));
responseStreamList.AppendElement(
DeserializeCacheStream(aPut.response().body()));
mManager->CachePutAll(this, aRequestId, mCacheId, putList, requestStreamList,
responseStreamList);
return true;
}
bool
CacheParent::RecvDelete(const RequestId& aRequestId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams)
{
MOZ_ASSERT(mManager);
mManager->CacheDelete(this, aRequestId, mCacheId, aRequest, aParams);
return true;
}
bool
CacheParent::RecvKeys(const RequestId& aRequestId,
const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams)
{
MOZ_ASSERT(mManager);
mManager->CacheKeys(this, aRequestId, mCacheId, aRequest, aParams);
return true;
}
void
CacheParent::OnCacheMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aSavedResponse,
StreamList* aStreamList)
{
AutoParentResponseOrVoid response(Manager());
// no match
if (NS_FAILED(aRv) || !aSavedResponse || !aStreamList) {
if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send Match response.");
}
return;
}
if (aSavedResponse) {
response.Add(*aSavedResponse, aStreamList);
}
if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send Match response.");
}
}
void
CacheParent::OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedResponse>& aSavedResponses,
StreamList* aStreamList)
{
AutoParentResponseList responses(Manager(), aSavedResponses.Length());
for (uint32_t i = 0; i < aSavedResponses.Length(); ++i) {
responses.Add(aSavedResponses[i], aStreamList);
}
if (!SendMatchAllResponse(aRequestId, aRv, responses.SendAsResponseList())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send MatchAll response.");
}
}
void
CacheParent::OnCachePutAll(RequestId aRequestId, nsresult aRv)
{
if (!SendPutResponse(aRequestId, aRv)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send Put response.");
}
}
void
CacheParent::OnCacheDelete(RequestId aRequestId, nsresult aRv, bool aSuccess)
{
if (!SendDeleteResponse(aRequestId, aRv, aSuccess)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send Delete response.");
}
}
void
CacheParent::OnCacheKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedRequest>& aSavedRequests,
StreamList* aStreamList)
{
AutoParentRequestList requests(Manager(), aSavedRequests.Length());
for (uint32_t i = 0; i < aSavedRequests.Length(); ++i) {
requests.Add(aSavedRequests[i], aStreamList);
}
if (!SendKeysResponse(aRequestId, aRv, requests.SendAsRequestList())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send Keys response.");
}
}
void
CacheParent::OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, nsresult aRv)
{
aFetchPut->ClearListener();
mFetchPutList.RemoveElement(aFetchPut);
if (!SendAddAllResponse(aRequestId, aRv)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to send AddAll response.");
}
}
already_AddRefed<nsIInputStream>
CacheParent::DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid)
{
if (aStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
return nullptr;
}
const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(readStream);
if (stream) {
return stream.forget();
}
nsAutoTArray<FileDescriptor, 4> fds;
if (readStream.fds().type() ==
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
FileDescriptorSetParent* fdSetActor =
static_cast<FileDescriptorSetParent*>(readStream.fds().get_PFileDescriptorSetParent());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
if (!fdSetActor->Send__delete__(fdSetActor)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to delete fd set actor.");
}
}
return DeserializeInputStream(readStream.params(), fds);
}
} // namespace cache
} // namespace dom
} // namesapce mozilla

87
dom/cache/CacheParent.h vendored Normal file
View File

@ -0,0 +1,87 @@
/* -*- 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_CacheParent_h
#define mozilla_dom_cache_CacheParent_h
#include "mozilla/dom/cache/FetchPut.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/PCacheParent.h"
#include "mozilla/dom/cache/Types.h"
struct nsID;
template <class T> class nsRefPtr;
namespace mozilla {
namespace dom {
namespace cache {
class CacheDBConnection;
class CacheStreamControlParent;
struct SavedResponse;
struct StreamHolder;
class CacheParent MOZ_FINAL : public PCacheParent
, public Manager::Listener
, public FetchPut::Listener
{
public:
CacheParent(cache::Manager* aManager, CacheId aCacheId);
virtual ~CacheParent();
private:
// PCacheParent method
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool RecvTeardown() MOZ_OVERRIDE;
virtual bool
RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) MOZ_OVERRIDE;
virtual bool
RecvMatchAll(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams) MOZ_OVERRIDE;
virtual bool
RecvAddAll(const RequestId& aRequestId,
nsTArray<PCacheRequest>&& aRequests) MOZ_OVERRIDE;
virtual bool
RecvPut(const RequestId& aRequestId, const CacheRequestResponse& aPut);
virtual bool
RecvDelete(const RequestId& aRequestId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) MOZ_OVERRIDE;
virtual bool
RecvKeys(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams) MOZ_OVERRIDE;
// Manager::Listener methods
virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aSavedResponse,
StreamList* aStreamList) MOZ_OVERRIDE;
virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedResponse>& aSavedResponses,
StreamList* aStreamList) MOZ_OVERRIDE;
virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) MOZ_OVERRIDE;
virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
bool aSuccess) MOZ_OVERRIDE;
virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedRequest>& aSavedRequests,
StreamList* aStreamList) MOZ_OVERRIDE;
// FetchPut::Listener methods
virtual void OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId,
nsresult aRv) MOZ_OVERRIDE;
already_AddRefed<nsIInputStream>
DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid);
nsRefPtr<cache::Manager> mManager;
const CacheId mCacheId;
nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheParent_h

631
dom/cache/CacheStorage.cpp vendored Normal file
View File

@ -0,0 +1,631 @@
/* -*- 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/CacheStorage.h"
#include "mozilla/unused.h"
#include "mozilla/dom/CacheStorageBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CacheStorageChild.h"
#include "mozilla/dom/cache/Feature.h"
#include "mozilla/dom/cache/PCacheChild.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsIGlobalObject.h"
#include "nsIScriptSecurityManager.h"
#include "WorkerPrivate.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::unused;
using mozilla::ErrorResult;
using mozilla::dom::workers::WorkerPrivate;
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::PBackgroundChild;
using mozilla::ipc::IProtocol;
using mozilla::ipc::PrincipalInfo;
using mozilla::ipc::PrincipalToPrincipalInfo;
NS_IMPL_CYCLE_COLLECTING_ADDREF(mozilla::dom::cache::CacheStorage);
NS_IMPL_CYCLE_COLLECTING_RELEASE(mozilla::dom::cache::CacheStorage);
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CacheStorage, mGlobal,
mRequestPromises)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_END
// static
already_AddRefed<CacheStorage>
CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
nsIPrincipal* aPrincipal, ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(NS_IsMainThread());
bool nullPrincipal;
nsresult rv = aPrincipal->GetIsNullPrincipal(&nullPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
if (nullPrincipal) {
NS_WARNING("CacheStorage not supported on null principal.");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// An unknown appId means that this principal was created for the codebase
// without all the security information from the end document or worker.
// We require exact knowledge of this information before allowing the
// caller to touch the disk using the Cache API.
bool unknownAppId = false;
aPrincipal->GetUnknownAppId(&unknownAppId);
if (unknownAppId) {
NS_WARNING("CacheStorage not supported on principal with unknown appId.");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
PrincipalInfo principalInfo;
rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return nullptr;
}
nsRefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal,
principalInfo, nullptr);
return ref.forget();
}
// static
already_AddRefed<CacheStorage>
CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
WorkerPrivate* aWorkerPrivate, ErrorResult& aRv)
{
MOZ_ASSERT(aGlobal);
MOZ_ASSERT(aWorkerPrivate);
aWorkerPrivate->AssertIsOnWorkerThread();
nsRefPtr<Feature> feature = Feature::Create(aWorkerPrivate);
if (!feature) {
NS_WARNING("Worker thread is shutting down.");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
const PrincipalInfo& principalInfo = aWorkerPrivate->GetPrincipalInfo();
if (principalInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
NS_WARNING("CacheStorage not supported on null principal.");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
if (principalInfo.type() == PrincipalInfo::TContentPrincipalInfo &&
principalInfo.get_ContentPrincipalInfo().appId() ==
nsIScriptSecurityManager::UNKNOWN_APP_ID) {
NS_WARNING("CacheStorage not supported on principal with unknown appId.");
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<CacheStorage> ref = new CacheStorage(aNamespace, aGlobal,
principalInfo, feature);
return ref.forget();
}
CacheStorage::CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
const PrincipalInfo& aPrincipalInfo, Feature* aFeature)
: mNamespace(aNamespace)
, mGlobal(aGlobal)
, mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo))
, mFeature(aFeature)
, mActor(nullptr)
, mFailedActor(false)
{
MOZ_ASSERT(mGlobal);
// If the PBackground actor is already initialized then we can
// immediately use it
PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
if (actor) {
ActorCreated(actor);
return;
}
// Otherwise we must begin the PBackground initialization process and
// wait for the async ActorCreated() callback.
MOZ_ASSERT(NS_IsMainThread());
bool ok = BackgroundChild::GetOrCreateForCurrentThread(this);
if (!ok) {
ActorFailed();
}
}
already_AddRefed<Promise>
CacheStorage::Match(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
RequestId requestId = AddRequestPromise(promise, aRv);
Entry entry;
entry.mRequestId = requestId;
entry.mOp = OP_MATCH;
entry.mOptions = aOptions;
entry.mRequest = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (aRv.Failed()) {
return nullptr;
}
mPendingRequests.AppendElement(entry);
MaybeRunPendingRequests();
return promise.forget();
}
already_AddRefed<Promise>
CacheStorage::Has(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
RequestId requestId = AddRequestPromise(promise, aRv);
Entry* entry = mPendingRequests.AppendElement();
entry->mRequestId = requestId;
entry->mOp = OP_HAS;
entry->mKey = aKey;
MaybeRunPendingRequests();
return promise.forget();
}
already_AddRefed<Promise>
CacheStorage::Open(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
RequestId requestId = AddRequestPromise(promise, aRv);
Entry* entry = mPendingRequests.AppendElement();
entry->mRequestId = requestId;
entry->mOp = OP_OPEN;
entry->mKey = aKey;
MaybeRunPendingRequests();
return promise.forget();
}
already_AddRefed<Promise>
CacheStorage::Delete(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
RequestId requestId = AddRequestPromise(promise, aRv);
Entry* entry = mPendingRequests.AppendElement();
entry->mRequestId = requestId;
entry->mOp = OP_DELETE;
entry->mKey = aKey;
MaybeRunPendingRequests();
return promise.forget();
}
already_AddRefed<Promise>
CacheStorage::Keys(ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
RequestId requestId = AddRequestPromise(promise, aRv);
Entry* entry = mPendingRequests.AppendElement();
entry->mRequestId = requestId;
entry->mOp = OP_KEYS;
MaybeRunPendingRequests();
return promise.forget();
}
// static
bool
CacheStorage::PrefEnabled(JSContext* aCx, JSObject* aObj)
{
return Cache::PrefEnabled(aCx, aObj);
}
nsISupports*
CacheStorage::GetParentObject() const
{
return mGlobal;
}
JSObject*
CacheStorage::WrapObject(JSContext* aContext)
{
return mozilla::dom::CacheStorageBinding::Wrap(aContext, this);
}
void
CacheStorage::ActorCreated(PBackgroundChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
MOZ_ASSERT(aActor);
if (NS_WARN_IF(mFeature && mFeature->Notified())) {
ActorFailed();
return;
}
// Feature ownership is passed to the CacheStorageChild actor and any actors
// it may create. The Feature will keep the worker thread alive until the
// actors can gracefully shutdown.
CacheStorageChild* newActor = new CacheStorageChild(this, mFeature);
PCacheStorageChild* constructedActor =
aActor->SendPCacheStorageConstructor(newActor, mNamespace, *mPrincipalInfo);
if (NS_WARN_IF(!constructedActor)) {
ActorFailed();
return;
}
mFeature = nullptr;
MOZ_ASSERT(constructedActor == newActor);
mActor = newActor;
MaybeRunPendingRequests();
MOZ_ASSERT(mPendingRequests.IsEmpty());
}
void
CacheStorage::ActorFailed()
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
MOZ_ASSERT(!mFailedActor);
mFailedActor = true;
mFeature = nullptr;
for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
RequestId requestId = mPendingRequests[i].mRequestId;
nsRefPtr<Promise> promise = RemoveRequestPromise(requestId);
promise->MaybeReject(NS_ERROR_UNEXPECTED);
}
mPendingRequests.Clear();
}
void
CacheStorage::DestroyInternal(CacheStorageChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
MOZ_ASSERT(mActor);
MOZ_ASSERT(mActor == aActor);
mActor->ClearListener();
mActor = nullptr;
// Note that we will never get an actor again in case another request is
// made before this object is destructed.
ActorFailed();
}
void
CacheStorage::RecvMatchResponse(RequestId aRequestId, nsresult aRv,
const PCacheResponseOrVoid& aResponse)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
// Convert the response immediately if its present. This ensures that
// any stream actors are cleaned up, even if we error out below.
nsRefPtr<Response> response;
if (aResponse.type() == PCacheResponseOrVoid::TPCacheResponse) {
response = ToResponse(aResponse);
}
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
// If cache name was specified in the request options and the cache does
// not exist, then an error code will already have been set. If we
// still do not have a response, then we just resolve undefined like a
// normal Cache::Match.
if (!response) {
promise->MaybeResolve(JS::UndefinedHandleValue);
return;
}
promise->MaybeResolve(response);
}
void
CacheStorage::RecvHasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(aSuccess);
}
void
CacheStorage::RecvOpenResponse(RequestId aRequestId, nsresult aRv,
CacheChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
// Unlike most of our async callback Recv*() methods, this one gets back
// an actor. We need to make sure to clean it up in case of error.
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
if (aActor) {
// We cannot use the CacheChild::StartDestroy() method because there
// is no Cache object associated with the actor yet. Instead, just
// send the underlying Teardown message.
unused << aActor->SendTeardown();
}
promise->MaybeReject(aRv);
return;
}
if (!aActor) {
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return;
}
nsRefPtr<Cache> cache = new Cache(mGlobal, aActor);
promise->MaybeResolve(cache);
}
void
CacheStorage::RecvDeleteResponse(RequestId aRequestId, nsresult aRv,
bool aSuccess)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(aSuccess);
}
void
CacheStorage::RecvKeysResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (NS_FAILED(aRv)) {
promise->MaybeReject(aRv);
return;
}
promise->MaybeResolve(aKeys);
}
nsIGlobalObject*
CacheStorage::GetGlobalObject() const
{
return mGlobal;
}
#ifdef DEBUG
void
CacheStorage::AssertOwningThread() const
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
}
#endif
void
CacheStorage::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
// Do nothing. The Promise will automatically drop the ref to us after
// calling the callback. This is what we want as we only registered in order
// to be held alive via the Promise handle.
}
void
CacheStorage::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue)
{
// Do nothing. The Promise will automatically drop the ref to us after
// calling the callback. This is what we want as we only registered in order
// to be held alive via the Promise handle.
}
CacheStorage::~CacheStorage()
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mActor) {
mActor->StartDestroy();
// DestroyInternal() is called synchronously by StartDestroy(). So we
// should have already cleared the mActor.
MOZ_ASSERT(!mActor);
}
}
void
CacheStorage::MaybeRunPendingRequests()
{
if (!mActor) {
return;
}
for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
// Note, the entry can be modified below due to Request/Response body
// being marked used.
Entry& entry = mPendingRequests[i];
RequestId requestId = entry.mRequestId;
switch(entry.mOp) {
case OP_MATCH:
{
AutoChildRequest request(this);
ErrorResult rv;
request.Add(entry.mRequest, IgnoreBody, PassThroughReferrer,
IgnoreInvalidScheme, rv);
if (NS_WARN_IF(rv.Failed())) {
nsRefPtr<Promise> promise = RemoveRequestPromise(requestId);
promise->MaybeReject(rv);
break;
}
PCacheQueryParams params;
ToPCacheQueryParams(params, entry.mOptions);
unused << mActor->SendMatch(requestId, request.SendAsRequest(), params);
break;
}
case OP_HAS:
unused << mActor->SendHas(requestId, entry.mKey);
break;
case OP_OPEN:
unused << mActor->SendOpen(requestId, entry.mKey);
break;
case OP_DELETE:
unused << mActor->SendDelete(requestId, entry.mKey);
break;
case OP_KEYS:
unused << mActor->SendKeys(requestId);
break;
default:
MOZ_ASSERT_UNREACHABLE("Unknown pending CacheStorage op.");
}
}
mPendingRequests.Clear();
}
RequestId
CacheStorage::AddRequestPromise(Promise* aPromise, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
MOZ_ASSERT(aPromise);
MOZ_ASSERT(!mRequestPromises.Contains(aPromise));
// Register ourself as a promise handler so that the promise will hold us
// alive. This allows the client code to drop the ref to the CacheStorage
// object and just keep their promise. This is fairly common in promise
// chaining code.
aPromise->AppendNativeHandler(this);
mRequestPromises.AppendElement(aPromise);
// (Ab)use the promise pointer as our request ID. This is a fast, thread-safe
// way to get a unique ID for the promise to be resolved later.
return reinterpret_cast<RequestId>(aPromise);
}
already_AddRefed<Promise>
CacheStorage::RemoveRequestPromise(RequestId aRequestId)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
MOZ_ASSERT(aRequestId != INVALID_REQUEST_ID);
for (uint32_t i = 0; i < mRequestPromises.Length(); ++i) {
nsRefPtr<Promise>& promise = mRequestPromises.ElementAt(i);
// To be safe, only cast promise pointers to our integer RequestId
// type and never cast an integer to a pointer.
if (aRequestId == reinterpret_cast<RequestId>(promise.get())) {
nsRefPtr<Promise> ref;
ref.swap(promise);
mRequestPromises.RemoveElementAt(i);
return ref.forget();
}
}
MOZ_ASSERT_UNREACHABLE("Received response without a matching promise!");
return nullptr;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

165
dom/cache/CacheStorage.h vendored Normal file
View File

@ -0,0 +1,165 @@
/* -*- 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_CacheStorage_h
#define mozilla_dom_cache_CacheStorage_h
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "nsWrapperCache.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
class nsIGlobalObject;
namespace mozilla {
class ErrorResult;
namespace ipc {
class IProtocol;
class PrincipalInfo;
}
namespace dom {
class Promise;
namespace workers {
class WorkerPrivate;
}
namespace cache {
class CacheChild;
class CacheStorageChild;
class Feature;
class PCacheRequest;
class PCacheResponseOrVoid;
class CacheStorage MOZ_FINAL : public nsIIPCBackgroundChildCreateCallback
, public nsWrapperCache
, public TypeUtils
, public PromiseNativeHandler
{
typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
public:
static already_AddRefed<CacheStorage>
CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
nsIPrincipal* aPrincipal, ErrorResult& aRv);
static already_AddRefed<CacheStorage>
CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
workers::WorkerPrivate* aWorkerPrivate, ErrorResult& aRv);
// webidl interface methods
already_AddRefed<Promise> Match(const RequestOrUSVString& aRequest,
const CacheQueryOptions& aOptions,
ErrorResult& aRv);
already_AddRefed<Promise> Has(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Open(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Delete(const nsAString& aKey, ErrorResult& aRv);
already_AddRefed<Promise> Keys(ErrorResult& aRv);
// binding methods
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
nsISupports* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aContext) MOZ_OVERRIDE;
// nsIIPCbackgroundChildCreateCallback methods
virtual void ActorCreated(PBackgroundChild* aActor) MOZ_OVERRIDE;
virtual void ActorFailed() MOZ_OVERRIDE;
// Called when CacheStorageChild actor is being destroyed
void DestroyInternal(CacheStorageChild* aActor);
// Methods forwarded from CacheStorageChild
void RecvMatchResponse(RequestId aRequestId, nsresult aRv,
const PCacheResponseOrVoid& aResponse);
void RecvHasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
void RecvOpenResponse(RequestId aRequestId, nsresult aRv,
CacheChild* aActor);
void RecvDeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
void RecvKeysResponse(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys);
// TypeUtils methods
virtual nsIGlobalObject* GetGlobalObject() const MOZ_OVERRIDE;
#ifdef DEBUG
virtual void AssertOwningThread() const MOZ_OVERRIDE;
#endif
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE;
private:
CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
~CacheStorage();
void MaybeRunPendingRequests();
RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
// Would like to use CacheInitData here, but we cannot because
// its an IPC struct which breaks webidl by including windows.h.
const Namespace mNamespace;
nsCOMPtr<nsIGlobalObject> mGlobal;
UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
nsRefPtr<Feature> mFeature;
CacheStorageChild* mActor;
nsTArray<nsRefPtr<Promise>> mRequestPromises;
enum Op
{
OP_MATCH,
OP_HAS,
OP_OPEN,
OP_DELETE,
OP_KEYS
};
struct Entry
{
RequestId mRequestId;
Op mOp;
// Would prefer to use PCacheRequest/PCacheCacheQueryOptions, but can't
// because they introduce a header dependency on windows.h which
// breaks the bindings build.
nsRefPtr<InternalRequest> mRequest;
CacheQueryOptions mOptions;
// It would also be nice to union the key with the match args above,
// but VS2013 doesn't like these types in unions because of copy
// constructors.
nsString mKey;
};
nsTArray<Entry> mPendingRequests;
bool mFailedActor;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(CacheStorage,
nsIIPCBackgroundChildCreateCallback)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheStorage_h

174
dom/cache/CacheStorageChild.cpp vendored Normal file
View File

@ -0,0 +1,174 @@
/* -*- 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/CacheStorageChild.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/StreamUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
// declared in ActorUtils.h
void
DeallocPCacheStorageChild(PCacheStorageChild* aActor)
{
delete aActor;
}
CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature)
: mListener(aListener)
{
MOZ_COUNT_CTOR(cache::CacheStorageChild);
MOZ_ASSERT(mListener);
SetFeature(aFeature);
}
CacheStorageChild::~CacheStorageChild()
{
MOZ_COUNT_DTOR(cache::CacheStorageChild);
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
MOZ_ASSERT(!mListener);
}
void
CacheStorageChild::ClearListener()
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
MOZ_ASSERT(mListener);
mListener = nullptr;
}
void
CacheStorageChild::StartDestroy()
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
// StartDestroy() can get called from either CacheStorage or the Feature.
// Theoretically we can get double called if the right race happens. Handle
// that by just ignoring the second StartDestroy() call.
if (!listener) {
return;
}
listener->DestroyInternal(this);
// CacheStorage listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
// Start actor destruction from parent process
unused << SendTeardown();
}
void
CacheStorageChild::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (listener) {
listener->DestroyInternal(this);
// CacheStorage listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
}
RemoveFeature();
}
bool
CacheStorageChild::RecvMatchResponse(const RequestId& aRequestId,
const nsresult& aRv,
const PCacheResponseOrVoid& aResponseOrVoid)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
AddFeatureToStreamChild(aResponseOrVoid, GetFeature());
nsRefPtr<CacheStorage> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aResponseOrVoid);
return true;
}
listener->RecvMatchResponse(aRequestId, aRv, aResponseOrVoid);
return true;
}
bool
CacheStorageChild::RecvHasResponse(const RequestId& aRequestId,
const nsresult& aRv,
const bool& aSuccess)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (listener) {
listener->RecvHasResponse(aRequestId, aRv, aSuccess);
}
return true;
}
bool
CacheStorageChild::RecvOpenResponse(const RequestId& aRequestId,
const nsresult& aRv,
PCacheChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (!listener || FeatureNotified()) {
if (aActor) {
unused << aActor->SendTeardown();
}
return true;
}
CacheChild* cacheChild = static_cast<CacheChild*>(aActor);
// Since FeatureNotified() returned false above, we are guaranteed that
// the feature won't try to shutdown the actor until after we create the
// Cache DOM object in the listener's RecvOpenResponse() method. This
// is important because StartShutdown() expects a Cache object listener.
cacheChild->SetFeature(GetFeature());
listener->RecvOpenResponse(aRequestId, aRv, cacheChild);
return true;
}
bool
CacheStorageChild::RecvDeleteResponse(const RequestId& aRequestId,
const nsresult& aRv,
const bool& aResult)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (listener) {
listener->RecvDeleteResponse(aRequestId, aRv, aResult);
}
return true;
}
bool
CacheStorageChild::RecvKeysResponse(const RequestId& aRequestId,
const nsresult& aRv,
nsTArray<nsString>&& aKeys)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (listener) {
listener->RecvKeysResponse(aRequestId, aRv, aKeys);
}
return true;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

72
dom/cache/CacheStorageChild.h vendored Normal file
View File

@ -0,0 +1,72 @@
/* -*- 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_CacheStorageChild_h
#define mozilla_dom_cache_CacheStorageChild_h
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/PCacheStorageChild.h"
namespace mozilla {
namespace dom {
namespace cache {
class CacheStorage;
class PCacheChild;
class Feature;
class CacheStorageChild MOZ_FINAL : public PCacheStorageChild
, public ActorChild
{
public:
CacheStorageChild(CacheStorage* aListener, Feature* aFeature);
~CacheStorageChild();
// Must be called by the associated CacheStorage listener in its
// ActorDestroy() method. Also, CacheStorage must Send__delete__() the
// actor in its destructor to trigger ActorDestroy() if it has not been
// called yet.
void ClearListener();
// ActorChild methods
// Synchronously call ActorDestroy on our CacheStorage listener and then start
// the actor destruction asynchronously from the parent-side.
virtual void StartDestroy() MOZ_OVERRIDE;
private:
// PCacheStorageChild methods
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool RecvMatchResponse(const RequestId& aRequestId,
const nsresult& aRv,
const PCacheResponseOrVoid& response) MOZ_OVERRIDE;
virtual bool RecvHasResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
const bool& aSuccess) MOZ_OVERRIDE;
virtual bool RecvOpenResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
PCacheChild* aActor) MOZ_OVERRIDE;
virtual bool RecvDeleteResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
const bool& aResult) MOZ_OVERRIDE;
virtual bool RecvKeysResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
nsTArray<nsString>&& aKeys) MOZ_OVERRIDE;
// Use a weak ref so actor does not hold DOM object alive past content use.
// The CacheStorage object must call ClearListener() to null this before its
// destroyed.
CacheStorage* MOZ_NON_OWNING_REF mListener;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheStorageChild_h

450
dom/cache/CacheStorageParent.cpp vendored Normal file
View File

@ -0,0 +1,450 @@
/* -*- 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/CacheStorageParent.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CacheParent.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/ManagerId.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
#include "mozilla/dom/cache/StreamList.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/PFileDescriptorSetParent.h"
#include "mozilla/DebugOnly.h"
#include "nsCOMPtr.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::ipc::PBackgroundParent;
using mozilla::ipc::PFileDescriptorSetParent;
using mozilla::ipc::PrincipalInfo;
// declared in ActorUtils.h
PCacheStorageParent*
AllocPCacheStorageParent(PBackgroundParent* aManagingActor,
Namespace aNamespace,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo)
{
return new CacheStorageParent(aManagingActor, aNamespace, aPrincipalInfo);
}
// declared in ActorUtils.h
void
DeallocPCacheStorageParent(PCacheStorageParent* aActor)
{
delete aActor;
}
CacheStorageParent::CacheStorageParent(PBackgroundParent* aManagingActor,
Namespace aNamespace,
const PrincipalInfo& aPrincipalInfo)
: mNamespace(aNamespace)
, mVerifiedStatus(NS_OK)
{
MOZ_COUNT_CTOR(cache::CacheStorageParent);
MOZ_ASSERT(aManagingActor);
// Start the async principal verification process immediately.
mVerifier = PrincipalVerifier::CreateAndDispatch(this, aManagingActor,
aPrincipalInfo);
MOZ_ASSERT(mVerifier);
}
CacheStorageParent::~CacheStorageParent()
{
MOZ_COUNT_DTOR(cache::CacheStorageParent);
MOZ_ASSERT(!mVerifier);
MOZ_ASSERT(!mManager);
}
void
CacheStorageParent::ActorDestroy(ActorDestroyReason aReason)
{
if (mVerifier) {
mVerifier->ClearListener();
mVerifier = nullptr;
}
if (mManager) {
MOZ_ASSERT(!mActiveRequests.IsEmpty());
mManager->RemoveListener(this);
mManager = nullptr;
}
}
bool
CacheStorageParent::RecvTeardown()
{
if (!Send__delete__(this)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to delete actor.");
}
return true;
}
bool
CacheStorageParent::RecvMatch(const RequestId& aRequestId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams)
{
if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
if (!SendMatchResponse(aRequestId, mVerifiedStatus, void_t())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Match response.");
}
return true;
}
// queue requests if we are still waiting for principal verification
if (!mManagerId) {
Entry* entry = mPendingRequests.AppendElement();
entry->mOp = OP_MATCH;
entry->mRequestId = aRequestId;
entry->mRequest = aRequest;
entry->mParams = aParams;
return true;
}
nsRefPtr<cache::Manager> manager;
nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendMatchResponse(aRequestId, rv, void_t())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Match response.");
}
return true;
}
manager->StorageMatch(this, aRequestId, mNamespace, aRequest,
aParams);
return true;
}
bool
CacheStorageParent::RecvHas(const RequestId& aRequestId, const nsString& aKey)
{
if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
if (!SendHasResponse(aRequestId, mVerifiedStatus, false)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Has response.");
}
return true;
}
// queue requests if we are still waiting for principal verification
if (!mManagerId) {
Entry* entry = mPendingRequests.AppendElement();
entry->mOp = OP_HAS;
entry->mRequestId = aRequestId;
entry->mKey = aKey;
return true;
}
nsRefPtr<cache::Manager> manager;
nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendHasResponse(aRequestId, rv, false)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Has response.");
}
return true;
}
manager->StorageHas(this, aRequestId, mNamespace, aKey);
return true;
}
bool
CacheStorageParent::RecvOpen(const RequestId& aRequestId, const nsString& aKey)
{
if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
if (!SendOpenResponse(aRequestId, mVerifiedStatus, nullptr)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Open response.");
}
return true;
}
// queue requests if we are still waiting for principal verification
if (!mManagerId) {
Entry* entry = mPendingRequests.AppendElement();
entry->mOp = OP_OPEN;
entry->mRequestId = aRequestId;
entry->mKey = aKey;
return true;
}
nsRefPtr<cache::Manager> manager;
nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendOpenResponse(aRequestId, rv, nullptr)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Open response.");
}
return true;
}
manager->StorageOpen(this, aRequestId, mNamespace, aKey);
return true;
}
bool
CacheStorageParent::RecvDelete(const RequestId& aRequestId,
const nsString& aKey)
{
if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
if (!SendDeleteResponse(aRequestId, mVerifiedStatus, false)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Delete response.");
}
return true;
}
// queue requests if we are still waiting for principal verification
if (!mManagerId) {
Entry* entry = mPendingRequests.AppendElement();
entry->mOp = OP_DELETE;
entry->mRequestId = aRequestId;
entry->mKey = aKey;
return true;
}
nsRefPtr<cache::Manager> manager;
nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendDeleteResponse(aRequestId, rv, false)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Delete response.");
}
return true;
}
manager->StorageDelete(this, aRequestId, mNamespace, aKey);
return true;
}
bool
CacheStorageParent::RecvKeys(const RequestId& aRequestId)
{
if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
if (!SendKeysResponse(aRequestId, mVerifiedStatus, nsTArray<nsString>())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Keys response.");
}
}
// queue requests if we are still waiting for principal verification
if (!mManagerId) {
Entry* entry = mPendingRequests.AppendElement();
entry->mOp = OP_DELETE;
entry->mRequestId = aRequestId;
return true;
}
nsRefPtr<cache::Manager> manager;
nsresult rv = RequestManager(aRequestId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
if (!SendKeysResponse(aRequestId, rv, nsTArray<nsString>())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Keys response.");
}
return true;
}
manager->StorageKeys(this, aRequestId, mNamespace);
return true;
}
void
CacheStorageParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
{
MOZ_ASSERT(mVerifier);
MOZ_ASSERT(!mManagerId);
MOZ_ASSERT(!mManager);
MOZ_ASSERT(NS_SUCCEEDED(mVerifiedStatus));
if (NS_WARN_IF(NS_FAILED(aRv))) {
mVerifiedStatus = aRv;
}
mManagerId = aManagerId;
mVerifier->ClearListener();
mVerifier = nullptr;
RetryPendingRequests();
}
void
CacheStorageParent::OnStorageMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aSavedResponse,
StreamList* aStreamList)
{
PCacheResponseOrVoid responseOrVoid;
ReleaseManager(aRequestId);
AutoParentResponseOrVoid response(Manager());
// no match
if (NS_FAILED(aRv) || !aSavedResponse) {
if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Match response.");
}
return;
}
if (aSavedResponse) {
response.Add(*aSavedResponse, aStreamList);
}
if (!SendMatchResponse(aRequestId, aRv, response.SendAsResponseOrVoid())) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Match response.");
}
}
void
CacheStorageParent::OnStorageHas(RequestId aRequestId, nsresult aRv,
bool aCacheFound)
{
ReleaseManager(aRequestId);
if (!SendHasResponse(aRequestId, aRv, aCacheFound)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Has response.");
}
}
void
CacheStorageParent::OnStorageOpen(RequestId aRequestId, nsresult aRv,
CacheId aCacheId)
{
if (NS_FAILED(aRv)) {
ReleaseManager(aRequestId);
if (!SendOpenResponse(aRequestId, aRv, nullptr)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Open response.");
}
return;
}
MOZ_ASSERT(mManager);
CacheParent* actor = new CacheParent(mManager, aCacheId);
ReleaseManager(aRequestId);
PCacheParent* base = Manager()->SendPCacheConstructor(actor);
actor = static_cast<CacheParent*>(base);
if (!SendOpenResponse(aRequestId, aRv, actor)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Open response.");
}
}
void
CacheStorageParent::OnStorageDelete(RequestId aRequestId, nsresult aRv,
bool aCacheDeleted)
{
ReleaseManager(aRequestId);
if (!SendDeleteResponse(aRequestId, aRv, aCacheDeleted)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Delete response.");
}
}
void
CacheStorageParent::OnStorageKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys)
{
ReleaseManager(aRequestId);
if (!SendKeysResponse(aRequestId, aRv, aKeys)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("CacheStorage failed to send Keys response.");
}
}
void
CacheStorageParent::RetryPendingRequests()
{
MOZ_ASSERT(mManagerId || NS_FAILED(mVerifiedStatus));
for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
const Entry& entry = mPendingRequests[i];
switch(entry.mOp) {
case OP_MATCH:
RecvMatch(entry.mRequestId, entry.mRequest, entry.mParams);
break;
case OP_HAS:
RecvHas(entry.mRequestId, entry.mKey);
break;
case OP_OPEN:
RecvOpen(entry.mRequestId, entry.mKey);
break;
case OP_DELETE:
RecvDelete(entry.mRequestId, entry.mKey);
break;
case OP_KEYS:
RecvKeys(entry.mRequestId);
break;
default:
MOZ_ASSERT_UNREACHABLE("Pending request within unknown op");
}
}
mPendingRequests.Clear();
mPendingRequests.Compact();
}
nsresult
CacheStorageParent::RequestManager(RequestId aRequestId,
cache::Manager** aManagerOut)
{
MOZ_ASSERT(!mActiveRequests.Contains(aRequestId));
nsRefPtr<cache::Manager> ref = mManager;
if (!ref) {
MOZ_ASSERT(mActiveRequests.IsEmpty());
nsresult rv = cache::Manager::GetOrCreate(mManagerId, getter_AddRefs(ref));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
mManager = ref;
}
mActiveRequests.AppendElement(aRequestId);
ref.forget(aManagerOut);
return NS_OK;
}
void
CacheStorageParent::ReleaseManager(RequestId aRequestId)
{
// Note that if the child process dies we also clean up the mManager in
// ActorDestroy(). There is no race with this method, however, because
// ActorDestroy removes this object from the Manager's listener list.
// Therefore ReleaseManager() should never be called after ActorDestroy()
// runs.
MOZ_ASSERT(mManager);
MOZ_ASSERT(!mActiveRequests.IsEmpty());
MOZ_ALWAYS_TRUE(mActiveRequests.RemoveElement(aRequestId));
if (mActiveRequests.IsEmpty()) {
mManager->RemoveListener(this);
mManager = nullptr;
}
}
} // namespace cache
} // namespace dom
} // namespace mozilla

108
dom/cache/CacheStorageParent.h vendored Normal file
View File

@ -0,0 +1,108 @@
/* -*- 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_CacheStorageParent_h
#define mozilla_dom_cache_CacheStorageParent_h
#include "mozilla/dom/cache/CacheInitData.h"
#include "mozilla/dom/cache/PCacheStorageParent.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/PrincipalVerifier.h"
#include "mozilla/dom/cache/Types.h"
template <class T> class nsRefPtr;
namespace mozilla {
namespace dom {
namespace cache {
class CacheStreamControlParent;
class ManagerId;
class CacheStorageParent MOZ_FINAL : public PCacheStorageParent
, public PrincipalVerifier::Listener
, public Manager::Listener
{
public:
CacheStorageParent(PBackgroundParent* aManagingActor, Namespace aNamespace,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
virtual ~CacheStorageParent();
private:
// PCacheStorageParent methods
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool RecvTeardown() MOZ_OVERRIDE;
virtual bool RecvMatch(const RequestId& aRequestId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) MOZ_OVERRIDE;
virtual bool RecvHas(const RequestId& aRequestId,
const nsString& aKey) MOZ_OVERRIDE;
virtual bool RecvOpen(const RequestId& aRequestId,
const nsString& aKey) MOZ_OVERRIDE;
virtual bool RecvDelete(const RequestId& aRequestId,
const nsString& aKey) MOZ_OVERRIDE;
virtual bool RecvKeys(const RequestId& aRequestId) MOZ_OVERRIDE;
// PrincipalVerifier::Listener methods
virtual void OnPrincipalVerified(nsresult aRv,
ManagerId* aManagerId) MOZ_OVERRIDE;
// Manager::Listener methods
virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aResponse,
StreamList* aStreamList) MOZ_OVERRIDE;
virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
bool aCacheFound) MOZ_OVERRIDE;
virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
CacheId aCacheId) MOZ_OVERRIDE;
virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
bool aCacheDeleted) MOZ_OVERRIDE;
virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys) MOZ_OVERRIDE;
CacheStreamControlParent*
SerializeReadStream(CacheStreamControlParent *aStreamControl, const nsID& aId,
StreamList* aStreamList,
PCacheReadStream* aReadStreamOut);
void RetryPendingRequests();
nsresult RequestManager(RequestId aRequestId, cache::Manager** aManagerOut);
void ReleaseManager(RequestId aRequestId);
const Namespace mNamespace;
nsRefPtr<PrincipalVerifier> mVerifier;
nsresult mVerifiedStatus;
nsRefPtr<ManagerId> mManagerId;
nsRefPtr<cache::Manager> mManager;
enum Op
{
OP_MATCH,
OP_HAS,
OP_OPEN,
OP_DELETE,
OP_KEYS
};
struct Entry
{
Op mOp;
RequestId mRequestId;
nsString mKey;
PCacheRequest mRequest;
PCacheQueryParams mParams;
};
nsTArray<Entry> mPendingRequests;
nsTArray<RequestId> mActiveRequests;
};
} // namesapce cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheStorageParent_h

137
dom/cache/CacheStreamControlChild.cpp vendored Normal file
View File

@ -0,0 +1,137 @@
/* -*- 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/CacheStreamControlChild.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
namespace cache {
// declared in ActorUtils.h
PCacheStreamControlChild*
AllocPCacheStreamControlChild()
{
return new CacheStreamControlChild();
}
// declared in ActorUtils.h
void
DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
{
delete aActor;
}
CacheStreamControlChild::CacheStreamControlChild()
: mDestroyStarted(false)
{
MOZ_COUNT_CTOR(cache::CacheStreamControlChild);
}
CacheStreamControlChild::~CacheStreamControlChild()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
MOZ_COUNT_DTOR(cache::CacheStreamControlChild);
}
void
CacheStreamControlChild::AddListener(ReadStream* aListener)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mListeners.Contains(aListener));
mListeners.AppendElement(aListener);
}
void
CacheStreamControlChild::RemoveListener(ReadStream* aListener)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
MOZ_ASSERT(aListener);
MOZ_ALWAYS_TRUE(mListeners.RemoveElement(aListener));
}
void
CacheStreamControlChild::NoteClosed(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
unused << SendNoteClosed(aId);
}
void
CacheStreamControlChild::StartDestroy()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
// This can get called twice under some circumstances. For example, if the
// actor is added to a Feature that has already been notified and the Cache
// actor has no mListener.
if (mDestroyStarted) {
return;
}
mDestroyStarted = true;
// Begin shutting down all streams. This is the same as if the parent had
// asked us to shutdown. So simulate the CloseAll IPC message.
RecvCloseAll();
}
void
CacheStreamControlChild::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
// Note, we cannot trigger IPC traffic here. So use
// CloseStreamWithoutReporting().
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
stream->CloseStreamWithoutReporting();
}
mListeners.Clear();
RemoveFeature();
}
bool
CacheStreamControlChild::RecvClose(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
DebugOnly<uint32_t> closedCount = 0;
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
// note, multiple streams may exist for same ID
if (stream->MatchId(aId)) {
stream->CloseStream();
closedCount += 1;
}
}
MOZ_ASSERT(closedCount > 0);
return true;
}
bool
CacheStreamControlChild::RecvCloseAll()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
stream->CloseStream();
}
return true;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

53
dom/cache/CacheStreamControlChild.h vendored Normal file
View File

@ -0,0 +1,53 @@
/* -*- 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_CacheStreamControlChild_h
#define mozilla_dom_cache_CacheStreamControlChild_h
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/PCacheStreamControlChild.h"
#include "nsTObserverArray.h"
namespace mozilla {
namespace dom {
namespace cache {
class ReadStream;
class CacheStreamControlChild MOZ_FINAL : public PCacheStreamControlChild
, public ActorChild
{
public:
CacheStreamControlChild();
~CacheStreamControlChild();
void AddListener(ReadStream* aListener);
void RemoveListener(ReadStream* aListener);
void NoteClosed(const nsID& aId);
// ActorChild methods
virtual void StartDestroy() MOZ_OVERRIDE;
private:
// PCacheStreamControlChild methods
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool RecvClose(const nsID& aId) MOZ_OVERRIDE;
virtual bool RecvCloseAll() MOZ_OVERRIDE;
typedef nsTObserverArray<ReadStream*> ReadStreamList;
ReadStreamList mListeners;
bool mDestroyStarted;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheStreamControlChild_h

147
dom/cache/CacheStreamControlParent.cpp vendored Normal file
View File

@ -0,0 +1,147 @@
/* -*- 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/CacheStreamControlParent.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/StreamList.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace dom {
namespace cache {
// declared in ActorUtils.h
void
DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
{
delete aActor;
}
CacheStreamControlParent::CacheStreamControlParent()
{
MOZ_COUNT_CTOR(cache::CacheStreamControlParent);
}
CacheStreamControlParent::~CacheStreamControlParent()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(!mStreamList);
MOZ_COUNT_DTOR(cache::CacheStreamControlParent);
}
void
CacheStreamControlParent::AddListener(ReadStream* aListener)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mListeners.Contains(aListener));
mListeners.AppendElement(aListener);
}
void
CacheStreamControlParent::RemoveListener(ReadStream* aListener)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(aListener);
DebugOnly<bool> removed = mListeners.RemoveElement(aListener);
MOZ_ASSERT(removed);
}
void
CacheStreamControlParent::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(mStreamList);
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
stream->CloseStreamWithoutReporting();
}
mStreamList->RemoveStreamControl(this);
mStreamList->NoteClosedAll();
mStreamList = nullptr;
}
bool
CacheStreamControlParent::RecvNoteClosed(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(mStreamList);
mStreamList->NoteClosed(aId);
return true;
}
void
CacheStreamControlParent::SetStreamList(StreamList* aStreamList)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
MOZ_ASSERT(!mStreamList);
mStreamList = aStreamList;
}
void
CacheStreamControlParent::Close(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
NotifyClose(aId);
unused << SendClose(aId);
}
void
CacheStreamControlParent::CloseAll()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
NotifyCloseAll();
unused << SendCloseAll();
}
void
CacheStreamControlParent::Shutdown()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
if (!Send__delete__(this)) {
// child process is gone, allow actor to be destroyed normally
NS_WARNING("Cache failed to delete stream actor.");
return;
}
}
void
CacheStreamControlParent::NotifyClose(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
DebugOnly<uint32_t> closedCount = 0;
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
// note, multiple streams may exist for same ID
if (stream->MatchId(aId)) {
stream->CloseStream();
closedCount += 1;
}
}
MOZ_ASSERT(closedCount > 0);
}
void
CacheStreamControlParent::NotifyCloseAll()
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
ReadStreamList::ForwardIterator iter(mListeners);
while (iter.HasMore()) {
nsRefPtr<ReadStream> stream = iter.GetNext();
stream->CloseStream();
}
}
} // namespace cache
} // namespace dom
} // namespace mozilla

57
dom/cache/CacheStreamControlParent.h vendored Normal file
View File

@ -0,0 +1,57 @@
/* -*- 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_CacheStreamControlParent_h
#define mozilla_dom_cache_CacheStreamControlParent_h
#include "mozilla/dom/cache/PCacheStreamControlParent.h"
#include "nsTObserverArray.h"
namespace mozilla {
namespace dom {
namespace cache {
class ReadStream;
class StreamList;
class CacheStreamControlParent : public PCacheStreamControlParent
{
public:
CacheStreamControlParent();
~CacheStreamControlParent();
void AddListener(ReadStream* aListener);
void RemoveListener(ReadStream* aListener);
void SetStreamList(StreamList* aStreamList);
void Close(const nsID& aId);
void CloseAll();
void Shutdown();
// PCacheStreamControlParent methods
virtual void ActorDestroy(ActorDestroyReason aReason) MOZ_OVERRIDE;
virtual bool RecvNoteClosed(const nsID& aId) MOZ_OVERRIDE;
private:
void NotifyClose(const nsID& aId);
void NotifyCloseAll();
// Cycle with StreamList via a weak-ref to us. Cleanup occurs when the actor
// is deleted by the PBackground manager. ActorDestroy() then calls
// StreamList::RemoveStreamControl() to clear the weak ref.
nsRefPtr<StreamList> mStreamList;
typedef nsTObserverArray<ReadStream*> ReadStreamList;
ReadStreamList mListeners;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheStreamControlParent_h

630
dom/cache/Context.cpp vendored Normal file
View File

@ -0,0 +1,630 @@
/* -*- 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/Context.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/Action.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/ManagerId.h"
#include "mozilla/dom/quota/OriginOrPatternString.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "nsIFile.h"
#include "nsIPrincipal.h"
#include "nsIRunnable.h"
#include "nsThreadUtils.h"
namespace {
using mozilla::dom::Nullable;
using mozilla::dom::cache::QuotaInfo;
using mozilla::dom::quota::OriginOrPatternString;
using mozilla::dom::quota::QuotaManager;
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
using mozilla::dom::quota::PersistenceType;
// Executed when the context is destroyed to release our lock on the
// QuotaManager.
class QuotaReleaseRunnable MOZ_FINAL : public nsRunnable
{
public:
QuotaReleaseRunnable(const QuotaInfo& aQuotaInfo, const nsACString& aQuotaId)
: mQuotaInfo(aQuotaInfo)
, mQuotaId(aQuotaId)
{ }
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
QuotaManager* qm = QuotaManager::Get();
MOZ_ASSERT(qm);
qm->AllowNextSynchronizedOp(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
mQuotaId);
return NS_OK;
}
private:
~QuotaReleaseRunnable() { }
const QuotaInfo mQuotaInfo;
const nsCString mQuotaId;
};
} // anonymous namespace
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::DebugOnly;
using mozilla::dom::quota::OriginOrPatternString;
using mozilla::dom::quota::QuotaManager;
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
using mozilla::dom::quota::PersistenceType;
// Executed to perform the complicated dance of steps necessary to initialize
// the QuotaManager. This must be performed for each origin before any disk
// IO occurrs.
class Context::QuotaInitRunnable MOZ_FINAL : public nsIRunnable
, public Action::Resolver
{
public:
QuotaInitRunnable(Context* aContext,
Manager* aManager,
const nsACString& aQuotaId,
Action* aQuotaIOThreadAction)
: mContext(aContext)
, mManager(aManager)
, mQuotaId(aQuotaId)
, mQuotaIOThreadAction(aQuotaIOThreadAction)
, mInitiatingThread(NS_GetCurrentThread())
, mState(STATE_INIT)
, mResult(NS_OK)
{
MOZ_ASSERT(mContext);
MOZ_ASSERT(mManager);
MOZ_ASSERT(mInitiatingThread);
}
nsresult Dispatch()
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
MOZ_ASSERT(mState == STATE_INIT);
mState = STATE_CALL_WAIT_FOR_OPEN_ALLOWED;
nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
mState = STATE_COMPLETE;
Clear();
}
return rv;
}
virtual void Resolve(nsresult aRv) MOZ_OVERRIDE
{
// Depending on the error or success path, this can run on either the
// main thread or the QuotaManager IO thread. The IO thread is an
// idle thread which may be destroyed and recreated, so its hard to
// assert on.
MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(aRv));
mResult = aRv;
mState = STATE_COMPLETING;
nsresult rv = mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Shutdown must be delayed until all Contexts are destroyed. Crash for
// this invariant violation.
MOZ_CRASH("Failed to dispatch QuotaInitRunnable to initiating thread.");
}
}
private:
~QuotaInitRunnable()
{
MOZ_ASSERT(mState == STATE_COMPLETE);
MOZ_ASSERT(!mContext);
MOZ_ASSERT(!mQuotaIOThreadAction);
}
enum State
{
STATE_INIT,
STATE_CALL_WAIT_FOR_OPEN_ALLOWED,
STATE_WAIT_FOR_OPEN_ALLOWED,
STATE_ENSURE_ORIGIN_INITIALIZED,
STATE_RUNNING,
STATE_COMPLETING,
STATE_COMPLETE
};
void Clear()
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
MOZ_ASSERT(mContext);
mContext = nullptr;
mManager = nullptr;
mQuotaIOThreadAction = nullptr;
}
nsRefPtr<Context> mContext;
nsRefPtr<Manager> mManager;
const nsCString mQuotaId;
nsRefPtr<Action> mQuotaIOThreadAction;
nsCOMPtr<nsIThread> mInitiatingThread;
State mState;
nsresult mResult;
QuotaInfo mQuotaInfo;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
};
NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::QuotaInitRunnable,
Action::Resolver, nsIRunnable);
// The QuotaManager init state machine is represented in the following diagram:
//
// +---------------+
// | Start | Resolve(error)
// | (Orig Thread) +---------------------+
// +-------+-------+ |
// | |
// +----------v-----------+ |
// |CallWaitForOpenAllowed| Resolve(error) |
// | (Main Thread) +-----------------+
// +----------+-----------+ |
// | |
// +--------v---------+ |
// |WaitForOpenAllowed| Resolve(error) |
// | (Main Thread) +-------------------+
// +--------+---------+ |
// | |
// +----------v------------+ |
// |EnsureOriginInitialized| Resolve(error) |
// | (Quota IO Thread) +----------------+
// +----------+------------+ |
// | |
// +---------v---------+ +------v------+
// | Running | Resolve() | Completing |
// | (Quota IO Thread) +------------>(Orig Thread)|
// +-------------------+ +------+------+
// |
// +-----v----+
// | Complete |
// +----------+
//
// The initialization process proceeds through the main states. If an error
// occurs, then we transition back to Completing state back on the original
// thread.
NS_IMETHODIMP
Context::QuotaInitRunnable::Run()
{
// May run on different threads depending on the state. See individual
// state cases for thread assertions.
switch(mState) {
// -----------------------------------
case STATE_CALL_WAIT_FOR_OPEN_ALLOWED:
{
MOZ_ASSERT(NS_IsMainThread());
QuotaManager* qm = QuotaManager::GetOrCreate();
if (!qm) {
Resolve(NS_ERROR_FAILURE);
return NS_OK;
}
nsRefPtr<ManagerId> managerId = mManager->GetManagerId();
nsCOMPtr<nsIPrincipal> principal = managerId->Principal();
nsresult rv = qm->GetInfoFromPrincipal(principal,
&mQuotaInfo.mGroup,
&mQuotaInfo.mOrigin,
&mQuotaInfo.mIsApp);
if (NS_WARN_IF(NS_FAILED(rv))) {
Resolve(rv);
return NS_OK;
}
// QuotaManager::WaitForOpenAllowed() will hold a reference to us as
// a callback. We will then get executed again on the main thread when
// it is safe to open the quota directory.
mState = STATE_WAIT_FOR_OPEN_ALLOWED;
rv = qm->WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mQuotaInfo.mOrigin),
Nullable<PersistenceType>(PERSISTENCE_TYPE_DEFAULT),
mQuotaId, this);
if (NS_FAILED(rv)) {
Resolve(rv);
return NS_OK;
}
break;
}
// ------------------------------
case STATE_WAIT_FOR_OPEN_ALLOWED:
{
MOZ_ASSERT(NS_IsMainThread());
QuotaManager* qm = QuotaManager::Get();
MOZ_ASSERT(qm);
mState = STATE_ENSURE_ORIGIN_INITIALIZED;
nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
Resolve(rv);
return NS_OK;
}
break;
}
// ----------------------------------
case STATE_ENSURE_ORIGIN_INITIALIZED:
{
// Can't assert quota IO thread because its an idle thread that can get
// recreated. At least assert we're not on main thread or owning thread.
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(_mOwningThread.GetThread() != PR_GetCurrentThread());
QuotaManager* qm = QuotaManager::Get();
MOZ_ASSERT(qm);
nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
mQuotaInfo.mGroup,
mQuotaInfo.mOrigin,
mQuotaInfo.mIsApp,
getter_AddRefs(mQuotaInfo.mDir));
if (NS_FAILED(rv)) {
Resolve(rv);
return NS_OK;
}
mState = STATE_RUNNING;
if (!mQuotaIOThreadAction) {
Resolve(NS_OK);
return NS_OK;
}
// Execute the provided initialization Action. We pass ourselves as the
// Resolver. The Action must either call Resolve() immediately or hold
// a ref to us and call Resolve() later.
mQuotaIOThreadAction->RunOnTarget(this, mQuotaInfo);
break;
}
// -------------------
case STATE_COMPLETING:
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
if (mQuotaIOThreadAction) {
mQuotaIOThreadAction->CompleteOnInitiatingThread(mResult);
}
mContext->OnQuotaInit(mResult, mQuotaInfo);
mState = STATE_COMPLETE;
// Explicitly cleanup here as the destructor could fire on any of
// the threads we have bounced through.
Clear();
break;
}
// -----
default:
{
MOZ_CRASH("unexpected state in QuotaInitRunnable");
break;
}
}
return NS_OK;
}
// Runnable wrapper around Action objects dispatched on the Context. This
// runnable executes the Action on the appropriate threads while the Context
// is initialized.
class Context::ActionRunnable MOZ_FINAL : public nsIRunnable
, public Action::Resolver
{
public:
ActionRunnable(Context* aContext, nsIEventTarget* aTarget, Action* aAction,
const QuotaInfo& aQuotaInfo)
: mContext(aContext)
, mTarget(aTarget)
, mAction(aAction)
, mQuotaInfo(aQuotaInfo)
, mInitiatingThread(NS_GetCurrentThread())
, mState(STATE_INIT)
, mResult(NS_OK)
{
MOZ_ASSERT(mContext);
MOZ_ASSERT(mTarget);
MOZ_ASSERT(mAction);
MOZ_ASSERT(mQuotaInfo.mDir);
MOZ_ASSERT(mInitiatingThread);
}
nsresult Dispatch()
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
MOZ_ASSERT(mState == STATE_INIT);
mState = STATE_RUN_ON_TARGET;
nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
mState = STATE_COMPLETE;
Clear();
}
return rv;
}
bool MatchesCacheId(CacheId aCacheId) {
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
return mAction->MatchesCacheId(aCacheId);
}
void Cancel()
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
mAction->CancelOnInitiatingThread();
}
virtual void Resolve(nsresult aRv) MOZ_OVERRIDE
{
MOZ_ASSERT(mTarget == NS_GetCurrentThread());
MOZ_ASSERT(mState == STATE_RUNNING);
mResult = aRv;
mState = STATE_COMPLETING;
nsresult rv = mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Shutdown must be delayed until all Contexts are destroyed. Crash
// for this invariant violation.
MOZ_CRASH("Failed to dispatch ActionRunnable to initiating thread.");
}
}
private:
~ActionRunnable()
{
MOZ_ASSERT(mState == STATE_COMPLETE);
MOZ_ASSERT(!mContext);
MOZ_ASSERT(!mAction);
}
void Clear()
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
MOZ_ASSERT(mContext);
MOZ_ASSERT(mAction);
mContext->OnActionRunnableComplete(this);
mContext = nullptr;
mAction = nullptr;
}
enum State
{
STATE_INIT,
STATE_RUN_ON_TARGET,
STATE_RUNNING,
STATE_COMPLETING,
STATE_COMPLETE
};
nsRefPtr<Context> mContext;
nsCOMPtr<nsIEventTarget> mTarget;
nsRefPtr<Action> mAction;
const QuotaInfo mQuotaInfo;
nsCOMPtr<nsIThread> mInitiatingThread;
State mState;
nsresult mResult;
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
};
NS_IMPL_ISUPPORTS_INHERITED(mozilla::dom::cache::Context::ActionRunnable,
Action::Resolver, nsIRunnable);
// The ActionRunnable has a simpler state machine. It basically needs to run
// the action on the target thread and then complete on the original thread.
//
// +-------------+
// | Start |
// |(Orig Thread)|
// +-----+-------+
// |
// +-------v---------+
// | RunOnTarget |
// |Target IO Thread)+-------------------------------+
// +-------+---------+ |
// | |
// +-------v----------+ Resolve() +-------v-----+
// | Running | | Completing |
// |(Target IO Thread)+---------------------->(Orig Thread)|
// +------------------+ +-------+-----+
// |
// |
// +----v---+
// |Complete|
// +--------+
//
// Its important to note that synchronous actions will effectively Resolve()
// out of the Running state immediately. Asynchronous Actions may remain
// in the Running state for some time, but normally the ActionRunnable itself
// does not see any execution there. Its all handled internal to the Action.
NS_IMETHODIMP
Context::ActionRunnable::Run()
{
switch(mState) {
// ----------------------
case STATE_RUN_ON_TARGET:
{
MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
mState = STATE_RUNNING;
mAction->RunOnTarget(this, mQuotaInfo);
break;
}
// -------------------
case STATE_COMPLETING:
{
NS_ASSERT_OWNINGTHREAD(Action::Resolver);
mAction->CompleteOnInitiatingThread(mResult);
mState = STATE_COMPLETE;
// Explicitly cleanup here as the destructor could fire on any of
// the threads we have bounced through.
Clear();
break;
}
// -----------------
default:
{
MOZ_CRASH("unexpected state in ActionRunnable");
break;
}
}
return NS_OK;
}
// static
already_AddRefed<Context>
Context::Create(Manager* aManager, Action* aQuotaIOThreadAction)
{
nsRefPtr<Context> context = new Context(aManager);
nsRefPtr<QuotaInitRunnable> runnable =
new QuotaInitRunnable(context, aManager, NS_LITERAL_CSTRING("Cache"),
aQuotaIOThreadAction);
nsresult rv = runnable->Dispatch();
if (NS_FAILED(rv)) {
// Shutdown must be delayed until all Contexts are destroyed. Shutdown
// must also prevent any new Contexts from being constructed. Crash
// for this invariant violation.
MOZ_CRASH("Failed to dispatch QuotaInitRunnable.");
}
return context.forget();
}
Context::Context(Manager* aManager)
: mManager(aManager)
, mState(STATE_CONTEXT_INIT)
{
MOZ_ASSERT(mManager);
}
void
Context::Dispatch(nsIEventTarget* aTarget, Action* aAction)
{
NS_ASSERT_OWNINGTHREAD(Context);
MOZ_ASSERT(aTarget);
MOZ_ASSERT(aAction);
if (mState == STATE_CONTEXT_CANCELED) {
return;
} else if (mState == STATE_CONTEXT_INIT) {
PendingAction* pending = mPendingActions.AppendElement();
pending->mTarget = aTarget;
pending->mAction = aAction;
return;
}
MOZ_ASSERT(STATE_CONTEXT_READY);
DispatchAction(aTarget, aAction);
}
void
Context::CancelAll()
{
NS_ASSERT_OWNINGTHREAD(Context);
mState = STATE_CONTEXT_CANCELED;
mPendingActions.Clear();
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
runnable->Cancel();
}
}
void
Context::CancelForCacheId(CacheId aCacheId)
{
NS_ASSERT_OWNINGTHREAD(Context);
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
if (mPendingActions[i].mAction->MatchesCacheId(aCacheId)) {
mPendingActions.RemoveElementAt(i);
}
}
for (uint32_t i = 0; i < mActionRunnables.Length(); ++i) {
nsRefPtr<ActionRunnable> runnable = mActionRunnables[i];
if (runnable->MatchesCacheId(aCacheId)) {
runnable->Cancel();
}
}
}
Context::~Context()
{
NS_ASSERT_OWNINGTHREAD(Context);
MOZ_ASSERT(mManager);
// Unlock the quota dir as we go out of scope.
nsCOMPtr<nsIRunnable> runnable =
new QuotaReleaseRunnable(mQuotaInfo, NS_LITERAL_CSTRING("Cache"));
nsresult rv = NS_DispatchToMainThread(runnable, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Shutdown must be delayed until all Contexts are destroyed. Crash
// for this invariant violation.
MOZ_CRASH("Failed to dispatch QuotaReleaseRunnable to main thread.");
}
mManager->RemoveContext(this);
}
void
Context::DispatchAction(nsIEventTarget* aTarget, Action* aAction)
{
NS_ASSERT_OWNINGTHREAD(Context);
nsRefPtr<ActionRunnable> runnable =
new ActionRunnable(this, aTarget, aAction, mQuotaInfo);
nsresult rv = runnable->Dispatch();
if (NS_FAILED(rv)) {
// Shutdown must be delayed until all Contexts are destroyed. Crash
// for this invariant violation.
MOZ_CRASH("Failed to dispatch ActionRunnable to target thread.");
}
mActionRunnables.AppendElement(runnable);
}
void
Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo)
{
NS_ASSERT_OWNINGTHREAD(Context);
mQuotaInfo = aQuotaInfo;
if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) {
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
}
mPendingActions.Clear();
// Context will destruct after return here and last ref is released.
return;
}
MOZ_ASSERT(mState == STATE_CONTEXT_INIT);
mState = STATE_CONTEXT_READY;
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
DispatchAction(mPendingActions[i].mTarget, mPendingActions[i].mAction);
}
mPendingActions.Clear();
}
void
Context::OnActionRunnableComplete(ActionRunnable* aActionRunnable)
{
NS_ASSERT_OWNINGTHREAD(Context);
MOZ_ASSERT(aActionRunnable);
MOZ_ALWAYS_TRUE(mActionRunnables.RemoveElement(aActionRunnable));
}
} // namespace cache
} // namespace dom
} // namespace mozilla

109
dom/cache/Context.h vendored Normal file
View File

@ -0,0 +1,109 @@
/* -*- 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_Context_h
#define mozilla_dom_cache_Context_h
#include "mozilla/dom/cache/Types.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
class nsIEventTarget;
class nsIFile;
namespace mozilla {
namespace dom {
namespace cache {
class Action;
class Manager;
// The Context class is RAII-style class for managing IO operations within the
// Cache.
//
// When a Context is created it performs the complicated steps necessary to
// initialize the QuotaManager. Action objects dispatched on the Context are
// delayed until this initialization is complete. They are then allow to
// execute on any specified thread. Once all references to the Context are
// gone, then the steps necessary to release the QuotaManager are performed.
// Since pending Action objects reference the Context, this allows overlapping
// IO to opportunistically run without re-initializing the QuotaManager again.
//
// While the Context performs operations asynchronously on threads, all of
// methods in its public interface must be called on the same thread
// originally used to create the Context.
//
// As an invariant, all Context objects must be destroyed before permitting
// the "profile-before-change" shutdown event to complete. This is ensured
// via the code in ShutdownObserver.cpp.
class Context MOZ_FINAL
{
public:
static already_AddRefed<Context>
Create(Manager* aManager, Action* aQuotaIOThreadAction);
// Execute given action on the target once the quota manager has been
// initialized.
//
// Only callable from the thread that created the Context.
void Dispatch(nsIEventTarget* aTarget, Action* aAction);
// Cancel any Actions running or waiting to run. This should allow the
// Context to be released and Listener::RemoveContext() will be called
// when complete.
//
// Only callable from the thread that created the Context.
void CancelAll();
// Cancel any Actions running or waiting to run that operate on the given
// cache ID.
//
// Only callable from the thread that created the Context.
void CancelForCacheId(CacheId aCacheId);
private:
class QuotaInitRunnable;
class ActionRunnable;
enum State
{
STATE_CONTEXT_INIT,
STATE_CONTEXT_READY,
STATE_CONTEXT_CANCELED
};
struct PendingAction
{
nsCOMPtr<nsIEventTarget> mTarget;
nsRefPtr<Action> mAction;
};
explicit Context(Manager* aManager);
~Context();
void DispatchAction(nsIEventTarget* aTarget, Action* aAction);
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo);
void OnActionRunnableComplete(ActionRunnable* const aAction);
nsRefPtr<Manager> mManager;
State mState;
QuotaInfo mQuotaInfo;
nsTArray<PendingAction> mPendingActions;
// weak refs since ~ActionRunnable() removes itself from this list
nsTArray<ActionRunnable*> mActionRunnables;
public:
NS_INLINE_DECL_REFCOUNTING(cache::Context)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Context_h

169
dom/cache/DBAction.cpp vendored Normal file
View File

@ -0,0 +1,169 @@
/* -*- 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/DBAction.h"
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/net/nsFileProtocolHandler.h"
#include "mozIStorageConnection.h"
#include "mozIStorageService.h"
#include "mozStorageCID.h"
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
using mozilla::dom::quota::PersistenceType;
DBAction::DBAction(Mode aMode)
: mMode(aMode)
{
}
DBAction::~DBAction()
{
}
void
DBAction::RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aResolver);
MOZ_ASSERT(aQuotaInfo.mDir);
if (IsCanceled()) {
aResolver->Resolve(NS_ERROR_ABORT);
return;
}
nsCOMPtr<nsIFile> dbDir;
nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolver->Resolve(rv);
return;
}
rv = dbDir->Append(NS_LITERAL_STRING("cache"));
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolver->Resolve(rv);
return;
}
nsCOMPtr<mozIStorageConnection> conn;
rv = OpenConnection(aQuotaInfo, dbDir, getter_AddRefs(conn));
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolver->Resolve(rv);
return;
}
MOZ_ASSERT(conn);
RunWithDBOnTarget(aResolver, aQuotaInfo, dbDir, conn);
}
nsresult
DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
mozIStorageConnection** aConnOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aDBDir);
MOZ_ASSERT(aConnOut);
bool exists;
nsresult rv = aDBDir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (!exists) {
if (NS_WARN_IF(mMode != Create)) { return NS_ERROR_FILE_NOT_FOUND; }
rv = aDBDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
}
nsCOMPtr<nsIFile> dbFile;
rv = aDBDir->Clone(getter_AddRefs(dbFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = dbFile->Append(NS_LITERAL_STRING("caches.sqlite"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = dbFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Use our default file:// protocol handler directly to construct the database
// URL. This avoids any problems if a plugin registers a custom file://
// handler. If such a custom handler used javascript, then we would have a
// bad time running off the main thread here.
nsRefPtr<nsFileProtocolHandler> handler = new nsFileProtocolHandler();
rv = handler->Init();
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIURI> uri;
rv = handler->NewFileURI(dbFile, getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFileURL> dbFileUrl = do_QueryInterface(uri);
if (NS_WARN_IF(!dbFileUrl)) { return NS_ERROR_UNEXPECTED; }
nsAutoCString type;
PersistenceTypeToText(PERSISTENCE_TYPE_DEFAULT, type);
rv = dbFileUrl->SetQuery(
NS_LITERAL_CSTRING("persistenceType=") + type +
NS_LITERAL_CSTRING("&group=") + aQuotaInfo.mGroup +
NS_LITERAL_CSTRING("&origin=") + aQuotaInfo.mOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<mozIStorageService> ss =
do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
if (NS_WARN_IF(!ss)) { return NS_ERROR_UNEXPECTED; }
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut);
if (rv == NS_ERROR_FILE_CORRUPTED) {
NS_WARNING("Cache database corrupted. Recreating empty database.");
// There is nothing else we can do to recover. Also, this data can
// be deleted by QuotaManager at any time anyways.
rv = dbFile->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// TODO: clean up any orphaned body files (bug 1110446)
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, aConnOut);
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(*aConnOut);
return rv;
}
SyncDBAction::SyncDBAction(Mode aMode)
: DBAction(aMode)
{
}
SyncDBAction::~SyncDBAction()
{
}
void
SyncDBAction::RunWithDBOnTarget(Resolver* aResolver,
const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
mozIStorageConnection* aConn)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aResolver);
MOZ_ASSERT(aDBDir);
MOZ_ASSERT(aConn);
nsresult rv = RunSyncWithDBOnTarget(aQuotaInfo, aDBDir, aConn);
aResolver->Resolve(rv);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

77
dom/cache/DBAction.h vendored Normal file
View File

@ -0,0 +1,77 @@
/* -*- 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_DBAction_h
#define mozilla_dom_cache_DBAction_h
#include "mozilla/dom/cache/Action.h"
#include "mozilla/dom/cache/CacheInitData.h"
#include "nsRefPtr.h"
#include "nsString.h"
class mozIStorageConnection;
class nsIFile;
namespace mozilla {
namespace dom {
namespace cache {
class DBAction : public Action
{
protected:
// The mode specifies whether the database should already exist or if its
// ok to create a new database.
enum Mode
{
Existing,
Create
};
explicit DBAction(Mode aMode);
// Action objects are deleted through their base pointer
virtual ~DBAction();
// Just as the resolver must be ref'd until resolve, you may also
// ref the DB connection. The connection can only be referenced from the
// target thread and must be released upon resolve.
virtual void
RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
nsIFile* aDBDir, mozIStorageConnection* aConn) = 0;
private:
virtual void
RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo) MOZ_OVERRIDE;
nsresult OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aQuotaDir,
mozIStorageConnection** aConnOut);
const Mode mMode;
};
class SyncDBAction : public DBAction
{
protected:
explicit SyncDBAction(Mode aMode);
// Action objects are deleted through their base pointer
virtual ~SyncDBAction();
virtual nsresult
RunSyncWithDBOnTarget(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
mozIStorageConnection* aConn) = 0;
private:
virtual void
RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
nsIFile* aDBDir, mozIStorageConnection* aConn) MOZ_OVERRIDE;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_DBAction_h

1322
dom/cache/DBSchema.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

141
dom/cache/DBSchema.h vendored Normal file
View File

@ -0,0 +1,141 @@
/* -*- 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_DBSchema_h
#define mozilla_dom_cache_DBSchema_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/cache/Types.h"
#include "nsError.h"
#include "nsString.h"
#include "nsTArrayForwardDeclare.h"
class mozIStorageConnection;
class mozIStorageStatement;
struct nsID;
namespace mozilla {
namespace dom {
namespace cache {
class PCacheQueryParams;
class PCacheRequest;
class PCacheRequestOrVoid;
class PCacheResponse;
class PCacheResponseOrVoid;
struct SavedRequest;
struct SavedResponse;
// TODO: remove static class and use functions in cache namespace (bug 1110485)
class DBSchema MOZ_FINAL
{
public:
static nsresult CreateSchema(mozIStorageConnection* aConn);
static nsresult CreateCache(mozIStorageConnection* aConn,
CacheId* aCacheIdOut);
// TODO: improve naming (confusing with CacheDelete) (bug 1110485)
static nsresult DeleteCache(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<nsID>& aDeletedBodyIdListOut);
// TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
static nsresult IsCacheOrphaned(mozIStorageConnection* aConn,
CacheId aCacheId, bool* aOrphanedOut);
static nsresult CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut);
static nsresult CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams,
nsTArray<SavedResponse>& aSavedResponsesOut);
static nsresult CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const nsID* aRequestBodyId,
const PCacheResponse& aResponse,
const nsID* aResponseBodyId,
nsTArray<nsID>& aDeletedBodyIdListOut);
static nsresult CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
nsTArray<nsID>& aDeletedBodyIdListOut,
bool* aSuccessOut);
static nsresult CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams,
nsTArray<SavedRequest>& aSavedRequestsOut);
static nsresult StorageMatch(mozIStorageConnection* aConn,
Namespace aNamespace,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut);
static nsresult StorageGetCacheId(mozIStorageConnection* aConn,
Namespace aNamespace, const nsAString& aKey,
bool* aFoundCacheOut, CacheId* aCacheIdOut);
static nsresult StoragePutCache(mozIStorageConnection* aConn,
Namespace aNamespace, const nsAString& aKey,
CacheId aCacheId);
static nsresult StorageForgetCache(mozIStorageConnection* aConn,
Namespace aNamespace,
const nsAString& aKey);
static nsresult StorageGetKeys(mozIStorageConnection* aConn,
Namespace aNamespace,
nsTArray<nsString>& aKeysOut);
private:
typedef int32_t EntryId;
static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<EntryId>& aEntryIdListOut);
static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
nsTArray<EntryId>& aEntryIdListOut,
uint32_t aMaxResults = UINT32_MAX);
static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
const PCacheRequest& aRequest,
EntryId entryId, bool* aSuccessOut);
static nsresult DeleteEntries(mozIStorageConnection* aConn,
const nsTArray<EntryId>& aEntryIdList,
nsTArray<nsID>& aDeletedBodyIdListOut,
uint32_t aPos=0, int32_t aLen=-1);
static nsresult InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const nsID* aRequestBodyId,
const PCacheResponse& aResponse,
const nsID* aResponseBodyId);
static nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
SavedResponse* aSavedResponseOut);
static nsresult ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
SavedRequest* aSavedRequestOut);
static void AppendListParamsToQuery(nsACString& aQuery,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen);
static nsresult BindListParamsToQuery(mozIStorageStatement* aState,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen);
static nsresult BindId(mozIStorageStatement* aState, uint32_t aPos,
const nsID* aId);
static nsresult ExtractId(mozIStorageStatement* aState, uint32_t aPos,
nsID* aIdOut);
DBSchema() = delete;
~DBSchema() = delete;
static const int32_t kLatestSchemaVersion;
static const int32_t kMaxEntriesPerStatement;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_DBSchema_h

108
dom/cache/Feature.cpp vendored Normal file
View File

@ -0,0 +1,108 @@
/* -*- 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/Feature.h"
#include "mozilla/dom/cache/ActorChild.h"
#include "WorkerPrivate.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::dom::workers::Running;
using mozilla::dom::workers::Status;
using mozilla::dom::workers::WorkerPrivate;
// static
already_AddRefed<Feature>
Feature::Create(WorkerPrivate* aWorkerPrivate)
{
MOZ_ASSERT(aWorkerPrivate);
nsRefPtr<Feature> feature = new Feature(aWorkerPrivate);
if (!aWorkerPrivate->AddFeature(aWorkerPrivate->GetJSContext(), feature)) {
return nullptr;
}
return feature.forget();
}
void
Feature::AddActor(ActorChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(Feature);
MOZ_ASSERT(aActor);
MOZ_ASSERT(!mActorList.Contains(aActor));
mActorList.AppendElement(aActor);
// Allow an actor to be added after we've entered the Notifying case. We
// can't stop the actor creation from racing with out destruction of the
// other actors and we need to wait for this extra one to close as well.
// Signal it should destroy itself right away.
if (mNotified) {
aActor->StartDestroy();
}
}
void
Feature::RemoveActor(ActorChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(Feature);
MOZ_ASSERT(aActor);
DebugOnly<bool> removed = mActorList.RemoveElement(aActor);
MOZ_ASSERT(removed);
MOZ_ASSERT(!mActorList.Contains(aActor));
}
bool
Feature::Notified() const
{
return mNotified;
}
bool
Feature::Notify(JSContext* aCx, Status aStatus)
{
NS_ASSERT_OWNINGTHREAD(Feature);
if (aStatus <= Running || mNotified) {
return true;
}
mNotified = true;
// Start the asynchronous destruction of our actors. These will call back
// into RemoveActor() once the actor is destroyed.
for (uint32_t i = 0; i < mActorList.Length(); ++i) {
mActorList[i]->StartDestroy();
}
return true;
}
Feature::Feature(WorkerPrivate* aWorkerPrivate)
: mWorkerPrivate(aWorkerPrivate)
, mNotified(false)
{
MOZ_ASSERT(mWorkerPrivate);
}
Feature::~Feature()
{
NS_ASSERT_OWNINGTHREAD(Feature);
MOZ_ASSERT(mActorList.IsEmpty());
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

54
dom/cache/Feature.h vendored Normal file
View File

@ -0,0 +1,54 @@
/* -*- 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_Feature_h
#define mozilla_dom_cache_Feature_h
#include "nsISupportsImpl.h"
#include "nsTArray.h"
#include "WorkerFeature.h"
namespace mozilla {
namespace workers {
class WorkerPrivate;
}
namespace dom {
namespace cache {
class ActorChild;
class Feature MOZ_FINAL : public workers::WorkerFeature
{
public:
static already_AddRefed<Feature> Create(workers::WorkerPrivate* aWorkerPrivate);
void AddActor(ActorChild* aActor);
void RemoveActor(ActorChild* aActor);
bool Notified() const;
// WorkerFeature methods
virtual bool Notify(JSContext* aCx, workers::Status aStatus) MOZ_OVERRIDE;
private:
explicit Feature(workers::WorkerPrivate *aWorkerPrivate);
~Feature();
workers::WorkerPrivate* mWorkerPrivate;
nsTArray<ActorChild*> mActorList;
bool mNotified;
public:
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::cache::Feature)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Feature_h

451
dom/cache/FetchPut.cpp vendored Normal file
View File

@ -0,0 +1,451 @@
/* -*- 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 "mozilla/dom/cache/PCacheTypes.h"
#include "nsContentUtils.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
class FetchPut::Runnable MOZ_FINAL : public nsRunnable
{
public:
explicit Runnable(FetchPut* aFetchPut)
: mFetchPut(aFetchPut)
{
MOZ_ASSERT(mFetchPut);
}
NS_IMETHOD Run() MOZ_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 MOZ_FINAL : public FetchDriverObserver
{
public:
explicit FetchObserver(FetchPut* aFetchPut)
: mFetchPut(aFetchPut)
{
}
virtual void OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE
{
MOZ_ASSERT(!mInternalResponse);
mInternalResponse = aResponse;
}
virtual void OnResponseEnd() MOZ_OVERRIDE
{
mFetchPut->FetchComplete(this, mInternalResponse);
mFetchPut = nullptr;
}
protected:
virtual ~FetchObserver() { }
private:
nsRefPtr<FetchPut> mFetchPut;
nsRefPtr<InternalResponse> mInternalResponse;
};
// static
nsresult
FetchPut::Create(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& 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, aRequestId, 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,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams)
: mListener(aListener)
, mManager(aManager)
, mRequestId(aRequestId)
, mCacheId(aCacheId)
, mInitiatingThread(NS_GetCurrentThread())
, mStateList(aRequests.Length())
, mPendingCount(0)
, mResult(NS_OK)
{
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->mPCacheRequest = 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);
}
nsresult
FetchPut::DispatchToMainThread()
{
MOZ_ASSERT(!mRunnable);
nsRefPtr<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(rv);
MaybeCompleteOnMainThread();
return;
}
for (uint32_t i = 0; i < mStateList.Length(); ++i) {
nsRefPtr<InternalRequest> internalRequest =
ToInternalRequest(mStateList[i].mPCacheRequest);
// 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(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() && NS_SUCCEEDED(mResult)) {
MaybeSetError(NS_ERROR_FAILURE);
}
for (uint32_t i = 0; i < mStateList.Length(); ++i) {
if (mStateList[i].mFetchObserver == aObserver) {
ErrorResult rv;
ToPCacheResponseWithoutBody(mStateList[i].mPCacheResponse,
*aInternalResponse, rv);
if (rv.Failed()) {
MaybeSetError(rv.ErrorCode());
return;
}
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 (NS_FAILED(mResult)) {
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].mPCacheRequest, putList)) {
MaybeSetError(NS_ERROR_DOM_INVALID_STATE_ERR);
MaybeNotifyListener();
return;
}
CacheRequestResponse* entry = putList.AppendElement();
entry->request() = mStateList[i].mPCacheRequest;
entry->response() = mStateList[i].mPCacheResponse;
requestStreamList.AppendElement(mStateList[i].mRequestStream.forget());
responseStreamList.AppendElement(mStateList[i].mResponseStream.forget());
}
mStateList.Clear();
mManager->CachePutAll(this, mRequestId, mCacheId, putList, requestStreamList,
responseStreamList);
}
// static
bool
FetchPut::MatchInPutList(const PCacheRequest& 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 =
new InternalHeaders(aRequest.headers());
for (uint32_t i = 0; i < aPutList.Length(); ++i) {
const PCacheRequest& cachedRequest = aPutList[i].request();
const PCacheResponse& 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 =
new InternalHeaders(cachedRequest.headers());
nsRefPtr<InternalHeaders> cachedResponseHeaders =
new InternalHeaders(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) {
if (varyHeaders[i].EqualsLiteral("*")) {
continue;
}
// The VARY header could in theory contain an illegal header name. So
// we need to detect the error in the Get() calls below. Treat these
// as not matching.
ErrorResult headerRv;
nsAutoCString value;
requestHeaders->Get(varyHeaders[j], value, rv);
if (NS_WARN_IF(rv.Failed())) {
varyHeadersMatch = false;
break;
}
nsAutoCString cachedValue;
cachedRequestHeaders->Get(varyHeaders[j], value, rv);
if (NS_WARN_IF(rv.Failed())) {
varyHeadersMatch = false;
break;
}
if (value != cachedValue) {
varyHeadersMatch = false;
break;
}
}
// URL was equal and all vary headers match!
if (varyHeadersMatch) {
return true;
}
}
return false;
}
void
FetchPut::OnCachePutAll(RequestId aRequestId, nsresult aRv)
{
MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
MaybeSetError(aRv);
MaybeNotifyListener();
}
void
FetchPut::MaybeSetError(nsresult aRv)
{
if (NS_FAILED(mResult) || NS_SUCCEEDED(aRv)) {
return;
}
mResult = aRv;
}
void
FetchPut::MaybeNotifyListener()
{
MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
if (!mListener) {
return;
}
mListener->OnFetchPut(this, mRequestId, 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
} // namespace cache
} // namespace dom
} // namespace mozilla

122
dom/cache/FetchPut.h vendored Normal file
View File

@ -0,0 +1,122 @@
/* -*- 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/dom/cache/Manager.h"
#include "mozilla/dom/cache/PCacheTypes.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 {
class ErrorResult;
namespace dom {
class OwningRequestOrScalarValueString;
class Promise;
class Request;
class RequestOrScalarValueString;
class Response;
template<typename T> class Sequence;
namespace cache {
class FetchPut MOZ_FINAL : public Manager::Listener
, public TypeUtils
{
public:
typedef std::pair<nsRefPtr<Request>, nsRefPtr<Response>> PutPair;
class Listener
{
public:
virtual void
OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, nsresult aRv) = 0;
};
static nsresult
Create(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
FetchPut** aFetchPutOut);
void ClearListener();
private:
class Runnable;
class FetchObserver;
struct State
{
PCacheRequest mPCacheRequest;
nsCOMPtr<nsIInputStream> mRequestStream;
nsRefPtr<FetchObserver> mFetchObserver;
PCacheResponse mPCacheResponse;
nsCOMPtr<nsIInputStream> mResponseStream;
nsRefPtr<Request> mRequest;
nsRefPtr<Response> mResponse;
};
FetchPut(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& 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 PCacheRequest& aRequest,
const nsTArray<CacheRequestResponse>& aPutList);
virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) MOZ_OVERRIDE;
void MaybeSetError(nsresult aRv);
void MaybeNotifyListener();
// TypeUtils methods
virtual nsIGlobalObject* GetGlobalObject() const MOZ_OVERRIDE;
#ifdef DEBUG
virtual void AssertOwningThread() const MOZ_OVERRIDE;
#endif
Listener* mListener;
nsRefPtr<Manager> mManager;
const RequestId mRequestId;
const CacheId mCacheId;
nsCOMPtr<nsIThread> mInitiatingThread;
nsTArray<State> mStateList;
uint32_t mPendingCount;
nsresult 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

286
dom/cache/FileUtils.cpp vendored Normal file
View File

@ -0,0 +1,286 @@
/* -*- 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/FileUtils.h"
#include "mozilla/dom/quota/FileStreams.h"
#include "mozilla/SnappyCompressOutputStream.h"
#include "mozilla/unused.h"
#include "nsIFile.h"
#include "nsIUUIDGenerator.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsString.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::dom::quota::FileInputStream;
using mozilla::dom::quota::FileOutputStream;
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
using mozilla::unused;
// static
nsresult
FileUtils::BodyCreateDir(nsIFile* aBaseDir)
{
MOZ_ASSERT(aBaseDir);
nsCOMPtr<nsIFile> aBodyDir;
nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = aBodyDir->Create(nsIFile::DIRECTORY_TYPE, 0755);
if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
return NS_OK;
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
// static
nsresult
FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
nsIFile** aCacheDirOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aCacheDirOut);
*aCacheDirOut = nullptr;
nsresult rv = aBaseDir->Clone(aCacheDirOut);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(*aCacheDirOut);
rv = (*aCacheDirOut)->Append(NS_LITERAL_STRING("morgue"));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Some file systems have poor performance when there are too many files
// in a single directory. Mitigate this issue by spreading the body
// files out into sub-directories. We use the last byte of the ID for
// the name of the sub-directory.
nsAutoString subDirName;
subDirName.AppendInt(aId.m3[7]);
rv = (*aCacheDirOut)->Append(subDirName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = (*aCacheDirOut)->Create(nsIFile::DIRECTORY_TYPE, 0755);
if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
return NS_OK;
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
// static
nsresult
FileUtils::BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
nsIFile* aBaseDir, nsIInputStream* aSource,
void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aSource);
MOZ_ASSERT(aClosure);
MOZ_ASSERT(aCallback);
MOZ_ASSERT(aIdOut);
MOZ_ASSERT(aCopyContextOut);
nsresult rv;
nsCOMPtr<nsIUUIDGenerator> idGen =
do_GetService("@mozilla.org/uuid-generator;1", &rv);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = idGen->GenerateUUIDInPlace(aIdOut);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> finalFile;
rv = BodyIdToFile(aBaseDir, *aIdOut, BODY_FILE_FINAL,
getter_AddRefs(finalFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool exists;
rv = finalFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (NS_WARN_IF(exists)) { return NS_ERROR_FILE_ALREADY_EXISTS; }
nsCOMPtr<nsIFile> tmpFile;
rv = BodyIdToFile(aBaseDir, *aIdOut, BODY_FILE_TMP, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = tmpFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (NS_WARN_IF(exists)) { return NS_ERROR_FILE_ALREADY_EXISTS; }
nsCOMPtr<nsIOutputStream> fileStream =
FileOutputStream::Create(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
aQuotaInfo.mOrigin, tmpFile);
if (NS_WARN_IF(!fileStream)) { return NS_ERROR_UNEXPECTED; }
nsRefPtr<SnappyCompressOutputStream> compressed =
new SnappyCompressOutputStream(fileStream);
nsCOMPtr<nsIEventTarget> target =
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
rv = NS_AsyncCopy(aSource, compressed, target, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
compressed->BlockSize(), aCallback, aClosure,
true, true, // close streams
aCopyContextOut);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
// static
void
FileUtils::BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aCopyContext);
nsresult rv = NS_CancelAsyncCopy(aCopyContext, NS_ERROR_ABORT);
unused << NS_WARN_IF(NS_FAILED(rv));
// The partially written file must be cleaned up after the async copy
// makes its callback.
}
// static
nsresult
FileUtils::BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
{
MOZ_ASSERT(aBaseDir);
nsCOMPtr<nsIFile> tmpFile;
nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> finalFile;
rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL, getter_AddRefs(finalFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsAutoString finalFileName;
rv = finalFile->GetLeafName(finalFileName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = tmpFile->RenameTo(nullptr, finalFileName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
// static
nsresult
FileUtils::BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
const nsID& aId, nsIInputStream** aStreamOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aStreamOut);
nsCOMPtr<nsIFile> finalFile;
nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL,
getter_AddRefs(finalFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool exists;
rv = finalFile->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (NS_WARN_IF(!exists)) { return NS_ERROR_FILE_NOT_FOUND; }
nsCOMPtr<nsIInputStream> fileStream =
FileInputStream::Create(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
aQuotaInfo.mOrigin, finalFile);
if (NS_WARN_IF(!fileStream)) { return NS_ERROR_UNEXPECTED; }
fileStream.forget(aStreamOut);
return rv;
}
// static
nsresult
FileUtils::BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
{
nsresult rv = NS_OK;
for (uint32_t i = 0; i < aIdList.Length(); ++i) {
nsCOMPtr<nsIFile> tmpFile;
rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_TMP,
getter_AddRefs(tmpFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = tmpFile->Remove(false /* recursive */);
if (rv == NS_ERROR_FILE_NOT_FOUND ||
rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
rv = NS_OK;
}
// Only treat file deletion as a hard failure in DEBUG builds. Users
// can unfortunately hit this on windows if anti-virus is scanning files,
// etc.
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIFile> finalFile;
rv = BodyIdToFile(aBaseDir, aIdList[i], BODY_FILE_FINAL,
getter_AddRefs(finalFile));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = finalFile->Remove(false /* recursive */);
if (rv == NS_ERROR_FILE_NOT_FOUND ||
rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
rv = NS_OK;
}
// Again, only treat removal as hard failure in debug build.
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
return NS_OK;
}
// static
nsresult
FileUtils::BodyIdToFile(nsIFile* aBaseDir, const nsID& aId,
BodyFileType aType, nsIFile** aBodyFileOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aBodyFileOut);
*aBodyFileOut = nullptr;
nsresult rv = BodyGetCacheDir(aBaseDir, aId, aBodyFileOut);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(*aBodyFileOut);
char idString[NSID_LENGTH];
aId.ToProvidedString(idString);
NS_ConvertASCIItoUTF16 fileName(idString);
if (aType == BODY_FILE_FINAL) {
fileName.AppendLiteral(".final");
} else {
fileName.AppendLiteral(".tmp");
}
rv = (*aBodyFileOut)->Append(fileName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

68
dom/cache/FileUtils.h vendored Normal file
View File

@ -0,0 +1,68 @@
/* -*- 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_FileUtils_h
#define mozilla_dom_cache_FileUtils_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/cache/Types.h"
#include "nsStreamUtils.h"
#include "nsTArrayForwardDeclare.h"
struct nsID;
class nsIFile;
namespace mozilla {
namespace dom {
namespace cache {
// TODO: remove static class and use functions in cache namespace (bug 1110485)
class FileUtils MOZ_FINAL
{
public:
enum BodyFileType
{
BODY_FILE_FINAL,
BODY_FILE_TMP
};
static nsresult BodyCreateDir(nsIFile* aBaseDir);
static nsresult BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
nsIFile** aCacheDirOut);
static nsresult
BodyStartWriteStream(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
nsIInputStream* aSource, void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut);
static void
BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext);
static nsresult
BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
static nsresult
BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
nsIInputStream** aStreamOut);
static nsresult
BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
private:
static nsresult
BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
nsIFile** aBodyFileOut);
FileUtils() = delete;
~FileUtils() = delete;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_FileUtils_h

22
dom/cache/IPCUtils.h vendored Normal file
View File

@ -0,0 +1,22 @@
/* -*- 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_IPCUtils_h
#define mozilla_dom_cache_IPCUtils_h
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/cache/Types.h"
namespace IPC {
template<>
struct ParamTraits<mozilla::dom::cache::Namespace> :
public ContiguousEnumSerializer<mozilla::dom::cache::Namespace,
mozilla::dom::cache::DEFAULT_NAMESPACE,
mozilla::dom::cache::NUMBER_OF_NAMESPACES>
{};
}
#endif // mozilla_dom_cache_IPCUtils_h

1869
dom/cache/Manager.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

285
dom/cache/Manager.h vendored Normal file
View File

@ -0,0 +1,285 @@
/* -*- 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_Manager_h
#define mozilla_dom_cache_Manager_h
#include "mozilla/dom/cache/CacheInitData.h"
#include "mozilla/dom/cache/PCacheStreamControlParent.h"
#include "mozilla/dom/cache/Types.h"
#include "nsCOMPtr.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
#include "nsTArray.h"
class nsIInputStream;
class nsIOutputStream;
class nsIThread;
namespace mozilla {
namespace dom {
namespace cache {
class CacheRequestResponse;
class Context;
class ManagerId;
class PCacheQueryParams;
class PCacheRequest;
class PCacheRequestOrVoid;
class PCacheResponse;
struct SavedRequest;
struct SavedResponse;
class StreamList;
// The Manager is class is responsible for performing all of the underlying
// work for a Cache or CacheStorage operation. The DOM objects and IPC actors
// are basically just plumbing to get the request to the right Manager object
// running in the parent process.
//
// There should be exactly one Manager object for each origin or app using the
// Cache API. This uniqueness is defined by the ManagerId equality operator.
// The uniqueness is enforced by the Manager GetOrCreate() factory method.
//
// The Manager object can out live the IPC actors in the case where the child
// process is killed; e.g a child process OOM. The Manager object can
// The Manager object can potentially use non-trivial resources. Long lived
// DOM objects and their actors should not maintain a reference to the Manager
// while idle. Transient DOM objects that may keep a reference for their
// lifetimes.
//
// For example, once a CacheStorage DOM object is access it will live until its
// global is released. Therefore, CacheStorage should release its Manager
// reference after operations complete and it becomes idle. Cache objects,
// however, can be GC'd once content are done using them and can therefore keep
// their Manager reference alive. Its expected that more operations are
// performed on a Cache object, so keeping the Manager reference will help
// minimize overhead for each reference.
//
// As an invariant, all Manager objects must cease all IO before shutdown. This
// is enforced by the ShutdownObserver. If content still holds references to
// Cache DOM objects during shutdown, then all operations will begin rejecting.
class Manager MOZ_FINAL
{
public:
// Callback interface implemented by clients of Manager, such as CacheParent
// and CacheStorageParent. In general, if you call a Manager method you
// should expect to receive exactly one On*() callback. For example, if
// you call Manager::CacheMatch(), then you should expect to receive
// OnCacheMatch() back in response.
//
// Listener objects are set on a per-operation basis. So you pass the
// Listener to a call like Manager::CacheMatch(). Once set in this way,
// the Manager will continue to reference the Listener until RemoveListener()
// is called. This is done to allow the same listener to be used for
// multiple operations simultaneously without having to maintain an exact
// count of operations-in-flight.
//
// Note, the Manager only holds weak references to Listener objects.
// Listeners must call Manager::RemoveListener() before they are destroyed
// to clear these weak references.
//
// All public methods should be invoked on the same thread used to create
// the Manager.
class Listener
{
public:
virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aResponse,
StreamList* aStreamList) { }
virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedResponse>& aSavedResponses,
StreamList* aStreamList) { }
virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) { }
virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
bool aSuccess) { }
virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedRequest>& aSavedRequests,
StreamList* aStreamList) { }
virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aResponse,
StreamList* aStreamList) { }
virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
bool aCacheFound) { }
virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
CacheId aCacheId) { }
virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
bool aCacheDeleted) { }
virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys) { }
protected:
~Listener() { }
};
static nsresult GetOrCreate(ManagerId* aManagerId, Manager** aManagerOut);
static already_AddRefed<Manager> Get(ManagerId* aManagerId);
// Synchronously shutdown from main thread. This spins the event loop.
static void ShutdownAllOnMainThread();
// Must be called by Listener objects before they are destroyed.
void RemoveListener(Listener* aListener);
// Must be called by Context objects before they are destroyed.
void RemoveContext(Context* aContext);
// If an actor represents a long term reference to a cache or body stream,
// then they must call AddRefCacheId() or AddRefBodyId(). This will
// cause the Manager to keep the backing data store alive for the given
// object. The actor must then call ReleaseCacheId() or ReleaseBodyId()
// exactly once for every AddRef*() call it made. Any delayed deletion
// will then be performed.
void AddRefCacheId(CacheId aCacheId);
void ReleaseCacheId(CacheId aCacheId);
void AddRefBodyId(const nsID& aBodyId);
void ReleaseBodyId(const nsID& aBodyId);
already_AddRefed<ManagerId> GetManagerId() const;
// Methods to allow a StreamList to register themselves with the Manager.
// StreamList objects must call RemoveStreamList() before they are destroyed.
void AddStreamList(StreamList* aStreamList);
void RemoveStreamList(StreamList* aStreamList);
// TODO: consider moving CacheId up in the argument lists below (bug 1110485)
void CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams);
void CacheMatchAll(Listener* aListener, RequestId aRequestId,
CacheId aCacheId, const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams);
void CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
const nsTArray<CacheRequestResponse>& aPutList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
void CacheDelete(Listener* aListener, RequestId aRequestId,
CacheId aCacheId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams);
void CacheKeys(Listener* aListener, RequestId aRequestId,
CacheId aCacheId, const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams);
void StorageMatch(Listener* aListener, RequestId aRequestId,
Namespace aNamespace, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams);
void StorageHas(Listener* aListener, RequestId aRequestId,
Namespace aNamespace, const nsAString& aKey);
void StorageOpen(Listener* aListener, RequestId aRequestId,
Namespace aNamespace, const nsAString& aKey);
void StorageDelete(Listener* aListener, RequestId aRequestId,
Namespace aNamespace, const nsAString& aKey);
void StorageKeys(Listener* aListener, RequestId aRequestId,
Namespace aNamespace);
private:
class Factory;
class BaseAction;
class DeleteOrphanedCacheAction;
class CacheMatchAction;
class CacheMatchAllAction;
class CachePutAllAction;
class CacheDeleteAction;
class CacheKeysAction;
class StorageMatchAction;
class StorageHasAction;
class StorageOpenAction;
class StorageDeleteAction;
class StorageKeysAction;
typedef uint64_t ListenerId;
Manager(ManagerId* aManagerId, nsIThread* aIOThread);
~Manager();
void Shutdown();
already_AddRefed<Context> CurrentContext();
ListenerId SaveListener(Listener* aListener);
Listener* GetListener(ListenerId aListenerId) const;
bool SetCacheIdOrphanedIfRefed(CacheId aCacheId);
bool SetBodyIdOrphanedIfRefed(const nsID& aBodyId);
void NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList);
nsRefPtr<ManagerId> mManagerId;
nsCOMPtr<nsIThread> mIOThread;
// Weak reference cleared by RemoveContext() in Context destructor.
Context* MOZ_NON_OWNING_REF mContext;
// Weak references cleared by RemoveListener() in Listener destructors.
struct ListenerEntry
{
ListenerEntry()
: mId(UINT64_MAX),
mListener(nullptr)
{
}
ListenerEntry(ListenerId aId, Listener* aListener)
: mId(aId)
, mListener(aListener)
{
}
ListenerId mId;
Listener* mListener;
};
class ListenerEntryIdComparator
{
public:
bool Equals(const ListenerEntry& aA, const ListenerId& aB) const
{
return aA.mId == aB;
}
};
class ListenerEntryListenerComparator
{
public:
bool Equals(const ListenerEntry& aA, const Listener* aB) const
{
return aA.mListener == aB;
}
};
typedef nsTArray<ListenerEntry> ListenerList;
ListenerList mListeners;
static ListenerId sNextListenerId;
// Weak references cleared by RemoveStreamList() in StreamList destructors.
nsTArray<StreamList*> mStreamLists;
bool mShuttingDown;
struct CacheIdRefCounter
{
CacheId mCacheId;
MozRefCountType mCount;
bool mOrphaned;
};
nsTArray<CacheIdRefCounter> mCacheIdRefs;
struct BodyIdRefCounter
{
nsID mBodyId;
MozRefCountType mCount;
bool mOrphaned;
};
nsTArray<BodyIdRefCounter> mBodyIdRefs;
public:
NS_INLINE_DECL_REFCOUNTING(cache::Manager)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Manager_h

91
dom/cache/ManagerId.cpp vendored Normal file
View File

@ -0,0 +1,91 @@
/* -*- 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/ManagerId.h"
#include "nsIPrincipal.h"
#include "nsProxyRelease.h"
#include "nsRefPtr.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
// static
nsresult
ManagerId::Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut)
{
MOZ_ASSERT(NS_IsMainThread());
// The QuotaManager::GetInfoFromPrincipal() has special logic for system
// and about: principals. We currently don't need the system principal logic
// because ManagerId only uses the origin for in memory comparisons. We
// also don't do any special logic to host the same Cache for different about:
// pages, so we don't need those checks either.
//
// But, if we get the same QuotaManager directory for different about:
// origins, we probably only want one Manager instance. So, we might
// want to start using the QM's concept of origin uniqueness here.
//
// TODO: consider using QuotaManager's modified origin here (bug 1112071)
nsAutoCString origin;
nsresult rv = aPrincipal->GetOrigin(getter_Copies(origin));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
uint32_t appId;
rv = aPrincipal->GetAppId(&appId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool inBrowserElement;
rv = aPrincipal->GetIsInBrowserElement(&inBrowserElement);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsRefPtr<ManagerId> ref = new ManagerId(aPrincipal, origin, appId,
inBrowserElement);
ref.forget(aManagerIdOut);
return NS_OK;
}
already_AddRefed<nsIPrincipal>
ManagerId::Principal() const
{
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIPrincipal> ref = mPrincipal;
return ref.forget();
}
ManagerId::ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin,
uint32_t aAppId, bool aInBrowserElement)
: mPrincipal(aPrincipal)
, mOrigin(aOrigin)
, mAppId(aAppId)
, mInBrowserElement(aInBrowserElement)
{
MOZ_ASSERT(mPrincipal);
}
ManagerId::~ManagerId()
{
// If we're already on the main thread, then default destruction is fine
if (NS_IsMainThread()) {
return;
}
// Otherwise we need to proxy to main thread to do the release
// The PBackground worker thread shouldn't be running after the main thread
// is stopped. So main thread is guaranteed to exist here.
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
NS_ProxyRelease(mainThread, mPrincipal.forget().take());
}
} // namespace cache
} // namespace dom
} // namespace mozilla

81
dom/cache/ManagerId.h vendored Normal file
View File

@ -0,0 +1,81 @@
/* -*- 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_ManagerId_h
#define mozilla_dom_cache_ManagerId_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/cache/Types.h"
#include "nsCOMPtr.h"
#include "nsError.h"
#include "nsISupportsImpl.h"
#include "nsString.h"
class nsIPrincipal;
namespace mozilla {
namespace dom {
namespace cache {
class ManagerId MOZ_FINAL
{
public:
// nsTArray comparator that compares by value instead of pointer values.
class MOZ_STACK_CLASS Comparator MOZ_FINAL
{
public:
bool Equals(ManagerId *aA, ManagerId* aB) const { return *aA == *aB; }
bool LessThan(ManagerId *aA, ManagerId* aB) const { return *aA < *aB; }
};
// Main thread only
static nsresult Create(nsIPrincipal* aPrincipal, ManagerId** aManagerIdOut);
// Main thread only
already_AddRefed<nsIPrincipal> Principal() const;
const nsACString& Origin() const { return mOrigin; }
bool operator==(const ManagerId& aOther) const
{
return mOrigin == aOther.mOrigin &&
mAppId == aOther.mAppId &&
mInBrowserElement == aOther.mInBrowserElement;
}
bool operator<(const ManagerId& aOther) const
{
return mOrigin < aOther.mOrigin ||
(mOrigin == aOther.mOrigin && mAppId < aOther.mAppId) ||
(mOrigin == aOther.mOrigin && mAppId == aOther.mAppId &&
mInBrowserElement < aOther.mInBrowserElement);
}
private:
ManagerId(nsIPrincipal* aPrincipal, const nsACString& aOrigin,
uint32_t aAppId, bool aInBrowserElement);
~ManagerId();
ManagerId(const ManagerId&) = delete;
ManagerId& operator=(const ManagerId&) = delete;
// only accessible on main thread
nsCOMPtr<nsIPrincipal> mPrincipal;
// immutable to allow threadsfe access
const nsCString mOrigin;
const uint32_t mAppId;
const bool mInBrowserElement;
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::cache::ManagerId)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_ManagerId_h

46
dom/cache/PCache.ipdl vendored Normal file
View File

@ -0,0 +1,46 @@
/* 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 protocol PBackground;
include PCacheTypes;
include protocol PFileDescriptorSet;
include protocol PBlob; // FIXME: bug 792908
include protocol PCacheStreamControl;
using mozilla::dom::cache::RequestId from "mozilla/dom/cache/Types.h";
include "mozilla/dom/cache/IPCUtils.h";
namespace mozilla {
namespace dom {
namespace cache {
protocol PCache
{
manager PBackground;
parent:
Teardown();
Match(RequestId requestId, PCacheRequest request, PCacheQueryParams params);
MatchAll(RequestId requestId, PCacheRequestOrVoid request, PCacheQueryParams params);
AddAll(RequestId requestId, PCacheRequest[] requests);
Put(RequestId requestId, CacheRequestResponse aPut);
Delete(RequestId requestId, PCacheRequest request, PCacheQueryParams params);
Keys(RequestId requestId, PCacheRequestOrVoid request, PCacheQueryParams params);
child:
MatchResponse(RequestId requestId, nsresult aRv, PCacheResponseOrVoid aResponse);
MatchAllResponse(RequestId requestId, nsresult aRv, PCacheResponse[] responses);
AddAllResponse(RequestId requestId, nsresult aRv);
PutResponse(RequestId requestId, nsresult aRv);
DeleteResponse(RequestId requestId, nsresult aRv, bool success);
KeysResponse(RequestId requestId, nsresult aRv, PCacheRequest[] requests);
both:
__delete__();
};
} // namespace cache
} // namespace dom
} // namespace mozilla

44
dom/cache/PCacheStorage.ipdl vendored Normal file
View File

@ -0,0 +1,44 @@
/* 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 protocol PBackground;
include protocol PCache;
include PCacheTypes;
include protocol PFileDescriptorSet;
include protocol PBlob; // FIXME: bug 792908
include protocol PCacheStreamControl;
using mozilla::dom::cache::RequestId from "mozilla/dom/cache/IPCUtils.h";
namespace mozilla {
namespace dom {
namespace cache {
protocol PCacheStorage
{
manager PBackground;
parent:
Teardown();
Match(RequestId aRequestId, PCacheRequest aRequest,
PCacheQueryParams aParams);
Has(RequestId aRequestId, nsString aKey);
Open(RequestId aRequestId, nsString aKey);
Delete(RequestId aRequestId, nsString aKey);
Keys(RequestId aRequestId);
child:
MatchResponse(RequestId aRequestId, nsresult aRv,
PCacheResponseOrVoid aResponseOrVoid);
HasResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
OpenResponse(RequestId aRequestId, nsresult aRv, nullable PCache aActor);
DeleteResponse(RequestId aRequestId, nsresult aRv, bool aSuccess);
KeysResponse(RequestId aRequestId, nsresult aRv, nsString[] aKeys);
__delete__();
};
} // namespace cache
} // namespace dom
} // namespace mozilla

28
dom/cache/PCacheStreamControl.ipdl vendored Normal file
View File

@ -0,0 +1,28 @@
/* 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 protocol PBackground;
using struct nsID from "nsID.h";
namespace mozilla {
namespace dom {
namespace cache {
protocol PCacheStreamControl
{
manager PBackground;
parent:
NoteClosed(nsID aStreamId);
child:
Close(nsID aStreamId);
CloseAll();
__delete__();
};
} // namespace cache
} // namespace dom
} // namespace mozilla

89
dom/cache/PCacheTypes.ipdlh vendored Normal file
View File

@ -0,0 +1,89 @@
/* 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 protocol PCacheStreamControl;
include PHeaders;
include InputStreamParams;
using HeadersGuardEnum from "mozilla/dom/FetchIPCUtils.h";
using RequestCredentials from "mozilla/dom/FetchIPCUtils.h";
using RequestMode from "mozilla/dom/FetchIPCUtils.h";
using mozilla::dom::ResponseType from "mozilla/dom/FetchIPCUtils.h";
using mozilla::void_t from "ipc/IPCMessageUtils.h";
using struct nsID from "nsID.h";
namespace mozilla {
namespace dom {
namespace cache {
struct PCacheQueryParams
{
bool ignoreSearch;
bool ignoreMethod;
bool ignoreVary;
bool prefixMatch;
bool cacheNameSet;
nsString cacheName;
};
struct PCacheReadStream
{
nsID id;
OptionalInputStreamParams params;
OptionalFileDescriptorSet fds;
nullable PCacheStreamControl control;
};
union PCacheReadStreamOrVoid
{
void_t;
PCacheReadStream;
};
struct PCacheRequest
{
nsCString method;
nsString url;
nsString urlWithoutQuery;
PHeadersEntry[] headers;
HeadersGuardEnum headersGuard;
nsString referrer;
RequestMode mode;
RequestCredentials credentials;
PCacheReadStreamOrVoid body;
uint32_t context;
};
union PCacheRequestOrVoid
{
void_t;
PCacheRequest;
};
struct PCacheResponse
{
ResponseType type;
nsString url;
uint32_t status;
nsCString statusText;
PHeadersEntry[] headers;
HeadersGuardEnum headersGuard;
PCacheReadStreamOrVoid body;
};
union PCacheResponseOrVoid
{
void_t;
PCacheResponse;
};
struct CacheRequestResponse
{
PCacheRequest request;
PCacheResponse response;
};
} // namespace cache
} // namespace dom
} // namespace mozilla

208
dom/cache/PrincipalVerifier.cpp vendored Normal file
View File

@ -0,0 +1,208 @@
/* -*- 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/PrincipalVerifier.h"
#include "mozilla/AppProcessChecker.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/cache/ManagerId.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "nsIPrincipal.h"
#include "nsIScriptSecurityManager.h"
#include "nsNetUtil.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::ipc::AssertIsOnBackgroundThread;
using mozilla::ipc::BackgroundParent;
using mozilla::ipc::PBackgroundParent;
using mozilla::ipc::PrincipalInfo;
using mozilla::ipc::PrincipalInfoToPrincipal;
// static
already_AddRefed<PrincipalVerifier>
PrincipalVerifier::CreateAndDispatch(Listener* aListener,
PBackgroundParent* aActor,
const PrincipalInfo& aPrincipalInfo)
{
// We must get the ContentParent actor from the PBackgroundParent. This
// only works on the PBackground thread.
AssertIsOnBackgroundThread();
nsRefPtr<PrincipalVerifier> verifier = new PrincipalVerifier(aListener,
aActor,
aPrincipalInfo);
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(verifier)));
return verifier.forget();
}
void
PrincipalVerifier::ClearListener()
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(mListener);
mListener = nullptr;
}
PrincipalVerifier::PrincipalVerifier(Listener* aListener,
PBackgroundParent* aActor,
const PrincipalInfo& aPrincipalInfo)
: mListener(aListener)
, mActor(BackgroundParent::GetContentParent(aActor))
, mPrincipalInfo(aPrincipalInfo)
, mInitiatingThread(NS_GetCurrentThread())
, mResult(NS_OK)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(mListener);
MOZ_ASSERT(mInitiatingThread);
}
PrincipalVerifier::~PrincipalVerifier()
{
// Since the PrincipalVerifier is a Runnable that executes on multiple
// threads, its a race to see which thread de-refs us last. Therefore
// we cannot guarantee which thread we destruct on.
MOZ_ASSERT(!mListener);
// We should always be able to explicitly release the actor on the main
// thread.
MOZ_ASSERT(!mActor);
}
NS_IMETHODIMP
PrincipalVerifier::Run()
{
// Executed twice. First, on the main thread and then back on the
// originating thread.
if (NS_IsMainThread()) {
VerifyOnMainThread();
return NS_OK;
}
CompleteOnInitiatingThread();
return NS_OK;
}
void
PrincipalVerifier::VerifyOnMainThread()
{
MOZ_ASSERT(NS_IsMainThread());
// No matter what happens, we need to release the actor before leaving
// this method.
nsRefPtr<ContentParent> actor;
actor.swap(mActor);
nsresult rv;
nsRefPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo,
&rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchToInitiatingThread(rv);
return;
}
// We disallow null principal and unknown app IDs on the client side, but
// double-check here.
if (NS_WARN_IF(principal->GetIsNullPrincipal() ||
principal->GetUnknownAppId())) {
DispatchToInitiatingThread(NS_ERROR_FAILURE);
return;
}
// Verify that a child process claims to own the app for this principal
if (NS_WARN_IF(actor && !AssertAppPrincipal(actor, principal))) {
DispatchToInitiatingThread(NS_ERROR_FAILURE);
return;
}
actor = nullptr;
nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
if (NS_WARN_IF(!ssm)) {
DispatchToInitiatingThread(NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
return;
}
#ifdef DEBUG
// Sanity check principal origin by using it to construct a URI and security
// checking it. Don't do this for the system principal, though, as its origin
// is a synthetic [System Principal] string.
if (!ssm->IsSystemPrincipal(principal)) {
nsAutoCString origin;
rv = principal->GetOrigin(getter_Copies(origin));
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchToInitiatingThread(rv);
return;
}
nsCOMPtr<nsIURI> uri;
rv = NS_NewURI(getter_AddRefs(uri), origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchToInitiatingThread(rv);
return;
}
rv = principal->CheckMayLoad(uri, false, false);
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchToInitiatingThread(rv);
return;
}
}
#endif
rv = ManagerId::Create(principal, getter_AddRefs(mManagerId));
if (NS_WARN_IF(NS_FAILED(rv))) {
DispatchToInitiatingThread(rv);
return;
}
DispatchToInitiatingThread(NS_OK);
}
void
PrincipalVerifier::CompleteOnInitiatingThread()
{
AssertIsOnBackgroundThread();
// This can happen if the listener is destroyed before we finish. For
// example, if the child process OOMs and the actor is destroyed.
if (!mListener) {
return;
}
mListener->OnPrincipalVerified(mResult, mManagerId);
// The listener must clear their reference in OnPrincipalVerified()
MOZ_ASSERT(!mListener);
}
void
PrincipalVerifier::DispatchToInitiatingThread(nsresult aRv)
{
MOZ_ASSERT(NS_IsMainThread());
mResult = aRv;
// The Cache ShutdownObserver does not track all principal verifiers, so we
// cannot ensure this always succeeds. Instead, simply warn on failures.
// This will result in a new CacheStorage object delaying operations until
// shutdown completes and the browser goes away. This is as graceful as
// we can get here.
nsresult rv = mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Cache unable to complete principal verification due to shutdown.");
}
}
} // namesapce cache
} // namespace dom
} // namespace mozilla

75
dom/cache/PrincipalVerifier.h vendored Normal file
View File

@ -0,0 +1,75 @@
/* -*- 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_PrincipalVerifier_h
#define mozilla_dom_cache_PrincipalVerifier_h
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace ipc {
class PBackgroundParent;
}
namespace dom {
namespace cache {
class ManagerId;
class PrincipalVerifier MOZ_FINAL : public nsRunnable
{
public:
// An interface to be implemented by code wishing to use the
// PrincipalVerifier. Note, the Listener implementation is responsible
// for calling ClearListener() on the PrincipalVerifier to clear the
// weak reference.
class Listener
{
public:
virtual void OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId) = 0;
};
static already_AddRefed<PrincipalVerifier>
CreateAndDispatch(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
// The Listener must call ClearListener() when OnPrincipalVerified() is
// called or when the Listener is destroyed.
void ClearListener();
private:
PrincipalVerifier(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
virtual ~PrincipalVerifier();
void VerifyOnMainThread();
void CompleteOnInitiatingThread();
void DispatchToInitiatingThread(nsresult aRv);
// Weak reference cleared by ClearListener()
Listener* mListener;
// set in originating thread at construction, but must be accessed and
// released on main thread
nsRefPtr<ContentParent> mActor;
const mozilla::ipc::PrincipalInfo mPrincipalInfo;
nsCOMPtr<nsIThread> mInitiatingThread;
nsresult mResult;
nsRefPtr<ManagerId> mManagerId;
public:
NS_DECL_NSIRUNNABLE
};
} // namesapce cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_PrincipalVerifier_h

214
dom/cache/QuotaClient.cpp vendored Normal file
View File

@ -0,0 +1,214 @@
/* -*- 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/QuotaClient.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/quota/UsageInfo.h"
#include "nsIFile.h"
#include "nsISimpleEnumerator.h"
#include "nsThreadUtils.h"
namespace {
using mozilla::DebugOnly;
using mozilla::dom::cache::Manager;
using mozilla::dom::quota::Client;
using mozilla::dom::quota::PersistenceType;
using mozilla::dom::quota::QuotaManager;
using mozilla::dom::quota::UsageInfo;
static nsresult
GetBodyUsage(nsIFile* aDir, UsageInfo* aUsageInfo)
{
nsCOMPtr<nsISimpleEnumerator> entries;
nsresult rv = aDir->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool hasMore;
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore &&
!aUsageInfo->Canceled()) {
nsCOMPtr<nsISupports> entry;
rv = entries->GetNext(getter_AddRefs(entry));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
bool isDir;
rv = file->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (isDir) {
rv = GetBodyUsage(file, aUsageInfo);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
continue;
}
int64_t fileSize;
rv = file->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(fileSize >= 0);
aUsageInfo->AppendToFileUsage(fileSize);
}
return NS_OK;
}
class CacheQuotaClient MOZ_FINAL : public Client
{
public:
virtual Type
GetType() MOZ_OVERRIDE
{
return DOMCACHE;
}
virtual nsresult
InitOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
const nsACString& aOrigin, UsageInfo* aUsageInfo) MOZ_OVERRIDE
{
return NS_OK;
}
virtual nsresult
GetUsageForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
const nsACString& aOrigin,
UsageInfo* aUsageInfo) MOZ_OVERRIDE
{
QuotaManager* qm = QuotaManager::Get();
MOZ_ASSERT(qm);
nsCOMPtr<nsIFile> dir;
nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
getter_AddRefs(dir));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
rv = dir->Append(NS_LITERAL_STRING(DOMCACHE_DIRECTORY_NAME));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsISimpleEnumerator> entries;
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool hasMore;
while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore &&
!aUsageInfo->Canceled()) {
nsCOMPtr<nsISupports> entry;
rv = entries->GetNext(getter_AddRefs(entry));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsCOMPtr<nsIFile> file = do_QueryInterface(entry);
nsAutoString leafName;
rv = file->GetLeafName(leafName);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
bool isDir;
rv = file->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (isDir) {
if (leafName.EqualsLiteral("morgue")) {
rv = GetBodyUsage(file, aUsageInfo);
} else {
NS_WARNING("Unknown Cache directory found!");
}
continue;
}
// Ignore transient sqlite files
if (leafName.EqualsLiteral("caches.sqlite-journal") ||
leafName.EqualsLiteral("caches.sqlite-shm") ||
leafName.Find(NS_LITERAL_CSTRING("caches.sqlite-mj"), false, 0, 0) == 0) {
continue;
}
if (leafName.EqualsLiteral("caches.sqlite") ||
leafName.EqualsLiteral("caches.sqlite-wal")) {
int64_t fileSize;
rv = file->GetFileSize(&fileSize);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
MOZ_ASSERT(fileSize >= 0);
aUsageInfo->AppendToDatabaseUsage(fileSize);
continue;
}
NS_WARNING("Unknown Cache file found!");
}
return NS_OK;
}
virtual void
OnOriginClearCompleted(PersistenceType aPersistenceType,
const nsACString& aOrigin) MOZ_OVERRIDE
{
// nothing to do
}
virtual void
ReleaseIOThreadObjects() MOZ_OVERRIDE
{
// nothing to do
}
virtual bool
IsFileServiceUtilized() MOZ_OVERRIDE
{
return false;
}
virtual bool
IsTransactionServiceActivated() MOZ_OVERRIDE
{
// TODO: implement nsIOfflineStorage interface (bug 1110487)
return false;
}
virtual void
WaitForStoragesToComplete(nsTArray<nsIOfflineStorage*>& aStorages,
nsIRunnable* aCallback) MOZ_OVERRIDE
{
// TODO: implement nsIOfflineStorage interface (bug 1110487)
}
virtual void
ShutdownTransactionService() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
// spins the event loop and synchronously shuts down all Managers
Manager::ShutdownAllOnMainThread();
}
private:
~CacheQuotaClient() { }
public:
NS_INLINE_DECL_REFCOUNTING(CacheQuotaClient)
};
} // anonymous namespace;
namespace mozilla {
namespace dom {
namespace cache {
already_AddRefed<quota::Client> CreateQuotaClient()
{
nsRefPtr<CacheQuotaClient> ref = new CacheQuotaClient();
return ref.forget();
}
} // namespace cache
} // namespace dom
} // namespace mozilla

23
dom/cache/QuotaClient.h vendored Normal file
View File

@ -0,0 +1,23 @@
/* -*- 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_QuotaClient_h
#define mozilla_dom_cache_QuotaClient_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/quota/Client.h"
namespace mozilla {
namespace dom {
namespace cache {
already_AddRefed<quota::Client> CreateQuotaClient();
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_QuotaClient_h

543
dom/cache/ReadStream.cpp vendored Normal file
View File

@ -0,0 +1,543 @@
/* -*- 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/ReadStream.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheStreamControlChild.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/PCacheStreamControlChild.h"
#include "mozilla/dom/cache/PCacheStreamControlParent.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/InputStreamParams.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "mozilla/ipc/PBackgroundParent.h"
#include "mozilla/ipc/PFileDescriptorSetChild.h"
#include "mozilla/ipc/PFileDescriptorSetParent.h"
#include "mozilla/SnappyUncompressInputStream.h"
#include "nsIAsyncInputStream.h"
#include "nsTArray.h"
namespace {
using mozilla::unused;
using mozilla::void_t;
using mozilla::dom::cache::CacheStreamControlChild;
using mozilla::dom::cache::CacheStreamControlParent;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::dom::cache::PCacheStreamControlChild;
using mozilla::dom::cache::PCacheStreamControlParent;
using mozilla::dom::cache::ReadStream;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::PFileDescriptorSetChild;
using mozilla::ipc::PFileDescriptorSetParent;
// There are separate concrete implementations of ReadStream for the child
// and parent processes. This is unfortunately necessary because the
// actor types are distinct for these two cases. Also, the interface for
// reporting the close event differs slightly for the child and parent
// StreamControl actors.
// ----------------------------------------------------------------------------
class ReadStreamChild MOZ_FINAL : public ReadStream
{
public:
ReadStreamChild(PCacheStreamControlChild* aControl, const nsID& aId,
nsIInputStream* aStream)
: ReadStream(aId, aStream)
, mControl(static_cast<CacheStreamControlChild*>(aControl))
{
MOZ_ASSERT(mControl);
mControl->AddListener(this);
}
virtual ~ReadStreamChild()
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
NoteClosed();
}
virtual void NoteClosedOnOwningThread() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
if (mClosed) {
return;
}
mClosed = true;
mControl->RemoveListener(this);
mControl->NoteClosed(mId);
}
virtual void ForgetOnOwningThread() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
if (mClosed) {
return;
}
mClosed = true;
mControl->RemoveListener(this);
}
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE
{
MOZ_ASSERT(aReadStreamOut);
MOZ_ASSERT(!mClosed);
aReadStreamOut->controlParent() = nullptr;
aReadStreamOut->controlChild() = mControl;
}
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
const nsTArray<FileDescriptor>& fds) MOZ_OVERRIDE
{
MOZ_ASSERT(!mClosed);
PFileDescriptorSetChild* fdSet = nullptr;
if (!fds.IsEmpty()) {
fdSet = mControl->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
for (uint32_t i = 1; i < fds.Length(); ++i) {
unused << fdSet->SendAddFileDescriptor(fds[i]);
}
}
if (fdSet) {
aReadStreamOut->fds() = fdSet;
} else {
aReadStreamOut->fds() = void_t();
}
}
private:
CacheStreamControlChild* mControl;
};
// ----------------------------------------------------------------------------
class ReadStreamParent MOZ_FINAL : public ReadStream
{
public:
ReadStreamParent(PCacheStreamControlParent* aControl, const nsID& aId,
nsIInputStream* aStream)
: ReadStream(aId, aStream)
, mControl(static_cast<CacheStreamControlParent*>(aControl))
{
MOZ_ASSERT(mControl);
mControl->AddListener(this);
}
virtual ~ReadStreamParent()
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
NoteClosed();
}
virtual void NoteClosedOnOwningThread() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
if (mClosed) {
return;
}
mClosed = true;
mControl->RemoveListener(this);
// This can cause mControl to be destructed
mControl->RecvNoteClosed(mId);
mControl = nullptr;
}
virtual void ForgetOnOwningThread() MOZ_OVERRIDE
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
if (mClosed) {
return;
}
mClosed = true;
// This can cause mControl to be destroyed
mControl->RemoveListener(this);
mControl = nullptr;
}
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) MOZ_OVERRIDE
{
MOZ_ASSERT(aReadStreamOut);
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(mControl);
aReadStreamOut->controlChild() = nullptr;
aReadStreamOut->controlParent() = mControl;
}
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
const nsTArray<FileDescriptor>& fds) MOZ_OVERRIDE
{
MOZ_ASSERT(!mClosed);
MOZ_ASSERT(mControl);
PFileDescriptorSetParent* fdSet = nullptr;
if (!fds.IsEmpty()) {
fdSet = mControl->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
for (uint32_t i = 1; i < fds.Length(); ++i) {
unused << fdSet->SendAddFileDescriptor(fds[i]);
}
}
if (fdSet) {
aReadStreamOut->fds() = fdSet;
} else {
aReadStreamOut->fds() = void_t();
}
}
private:
CacheStreamControlParent* mControl;
};
// ----------------------------------------------------------------------------
} // anonymous namespace
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::unused;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::FileDescriptorSetChild;
using mozilla::ipc::FileDescriptorSetParent;
using mozilla::ipc::InputStreamParams;
using mozilla::ipc::OptionalFileDescriptorSet;
using mozilla::ipc::PFileDescriptorSetChild;
// Runnable to notify actors that the ReadStream has closed. This must
// be done on the thread associated with the PBackground actor. Must be
// cancelable to execute on Worker threads (which can occur when the
// ReadStream is constructed on a child process Worker thread).
class ReadStream::NoteClosedRunnable MOZ_FINAL : public nsCancelableRunnable
{
public:
explicit NoteClosedRunnable(ReadStream* aStream)
: mStream(aStream)
{ }
NS_IMETHOD Run()
{
mStream->NoteClosedOnOwningThread();
return NS_OK;
}
// Note, we must proceed with the Run() method since our actor will not
// clean itself up until we note that the stream is closed.
NS_IMETHOD Cancel()
{
Run();
return NS_OK;
}
private:
~NoteClosedRunnable() { }
nsRefPtr<ReadStream> mStream;
};
// Runnable to clear actors without reporting that the ReadStream has
// closed. Since this can trigger actor destruction, we need to do
// it on the thread associated with the PBackground actor. Must be
// cancelable to execute on Worker threads (which can occur when the
// ReadStream is constructed on a child process Worker thread).
class ReadStream::ForgetRunnable MOZ_FINAL : public nsCancelableRunnable
{
public:
explicit ForgetRunnable(ReadStream* aStream)
: mStream(aStream)
{ }
NS_IMETHOD Run()
{
mStream->ForgetOnOwningThread();
return NS_OK;
}
// Note, we must proceed with the Run() method so that we properly
// call RemoveListener on the actor.
NS_IMETHOD Cancel()
{
Run();
return NS_OK;
}
private:
~ForgetRunnable() { }
nsRefPtr<ReadStream> mStream;
};
NS_IMPL_ISUPPORTS(mozilla::dom::cache::ReadStream, nsIInputStream,
ReadStream);
// static
already_AddRefed<ReadStream>
ReadStream::Create(const PCacheReadStreamOrVoid& aReadStreamOrVoid)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
return nullptr;
}
return Create(aReadStreamOrVoid.get_PCacheReadStream());
}
// static
already_AddRefed<ReadStream>
ReadStream::Create(const PCacheReadStream& aReadStream)
{
// The parameter may or may not be for a Cache created stream. The way we
// tell is by looking at the stream control actor. If the actor exists,
// then we know the Cache created it.
if (!aReadStream.controlChild() && !aReadStream.controlParent()) {
return nullptr;
}
nsAutoTArray<FileDescriptor, 4> fds;
if (aReadStream.fds().type() ==
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
FileDescriptorSetChild* fdSetActor =
static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
unused << fdSetActor->Send__delete__(fdSetActor);
} else if (aReadStream.fds().type() ==
OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
FileDescriptorSetParent* fdSetActor =
static_cast<FileDescriptorSetParent*>(aReadStream.fds().get_PFileDescriptorSetParent());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
if (!fdSetActor->Send__delete__(fdSetActor)) {
// child process is gone, warn and allow actor to clean up normally
NS_WARNING("Cache failed to delete fd set actor.");
}
}
nsCOMPtr<nsIInputStream> stream =
DeserializeInputStream(aReadStream.params(), fds);
MOZ_ASSERT(stream);
// Currently we expect all cache read streams to be blocking file streams.
#ifdef DEBUG
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
MOZ_ASSERT(!asyncStream);
#endif
nsRefPtr<ReadStream> ref;
if (aReadStream.controlChild()) {
ref = new ReadStreamChild(aReadStream.controlChild(), aReadStream.id(),
stream);
} else {
ref = new ReadStreamParent(aReadStream.controlParent(), aReadStream.id(),
stream);
}
return ref.forget();
}
// static
already_AddRefed<ReadStream>
ReadStream::Create(PCacheStreamControlParent* aControl, const nsID& aId,
nsIInputStream* aStream)
{
nsRefPtr<ReadStream> ref = new ReadStreamParent(aControl, aId, aStream);
return ref.forget();
}
void
ReadStream::Serialize(PCacheReadStreamOrVoid* aReadStreamOut)
{
MOZ_ASSERT(aReadStreamOut);
PCacheReadStream stream;
Serialize(&stream);
*aReadStreamOut = stream;
}
void
ReadStream::Serialize(PCacheReadStream* aReadStreamOut)
{
MOZ_ASSERT(aReadStreamOut);
MOZ_ASSERT(!mClosed);
aReadStreamOut->id() = mId;
SerializeControl(aReadStreamOut);
nsAutoTArray<FileDescriptor, 4> fds;
SerializeInputStream(mStream, aReadStreamOut->params(), fds);
SerializeFds(aReadStreamOut, fds);
// We're passing ownership across the IPC barrier with the control, so
// do not signal that the stream is closed here.
Forget();
}
void
ReadStream::CloseStream()
{
Close();
}
void
ReadStream::CloseStreamWithoutReporting()
{
Forget();
}
bool
ReadStream::MatchId(const nsID& aId) const
{
return mId.Equals(aId);
}
ReadStream::ReadStream(const nsID& aId, nsIInputStream* aStream)
: mId(aId)
, mStream(aStream)
, mSnappyStream(new SnappyUncompressInputStream(aStream))
, mOwningThread(NS_GetCurrentThread())
, mClosed(false)
{
MOZ_ASSERT(mStream);
}
ReadStream::~ReadStream()
{
NS_ASSERT_OWNINGTHREAD(ReadStream);
// We cannot directly call NoteClosed() here. The concrete subclasses
// destructors must do this because it takes code paths through virtual
// methods. We don't want to execute these while partially destroyed.
MOZ_ASSERT(mClosed);
}
void
ReadStream::NoteClosed()
{
if (mClosed) {
return;
}
if (NS_GetCurrentThread() == mOwningThread) {
NoteClosedOnOwningThread();
return;
}
nsCOMPtr<nsIRunnable> runnable = new NoteClosedRunnable(this);
nsresult rv = mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch Cache ReadStream NoteClosed() runnable.");
}
}
void
ReadStream::Forget()
{
if (mClosed) {
return;
}
if (NS_GetCurrentThread() == mOwningThread) {
ForgetOnOwningThread();
return;
}
nsCOMPtr<nsIRunnable> runnable = new ForgetRunnable(this);
nsresult rv = mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
NS_WARNING("Failed to dispatch Cache ReadStream Forget() runnable.");
}
}
NS_IMETHODIMP
ReadStream::Close()
{
NoteClosed();
return mStream->Close();
}
NS_IMETHODIMP
ReadStream::Available(uint64_t* aNumAvailableOut)
{
nsresult rv = mSnappyStream->Available(aNumAvailableOut);
if (NS_FAILED(rv)) {
NoteClosed();
}
return rv;
}
NS_IMETHODIMP
ReadStream::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
{
MOZ_ASSERT(aNumReadOut);
nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
// Don't auto-close when end of stream is hit. We want to close
// this stream on a particular thread in the parent case.
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
*aNumReadOut == 0) {
NoteClosed();
}
return rv;
}
NS_IMETHODIMP
ReadStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t* aNumReadOut)
{
MOZ_ASSERT(aNumReadOut);
nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
aNumReadOut);
// Don't auto-close when end of stream is hit. We want to close
// this stream on a particular thread in the parent case.
if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK &&
rv != NS_ERROR_NOT_IMPLEMENTED) || *aNumReadOut == 0) {
NoteClosed();
}
return rv;
}
NS_IMETHODIMP
ReadStream::IsNonBlocking(bool* aNonBlockingOut)
{
return mSnappyStream->IsNonBlocking(aNonBlockingOut);
}
} // namespace cache
} // namespace dom
} // namespace mozilla

102
dom/cache/ReadStream.h vendored Normal file
View File

@ -0,0 +1,102 @@
/* -*- 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_ReadStream_h
#define mozilla_dom_cache_ReadStream_h
#include "mozilla/ipc/FileDescriptor.h"
#include "nsCOMPtr.h"
#include "nsID.h"
#include "nsIInputStream.h"
#include "nsISupportsImpl.h"
#include "nsTArrayForwardDeclare.h"
class nsIThread;
namespace mozilla {
namespace dom {
namespace cache {
class PCacheReadStream;
class PCacheReadStreamOrVoid;
class PCacheStreamControlParent;
// IID for the dom::cache::ReadStream interface
#define NS_DOM_CACHE_READSTREAM_IID \
{0x8e5da7c9, 0x0940, 0x4f1d, \
{0x97, 0x25, 0x5c, 0x59, 0x38, 0xdd, 0xb9, 0x9f}}
// Custom stream class for Request and Response bodies being read from
// a Cache. The main purpose of this class is to report back to the
// Cache's Manager when the stream is closed. This allows the Cache to
// accurately determine when the underlying body file can be deleted,
// etc.
//
// The ReadStream class also provides us with a convenient QI'able
// interface that we can use to pass additional meta-data with the
// stream channel. For example, Cache.put() can detect that the content
// script is passing a Cache-originated-stream back into the Cache
// again. This enables certain optimizations.
class ReadStream : public nsIInputStream
{
public:
static already_AddRefed<ReadStream>
Create(const PCacheReadStreamOrVoid& aReadStreamOrVoid);
static already_AddRefed<ReadStream>
Create(const PCacheReadStream& aReadStream);
static already_AddRefed<ReadStream>
Create(PCacheStreamControlParent* aControl, const nsID& aId,
nsIInputStream* aStream);
void Serialize(PCacheReadStreamOrVoid* aReadStreamOut);
void Serialize(PCacheReadStream* aReadStreamOut);
// methods called from the child and parent CacheStreamControl actors
void CloseStream();
void CloseStreamWithoutReporting();
bool MatchId(const nsID& aId) const;
protected:
class NoteClosedRunnable;
class ForgetRunnable;
ReadStream(const nsID& aId, nsIInputStream* aStream);
virtual ~ReadStream();
void NoteClosed();
void Forget();
virtual void NoteClosedOnOwningThread() = 0;
virtual void ForgetOnOwningThread() = 0;
virtual void SerializeControl(PCacheReadStream* aReadStreamOut) = 0;
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
const nsTArray<mozilla::ipc::FileDescriptor>& fds) = 0;
const nsID mId;
nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsIInputStream> mSnappyStream;
nsCOMPtr<nsIThread> mOwningThread;
bool mClosed;
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_CACHE_READSTREAM_IID);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIINPUTSTREAM
};
NS_DEFINE_STATIC_IID_ACCESSOR(ReadStream, NS_DOM_CACHE_READSTREAM_IID);
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_ReadStream_h

50
dom/cache/SavedTypes.h vendored Normal file
View File

@ -0,0 +1,50 @@
/* -*- 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_SavedTypes_h
#define mozilla_dom_cache_SavedTypes_h
// NOTE: This cannot be rolled into Types.h because the IPC dependency.
// breaks webidl unified builds.
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/Types.h"
#include "nsCOMPtr.h"
#include "nsID.h"
#include "nsIOutputStream.h"
namespace mozilla {
namespace dom {
namespace cache {
struct StreamHolder
{
nsCOMPtr<nsIOutputStream> mValue;
};
struct SavedRequest
{
SavedRequest() : mHasBodyId(false) { mValue.body() = void_t(); }
PCacheRequest mValue;
bool mHasBodyId;
nsID mBodyId;
CacheId mCacheId;
};
struct SavedResponse
{
SavedResponse() : mHasBodyId(false) { mValue.body() = void_t(); }
PCacheResponse mValue;
bool mHasBodyId;
nsID mBodyId;
CacheId mCacheId;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_SavedTypes_h

160
dom/cache/StreamList.cpp vendored Normal file
View File

@ -0,0 +1,160 @@
/* -*- 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/StreamList.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/Context.h"
#include "mozilla/dom/cache/Manager.h"
#include "nsIInputStream.h"
namespace mozilla {
namespace dom {
namespace cache {
StreamList::StreamList(Manager* aManager, Context* aContext)
: mManager(aManager)
, mContext(aContext)
, mCacheId(0)
, mStreamControl(nullptr)
, mActivated(false)
{
MOZ_ASSERT(mManager);
MOZ_ASSERT(mContext);
}
void
StreamList::SetStreamControl(CacheStreamControlParent* aStreamControl)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_ASSERT(aStreamControl);
// For cases where multiple streams are serialized for a single list
// then the control will get passed multiple times. This is ok, but
// it should be the same control each time.
if (mStreamControl) {
MOZ_ASSERT(aStreamControl == mStreamControl);
return;
}
mStreamControl = aStreamControl;
mStreamControl->SetStreamList(this);
}
void
StreamList::RemoveStreamControl(CacheStreamControlParent* aStreamControl)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_ASSERT(mStreamControl);
MOZ_ASSERT(mStreamControl == aStreamControl);
mStreamControl = nullptr;
}
void
StreamList::Activate(CacheId aCacheId)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_ASSERT(!mActivated);
MOZ_ASSERT(!mCacheId);
mActivated = true;
mCacheId = aCacheId;
mManager->AddRefCacheId(mCacheId);
mManager->AddStreamList(this);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->AddRefBodyId(mList[i].mId);
}
}
void
StreamList::Add(const nsID& aId, nsIInputStream* aStream)
{
// All streams should be added on IO thread before we set the stream
// control on the owning IPC thread.
MOZ_ASSERT(!mStreamControl);
MOZ_ASSERT(aStream);
Entry* entry = mList.AppendElement();
entry->mId = aId;
entry->mStream = aStream;
}
already_AddRefed<nsIInputStream>
StreamList::Extract(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (mList[i].mId == aId) {
return mList[i].mStream.forget();
}
}
return nullptr;
}
void
StreamList::NoteClosed(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (mList[i].mId == aId) {
mList.RemoveElementAt(i);
mManager->ReleaseBodyId(aId);
break;
}
}
if (mList.IsEmpty() && mStreamControl) {
mStreamControl->Shutdown();
}
}
void
StreamList::NoteClosedAll()
{
NS_ASSERT_OWNINGTHREAD(StreamList);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId);
}
mList.Clear();
if (mStreamControl) {
mStreamControl->Shutdown();
}
}
void
StreamList::Close(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(StreamList);
if (mStreamControl) {
mStreamControl->Close(aId);
}
}
void
StreamList::CloseAll()
{
NS_ASSERT_OWNINGTHREAD(StreamList);
if (mStreamControl) {
mStreamControl->CloseAll();
}
}
StreamList::~StreamList()
{
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_ASSERT(!mStreamControl);
if (mActivated) {
mManager->RemoveStreamList(this);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId);
}
mManager->ReleaseCacheId(mCacheId);
}
}
} // namespace cache
} // namespace dom
} // namespace mozilla

64
dom/cache/StreamList.h vendored Normal file
View File

@ -0,0 +1,64 @@
/* -*- 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_StreamList_h
#define mozilla_dom_cache_StreamList_h
#include "mozilla/dom/cache/Types.h"
#include "nsRefPtr.h"
#include "nsTArray.h"
class nsIInputStream;
namespace mozilla {
namespace dom {
namespace cache {
class CacheStreamControlParent;
class Context;
class Manager;
class StreamList
{
public:
StreamList(Manager* aManager, Context* aContext);
void SetStreamControl(CacheStreamControlParent* aStreamControl);
void RemoveStreamControl(CacheStreamControlParent* aStreamControl);
void Activate(CacheId aCacheId);
void Add(const nsID& aId, nsIInputStream* aStream);
already_AddRefed<nsIInputStream> Extract(const nsID& aId);
void NoteClosed(const nsID& aId);
void NoteClosedAll();
void Close(const nsID& aId);
void CloseAll();
private:
~StreamList();
struct Entry
{
nsID mId;
nsCOMPtr<nsIInputStream> mStream;
};
nsRefPtr<Manager> mManager;
nsRefPtr<Context> mContext;
CacheId mCacheId;
CacheStreamControlParent* mStreamControl;
nsTArray<Entry> mList;
bool mActivated;
public:
NS_INLINE_DECL_REFCOUNTING(cache::StreamList)
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_StreamList_h

151
dom/cache/StreamUtils.cpp vendored Normal file
View File

@ -0,0 +1,151 @@
/* -*- 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/StreamUtils.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheStreamControlChild.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
namespace mozilla {
namespace dom {
namespace cache {
namespace {
using mozilla::unused;
using mozilla::void_t;
using mozilla::dom::cache::CacheStreamControlChild;
using mozilla::dom::cache::Feature;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::FileDescriptorSetChild;
using mozilla::ipc::OptionalFileDescriptorSet;
void
StartDestroyStreamChild(const PCacheReadStream& aReadStream)
{
CacheStreamControlChild* cacheControl =
static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
if (cacheControl) {
cacheControl->StartDestroy();
}
if (aReadStream.fds().type() ==
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
nsAutoTArray<FileDescriptor, 4> fds;
FileDescriptorSetChild* fdSetActor =
static_cast<FileDescriptorSetChild*>(aReadStream.fds().get_PFileDescriptorSetChild());
MOZ_ASSERT(fdSetActor);
fdSetActor->ForgetFileDescriptors(fds);
MOZ_ASSERT(!fds.IsEmpty());
unused << fdSetActor->Send__delete__(fdSetActor);
}
}
void
AddFeatureToStreamChild(const PCacheReadStream& aReadStream, Feature* aFeature)
{
CacheStreamControlChild* cacheControl =
static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
if (cacheControl) {
cacheControl->SetFeature(aFeature);
}
}
} // anonymous namespace
void
StartDestroyStreamChild(const PCacheResponseOrVoid& aResponseOrVoid)
{
if (aResponseOrVoid.type() == PCacheResponseOrVoid::Tvoid_t) {
return;
}
StartDestroyStreamChild(aResponseOrVoid.get_PCacheResponse());
}
void
StartDestroyStreamChild(const PCacheResponse& aResponse)
{
if (aResponse.body().type() == PCacheReadStreamOrVoid::Tvoid_t) {
return;
}
StartDestroyStreamChild(aResponse.body().get_PCacheReadStream());
}
void
StartDestroyStreamChild(const nsTArray<PCacheResponse>& aResponses)
{
for (uint32_t i = 0; i < aResponses.Length(); ++i) {
StartDestroyStreamChild(aResponses[i]);
}
}
void
StartDestroyStreamChild(const nsTArray<PCacheRequest>& aRequests)
{
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
if (aRequests[i].body().type() == PCacheReadStreamOrVoid::Tvoid_t) {
continue;
}
StartDestroyStreamChild(aRequests[i].body().get_PCacheReadStream());
}
}
void
AddFeatureToStreamChild(const PCacheResponseOrVoid& aResponseOrVoid,
Feature* aFeature)
{
if (aResponseOrVoid.type() == PCacheResponseOrVoid::Tvoid_t) {
return;
}
AddFeatureToStreamChild(aResponseOrVoid.get_PCacheResponse(), aFeature);
}
void
AddFeatureToStreamChild(const PCacheResponse& aResponse,
Feature* aFeature)
{
if (aResponse.body().type() == PCacheReadStreamOrVoid::Tvoid_t) {
return;
}
AddFeatureToStreamChild(aResponse.body().get_PCacheReadStream(), aFeature);
}
void
AddFeatureToStreamChild(const nsTArray<PCacheResponse>& aResponses,
Feature* aFeature)
{
for (uint32_t i = 0; i < aResponses.Length(); ++i) {
AddFeatureToStreamChild(aResponses[i], aFeature);
}
}
void
AddFeatureToStreamChild(const nsTArray<PCacheRequest>& aRequests,
Feature* aFeature)
{
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
if (aRequests[i].body().type() == PCacheReadStreamOrVoid::Tvoid_t) {
continue;
}
AddFeatureToStreamChild(aRequests[i].body().get_PCacheReadStream(),
aFeature);
}
}
} // namespace cache
} // namespace dom
} // namespace mozilla

39
dom/cache/StreamUtils.h vendored Normal file
View File

@ -0,0 +1,39 @@
/* -*- 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_StreamUtils_h
#define mozilla_dom_cache_StreamUtils_h
#include "nsTArrayForwardDeclare.h"
namespace mozilla {
namespace dom {
namespace cache {
class Feature;
class PCacheRequest;
class PCacheResponse;
class PCacheResponseOrVoid;
void StartDestroyStreamChild(const PCacheResponseOrVoid& aResponseOrVoid);
void StartDestroyStreamChild(const PCacheResponse& aResponse);
void StartDestroyStreamChild(const nsTArray<PCacheResponse>& aResponses);
void StartDestroyStreamChild(const nsTArray<PCacheRequest>& aRequests);
void AddFeatureToStreamChild(const PCacheResponseOrVoid& aResponseOrVoid,
Feature* aFeature);
void AddFeatureToStreamChild(const PCacheResponse& aResponse,
Feature* aFeature);
void AddFeatureToStreamChild(const nsTArray<PCacheResponse>& aResponses,
Feature* aFeature);
void AddFeatureToStreamChild(const nsTArray<PCacheRequest>& aRequests,
Feature* aFeature);
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_StreamUtils_h

481
dom/cache/TypeUtils.cpp vendored Normal file
View File

@ -0,0 +1,481 @@
/* -*- 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/TypeUtils.h"
#include "mozilla/unused.h"
#include "mozilla/dom/CacheBinding.h"
#include "mozilla/dom/InternalRequest.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/cache/PCacheTypes.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"
#include "nsCOMPtr.h"
#include "nsIAsyncInputStream.h"
#include "nsIAsyncOutputStream.h"
#include "nsIIPCSerializableInputStream.h"
#include "nsStreamUtils.h"
#include "nsString.h"
#include "nsURLParsers.h"
namespace {
using mozilla::ErrorResult;
// 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");
}
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);
}
} // 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)
{
if (aIn.IsRequest()) {
Request& request = aIn.GetAsRequest();
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
CheckAndSetBodyUsed(&request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request.GetInternalRequest();
}
return ToInternalRequest(aIn.GetAsUSVString(), aRv);
}
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
BodyAction aBodyAction, ErrorResult& aRv)
{
if (aIn.IsRequest()) {
nsRefPtr<Request> request = aIn.GetAsRequest().get();
// Check and set bodyUsed flag immediately because its on Request
// instead of InternalRequest.
CheckAndSetBodyUsed(request, aBodyAction, aRv);
if (aRv.Failed()) { return nullptr; }
return request->GetInternalRequest();
}
return ToInternalRequest(aIn.GetAsUSVString(), aRv);
}
void
TypeUtils::ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
{
MOZ_ASSERT(aIn);
aIn->GetMethod(aOut.method());
nsAutoCString url;
aIn->GetURL(url);
CopyUTF8toUTF16(url, aOut.url());
bool schemeValid;
ProcessURL(aOut.url(), &schemeValid, &aOut.urlWithoutQuery(), aRv);
if (aRv.Failed()) {
return;
}
if (!schemeValid) {
if (aSchemeAction == TypeErrorOnInvalidScheme) {
NS_NAMED_LITERAL_STRING(label, "Request");
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();
MOZ_ASSERT(headers);
headers->GetPHeaders(aOut.headers());
aOut.headersGuard() = headers->Guard();
aOut.mode() = aIn->Mode();
aOut.credentials() = aIn->GetCredentialsMode();
aOut.context() = aIn->ContentPolicyType();
if (aBodyAction == IgnoreBody) {
aOut.body() = void_t();
return;
}
// BodyUsed flag is checked and set previously in ToInternalRequest()
nsCOMPtr<nsIInputStream> stream;
aIn->GetBody(getter_AddRefs(stream));
SerializeCacheStream(stream, &aOut.body(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
void
TypeUtils::ToPCacheResponseWithoutBody(PCacheResponse& aOut,
InternalResponse& aIn, ErrorResult& aRv)
{
aOut.type() = aIn.Type();
nsAutoCString url;
aIn.GetUrl(url);
CopyUTF8toUTF16(url, aOut.url());
if (aOut.url() != EmptyString()) {
// Pass all Response URL schemes through... The spec only requires we take
// action on invalid schemes for Request objects.
ProcessURL(aOut.url(), nullptr, nullptr, aRv);
if (aRv.Failed()) {
return;
}
}
aOut.status() = aIn.GetStatus();
aOut.statusText() = aIn.GetStatusText();
nsRefPtr<InternalHeaders> headers = aIn.Headers();
MOZ_ASSERT(headers);
headers->GetPHeaders(aOut.headers());
aOut.headersGuard() = headers->Guard();
}
void
TypeUtils::ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aRv)
{
if (aIn.BodyUsed()) {
aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR);
return;
}
nsRefPtr<InternalResponse> ir = aIn.GetInternalResponse();
ToPCacheResponseWithoutBody(aOut, *ir, aRv);
nsCOMPtr<nsIInputStream> stream;
aIn.GetBody(getter_AddRefs(stream));
if (stream) {
aIn.SetBodyUsed();
}
SerializeCacheStream(stream, &aOut.body(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
// static
void
TypeUtils::ToPCacheQueryParams(PCacheQueryParams& aOut,
const CacheQueryOptions& aIn)
{
aOut.ignoreSearch() = aIn.mIgnoreSearch;
aOut.ignoreMethod() = aIn.mIgnoreMethod;
aOut.ignoreVary() = aIn.mIgnoreVary;
aOut.prefixMatch() = aIn.mPrefixMatch;
aOut.cacheNameSet() = aIn.mCacheName.WasPassed();
if (aOut.cacheNameSet()) {
aOut.cacheName() = aIn.mCacheName.Value();
} else {
aOut.cacheName() = NS_LITERAL_STRING("");
}
}
already_AddRefed<Response>
TypeUtils::ToResponse(const PCacheResponse& aIn)
{
nsRefPtr<InternalResponse> ir;
switch (aIn.type())
{
case ResponseType::Error:
ir = InternalResponse::NetworkError();
break;
case ResponseType::Opaque:
ir = InternalResponse::OpaqueResponse();
break;
case ResponseType::Default:
ir = new InternalResponse(aIn.status(), aIn.statusText());
break;
case ResponseType::Basic:
{
nsRefPtr<InternalResponse> inner = new InternalResponse(aIn.status(),
aIn.statusText());
ir = InternalResponse::BasicResponse(inner);
break;
}
case ResponseType::Cors:
{
nsRefPtr<InternalResponse> inner = new InternalResponse(aIn.status(),
aIn.statusText());
ir = InternalResponse::CORSResponse(inner);
break;
}
default:
MOZ_CRASH("Unexpected ResponseType!");
}
MOZ_ASSERT(ir);
ir->SetUrl(NS_ConvertUTF16toUTF8(aIn.url()));
nsRefPtr<InternalHeaders> internalHeaders =
new InternalHeaders(aIn.headers(), aIn.headersGuard());
ErrorResult result;
ir->Headers()->SetGuard(aIn.headersGuard(), result);
MOZ_ASSERT(!result.Failed());
ir->Headers()->Fill(*internalHeaders, result);
MOZ_ASSERT(!result.Failed());
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
ir->SetBody(stream);
nsRefPtr<Response> ref = new Response(GetGlobalObject(), ir);
return ref.forget();
}
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const PCacheRequest& aIn)
{
nsRefPtr<InternalRequest> internalRequest = new InternalRequest();
internalRequest->SetMethod(aIn.method());
internalRequest->SetURL(NS_ConvertUTF16toUTF8(aIn.url()));
internalRequest->SetReferrer(aIn.referrer());
internalRequest->SetMode(aIn.mode());
internalRequest->SetCredentialsMode(aIn.credentials());
internalRequest->SetContentPolicyType(aIn.context());
nsRefPtr<InternalHeaders> internalHeaders =
new InternalHeaders(aIn.headers(), aIn.headersGuard());
ErrorResult result;
internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
MOZ_ASSERT(!result.Failed());
internalRequest->Headers()->Fill(*internalHeaders, result);
MOZ_ASSERT(!result.Failed());
nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
internalRequest->SetBody(stream);
return internalRequest.forget();
}
already_AddRefed<Request>
TypeUtils::ToRequest(const PCacheRequest& aIn)
{
nsRefPtr<InternalRequest> internalRequest = ToInternalRequest(aIn);
nsRefPtr<Request> request = new Request(GetGlobalObject(), internalRequest);
return request.forget();
}
void
TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv)
{
MOZ_ASSERT(aRequest);
if (aBodyAction == IgnoreBody) {
return;
}
if (aRequest->BodyUsed()) {
aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR);
return;
}
nsCOMPtr<nsIInputStream> stream;
aRequest->GetBody(getter_AddRefs(stream));
if (stream) {
aRequest->SetBodyUsed();
}
}
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const nsAString& aIn, ErrorResult& aRv)
{
RequestOrUSVString requestOrString;
requestOrString.SetAsUSVString().Rebind(aIn.Data(), aIn.Length());
// Re-create a GlobalObject stack object so we can use webidl Constructors.
AutoJSAPI jsapi;
if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
JSContext* cx = jsapi.cx();
GlobalObject global(cx, GetGlobalObject()->GetGlobalJSObject());
MOZ_ASSERT(!global.Failed());
nsRefPtr<Request> request = Request::Constructor(global, requestOrString,
RequestInit(), aRv);
if (NS_WARN_IF(aRv.Failed())) { return nullptr; }
return request->GetInternalRequest();
}
void
TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
PCacheReadStreamOrVoid* aStreamOut,
ErrorResult& aRv)
{
*aStreamOut = void_t();
if (!aStream) {
return;
}
nsRefPtr<ReadStream> controlled = do_QueryObject(aStream);
if (controlled) {
controlled->Serialize(aStreamOut);
return;
}
// TODO: implement CrossProcessPipe if we cannot directly serialize (bug 1110814)
nsCOMPtr<nsIIPCSerializableInputStream> serial = do_QueryInterface(aStream);
if (!serial) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
PCacheReadStream readStream;
readStream.controlChild() = nullptr;
readStream.controlParent() = nullptr;
nsAutoTArray<FileDescriptor, 4> fds;
SerializeInputStream(aStream, readStream.params(), fds);
PFileDescriptorSetChild* fdSet = nullptr;
if (!fds.IsEmpty()) {
// We should not be serializing until we have an actor ready
PBackgroundChild* manager = BackgroundChild::GetForCurrentThread();
MOZ_ASSERT(manager);
fdSet = manager->SendPFileDescriptorSetConstructor(fds[0]);
for (uint32_t i = 1; i < fds.Length(); ++i) {
unused << fdSet->SendAddFileDescriptor(fds[i]);
}
}
if (fdSet) {
readStream.fds() = fdSet;
} else {
readStream.fds() = void_t();
}
*aStreamOut = readStream;
}
nsIThread*
TypeUtils::GetStreamThread()
{
AssertOwningThread();
if (!mStreamThread) {
// Named threads only allow 16 bytes for their names. Try to make
// it meaningful...
// TODO: use a thread pool or singleton thread here (bug 1119864)
nsresult rv = NS_NewNamedThread("DOMCacheTypeU",
getter_AddRefs(mStreamThread));
if (NS_FAILED(rv) || !mStreamThread) {
MOZ_CRASH("Failed to create DOM Cache serialization thread.");
}
}
return mStreamThread;
}
} // namespace cache
} // namespace dom
} // namespace mozilla

123
dom/cache/TypeUtils.h vendored Normal file
View File

@ -0,0 +1,123 @@
/* -*- 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_TypesUtils_h
#define mozilla_dom_cache_TypesUtils_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "nsCOMPtr.h"
#include "nsError.h"
class nsIGlobalObject;
class nsIInputStream;
namespace mozilla {
namespace dom {
struct CacheQueryOptions;
class InternalRequest;
class InternalResponse;
class OwningRequestOrUSVString;
class Request;
class RequestOrUSVString;
class Response;
template<typename T> class Optional;
namespace cache {
class PCacheQueryParams;
class PCacheReadStream;
class PCacheReadStreamOrVoid;
class PCacheRequest;
class PCacheRequestOrVoid;
class PCacheResponse;
class PCacheStreamControlChild;
class TypeUtils
{
public:
enum BodyAction
{
IgnoreBody,
ReadBody
};
enum ReferrerAction
{
PassThroughReferrer,
ExpandReferrer
};
enum SchemeAction
{
IgnoreInvalidScheme,
TypeErrorOnInvalidScheme,
NetworkErrorOnInvalidScheme
};
~TypeUtils() { }
virtual nsIGlobalObject* GetGlobalObject() const = 0;
#ifdef DEBUG
virtual void AssertOwningThread() const = 0;
#else
inline void AssertOwningThread() const { }
#endif
already_AddRefed<InternalRequest>
ToInternalRequest(const RequestOrUSVString& aIn, BodyAction aBodyAction,
ErrorResult& aRv);
already_AddRefed<InternalRequest>
ToInternalRequest(const OwningRequestOrUSVString& aIn, BodyAction aBodyAction,
ErrorResult& aRv);
void
ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction, ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv);
void
ToPCacheResponseWithoutBody(PCacheResponse& aOut, InternalResponse& aIn,
ErrorResult& aRv);
void
ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aRv);
void
ToPCacheQueryParams(PCacheQueryParams& aOut, const CacheQueryOptions& aIn);
already_AddRefed<Response>
ToResponse(const PCacheResponse& aIn);
already_AddRefed<InternalRequest>
ToInternalRequest(const PCacheRequest& aIn);
already_AddRefed<Request>
ToRequest(const PCacheRequest& aIn);
private:
void
CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv);
already_AddRefed<InternalRequest>
ToInternalRequest(const nsAString& aIn, ErrorResult& aRv);
void
SerializeCacheStream(nsIInputStream* aStream, PCacheReadStreamOrVoid* aStreamOut,
ErrorResult& aRv);
nsIThread* GetStreamThread();
nsCOMPtr<nsIThread> mStreamThread;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_TypesUtils_h

44
dom/cache/Types.h vendored Normal file
View File

@ -0,0 +1,44 @@
/* -*- 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_Types_h
#define mozilla_dom_cache_Types_h
#include <stdint.h>
#include "nsCOMPtr.h"
#include "nsIFile.h"
#include "nsString.h"
namespace mozilla {
namespace dom {
namespace cache {
enum Namespace
{
DEFAULT_NAMESPACE,
CHROME_ONLY_NAMESPACE,
NUMBER_OF_NAMESPACES
};
typedef uintptr_t RequestId;
static const RequestId INVALID_REQUEST_ID = 0;
typedef int32_t CacheId;
struct QuotaInfo
{
QuotaInfo() : mIsApp(false) { }
nsCOMPtr<nsIFile> mDir;
nsCString mGroup;
nsCString mOrigin;
bool mIsApp;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_Types_h

83
dom/cache/moz.build vendored Normal file
View File

@ -0,0 +1,83 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXPORTS.mozilla.dom.cache += [
'Action.h',
'ActorChild.h',
'ActorUtils.h',
'AutoUtils.h',
'Cache.h',
'CacheChild.h',
'CacheParent.h',
'CacheStorage.h',
'CacheStorageChild.h',
'CacheStorageParent.h',
'CacheStreamControlChild.h',
'CacheStreamControlParent.h',
'Context.h',
'DBAction.h',
'DBSchema.h',
'Feature.h',
'FetchPut.h',
'FileUtils.h',
'IPCUtils.h',
'Manager.h',
'ManagerId.h',
'PrincipalVerifier.h',
'QuotaClient.h',
'ReadStream.h',
'SavedTypes.h',
'StreamList.h',
'StreamUtils.h',
'Types.h',
'TypeUtils.h',
]
UNIFIED_SOURCES += [
'Action.cpp',
'ActorChild.cpp',
'AutoUtils.cpp',
'Cache.cpp',
'CacheChild.cpp',
'CacheParent.cpp',
'CacheStorage.cpp',
'CacheStorageChild.cpp',
'CacheStorageParent.cpp',
'CacheStreamControlChild.cpp',
'CacheStreamControlParent.cpp',
'Context.cpp',
'DBAction.cpp',
'DBSchema.cpp',
'Feature.cpp',
'FetchPut.cpp',
'FileUtils.cpp',
'Manager.cpp',
'ManagerId.cpp',
'PrincipalVerifier.cpp',
'QuotaClient.cpp',
'ReadStream.cpp',
'StreamList.cpp',
'StreamUtils.cpp',
'TypeUtils.cpp',
]
IPDL_SOURCES += [
'CacheInitData.ipdlh',
'PCache.ipdl',
'PCacheStorage.ipdl',
'PCacheStreamControl.ipdl',
'PCacheTypes.ipdlh',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'../workers',
]
FAIL_ON_WARNINGS = True
FINAL_LIBRARY = 'xul'

View File

@ -4,6 +4,7 @@
include protocol PBackgroundIDBCursor;
include protocol PBackgroundIDBDatabase;
include protocol PBackgroundIDBDatabaseFile;
include protocol PBackgroundIDBRequest;
include protocol PBlob;

View File

@ -47,6 +47,7 @@ DIRS += [
'bindings',
'battery',
'browser-element',
'cache',
'canvas',
'cellbroadcast',
'contacts',

View File

@ -16,6 +16,7 @@ class nsIRunnable;
#define IDB_DIRECTORY_NAME "idb"
#define ASMJSCACHE_DIRECTORY_NAME "asmjs"
#define DOMCACHE_DIRECTORY_NAME "cache"
BEGIN_QUOTA_NAMESPACE
@ -39,6 +40,7 @@ public:
//LS,
//APPCACHE,
ASMJS,
DOMCACHE,
TYPE_MAX
};
@ -57,6 +59,10 @@ public:
aText.AssignLiteral(ASMJSCACHE_DIRECTORY_NAME);
break;
case DOMCACHE:
aText.AssignLiteral(DOMCACHE_DIRECTORY_NAME);
break;
case TYPE_MAX:
default:
NS_NOTREACHED("Bad id value!");
@ -75,6 +81,9 @@ public:
else if (aText.EqualsLiteral(ASMJSCACHE_DIRECTORY_NAME)) {
aType = ASMJS;
}
else if (aText.EqualsLiteral(DOMCACHE_DIRECTORY_NAME)) {
aType = DOMCACHE;
}
else {
return NS_ERROR_FAILURE;
}

View File

@ -30,6 +30,7 @@
#include "mozilla/CondVar.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/FileService.h"
#include "mozilla/dom/cache/QuotaClient.h"
#include "mozilla/dom/indexedDB/ActorsParent.h"
#include "mozilla/Mutex.h"
#include "mozilla/LazyIdleThread.h"
@ -1419,8 +1420,8 @@ QuotaManager::Init()
NS_WARNING("Unable to respond to testing pref changes!");
}
static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::TYPE_MAX == 2,
"Fix the registration!");
static_assert(Client::IDB == 0 && Client::ASMJS == 1 && Client::DOMCACHE == 2 &&
Client::TYPE_MAX == 3, "Fix the registration!");
NS_ASSERTION(mClients.Capacity() == Client::TYPE_MAX,
"Should be using an auto array with correct capacity!");
@ -1430,6 +1431,7 @@ QuotaManager::Init()
// Register clients.
mClients.AppendElement(idbClient);
mClients.AppendElement(asmjscache::CreateClient());
mClients.AppendElement(cache::CreateQuotaClient());
return NS_OK;
}

View File

@ -9,6 +9,7 @@
#include "FileDescriptorSetChild.h"
#include "mozilla/Assertions.h"
#include "mozilla/dom/PBlobChild.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/ipc/PBackgroundTestChild.h"
@ -46,6 +47,10 @@ public:
namespace mozilla {
namespace ipc {
using mozilla::dom::cache::PCacheChild;
using mozilla::dom::cache::PCacheStorageChild;
using mozilla::dom::cache::PCacheStreamControlChild;
// -----------------------------------------------------------------------------
// BackgroundChildImpl::ThreadLocal
// -----------------------------------------------------------------------------
@ -231,6 +236,51 @@ BackgroundChildImpl::DeallocPBroadcastChannelChild(
return true;
}
// -----------------------------------------------------------------------------
// Cache API
// -----------------------------------------------------------------------------
PCacheStorageChild*
BackgroundChildImpl::AllocPCacheStorageChild(const Namespace& aNamespace,
const PrincipalInfo& aPrincipalInfo)
{
MOZ_CRASH("CacheStorageChild actor must be provided to PBackground manager");
return nullptr;
}
bool
BackgroundChildImpl::DeallocPCacheStorageChild(PCacheStorageChild* aActor)
{
dom::cache::DeallocPCacheStorageChild(aActor);
return true;
}
PCacheChild*
BackgroundChildImpl::AllocPCacheChild()
{
return dom::cache::AllocPCacheChild();
}
bool
BackgroundChildImpl::DeallocPCacheChild(PCacheChild* aActor)
{
dom::cache::DeallocPCacheChild(aActor);
return true;
}
PCacheStreamControlChild*
BackgroundChildImpl::AllocPCacheStreamControlChild()
{
return dom::cache::AllocPCacheStreamControlChild();
}
bool
BackgroundChildImpl::DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
{
dom::cache::DeallocPCacheStreamControlChild(aActor);
return true;
}
} // namespace ipc
} // namespace mozilla

View File

@ -84,6 +84,24 @@ protected:
virtual bool
DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) MOZ_OVERRIDE;
virtual dom::cache::PCacheStorageChild*
AllocPCacheStorageChild(const dom::cache::Namespace& aNamespace,
const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE;
virtual bool
DeallocPCacheStorageChild(dom::cache::PCacheStorageChild* aActor) MOZ_OVERRIDE;
virtual dom::cache::PCacheChild* AllocPCacheChild() MOZ_OVERRIDE;
virtual bool
DeallocPCacheChild(dom::cache::PCacheChild* aActor) MOZ_OVERRIDE;
virtual dom::cache::PCacheStreamControlChild*
AllocPCacheStreamControlChild() MOZ_OVERRIDE;
virtual bool
DeallocPCacheStreamControlChild(dom::cache::PCacheStreamControlChild* aActor) MOZ_OVERRIDE;
};
class BackgroundChildImpl::ThreadLocal MOZ_FINAL

View File

@ -11,6 +11,7 @@
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/PBlobParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/indexedDB/ActorsParent.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/ipc/BackgroundParent.h"
@ -31,6 +32,9 @@
#endif
using mozilla::ipc::AssertIsOnBackgroundThread;
using mozilla::dom::cache::PCacheParent;
using mozilla::dom::cache::PCacheStorageParent;
using mozilla::dom::cache::PCacheStreamControlParent;
namespace {
@ -547,6 +551,48 @@ BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar()
return true;
}
PCacheStorageParent*
BackgroundParentImpl::AllocPCacheStorageParent(const Namespace& aNamespace,
const PrincipalInfo& aPrincipalInfo)
{
return dom::cache::AllocPCacheStorageParent(this, aNamespace, aPrincipalInfo);
}
bool
BackgroundParentImpl::DeallocPCacheStorageParent(PCacheStorageParent* aActor)
{
dom::cache::DeallocPCacheStorageParent(aActor);
return true;
}
PCacheParent*
BackgroundParentImpl::AllocPCacheParent()
{
MOZ_CRASH("CacheParent actor must be provided to PBackground manager");
return nullptr;
}
bool
BackgroundParentImpl::DeallocPCacheParent(PCacheParent* aActor)
{
dom::cache::DeallocPCacheParent(aActor);
return true;
}
PCacheStreamControlParent*
BackgroundParentImpl::AllocPCacheStreamControlParent()
{
MOZ_CRASH("CacheStreamControlParent actor must be provided to PBackground manager");
return nullptr;
}
bool
BackgroundParentImpl::DeallocPCacheStreamControlParent(PCacheStreamControlParent* aActor)
{
dom::cache::DeallocPCacheStreamControlParent(aActor);
return true;
}
} // namespace ipc
} // namespace mozilla

View File

@ -94,6 +94,24 @@ protected:
virtual bool
RecvShutdownServiceWorkerRegistrar() MOZ_OVERRIDE;
virtual dom::cache::PCacheStorageParent*
AllocPCacheStorageParent(const dom::cache::Namespace& aNamespace,
const PrincipalInfo& aPrincipalInfo) MOZ_OVERRIDE;
virtual bool
DeallocPCacheStorageParent(dom::cache::PCacheStorageParent* aActor) MOZ_OVERRIDE;
virtual dom::cache::PCacheParent* AllocPCacheParent() MOZ_OVERRIDE;
virtual bool
DeallocPCacheParent(dom::cache::PCacheParent* aActor) MOZ_OVERRIDE;
virtual dom::cache::PCacheStreamControlParent*
AllocPCacheStreamControlParent() MOZ_OVERRIDE;
virtual bool
DeallocPCacheStreamControlParent(dom::cache::PCacheStreamControlParent* aActor) MOZ_OVERRIDE;
};
} // namespace ipc

View File

@ -6,6 +6,9 @@ include protocol PBackgroundIDBFactory;
include protocol PBackgroundTest;
include protocol PBlob;
include protocol PBroadcastChannel;
include protocol PCache;
include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PVsync;
@ -14,6 +17,9 @@ include PBackgroundSharedTypes;
include PBackgroundIDBSharedTypes;
include ServiceWorkerRegistrarTypes;
using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
include "mozilla/dom/cache/IPCUtils.h";
namespace mozilla {
namespace ipc {
@ -23,6 +29,9 @@ sync protocol PBackground
manages PBackgroundTest;
manages PBlob;
manages PBroadcastChannel;
manages PCache;
manages PCacheStorage;
manages PCacheStreamControl;
manages PFileDescriptorSet;
manages PVsync;
@ -41,6 +50,12 @@ parent:
nsString scope);
ShutdownServiceWorkerRegistrar();
PCacheStorage(Namespace aNamespace, PrincipalInfo aPrincipalInfo);
child:
PCache();
PCacheStreamControl();
both:
PBlob(BlobConstructorParams params);