mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 940273 - Part 4 - Initial implementation of Service Worker Cache. r=ehsan,baku,janv
This commit is contained in:
parent
bad713c66b
commit
e0b9075c14
@ -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'
|
||||
|
@ -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
40
dom/cache/Action.cpp
vendored
Normal 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
88
dom/cache/Action.h
vendored
Normal 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
53
dom/cache/ActorChild.cpp
vendored
Normal 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
47
dom/cache/ActorChild.h
vendored
Normal 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
65
dom/cache/ActorUtils.h
vendored
Normal 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
438
dom/cache/AutoUtils.cpp
vendored
Normal 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
176
dom/cache/AutoUtils.h
vendored
Normal 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
586
dom/cache/Cache.cpp
vendored
Normal 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
133
dom/cache/Cache.h
vendored
Normal 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
187
dom/cache/CacheChild.cpp
vendored
Normal 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
75
dom/cache/CacheChild.h
vendored
Normal 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
24
dom/cache/CacheInitData.ipdlh
vendored
Normal 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
291
dom/cache/CacheParent.cpp
vendored
Normal 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
87
dom/cache/CacheParent.h
vendored
Normal 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
631
dom/cache/CacheStorage.cpp
vendored
Normal 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
165
dom/cache/CacheStorage.h
vendored
Normal 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
174
dom/cache/CacheStorageChild.cpp
vendored
Normal 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
72
dom/cache/CacheStorageChild.h
vendored
Normal 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
450
dom/cache/CacheStorageParent.cpp
vendored
Normal 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
108
dom/cache/CacheStorageParent.h
vendored
Normal 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
137
dom/cache/CacheStreamControlChild.cpp
vendored
Normal 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
53
dom/cache/CacheStreamControlChild.h
vendored
Normal 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
147
dom/cache/CacheStreamControlParent.cpp
vendored
Normal 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
57
dom/cache/CacheStreamControlParent.h
vendored
Normal 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
630
dom/cache/Context.cpp
vendored
Normal 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
109
dom/cache/Context.h
vendored
Normal 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
169
dom/cache/DBAction.cpp
vendored
Normal 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
77
dom/cache/DBAction.h
vendored
Normal 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
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
141
dom/cache/DBSchema.h
vendored
Normal 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
108
dom/cache/Feature.cpp
vendored
Normal 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
54
dom/cache/Feature.h
vendored
Normal 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
451
dom/cache/FetchPut.cpp
vendored
Normal 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
122
dom/cache/FetchPut.h
vendored
Normal 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
286
dom/cache/FileUtils.cpp
vendored
Normal 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
68
dom/cache/FileUtils.h
vendored
Normal 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
22
dom/cache/IPCUtils.h
vendored
Normal 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
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
285
dom/cache/Manager.h
vendored
Normal 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
91
dom/cache/ManagerId.cpp
vendored
Normal 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
81
dom/cache/ManagerId.h
vendored
Normal 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
46
dom/cache/PCache.ipdl
vendored
Normal 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
44
dom/cache/PCacheStorage.ipdl
vendored
Normal 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
28
dom/cache/PCacheStreamControl.ipdl
vendored
Normal 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
89
dom/cache/PCacheTypes.ipdlh
vendored
Normal 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
208
dom/cache/PrincipalVerifier.cpp
vendored
Normal 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
75
dom/cache/PrincipalVerifier.h
vendored
Normal 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
214
dom/cache/QuotaClient.cpp
vendored
Normal 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
23
dom/cache/QuotaClient.h
vendored
Normal 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
543
dom/cache/ReadStream.cpp
vendored
Normal 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
102
dom/cache/ReadStream.h
vendored
Normal 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
50
dom/cache/SavedTypes.h
vendored
Normal 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
160
dom/cache/StreamList.cpp
vendored
Normal 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
64
dom/cache/StreamList.h
vendored
Normal 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
151
dom/cache/StreamUtils.cpp
vendored
Normal 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
39
dom/cache/StreamUtils.h
vendored
Normal 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
481
dom/cache/TypeUtils.cpp
vendored
Normal 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
123
dom/cache/TypeUtils.h
vendored
Normal 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
44
dom/cache/Types.h
vendored
Normal 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
83
dom/cache/moz.build
vendored
Normal 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'
|
@ -4,6 +4,7 @@
|
||||
|
||||
include protocol PBackgroundIDBCursor;
|
||||
include protocol PBackgroundIDBDatabase;
|
||||
include protocol PBackgroundIDBDatabaseFile;
|
||||
include protocol PBackgroundIDBRequest;
|
||||
include protocol PBlob;
|
||||
|
||||
|
@ -47,6 +47,7 @@ DIRS += [
|
||||
'bindings',
|
||||
'battery',
|
||||
'browser-element',
|
||||
'cache',
|
||||
'canvas',
|
||||
'cellbroadcast',
|
||||
'contacts',
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user