import change from rmottola/Arctic-Fox:

- Bug 1149987 - Part 2: Make ErrorResult unassignable; r=bzbarsky (32661559b)
- Bug 1149987 - Part 3: Give ErrorResult a move constructor and a move assignment operator; (27f4c6125)
- Bug 1149987 - Part 4: Do not attempt to delete ErrorResult::mMessage when deserializing the object from IPDL; r=bzbarsky (0f9dcc603)
- Bug 1110485 P0 Add an ErrorResult constructor that takes nsresult. (72a779666)
- Bug 1110485 P1 Refactor Cache IPC requests to use a separate actor. (a7e4c1959)
- Bug 1127914 - Part 1 - Duplicate keyed histograms for double submission. (78673277f)
- Bug 1127914 - Part 2 - Duplicate normal histograms for double submission. (55c302057)
- Bug 1127914 - Part 3 - Submit duplicate histogram data for 'non-classic' telemetry sessions. r=vladan (bb3e49c43)
- Bug 1120362 - Part 1 - Enable snapshotting and clearing subsession histograms. (14378a6e5)
- Bug 1120362 - Part 2 - Enable snapshotting and clearing keyed subsession histograms. r=vladan (c0e0bfb3e)
- partial apply of Bug 1119281 - Fix missing telemetry client id (ae0dc0194)
- Bug 1122047 - Part 1 - Sketch out Telemetry environment module. (0419391b0)
- Bug 1122047 - Part 2 - Make TelemetryPing shutdown properly on delayed initialization (0102cef09)
- Bug 1122061 - Give TelemetryPing a common API for sending pings. (999cb825d)
- Bug 1122061 - Move TelemetrySession tests out of test_telemetryPing.js. (2d5b61de1)
- Bug 1120362 - Part 3 - Reset subsession histograms on telemetry payload collections. r=vladan (0d3f04df1)
- Bug 1120362 - Part 4 - Start new telemetry subsessions on local midnight. r=vladan (93eb9ca21)
- Bug 1120363 - Break up Telemetry sessions on environment changes. (a7c8d70c7)
- Bug 1122052 - Remove duplicated data from TelemetrySession. (bb905d602)
- Bug 1122050 - Remove persona and experiment data from TelemetrySession. (40ca59a9e)
- Bug 1134268 - Part 1 - Fix and order Telemetry shutdown for TelemetryPing and TelemetrySession. r=yoric (30d0f0656)
- Bug 1134268 - Part 2 - Fixup TelemetryEnvironment shutdown if the module wasnt initialized. r=vladan (ec2875fea)
- Bug 1135076 - Missing histograms in childPayloads. r=vladan (9f317cf9d)
- Bug 1134279 - Make TelemetryPing and TelemetrySession code use the "FHR enabled" & "Telemetry enabled" prefs properly. r=vladan (4050d7f24)
- Bug 1128768: Part 1 - Modify IPC to allow retrieval of topmost routing id on the stack; (cd2e8a2f0)
- Bug 1129249 - Add a "restyle" feature to profiler and split the style label in Cleopatra based on the restyleSource, r=dholbert,mstange (b37df94d1)
- Bug 1150684: Remove XPCOM.h from IOInterposer.h (5b7e1cef3)
- Bug 1093934 - Create a XPCOM library that can be used to support standalone WebRTC. (9ec8a819f)
- Merge branch 'master' of https://github.com/rmottola/Arctic-Fox (d0f05eea4)
- Bug 1128768: Part 2,3,4 - Refactor hang annotation code; (f5086aba9) (with xpcom/threads/ fixes for my tele-removed tree)
- Bug 1128768: Part 5 - Update plugin code to retrieve SWF file for hang annotations; (774a47aec)
- Bug 1110485 P2 Remove 'P' prefix from non-protocol IPC types in Cache API. r=baku (ea29a10cf)
- Bug 1110485 P3 Move Fetch IPC PHeaderEntry type to Cache. Rename HeadesEntry. (9eba0aca0)
- Bug 1110485 P4 Keep Cache Actors alive during async operations. (eb75f2316)
- Bug 1110485 P5 Replace useless DBSchema class type with namespace. (159b902db)
- Bug 1110485 P6 Remove useless cache::FileUtils type (1bdf00fc3)
- Bug 1110485 P7 Rename DeleteCache() to DeleteCacheId() better distinguish it from CacheDelete(). (5199f9d6f)
- Bug 1110485 P8 Correctly set the Feature on the stream control child actor. (c8673cb13)
- Bug 1150691 Fix Cache API race with storage invalidation. (2723dff50)
- Bug 1151892 Refactor Cache Manager Context usage to be more sane and fix shutdown assert. r=ehsan (ea96381cf)
- Bug 1136331 - OdinMonkey: allow stdlib calls in heap expressions (2fc5e2bfd)
- Bug 1141439 - Exit with an error code instead of falling through the REMOTE_NOT_FOUND code path when the X-remote returns an explicit command line handler error. (afcf9b1aa)
- Bug 1135825: Add missing MOZ_OVERRIDE annotation in RTCIdentityProviderRegistrar.h (e8beec4e8)
- (Bug 1135138 is not merged due to broken build)
This commit is contained in:
2019-07-25 21:33:11 +08:00
parent 2a3dd7f84c
commit 56aad8a83e
142 changed files with 7623 additions and 3927 deletions
+54 -3
View File
@@ -13,6 +13,7 @@
#include "mozilla/FloatingPoint.h"
#include "mozilla/Assertions.h"
#include "mozilla/Preferences.h"
#include "mozilla/unused.h"
#include "AccessCheck.h"
#include "jsfriendapi.h"
@@ -147,6 +148,9 @@ ErrorResult::ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
message->mArgs.AppendElement(*va_arg(ap, nsString*));
}
mMessage = message;
#ifdef DEBUG
mHasMessage = true;
#endif
}
void
@@ -154,6 +158,7 @@ ErrorResult::SerializeMessage(IPC::Message* aMsg) const
{
using namespace IPC;
MOZ_ASSERT(mMessage);
MOZ_ASSERT(mHasMessage);
WriteParam(aMsg, mMessage->mArgs);
WriteParam(aMsg, mMessage->mErrorNumber);
}
@@ -167,10 +172,11 @@ ErrorResult::DeserializeMessage(const IPC::Message* aMsg, void** aIter)
!ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
return false;
}
if (mMessage) {
delete mMessage;
}
MOZ_ASSERT(!mHasMessage);
mMessage = readMessage.forget();
#ifdef DEBUG
mHasMessage = true;
#endif
return true;
}
@@ -196,6 +202,7 @@ void
ErrorResult::ReportErrorWithMessage(JSContext* aCx)
{
MOZ_ASSERT(mMessage, "ReportErrorWithMessage() can be called only once");
MOZ_ASSERT(mHasMessage);
Message* message = mMessage;
const uint32_t argCount = message->mArgs.Length();
@@ -218,6 +225,9 @@ ErrorResult::ClearMessage()
if (IsErrorWithMessage()) {
delete mMessage;
mMessage = nullptr;
#ifdef DEBUG
mHasMessage = false;
#endif
}
}
@@ -229,6 +239,9 @@ ErrorResult::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
if (IsErrorWithMessage()) {
delete mMessage;
#ifdef DEBUG
mHasMessage = false;
#endif
}
// Make sure mJSException is initialized _before_ we try to root it. But
@@ -375,6 +388,44 @@ ErrorResult::SuppressException()
mResult = NS_OK;
}
ErrorResult&
ErrorResult::operator=(ErrorResult&& aRHS)
{
#ifdef DEBUG
mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
aRHS.mMightHaveUnreportedJSException = false;
#endif
if (aRHS.IsErrorWithMessage()) {
mMessage = aRHS.mMessage;
aRHS.mMessage = nullptr;
#ifdef DEBUG
mHasMessage = aRHS.mHasMessage;
aRHS.mHasMessage = false;
#endif
} else if (aRHS.IsJSException()) {
JSContext* cx = nsContentUtils::GetDefaultJSContextForThread();
MOZ_ASSERT(cx);
mJSException.setUndefined();
if (!js::AddRawValueRoot(cx, &mJSException, "ErrorResult::mJSException")) {
MOZ_CRASH("Could not root mJSException, we're about to OOM");
}
mJSException = aRHS.mJSException;
aRHS.mJSException.setUndefined();
js::RemoveRawValueRoot(cx, &aRHS.mJSException);
} else {
// Null out the union on both sides for hygiene purposes.
mMessage = aRHS.mMessage = nullptr;
#ifdef DEBUG
mHasMessage = aRHS.mHasMessage = false;
#endif
}
// Note: It's important to do this last, since this affects the condition
// checks above!
mResult = aRHS.mResult;
aRHS.mResult = NS_OK;
return *this;
}
namespace dom {
bool
+40 -22
View File
@@ -17,6 +17,7 @@
#include "nscore.h"
#include "nsStringGlue.h"
#include "mozilla/Assertions.h"
#include "mozilla/Move.h"
namespace IPC {
class Message;
@@ -47,6 +48,7 @@ public:
#ifdef DEBUG
mMightHaveUnreportedJSException = false;
mHasMessage = false;
#endif
}
@@ -54,19 +56,25 @@ public:
~ErrorResult() {
MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage);
MOZ_ASSERT(!mMightHaveUnreportedJSException);
MOZ_ASSERT(!mHasMessage);
}
#endif
ErrorResult(ErrorResult&& aRHS)
{
*this = Move(aRHS);
}
ErrorResult& operator=(ErrorResult&& aRHS);
explicit ErrorResult(nsresult aRv)
: ErrorResult()
{
AssignErrorCode(aRv);
}
void Throw(nsresult rv) {
MOZ_ASSERT(NS_FAILED(rv), "Please don't try throwing success");
MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
MOZ_ASSERT(rv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
mResult = rv;
AssignErrorCode(rv);
}
// Use SuppressException when you want to suppress any exception that might be
@@ -147,14 +155,7 @@ public:
// Throw() here because people can easily pass success codes to
// this.
void operator=(nsresult rv) {
MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
MOZ_ASSERT(rv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
MOZ_ASSERT(rv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
MOZ_ASSERT(rv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
mResult = rv;
AssignErrorCode(rv);
}
bool Failed() const {
@@ -166,6 +167,24 @@ public:
}
private:
friend struct IPC::ParamTraits<ErrorResult>;
void SerializeMessage(IPC::Message* aMsg) const;
bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
void ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
nsresult errorType);
void AssignErrorCode(nsresult aRv) {
MOZ_ASSERT(aRv != NS_ERROR_TYPE_ERR, "Use ThrowTypeError()");
MOZ_ASSERT(aRv != NS_ERROR_RANGE_ERR, "Use ThrowRangeError()");
MOZ_ASSERT(!IsErrorWithMessage(), "Don't overwrite errors with message");
MOZ_ASSERT(aRv != NS_ERROR_DOM_JS_EXCEPTION, "Use ThrowJSException()");
MOZ_ASSERT(!IsJSException(), "Don't overwrite JS exceptions");
MOZ_ASSERT(aRv != NS_ERROR_XPC_NOT_ENOUGH_ARGS, "Use ThrowNotEnoughArgsError()");
MOZ_ASSERT(!IsNotEnoughArgsError(), "Don't overwrite not enough args error");
mResult = aRv;
}
nsresult mResult;
struct Message;
// mMessage is set by ThrowErrorWithMessage and cleared (and deallocated) by
@@ -177,21 +196,20 @@ private:
JS::Value mJSException; // valid when IsJSException()
};
friend struct IPC::ParamTraits<ErrorResult>;
void SerializeMessage(IPC::Message* aMsg) const;
bool DeserializeMessage(const IPC::Message* aMsg, void** aIter);
#ifdef DEBUG
// Used to keep track of codepaths that might throw JS exceptions,
// for assertion purposes.
bool mMightHaveUnreportedJSException;
// Used to keep track of whether mMessage has ever been assigned to.
// We need to check this in order to ensure that not attempting to
// delete mMessage in DeserializeMessage doesn't leak memory.
bool mHasMessage;
#endif
// Not to be implemented, to make sure people always pass this by
// reference, not by value.
ErrorResult(const ErrorResult&) = delete;
void ThrowErrorWithMessage(va_list ap, const dom::ErrNum errorNumber,
nsresult errorType);
void operator=(const ErrorResult&) = delete;
};
/******************************************************************************
+2
View File
@@ -7,6 +7,7 @@
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/Feature.h"
#include "nsThreadUtils.h"
namespace mozilla {
namespace dom {
@@ -25,6 +26,7 @@ ActorChild::SetFeature(Feature* aFeature)
void
ActorChild::RemoveFeature()
{
MOZ_ASSERT_IF(!NS_IsMainThread(), mFeature);
if (mFeature) {
mFeature->RemoveActor(this);
mFeature = nullptr;
+349 -271
View File
@@ -7,6 +7,7 @@
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheParent.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/ReadStream.h"
@@ -21,8 +22,8 @@ namespace {
using mozilla::unused;
using mozilla::dom::cache::CachePushStreamChild;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::dom::cache::PCacheReadStreamOrVoid;
using mozilla::dom::cache::CacheReadStream;
using mozilla::dom::cache::CacheReadStreamOrVoid;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::FileDescriptorSetChild;
using mozilla::ipc::FileDescriptorSetParent;
@@ -35,7 +36,7 @@ enum CleanupAction
};
void
CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
CleanupChildFds(CacheReadStream& aReadStream, CleanupAction aAction)
{
if (aReadStream.fds().type() !=
OptionalFileDescriptorSet::TPFileDescriptorSetChild) {
@@ -59,7 +60,7 @@ CleanupChildFds(PCacheReadStream& aReadStream, CleanupAction aAction)
}
void
CleanupChildPushStream(PCacheReadStream& aReadStream, CleanupAction aAction)
CleanupChildPushStream(CacheReadStream& aReadStream, CleanupAction aAction)
{
if (!aReadStream.pushStreamChild()) {
return;
@@ -78,24 +79,24 @@ CleanupChildPushStream(PCacheReadStream& aReadStream, CleanupAction aAction)
}
void
CleanupChild(PCacheReadStream& aReadStream, CleanupAction aAction)
CleanupChild(CacheReadStream& aReadStream, CleanupAction aAction)
{
CleanupChildFds(aReadStream, aAction);
CleanupChildPushStream(aReadStream, aAction);
}
void
CleanupChild(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
CleanupChild(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
return;
}
CleanupChild(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
CleanupChild(aReadStreamOrVoid.get_CacheReadStream(), aAction);
}
void
CleanupParentFds(PCacheReadStream& aReadStream, CleanupAction aAction)
CleanupParentFds(CacheReadStream& aReadStream, CleanupAction aAction)
{
if (aReadStream.fds().type() !=
OptionalFileDescriptorSet::TPFileDescriptorSetParent) {
@@ -119,13 +120,13 @@ CleanupParentFds(PCacheReadStream& aReadStream, CleanupAction aAction)
}
void
CleanupParentFds(PCacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
CleanupParentFds(CacheReadStreamOrVoid& aReadStreamOrVoid, CleanupAction aAction)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
return;
}
CleanupParentFds(aReadStreamOrVoid.get_PCacheReadStream(), aAction);
CleanupParentFds(aReadStreamOrVoid.get_CacheReadStream(), aAction);
}
} // anonymous namespace
@@ -136,171 +137,381 @@ namespace cache {
using mozilla::ipc::PBackgroundParent;
AutoChildBase::AutoChildBase(TypeUtils* aTypeUtils)
// --------------------------------------------
AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
const CacheOpArgs& aOpArgs)
: mTypeUtils(aTypeUtils)
, mOpArgs(aOpArgs)
, 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 ? Forget : Delete;
CleanupChild(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()
AutoChildOpArgs::~AutoChildOpArgs()
{
CleanupAction action = mSent ? Forget : Delete;
for (uint32_t i = 0; i < mRequestList.Length(); ++i) {
CleanupChild(mRequestList[i].body(), action);
switch(mOpArgs.type()) {
case CacheOpArgs::TCacheMatchArgs:
{
CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
CleanupChild(args.request().body(), action);
break;
}
case CacheOpArgs::TCacheMatchAllArgs:
{
CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
if (args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t) {
break;
}
CleanupChild(args.requestOrVoid().get_CacheRequest().body(), action);
break;
}
case CacheOpArgs::TCacheAddAllArgs:
{
CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
auto& list = args.requestList();
for (uint32_t i = 0; i < list.Length(); ++i) {
CleanupChild(list[i].body(), action);
}
break;
}
case CacheOpArgs::TCachePutAllArgs:
{
CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
auto& list = args.requestResponseList();
for (uint32_t i = 0; i < list.Length(); ++i) {
CleanupChild(list[i].request().body(), action);
CleanupChild(list[i].response().body(), action);
}
break;
}
case CacheOpArgs::TCacheDeleteArgs:
{
CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
CleanupChild(args.request().body(), action);
break;
}
case CacheOpArgs::TCacheKeysArgs:
{
CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
if (args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t) {
break;
}
CleanupChild(args.requestOrVoid().get_CacheRequest().body(), action);
break;
}
case CacheOpArgs::TStorageMatchArgs:
{
StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
CleanupChild(args.request().body(), action);
break;
}
default:
// Other types do not need cleanup
break;
}
}
void
AutoChildRequestList::Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
AutoChildOpArgs::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.
switch(mOpArgs.type()) {
case CacheOpArgs::TCacheMatchArgs:
{
CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
break;
}
case CacheOpArgs::TCacheMatchAllArgs:
{
CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
args.requestOrVoid() = CacheRequest();
mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
aRequest, aBodyAction, aReferrerAction,
aSchemeAction, aRv);
break;
}
case CacheOpArgs::TCacheAddAllArgs:
{
CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
PCacheRequest* request = mRequestList.AppendElement();
mTypeUtils->ToPCacheRequest(*request, aRequest, aBodyAction, aReferrerAction,
aSchemeAction, aRv);
if (aRv.Failed()) {
mRequestList.RemoveElementAt(mRequestList.Length() - 1);
// The FileDescriptorSetChild asserts in its destructor that all fds have
// been removed. The copy constructor, however, simply duplicates the
// fds without removing any. This means each temporary and copy must be
// explicitly cleaned up.
//
// Avoid a lot of this hassle by making sure we only create one here. On
// error we remove it.
CacheRequest& request = *args.requestList().AppendElement();
mTypeUtils->ToCacheRequest(request, aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
if (aRv.Failed()) {
args.requestList().RemoveElementAt(args.requestList().Length() - 1);
}
break;
}
case CacheOpArgs::TCacheDeleteArgs:
{
CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
break;
}
case CacheOpArgs::TCacheKeysArgs:
{
CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
args.requestOrVoid() = CacheRequest();
mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
aRequest, aBodyAction, aReferrerAction,
aSchemeAction, aRv);
break;
}
case CacheOpArgs::TStorageMatchArgs:
{
StorageMatchArgs& args = mOpArgs.get_StorageMatchArgs();
mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
break;
}
default:
MOZ_CRASH("Cache args type cannot send a Request!");
}
}
const nsTArray<PCacheRequest>&
AutoChildRequestList::SendAsRequestList()
void
AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
Response& aResponse, ErrorResult& aRv)
{
MOZ_ASSERT(!mSent);
switch(mOpArgs.type()) {
case CacheOpArgs::TCachePutAllArgs:
{
CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
// 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.
CacheRequestResponse& pair = *args.requestResponseList().AppendElement();
pair.request().body() = void_t();
pair.response().body() = void_t();
mTypeUtils->ToCacheRequest(pair.request(), aRequest, aBodyAction,
aReferrerAction, aSchemeAction, aRv);
if (!aRv.Failed()) {
mTypeUtils->ToCacheResponse(pair.response(), aResponse, aRv);
}
if (aRv.Failed()) {
CleanupChild(pair.request().body(), Delete);
args.requestResponseList().RemoveElementAt(
args.requestResponseList().Length() - 1);
}
break;
}
default:
MOZ_CRASH("Cache args type cannot send a Request/Response pair!");
}
}
const CacheOpArgs&
AutoChildOpArgs::SendAsOpArgs()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mRequestList;
return mOpArgs;
}
// --------------------------------------------
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 ? Forget : Delete;
CleanupChild(mRequestResponse.request().body(), action);
CleanupChild(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)
AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
const CacheOpResult& aOpResult)
: mManager(aManager)
, mOpResult(aOpResult)
, mStreamControl(nullptr)
, mSent(false)
{
MOZ_ASSERT(mManager);
}
AutoParentBase::~AutoParentBase()
AutoParentOpResult::~AutoParentOpResult()
{
if (!mSent && mStreamControl) {
CleanupAction action = mSent ? Forget : Delete;
switch (mOpResult.type()) {
case CacheOpResult::TCacheMatchResult:
{
CacheMatchResult& result = mOpResult.get_CacheMatchResult();
if (result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t) {
break;
}
CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
action);
break;
}
case CacheOpResult::TCacheMatchAllResult:
{
CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
for (uint32_t i = 0; i < result.responseList().Length(); ++i) {
CleanupParentFds(result.responseList()[i].body(), action);
}
break;
}
case CacheOpResult::TCacheKeysResult:
{
CacheKeysResult& result = mOpResult.get_CacheKeysResult();
for (uint32_t i = 0; i < result.requestList().Length(); ++i) {
CleanupParentFds(result.requestList()[i].body(), action);
}
break;
}
case CacheOpResult::TStorageMatchResult:
{
StorageMatchResult& result = mOpResult.get_StorageMatchResult();
if (result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t) {
break;
}
CleanupParentFds(result.responseOrVoid().get_CacheResponse().body(),
action);
break;
}
case CacheOpResult::TStorageOpenResult:
{
StorageOpenResult& result = mOpResult.get_StorageOpenResult();
if (action == Forget || result.actorParent() == nullptr) {
break;
}
unused << PCacheParent::Send__delete__(result.actorParent());
}
default:
// other types do not need clean up
break;
}
if (action == Delete && mStreamControl) {
unused << PCacheStreamControlParent::Send__delete__(mStreamControl);
}
}
void
AutoParentBase::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
PCacheReadStream* aReadStreamOut)
AutoParentOpResult::Add(CacheId aOpenedCacheId, Manager* aManager)
{
MOZ_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
MOZ_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
mOpResult.get_StorageOpenResult().actorParent() =
mManager->SendPCacheConstructor(new CacheParent(aManager, aOpenedCacheId));
}
void
AutoParentOpResult::Add(const SavedResponse& aSavedResponse,
StreamList* aStreamList)
{
MOZ_ASSERT(!mSent);
switch (mOpResult.type()) {
case CacheOpResult::TCacheMatchResult:
{
CacheMatchResult& result = mOpResult.get_CacheMatchResult();
MOZ_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
result.responseOrVoid() = aSavedResponse.mValue;
SerializeResponseBody(aSavedResponse, aStreamList,
&result.responseOrVoid().get_CacheResponse());
break;
}
case CacheOpResult::TCacheMatchAllResult:
{
CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
result.responseList().AppendElement(aSavedResponse.mValue);
SerializeResponseBody(aSavedResponse, aStreamList,
&result.responseList().LastElement());
break;
}
case CacheOpResult::TStorageMatchResult:
{
StorageMatchResult& result = mOpResult.get_StorageMatchResult();
MOZ_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
result.responseOrVoid() = aSavedResponse.mValue;
SerializeResponseBody(aSavedResponse, aStreamList,
&result.responseOrVoid().get_CacheResponse());
break;
}
default:
MOZ_CRASH("Cache result type cannot handle returning a Response!");
}
}
void
AutoParentOpResult::Add(const SavedRequest& aSavedRequest,
StreamList* aStreamList)
{
MOZ_ASSERT(!mSent);
switch (mOpResult.type()) {
case CacheOpResult::TCacheKeysResult:
{
CacheKeysResult& result = mOpResult.get_CacheKeysResult();
result.requestList().AppendElement(aSavedRequest.mValue);
CacheRequest& request = result.requestList().LastElement();
if (!aSavedRequest.mHasBodyId) {
request.body() = void_t();
break;
}
request.body() = CacheReadStream();
SerializeReadStream(aSavedRequest.mBodyId, aStreamList,
&request.body().get_CacheReadStream());
break;
}
default:
MOZ_CRASH("Cache result type cannot handle returning a Request!");
}
}
const CacheOpResult&
AutoParentOpResult::SendAsOpResult()
{
MOZ_ASSERT(!mSent);
mSent = true;
return mOpResult;
}
void
AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
StreamList* aStreamList,
CacheResponse* aResponseOut)
{
MOZ_ASSERT(aResponseOut);
if (!aSavedResponse.mHasBodyId) {
aResponseOut->body() = void_t();
return;
}
aResponseOut->body() = CacheReadStream();
SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
&aResponseOut->body().get_CacheReadStream());
}
void
AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
CacheReadStream* aReadStreamOut)
{
MOZ_ASSERT(aStreamList);
MOZ_ASSERT(aReadStreamOut);
@@ -328,139 +539,6 @@ AutoParentBase::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
readStream->Serialize(aReadStreamOut);
}
// --------------------------------------------
AutoParentRequestList::AutoParentRequestList(PBackgroundParent* aManager,
uint32_t aCapacity)
: AutoParentBase(aManager)
{
mRequestList.SetCapacity(aCapacity);
}
AutoParentRequestList::~AutoParentRequestList()
{
CleanupAction action = mSent ? Forget : Delete;
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 ? Forget : Delete;
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 ? Forget : Delete;
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
+34 -105
View File
@@ -8,7 +8,8 @@
#define mozilla_dom_cache_AutoUtils_h
#include "mozilla/Attributes.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "nsTArray.h"
@@ -29,6 +30,7 @@ class InternalRequest;
namespace cache {
class CacheStreamControlParent;
class Manager;
struct SavedRequest;
struct SavedResponse;
class StreamList;
@@ -41,129 +43,56 @@ class StreamList;
// 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
class MOZ_STACK_CLASS AutoChildOpArgs final
{
protected:
public:
typedef TypeUtils::BodyAction BodyAction;
typedef TypeUtils::ReferrerAction ReferrerAction;
typedef TypeUtils::SchemeAction SchemeAction;
AutoChildBase(TypeUtils* aTypeUtils);
virtual ~AutoChildBase() = 0;
AutoChildOpArgs(TypeUtils* aTypeUtils, const CacheOpArgs& aOpArgs);
~AutoChildOpArgs();
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
ErrorResult& aRv);
void Add(InternalRequest* aRequest, BodyAction aBodyAction,
ReferrerAction aReferrerAction, SchemeAction aSchemeAction,
Response& aResponse, ErrorResult& aRv);
const CacheOpArgs& SendAsOpArgs();
private:
TypeUtils* mTypeUtils;
CacheOpArgs mOpArgs;
bool mSent;
};
class MOZ_STACK_CLASS AutoChildRequest final : public AutoChildBase
class MOZ_STACK_CLASS AutoParentOpResult final
{
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 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 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 final : public AutoParentBase
{
public:
AutoParentRequestList(mozilla::ipc::PBackgroundParent* aManager,
uint32_t aCapacity);
~AutoParentRequestList();
AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
const CacheOpResult& aOpResult);
~AutoParentOpResult();
void Add(CacheId aOpenedCacheId, Manager* aManager);
void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
void Add(const SavedRequest& aSavedRequest, StreamList* aStreamList);
const nsTArray<PCacheRequest>& SendAsRequestList();
const CacheOpResult& SendAsOpResult();
private:
// Allocates ~5k inline in the stack-only class
nsAutoTArray<PCacheRequest, 32> mRequestList;
};
void SerializeResponseBody(const SavedResponse& aSavedResponse,
StreamList* aStreamList,
CacheResponse* aResponseOut);
class MOZ_STACK_CLASS AutoParentResponseList final : public AutoParentBase
{
public:
AutoParentResponseList(mozilla::ipc::PBackgroundParent* aManager,
uint32_t aCapacity);
~AutoParentResponseList();
void SerializeReadStream(const nsID& aId, StreamList* aStreamList,
CacheReadStream* aReadStreamOut);
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 final : public AutoParentBase
{
public:
explicit AutoParentResponseOrVoid(mozilla::ipc::PBackgroundParent* aManager);
~AutoParentResponseOrVoid();
void Add(const SavedResponse& aSavedResponse, StreamList* aStreamList);
const PCacheResponseOrVoid& SendAsResponseOrVoid();
private:
PCacheResponseOrVoid mResponseOrVoid;
mozilla::ipc::PBackgroundParent* mManager;
CacheOpResult mOpResult;
CacheStreamControlParent* mStreamControl;
bool mSent;
};
} // namespace cache
+52 -292
View File
@@ -16,7 +16,6 @@
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CachePushStreamChild.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"
@@ -79,17 +78,7 @@ 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_CLASS(mozilla::dom::cache::Cache)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::Cache)
tmp->DisconnectFromActor();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::Cache)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::Cache)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::Cache, mGlobal);
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Cache)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@@ -111,31 +100,22 @@ Cache::Match(const RequestOrUSVString& aRequest,
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest, IgnoreBody, aRv);
if (aRv.Failed()) {
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
AutoChildRequest request(this);
CacheQueryParams params;
ToCacheQueryParams(params, aOptions);
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (aRv.Failed()) {
AutoChildOpArgs args(this, CacheMatchArgs(CacheRequest(), params));
args.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
if (NS_WARN_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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -144,12 +124,10 @@ Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
CacheQueryParams params;
ToCacheQueryParams(params, aOptions);
AutoChildRequest request(this);
AutoChildOpArgs args(this, CacheMatchAllArgs(void_t(), params));
if (aRequest.WasPassed()) {
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
@@ -158,21 +136,13 @@ Cache::MatchAll(const Optional<RequestOrUSVString>& aRequest,
return nullptr;
}
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
args.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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -184,27 +154,19 @@ Cache::Add(const RequestOrUSVString& aRequest, ErrorResult& 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);
AutoChildOpArgs args(this, CacheAddAllArgs());
args.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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -213,18 +175,18 @@ Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
{
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()) {
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
promise->MaybeResolve(JS::UndefinedHandleValue);
return promise.forget();
}
AutoChildRequestList requests(this, aRequests.Length());
AutoChildOpArgs args(this, CacheAddAllArgs());
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
if (!IsValidPutRequestMethod(aRequests[i], aRv)) {
@@ -237,18 +199,13 @@ Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests,
return nullptr;
}
requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme,
aRv);
args.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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -261,32 +218,20 @@ Cache::Put(const RequestOrUSVString& aRequest, Response& aResponse,
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);
AutoChildOpArgs args(this, CachePutAllArgs());
args.Add(ir, ReadBody, PassThroughReferrer, TypeErrorOnInvalidScheme,
aResponse, 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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -295,30 +240,22 @@ Cache::Delete(const RequestOrUSVString& aRequest,
{
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);
CacheQueryParams params;
ToCacheQueryParams(params, aOptions);
AutoChildOpArgs args(this, CacheDeleteArgs(CacheRequest(), params));
args.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();
return ExecuteOp(args, aRv);
}
already_AddRefed<Promise>
@@ -327,12 +264,10 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
{
MOZ_ASSERT(mActor);
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
CacheQueryParams params;
ToCacheQueryParams(params, aOptions);
AutoChildRequest request(this);
AutoChildOpArgs args(this, CacheKeysArgs(void_t(), params));
if (aRequest.WasPassed()) {
nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequest.Value(),
@@ -341,20 +276,13 @@ Cache::Keys(const Optional<RequestOrUSVString>& aRequest,
return nullptr;
}
request.Add(ir, IgnoreBody, PassThroughReferrer, IgnoreInvalidScheme, aRv);
args.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();
return ExecuteOp(args, aRv);
}
// static
@@ -401,123 +329,6 @@ Cache::DestroyInternal(CacheChild* aActor)
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,
const mozilla::ErrorResult& aError)
{
nsRefPtr<Promise> promise = RemoveRequestPromise(aRequestId);
if (aError.Failed()) {
// TODO: Remove this const_cast (bug 1152078).
// It is safe for now since this ErrorResult is handed off to us by IPDL
// and is thrown into the trash afterwards.
promise->MaybeReject(const_cast<ErrorResult&>(aError));
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
{
@@ -538,36 +349,12 @@ Cache::CreatePushStream(nsIAsyncInputStream* aStream)
NS_ASSERT_OWNINGTHREAD(Cache);
MOZ_ASSERT(mActor);
MOZ_ASSERT(aStream);
auto actor = mActor->SendPCachePushStreamConstructor(
new CachePushStreamChild(mActor->GetFeature(), aStream));
MOZ_ASSERT(actor);
return static_cast<CachePushStreamChild*>(actor);
}
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.
return mActor->CreatePushStream(aStream);
}
Cache::~Cache()
{
DisconnectFromActor();
}
void
Cache::DisconnectFromActor()
{
NS_ASSERT_OWNINGTHREAD(Cache);
if (mActor) {
mActor->StartDestroy();
// DestroyInternal() is called synchronously by StartDestroy(). So we
@@ -576,43 +363,16 @@ Cache::DisconnectFromActor()
}
}
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)
Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv)
{
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();
}
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
MOZ_ASSERT_UNREACHABLE("Received response without a matching promise!");
return nullptr;
mActor->ExecuteOp(mGlobal, promise, aOpArgs.SendAsOpArgs());
return promise.forget();
}
} // namespace cache
+4 -30
View File
@@ -7,7 +7,6 @@
#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"
@@ -33,12 +32,10 @@ template<typename T> class Sequence;
namespace cache {
class AutoChildOpArgs;
class CacheChild;
class PCacheRequest;
class PCacheResponse;
class PCacheResponseOrVoid;
class Cache final : public PromiseNativeHandler
class Cache final : public nsISupports
, public nsWrapperCache
, public TypeUtils
{
@@ -76,20 +73,6 @@ public:
// 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,
const mozilla::ErrorResult& aError);
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 override;
@@ -101,26 +84,17 @@ public:
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
private:
~Cache();
// Called when we're destroyed or CCed.
void DisconnectFromActor();
// TODO: Replace with actor-per-request model during refactor (bug 1110485)
RequestId AddRequestPromise(Promise* aPromise, ErrorResult& aRv);
already_AddRefed<Promise> RemoveRequestPromise(RequestId aRequestId);
already_AddRefed<Promise>
ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv);
nsCOMPtr<nsIGlobalObject> mGlobal;
CacheChild* mActor;
nsTArray<nsRefPtr<Promise>> mRequestPromises;
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+52 -87
View File
@@ -9,8 +9,8 @@
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/PCachePushStreamChild.h"
#include "mozilla/dom/cache/StreamUtils.h"
#include "mozilla/dom/cache/CacheOpChild.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
namespace mozilla {
namespace dom {
@@ -32,6 +32,7 @@ DeallocPCacheChild(PCacheChild* aActor)
CacheChild::CacheChild()
: mListener(nullptr)
, mNumChildActors(0)
{
MOZ_COUNT_CTOR(cache::CacheChild);
}
@@ -41,6 +42,7 @@ CacheChild::~CacheChild()
MOZ_COUNT_DTOR(cache::CacheChild);
NS_ASSERT_OWNINGTHREAD(CacheChild);
MOZ_ASSERT(!mListener);
MOZ_ASSERT(!mNumChildActors);
}
void
@@ -60,6 +62,25 @@ CacheChild::ClearListener()
mListener = nullptr;
}
void
CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs)
{
mNumChildActors += 1;
MOZ_ALWAYS_TRUE(SendPCacheOpConstructor(
new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs));
}
CachePushStreamChild*
CacheChild::CreatePushStream(nsIAsyncInputStream* aStream)
{
mNumChildActors += 1;
auto actor = SendPCachePushStreamConstructor(
new CachePushStreamChild(GetFeature(), aStream));
MOZ_ASSERT(actor);
return static_cast<CachePushStreamChild*>(actor);
}
void
CacheChild::StartDestroy()
{
@@ -77,6 +98,14 @@ CacheChild::StartDestroy()
// Cache listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. SendTeardown() will be called when the count drops to zero
// in NoteDeletedActor().
if (mNumChildActors) {
return;
}
// Start actor destruction from parent process
unused << SendTeardown();
}
@@ -95,6 +124,21 @@ CacheChild::ActorDestroy(ActorDestroyReason aReason)
RemoveFeature();
}
PCacheOpChild*
CacheChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
{
MOZ_CRASH("CacheOpChild should be manually constructed.");
return nullptr;
}
bool
CacheChild::DeallocPCacheOpChild(PCacheOpChild* aActor)
{
delete aActor;
NoteDeletedActor();
return true;
}
PCachePushStreamChild*
CacheChild::AllocPCachePushStreamChild()
{
@@ -106,96 +150,17 @@ bool
CacheChild::DeallocPCachePushStreamChild(PCachePushStreamChild* aActor)
{
delete aActor;
NoteDeletedActor();
return true;
}
bool
CacheChild::RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse)
void
CacheChild::NoteDeletedActor()
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
AddFeatureToStreamChild(aResponse, GetFeature());
nsRefPtr<Cache> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aResponse);
return true;
mNumChildActors -= 1;
if (!mNumChildActors && !mListener) {
unused << SendTeardown();
}
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 mozilla::ErrorResult& aError)
{
NS_ASSERT_OWNINGTHREAD(CacheChild);
nsRefPtr<Cache> listener = mListener;
if (listener) {
listener->RecvAddAllResponse(requestId, aError);
}
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
+25 -18
View File
@@ -10,11 +10,19 @@
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/PCacheChild.h"
class nsIAsyncInputStream;
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class Promise;
namespace cache {
class Cache;
class CacheOpArgs;
class CachePushStreamChild;
class CacheChild final : public PCacheChild
, public ActorChild
@@ -30,6 +38,13 @@ public:
// trigger ActorDestroy() if it has not been called yet.
void ClearListener();
void
ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs);
CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream);
// ActorChild methods
// Synchronously call ActorDestroy on our Cache listener and then start the
@@ -41,35 +56,27 @@ private:
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
virtual PCacheOpChild*
AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
virtual bool
DeallocPCacheOpChild(PCacheOpChild* aActor) override;
virtual PCachePushStreamChild*
AllocPCachePushStreamChild() override;
virtual bool
DeallocPCachePushStreamChild(PCachePushStreamChild* aActor) override;
virtual bool
RecvMatchResponse(const RequestId& requestId, const nsresult& aRv,
const PCacheResponseOrVoid& aResponse) override;
virtual bool
RecvMatchAllResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheResponse>&& responses) override;
virtual bool
RecvAddAllResponse(const RequestId& requestId,
const mozilla::ErrorResult& aError) override;
virtual bool
RecvPutResponse(const RequestId& aRequestId,
const nsresult& aRv) override;
virtual bool
RecvDeleteResponse(const RequestId& requestId, const nsresult& aRv,
const bool& result) override;
virtual bool
RecvKeysResponse(const RequestId& requestId, const nsresult& aRv,
nsTArray<PCacheRequest>&& requests) override;
// utility methods
void
NoteDeletedActor();
// 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;
uint32_t mNumChildActors;
NS_DECL_OWNINGTHREAD
};
-24
View File
@@ -1,24 +0,0 @@
/* 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
+248
View File
@@ -0,0 +1,248 @@
/* -*- 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/CacheOpChild.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/cache/Cache.h"
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CacheStreamControlChild.h"
namespace mozilla {
namespace dom {
namespace cache {
namespace {
void
AddFeatureToStreamChild(const CacheReadStream& aReadStream, Feature* aFeature)
{
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
CacheStreamControlChild* cacheControl =
static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
if (cacheControl) {
cacheControl->SetFeature(aFeature);
}
}
void
AddFeatureToStreamChild(const CacheResponse& aResponse, Feature* aFeature)
{
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
if (aResponse.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
return;
}
AddFeatureToStreamChild(aResponse.body().get_CacheReadStream(), aFeature);
}
void
AddFeatureToStreamChild(const CacheRequest& aRequest, Feature* aFeature)
{
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
if (aRequest.body().type() == CacheReadStreamOrVoid::Tvoid_t) {
return;
}
AddFeatureToStreamChild(aRequest.body().get_CacheReadStream(), aFeature);
}
} // anonymous namespace
CacheOpChild::CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal,
Promise* aPromise)
: mGlobal(aGlobal)
, mPromise(aPromise)
{
MOZ_ASSERT(mGlobal);
MOZ_ASSERT(mPromise);
MOZ_ASSERT_IF(!NS_IsMainThread(), aFeature);
SetFeature(aFeature);
}
CacheOpChild::~CacheOpChild()
{
NS_ASSERT_OWNINGTHREAD(CacheOpChild);
MOZ_ASSERT(!mPromise);
}
void
CacheOpChild::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheOpChild);
// If the actor was terminated for some unknown reason, then indicate the
// operation is dead.
if (mPromise) {
mPromise->MaybeReject(NS_ERROR_FAILURE);
mPromise = nullptr;
}
RemoveFeature();
}
bool
CacheOpChild::Recv__delete__(const ErrorResult& aRv,
const CacheOpResult& aResult)
{
NS_ASSERT_OWNINGTHREAD(CacheOpChild);
if (aRv.Failed()) {
MOZ_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
// TODO: Remove this const_cast (bug 1152078).
// It is safe for now since this ErrorResult is handed off to us by IPDL
// and is thrown into the trash afterwards.
mPromise->MaybeReject(const_cast<ErrorResult&>(aRv));
mPromise = nullptr;
return true;
}
switch (aResult.type()) {
case CacheOpResult::TCacheMatchResult:
{
HandleResponse(aResult.get_CacheMatchResult().responseOrVoid());
break;
}
case CacheOpResult::TCacheMatchAllResult:
{
HandleResponseList(aResult.get_CacheMatchAllResult().responseList());
break;
}
case CacheOpResult::TCacheAddAllResult:
case CacheOpResult::TCachePutAllResult:
{
mPromise->MaybeResolve(JS::UndefinedHandleValue);
break;
}
case CacheOpResult::TCacheDeleteResult:
{
mPromise->MaybeResolve(aResult.get_CacheDeleteResult().success());
break;
}
case CacheOpResult::TCacheKeysResult:
{
HandleRequestList(aResult.get_CacheKeysResult().requestList());
break;
}
case CacheOpResult::TStorageMatchResult:
{
HandleResponse(aResult.get_StorageMatchResult().responseOrVoid());
break;
}
case CacheOpResult::TStorageHasResult:
{
mPromise->MaybeResolve(aResult.get_StorageHasResult().success());
break;
}
case CacheOpResult::TStorageOpenResult:
{
auto actor = static_cast<CacheChild*>(
aResult.get_StorageOpenResult().actorChild());
actor->SetFeature(GetFeature());
nsRefPtr<Cache> cache = new Cache(mGlobal, actor);
mPromise->MaybeResolve(cache);
break;
}
case CacheOpResult::TStorageDeleteResult:
{
mPromise->MaybeResolve(aResult.get_StorageDeleteResult().success());
break;
}
case CacheOpResult::TStorageKeysResult:
{
mPromise->MaybeResolve(aResult.get_StorageKeysResult().keyList());
break;
}
default:
MOZ_CRASH("Unknown Cache op result type!");
}
mPromise = nullptr;
return true;
}
void
CacheOpChild::StartDestroy()
{
NS_ASSERT_OWNINGTHREAD(CacheOpChild);
// Do not cancel on-going operations when Feature calls this. Instead, keep
// the Worker alive until we are done.
}
nsIGlobalObject*
CacheOpChild::GetGlobalObject() const
{
return mGlobal;
}
#ifdef DEBUG
void
CacheOpChild::AssertOwningThread() const
{
NS_ASSERT_OWNINGTHREAD(CacheOpChild);
}
#endif
CachePushStreamChild*
CacheOpChild::CreatePushStream(nsIAsyncInputStream* aStream)
{
MOZ_CRASH("CacheOpChild should never create a push stream actor!");
}
void
CacheOpChild::HandleResponse(const CacheResponseOrVoid& aResponseOrVoid)
{
if (aResponseOrVoid.type() == CacheResponseOrVoid::Tvoid_t) {
mPromise->MaybeResolve(JS::UndefinedHandleValue);
return;
}
const CacheResponse& cacheResponse = aResponseOrVoid.get_CacheResponse();
AddFeatureToStreamChild(cacheResponse, GetFeature());
nsRefPtr<Response> response = ToResponse(cacheResponse);
mPromise->MaybeResolve(response);
}
void
CacheOpChild::HandleResponseList(const nsTArray<CacheResponse>& aResponseList)
{
nsAutoTArray<nsRefPtr<Response>, 256> responses;
responses.SetCapacity(aResponseList.Length());
for (uint32_t i = 0; i < aResponseList.Length(); ++i) {
AddFeatureToStreamChild(aResponseList[i], GetFeature());
responses.AppendElement(ToResponse(aResponseList[i]));
}
mPromise->MaybeResolve(responses);
}
void
CacheOpChild::HandleRequestList(const nsTArray<CacheRequest>& aRequestList)
{
nsAutoTArray<nsRefPtr<Request>, 256> requests;
requests.SetCapacity(aRequestList.Length());
for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
AddFeatureToStreamChild(aRequestList[i], GetFeature());
requests.AppendElement(ToRequest(aRequestList[i]));
}
mPromise->MaybeResolve(requests);
}
} // namespace cache
} // namespace dom
} // namespace mozilla
+80
View File
@@ -0,0 +1,80 @@
/* -*- 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_CacheOpChild_h
#define mozilla_dom_cache_CacheOpChild_h
#include "mozilla/dom/cache/ActorChild.h"
#include "mozilla/dom/cache/PCacheOpChild.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/RefPtr.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class Promise;
namespace cache {
class CacheOpChild final : public PCacheOpChild
, public ActorChild
, public TypeUtils
{
friend class CacheChild;
friend class CacheStorageChild;
private:
// This class must be constructed by CacheChild or CacheStorageChild using
// their ExecuteOp() factory method.
CacheOpChild(Feature* aFeature, nsIGlobalObject* aGlobal, Promise* aPromise);
~CacheOpChild();
// PCacheOpChild methods
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
virtual bool
Recv__delete__(const ErrorResult& aRv, const CacheOpResult& aResult) override;
// ActorChild methods
virtual void
StartDestroy() override;
// TypeUtils methods
virtual nsIGlobalObject*
GetGlobalObject() const override;
#ifdef DEBUG
virtual void
AssertOwningThread() const override;
#endif
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
// Utility methods
void
HandleResponse(const CacheResponseOrVoid& aResponseOrVoid);
void
HandleResponseList(const nsTArray<CacheResponse>& aResponseList);
void
HandleRequestList(const nsTArray<CacheRequest>& aRequestList);
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<Promise> mPromise;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheOpChild_h
+287
View File
@@ -0,0 +1,287 @@
/* -*- 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/CacheOpParent.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CachePushStreamParent.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/SavedTypes.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
#include "mozilla/ipc/InputStreamUtils.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::ipc::FileDescriptorSetParent;
using mozilla::ipc::PBackgroundParent;
CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
const CacheOpArgs& aOpArgs)
: mIpcManager(aIpcManager)
, mCacheId(aCacheId)
, mNamespace(INVALID_NAMESPACE)
, mOpArgs(aOpArgs)
{
MOZ_ASSERT(mIpcManager);
}
CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager,
Namespace aNamespace, const CacheOpArgs& aOpArgs)
: mIpcManager(aIpcManager)
, mCacheId(INVALID_CACHE_ID)
, mNamespace(aNamespace)
, mOpArgs(aOpArgs)
{
MOZ_ASSERT(mIpcManager);
}
CacheOpParent::~CacheOpParent()
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
}
void
CacheOpParent::Execute(ManagerId* aManagerId)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
MOZ_ASSERT(!mManager);
MOZ_ASSERT(!mVerifier);
nsRefPtr<Manager> manager;
nsresult rv = Manager::GetOrCreate(aManagerId, getter_AddRefs(manager));
if (NS_WARN_IF(NS_FAILED(rv))) {
unused << Send__delete__(this, ErrorResult(rv), void_t());
return;
}
Execute(manager);
}
void
CacheOpParent::Execute(Manager* aManager)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
MOZ_ASSERT(!mManager);
MOZ_ASSERT(!mVerifier);
mManager = aManager;
// Handle add/addAll op with a FetchPut object
if (mOpArgs.type() == CacheOpArgs::TCacheAddAllArgs) {
MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
const CacheAddAllArgs& args = mOpArgs.get_CacheAddAllArgs();
const nsTArray<CacheRequest>& list = args.requestList();
nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
for (uint32_t i = 0; i < list.Length(); ++i) {
requestStreamList.AppendElement(DeserializeCacheStream(list[i].body()));
}
nsRefPtr<FetchPut> fetchPut;
nsresult rv = FetchPut::Create(this, mManager, mCacheId, list,
requestStreamList, getter_AddRefs(fetchPut));
if (NS_WARN_IF(NS_FAILED(rv))) {
OnOpComplete(ErrorResult(rv), CacheAddAllResult());
return;
}
mFetchPutList.AppendElement(fetchPut.forget());
return;
}
// Handle put op
if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
const CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
const nsTArray<CacheRequestResponse>& list = args.requestResponseList();
nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
nsAutoTArray<nsCOMPtr<nsIInputStream>, 256> responseStreamList;
for (uint32_t i = 0; i < list.Length(); ++i) {
requestStreamList.AppendElement(
DeserializeCacheStream(list[i].request().body()));
responseStreamList.AppendElement(
DeserializeCacheStream(list[i].response().body()));
}
mManager->ExecutePutAll(this, mCacheId, args.requestResponseList(),
requestStreamList, responseStreamList);
return;
}
// Handle all other cache ops
if (mCacheId != INVALID_CACHE_ID) {
MOZ_ASSERT(mNamespace == INVALID_NAMESPACE);
mManager->ExecuteCacheOp(this, mCacheId, mOpArgs);
return;
}
// Handle all storage ops
MOZ_ASSERT(mNamespace != INVALID_NAMESPACE);
mManager->ExecuteStorageOp(this, mNamespace, mOpArgs);
}
void
CacheOpParent::WaitForVerification(PrincipalVerifier* aVerifier)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
MOZ_ASSERT(!mManager);
MOZ_ASSERT(!mVerifier);
mVerifier = aVerifier;
mVerifier->AddListener(this);
}
void
CacheOpParent::ActorDestroy(ActorDestroyReason aReason)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
if (mVerifier) {
mVerifier->RemoveListener(this);
mVerifier = nullptr;
}
for (uint32_t i = 0; i < mFetchPutList.Length(); ++i) {
mFetchPutList[i]->ClearListener();
}
mFetchPutList.Clear();
if (mManager) {
mManager->RemoveListener(this);
mManager = nullptr;
}
mIpcManager = nullptr;
}
void
CacheOpParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
mVerifier->RemoveListener(this);
mVerifier = nullptr;
if (NS_WARN_IF(NS_FAILED(aRv))) {
unused << Send__delete__(this, ErrorResult(aRv), void_t());
return;
}
Execute(aManagerId);
}
void
CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
MOZ_ASSERT(mIpcManager);
MOZ_ASSERT(mManager);
// Never send an op-specific result if we have an error. Instead, send
// void_t() to ensure that we don't leak actors on the child side.
if (aRv.Failed()) {
unused << Send__delete__(this, aRv, void_t());
aRv.ClearMessage(); // This may contain a TypeError.
return;
}
// The result must contain the appropriate type at this point. It may
// or may not contain the additional result data yet. For types that
// do not need special processing, it should already be set. If the
// result requires actor-specific operations, then we do that below.
// If the type and data types don't match, then we will trigger an
// assertion in AutoParentOpResult::Add().
AutoParentOpResult result(mIpcManager, aResult);
if (aOpenedCacheId != INVALID_CACHE_ID) {
result.Add(aOpenedCacheId, mManager);
}
for (uint32_t i = 0; i < aSavedResponseList.Length(); ++i) {
result.Add(aSavedResponseList[i], aStreamList);
}
for (uint32_t i = 0; i < aSavedRequestList.Length(); ++i) {
result.Add(aSavedRequestList[i], aStreamList);
}
unused << Send__delete__(this, aRv, result.SendAsOpResult());
}
void
CacheOpParent::OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheOpParent);
MOZ_ASSERT(aFetchPut);
aFetchPut->ClearListener();
MOZ_ALWAYS_TRUE(mFetchPutList.RemoveElement(aFetchPut));
OnOpComplete(Move(aRv), CacheAddAllResult());
}
already_AddRefed<nsIInputStream>
CacheOpParent::DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid)
{
if (aStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
return nullptr;
}
nsCOMPtr<nsIInputStream> stream;
const CacheReadStream& readStream = aStreamOrVoid.get_CacheReadStream();
// Option 1: A push stream actor was sent for nsPipe data
if (readStream.pushStreamParent()) {
MOZ_ASSERT(!readStream.controlParent());
CachePushStreamParent* pushStream =
static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
stream = pushStream->TakeReader();
MOZ_ASSERT(stream);
return stream.forget();
}
// Option 2: One of our own ReadStreams was passed back to us with a stream
// control actor.
stream = ReadStream::Create(readStream);
if (stream) {
return stream.forget();
}
// Option 3: A stream was serialized using normal methods.
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
} // namespace mozilla
+87
View File
@@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_cache_CacheOpParent_h
#define mozilla_dom_cache_CacheOpParent_h
#include "mozilla/dom/cache/FetchPut.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/PCacheOpParent.h"
#include "mozilla/dom/cache/PrincipalVerifier.h"
#include "nsTArray.h"
namespace mozilla {
namespace ipc {
class PBackgroundParent;
}
namespace dom {
namespace cache {
class CacheOpParent final : public PCacheOpParent
, public PrincipalVerifier::Listener
, public Manager::Listener
, public FetchPut::Listener
{
// to allow use of convenience overrides
using Manager::Listener::OnOpComplete;
public:
CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager, CacheId aCacheId,
const CacheOpArgs& aOpArgs);
CacheOpParent(mozilla::ipc::PBackgroundParent* aIpcManager,
Namespace aNamespace, const CacheOpArgs& aOpArgs);
~CacheOpParent();
void
Execute(ManagerId* aManagerId);
void
Execute(Manager* aManager);
void
WaitForVerification(PrincipalVerifier* aVerifier);
private:
// PCacheOpParent methods
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
// PrincipalVerifier::Listener methods
virtual void
OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId) override;
// Manager::Listener methods
virtual void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList) override;
// FetchPut::Listener methods
virtual void
OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) override;
// utility methods
already_AddRefed<nsIInputStream>
DeserializeCacheStream(const CacheReadStreamOrVoid& aStreamOrVoid);
mozilla::ipc::PBackgroundParent* mIpcManager;
const CacheId mCacheId;
const Namespace mNamespace;
const CacheOpArgs mOpArgs;
nsRefPtr<Manager> mManager;
nsRefPtr<PrincipalVerifier> mVerifier;
nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
NS_DECL_OWNINGTHREAD
};
} // namespace cache
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_cache_CacheOpParent_h
+33 -252
View File
@@ -6,27 +6,14 @@
#include "mozilla/dom/cache/CacheParent.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/cache/AutoUtils.h"
#include "mozilla/dom/cache/CacheOpParent.h"
#include "mozilla/dom/cache/CachePushStreamParent.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::dom::ErrNum;
using mozilla::ipc::FileDescriptorSetParent;
using mozilla::ipc::PFileDescriptorSetParent;
// Declared in ActorUtils.h
void
DeallocPCacheParent(PCacheParent* aActor)
@@ -47,22 +34,48 @@ 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;
}
PCacheOpParent*
CacheParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
{
if (aOpArgs.type() != CacheOpArgs::TCacheMatchArgs &&
aOpArgs.type() != CacheOpArgs::TCacheMatchAllArgs &&
aOpArgs.type() != CacheOpArgs::TCacheAddAllArgs &&
aOpArgs.type() != CacheOpArgs::TCachePutAllArgs &&
aOpArgs.type() != CacheOpArgs::TCacheDeleteArgs &&
aOpArgs.type() != CacheOpArgs::TCacheKeysArgs)
{
MOZ_CRASH("Invalid operation sent to Cache actor!");
}
return new CacheOpParent(Manager(), mCacheId, aOpArgs);
}
bool
CacheParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
{
delete aActor;
return true;
}
bool
CacheParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
const CacheOpArgs& aOpArgs)
{
auto actor = static_cast<CacheOpParent*>(aActor);
actor->Execute(mManager);
return true;
}
PCachePushStreamParent*
CacheParent::AllocPCachePushStreamParent()
{
@@ -86,238 +99,6 @@ CacheParent::RecvTeardown()
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))) {
MOZ_ASSERT(rv != NS_ERROR_TYPE_ERR);
ErrorResult error;
error.Throw(rv);
if (!SendAddAllResponse(aRequestId, error)) {
// 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, const ErrorResult& 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;
}
nsCOMPtr<nsIInputStream> stream;
const PCacheReadStream& readStream = aStreamOrVoid.get_PCacheReadStream();
// Option 1: A push stream actor was sent for nsPipe data
if (readStream.pushStreamParent()) {
MOZ_ASSERT(!readStream.controlParent());
CachePushStreamParent* pushStream =
static_cast<CachePushStreamParent*>(readStream.pushStreamParent());
stream = pushStream->TakeReader();
MOZ_ASSERT(stream);
return stream.forget();
}
// Option 2: One of our own ReadStreams was passed back to us with a stream
// control actor.
stream = ReadStream::Create(readStream);
if (stream) {
return stream.forget();
}
// Option 3: A stream was serialized using normal methods.
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
+18 -49
View File
@@ -7,77 +7,46 @@
#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 {
struct SavedResponse;
class Manager;
class CacheParent final : public PCacheParent
, public Manager::Listener
, public FetchPut::Listener
{
public:
CacheParent(cache::Manager* aManager, CacheId aCacheId);
virtual ~CacheParent();
private:
// PCacheParent method
// PCacheParent methods
virtual void ActorDestroy(ActorDestroyReason aReason) override;
virtual PCachePushStreamParent* AllocPCachePushStreamParent() override;
virtual bool DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
virtual bool RecvTeardown() override;
virtual bool
RecvMatch(const RequestId& aRequestId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) override;
virtual bool
RecvMatchAll(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams) override;
virtual bool
RecvAddAll(const RequestId& aRequestId,
nsTArray<PCacheRequest>&& aRequests) override;
virtual bool
RecvPut(const RequestId& aRequestId,
const CacheRequestResponse& aPut) override;
virtual bool
RecvDelete(const RequestId& aRequestId, const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) override;
virtual bool
RecvKeys(const RequestId& aRequestId, const PCacheRequestOrVoid& aRequest,
const PCacheQueryParams& aParams) override;
// Manager::Listener methods
virtual void OnCacheMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aSavedResponse,
StreamList* aStreamList) override;
virtual void OnCacheMatchAll(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedResponse>& aSavedResponses,
StreamList* aStreamList) override;
virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
virtual void OnCacheDelete(RequestId aRequestId, nsresult aRv,
bool aSuccess) override;
virtual void OnCacheKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<SavedRequest>& aSavedRequests,
StreamList* aStreamList) override;
virtual PCacheOpParent*
AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
// FetchPut::Listener methods
virtual void OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId,
const mozilla::ErrorResult& aRv) override;
virtual bool
DeallocPCacheOpParent(PCacheOpParent* aActor) override;
already_AddRefed<nsIInputStream>
DeserializeCacheStream(const PCacheReadStreamOrVoid& aStreamOrVoid);
virtual bool
RecvPCacheOpConstructor(PCacheOpParent* actor,
const CacheOpArgs& aOpArgs) override;
virtual PCachePushStreamParent*
AllocPCachePushStreamParent() override;
virtual bool
DeallocPCachePushStreamParent(PCachePushStreamParent* aActor) override;
virtual bool
RecvTeardown() override;
nsRefPtr<cache::Manager> mManager;
const CacheId mCacheId;
nsTArray<nsRefPtr<FetchPut>> mFetchPutList;
};
} // namespace cache
+7 -4
View File
@@ -20,17 +20,20 @@ namespace cache {
class CachePushStreamChild final : public PCachePushStreamChild
, public ActorChild
{
friend class CacheChild;
public:
CachePushStreamChild(Feature* aFeature, nsIAsyncInputStream* aStream);
~CachePushStreamChild();
void Start();
virtual void StartDestroy() override;
void Start();
private:
class Callback;
// This class must be constructed using CacheChild::CreatePushStream()
CachePushStreamChild(Feature* aFeature, nsIAsyncInputStream* aStream);
~CachePushStreamChild();
// PCachePushStreamChild methods
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
+81 -286
View File
@@ -41,24 +41,26 @@ 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_CLASS(mozilla::dom::cache::CacheStorage)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(mozilla::dom::cache::CacheStorage)
tmp->DisconnectFromActor();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal, mRequestPromises)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(mozilla::dom::cache::CacheStorage)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal, mRequestPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(mozilla::dom::cache::CacheStorage)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(mozilla::dom::cache::CacheStorage,
mGlobal);
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CacheStorage)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
NS_INTERFACE_MAP_END
// We cannot reference IPC types in a webidl binding implementation header. So
// define this in the .cpp and use heap storage in the mPendingRequests list.
struct CacheStorage::Entry final
{
nsRefPtr<Promise> mPromise;
CacheOpArgs mArgs;
// We cannot add the requests until after the actor is present. So store
// the request data separately for now.
nsRefPtr<InternalRequest> mRequest;
};
// static
already_AddRefed<CacheStorage>
CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
@@ -175,29 +177,31 @@ CacheStorage::Match(const RequestOrUSVString& aRequest,
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<InternalRequest> request = ToInternalRequest(aRequest, IgnoreBody,
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
if (!promise) {
return nullptr;
}
if (mFailedActor) {
promise->MaybeReject(NS_ERROR_UNEXPECTED);
return promise.forget();
}
CacheQueryParams params;
ToCacheQueryParams(params, aOptions);
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);
nsAutoPtr<Entry> entry(new Entry());
entry->mPromise = promise;
entry->mArgs = StorageMatchArgs(CacheRequest(), params);
entry->mRequest = request;
mPendingRequests.AppendElement(entry.forget());
MaybeRunPendingRequests();
return promise.forget();
@@ -208,23 +212,21 @@ CacheStorage::Has(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
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;
nsAutoPtr<Entry> entry(new Entry());
entry->mPromise = promise;
entry->mArgs = StorageHasArgs(nsString(aKey));
mPendingRequests.AppendElement(entry.forget());
MaybeRunPendingRequests();
return promise.forget();
@@ -235,23 +237,21 @@ CacheStorage::Open(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
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;
nsAutoPtr<Entry> entry(new Entry());
entry->mPromise = promise;
entry->mArgs = StorageOpenArgs(nsString(aKey));
mPendingRequests.AppendElement(entry.forget());
MaybeRunPendingRequests();
return promise.forget();
@@ -262,23 +262,21 @@ CacheStorage::Delete(const nsAString& aKey, ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
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;
nsAutoPtr<Entry> entry(new Entry());
entry->mPromise = promise;
entry->mArgs = StorageDeleteArgs(nsString(aKey));
mPendingRequests.AppendElement(entry.forget());
MaybeRunPendingRequests();
return promise.forget();
@@ -289,22 +287,21 @@ CacheStorage::Keys(ErrorResult& aRv)
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mFailedActor) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
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;
nsAutoPtr<Entry> entry(new Entry());
entry->mPromise = promise;
entry->mArgs = StorageKeysArgs();
mPendingRequests.AppendElement(entry.forget());
MaybeRunPendingRequests();
return promise.forget();
@@ -371,9 +368,8 @@ CacheStorage::ActorFailed()
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);
nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
entry->mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
}
mPendingRequests.Clear();
}
@@ -392,117 +388,6 @@ CacheStorage::DestroyInternal(CacheStorageChild* aActor)
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
{
@@ -524,32 +409,9 @@ CacheStorage::CreatePushStream(nsIAsyncInputStream* aStream)
MOZ_CRASH("CacheStorage should never create a push stream.");
}
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()
{
DisconnectFromActor();
}
void
CacheStorage::DisconnectFromActor()
{
NS_ASSERT_OWNINGTHREAD(CacheStorage);
if (mActor) {
mActor->StartDestroy();
// DestroyInternal() is called synchronously by StartDestroy(). So we
@@ -566,89 +428,22 @@ CacheStorage::MaybeRunPendingRequests()
}
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.");
ErrorResult rv;
nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
AutoChildOpArgs args(this, entry->mArgs);
if (entry->mRequest) {
args.Add(entry->mRequest, IgnoreBody, PassThroughReferrer,
IgnoreInvalidScheme, rv);
}
if (rv.Failed()) {
entry->mPromise->MaybeReject(rv);
continue;
}
mActor->ExecuteOp(mGlobal, entry->mPromise, args.SendAsOpArgs());
}
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
+4 -54
View File
@@ -8,7 +8,6 @@
#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"
@@ -38,15 +37,12 @@ namespace workers {
namespace cache {
class CacheChild;
class CacheStorageChild;
class Feature;
class PCacheResponseOrVoid;
class CacheStorage final : public nsIIPCBackgroundChildCreateCallback
, public nsWrapperCache
, public TypeUtils
, public PromiseNativeHandler
{
typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
@@ -81,16 +77,6 @@ public:
// 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 override;
#ifdef DEBUG
@@ -100,60 +86,24 @@ public:
virtual CachePushStreamChild*
CreatePushStream(nsIAsyncInputStream* aStream) override;
// PromiseNativeHandler methods
virtual void
ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
virtual void
RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
private:
CacheStorage(Namespace aNamespace, nsIGlobalObject* aGlobal,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo, Feature* aFeature);
~CacheStorage();
// Called when we're destroyed or CCed.
void DisconnectFromActor();
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;
// weak ref cleared in DestroyInternal
CacheStorageChild* mActor;
nsTArray<nsRefPtr<Promise>> mRequestPromises;
enum Op
{
OP_MATCH,
OP_HAS,
OP_OPEN,
OP_DELETE,
OP_KEYS
};
struct Entry;
nsTArray<nsAutoPtr<Entry>> mPendingRequests;
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:
+32 -77
View File
@@ -8,8 +8,8 @@
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheChild.h"
#include "mozilla/dom/cache/CacheOpChild.h"
#include "mozilla/dom/cache/CacheStorage.h"
#include "mozilla/dom/cache/StreamUtils.h"
namespace mozilla {
namespace dom {
@@ -24,6 +24,7 @@ DeallocPCacheStorageChild(PCacheStorageChild* aActor)
CacheStorageChild::CacheStorageChild(CacheStorage* aListener, Feature* aFeature)
: mListener(aListener)
, mNumChildActors(0)
{
MOZ_COUNT_CTOR(cache::CacheStorageChild);
MOZ_ASSERT(mListener);
@@ -46,6 +47,15 @@ CacheStorageChild::ClearListener()
mListener = nullptr;
}
void
CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs)
{
mNumChildActors += 1;
unused << SendPCacheOpConstructor(
new CacheOpChild(GetFeature(), aGlobal, aPromise), aArgs);
}
void
CacheStorageChild::StartDestroy()
{
@@ -65,6 +75,14 @@ CacheStorageChild::StartDestroy()
// CacheStorage listener should call ClearListener() in DestroyInternal()
MOZ_ASSERT(!mListener);
// If we have outstanding child actors, then don't destroy ourself yet.
// The child actors should be short lived and we should allow them to complete
// if possible. SendTeardown() will be called when the count drops to zero
// in NoteDeletedActor().
if (mNumChildActors) {
return;
}
// Start actor destruction from parent process
unused << SendTeardown();
}
@@ -83,92 +101,29 @@ CacheStorageChild::ActorDestroy(ActorDestroyReason aReason)
RemoveFeature();
}
bool
CacheStorageChild::RecvMatchResponse(const RequestId& aRequestId,
const nsresult& aRv,
const PCacheResponseOrVoid& aResponseOrVoid)
PCacheOpChild*
CacheStorageChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
AddFeatureToStreamChild(aResponseOrVoid, GetFeature());
nsRefPtr<CacheStorage> listener = mListener;
if (!listener) {
StartDestroyStreamChild(aResponseOrVoid);
return true;
}
listener->RecvMatchResponse(aRequestId, aRv, aResponseOrVoid);
return true;
MOZ_CRASH("CacheOpChild should be manually constructed.");
return nullptr;
}
bool
CacheStorageChild::RecvHasResponse(const RequestId& aRequestId,
const nsresult& aRv,
const bool& aSuccess)
CacheStorageChild::DeallocPCacheOpChild(PCacheOpChild* aActor)
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (listener) {
listener->RecvHasResponse(aRequestId, aRv, aSuccess);
}
delete aActor;
NoteDeletedActor();
return true;
}
bool
CacheStorageChild::RecvOpenResponse(const RequestId& aRequestId,
const nsresult& aRv,
PCacheChild* aActor)
void
CacheStorageChild::NoteDeletedActor()
{
NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
nsRefPtr<CacheStorage> listener = mListener;
if (!listener || FeatureNotified()) {
if (aActor) {
unused << aActor->SendTeardown();
}
return true;
MOZ_ASSERT(mNumChildActors);
mNumChildActors -= 1;
if (!mNumChildActors && !mListener) {
unused << SendTeardown();
}
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.
if (cacheChild) {
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
+21 -16
View File
@@ -11,10 +11,16 @@
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/PCacheStorageChild.h"
class nsIGlobalObject;
namespace mozilla {
namespace dom {
class Promise;
namespace cache {
class CacheOpArgs;
class CacheStorage;
class PCacheChild;
class Feature;
@@ -27,11 +33,15 @@ public:
~CacheStorageChild();
// Must be called by the associated CacheStorage listener in its
// ActorDestroy() method. Also, CacheStorage must Send__delete__() the
// ActorDestroy() method. Also, CacheStorage must call SendDestroy() on the
// actor in its destructor to trigger ActorDestroy() if it has not been
// called yet.
void ClearListener();
void
ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
const CacheOpArgs& aArgs);
// ActorChild methods
// Synchronously call ActorDestroy on our CacheStorage listener and then start
@@ -42,26 +52,21 @@ private:
// PCacheStorageChild methods
virtual void ActorDestroy(ActorDestroyReason aReason) override;
virtual bool RecvMatchResponse(const RequestId& aRequestId,
const nsresult& aRv,
const PCacheResponseOrVoid& response) override;
virtual bool RecvHasResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
const bool& aSuccess) override;
virtual bool RecvOpenResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
PCacheChild* aActor) override;
virtual bool RecvDeleteResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
const bool& aResult) override;
virtual bool RecvKeysResponse(const cache::RequestId& aRequestId,
const nsresult& aRv,
nsTArray<nsString>&& aKeys) override;
virtual PCacheOpChild*
AllocPCacheOpChild(const CacheOpArgs& aOpArgs) override;
virtual bool
DeallocPCacheOpChild(PCacheOpChild* aActor) override;
// utility methods
void
NoteDeletedActor();
// 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;
uint32_t mNumChildActors;
NS_DECL_OWNINGTHREAD
};
+47 -355
View File
@@ -6,28 +6,18 @@
#include "mozilla/dom/cache/CacheStorageParent.h"
#include "mozilla/unused.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/CacheOpParent.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
@@ -65,22 +55,60 @@ CacheStorageParent::~CacheStorageParent()
{
MOZ_COUNT_DTOR(cache::CacheStorageParent);
MOZ_ASSERT(!mVerifier);
MOZ_ASSERT(!mManager);
}
void
CacheStorageParent::ActorDestroy(ActorDestroyReason aReason)
{
if (mVerifier) {
mVerifier->ClearListener();
mVerifier->RemoveListener(this);
mVerifier = nullptr;
}
}
if (mManager) {
MOZ_ASSERT(!mActiveRequests.IsEmpty());
mManager->RemoveListener(this);
mManager = nullptr;
PCacheOpParent*
CacheStorageParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
{
if (aOpArgs.type() != CacheOpArgs::TStorageMatchArgs &&
aOpArgs.type() != CacheOpArgs::TStorageHasArgs &&
aOpArgs.type() != CacheOpArgs::TStorageOpenArgs &&
aOpArgs.type() != CacheOpArgs::TStorageDeleteArgs &&
aOpArgs.type() != CacheOpArgs::TStorageKeysArgs)
{
MOZ_CRASH("Invalid operation sent to CacheStorage actor!");
}
return new CacheOpParent(Manager(), mNamespace, aOpArgs);
}
bool
CacheStorageParent::DeallocPCacheOpParent(PCacheOpParent* aActor)
{
delete aActor;
return true;
}
bool
CacheStorageParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
const CacheOpArgs& aOpArgs)
{
auto actor = static_cast<CacheOpParent*>(aActor);
if (mVerifier) {
MOZ_ASSERT(!mManagerId);
actor->WaitForVerification(mVerifier);
return true;
}
if (NS_FAILED(mVerifiedStatus)) {
unused << CacheOpParent::Send__delete__(actor, ErrorResult(mVerifiedStatus),
void_t());
return true;
}
MOZ_ASSERT(mManagerId);
actor->Execute(mManagerId);
return true;
}
bool
@@ -93,190 +121,11 @@ CacheStorageParent::RecvTeardown()
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))) {
@@ -284,165 +133,8 @@ CacheStorageParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
}
mManagerId = aManagerId;
mVerifier->ClearListener();
mVerifier->RemoveListener(this);
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
+15 -63
View File
@@ -7,24 +7,18 @@
#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 final : public PCacheStorageParent
, public PrincipalVerifier::Listener
, public Manager::Listener
{
public:
CacheStorageParent(PBackgroundParent* aManagingActor, Namespace aNamespace,
@@ -33,72 +27,30 @@ public:
private:
// PCacheStorageParent methods
virtual void ActorDestroy(ActorDestroyReason aReason) override;
virtual bool RecvTeardown() override;
virtual bool RecvMatch(const RequestId& aRequestId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams) override;
virtual bool RecvHas(const RequestId& aRequestId,
const nsString& aKey) override;
virtual bool RecvOpen(const RequestId& aRequestId,
const nsString& aKey) override;
virtual bool RecvDelete(const RequestId& aRequestId,
const nsString& aKey) override;
virtual bool RecvKeys(const RequestId& aRequestId) override;
virtual void
ActorDestroy(ActorDestroyReason aReason) override;
virtual PCacheOpParent*
AllocPCacheOpParent(const CacheOpArgs& aOpArgs) override;
virtual bool
DeallocPCacheOpParent(PCacheOpParent* aActor) override;
virtual bool
RecvPCacheOpConstructor(PCacheOpParent* actor,
const CacheOpArgs& aOpArgs) override;
virtual bool
RecvTeardown() override;
// PrincipalVerifier::Listener methods
virtual void OnPrincipalVerified(nsresult aRv,
ManagerId* aManagerId) override;
// Manager::Listener methods
virtual void OnStorageMatch(RequestId aRequestId, nsresult aRv,
const SavedResponse* aResponse,
StreamList* aStreamList) override;
virtual void OnStorageHas(RequestId aRequestId, nsresult aRv,
bool aCacheFound) override;
virtual void OnStorageOpen(RequestId aRequestId, nsresult aRv,
CacheId aCacheId) override;
virtual void OnStorageDelete(RequestId aRequestId, nsresult aRv,
bool aCacheDeleted) override;
virtual void OnStorageKeys(RequestId aRequestId, nsresult aRv,
const nsTArray<nsString>& aKeys) 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
+29 -4
View File
@@ -9,7 +9,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/PBackgroundChild.h"
@@ -41,6 +41,7 @@ DeallocPCacheStreamControlChild(PCacheStreamControlChild* aActor)
CacheStreamControlChild::CacheStreamControlChild()
: mDestroyStarted(false)
, mDestroyDelayed(false)
{
MOZ_COUNT_CTOR(cache::CacheStreamControlChild);
}
@@ -63,13 +64,28 @@ CacheStreamControlChild::StartDestroy()
}
mDestroyStarted = true;
// If any of the streams have started to be read, then wait for them to close
// naturally.
if (HasEverBeenRead()) {
// Note that we are delaying so that we can re-check for active streams
// in NoteClosedAfterForget().
mDestroyDelayed = true;
return;
}
// Otherwise, if the streams have not been touched then just pre-emptively
// close them now. This handles the case where someone retrieves a Response
// from the Cache, but never accesses the body. We should not keep the
// Worker alive until that Response is GC'd just because of its ignored
// body stream.
// 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::SerializeControl(PCacheReadStream* aReadStreamOut)
CacheStreamControlChild::SerializeControl(CacheReadStream* aReadStreamOut)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
aReadStreamOut->controlParent() = nullptr;
@@ -77,7 +93,7 @@ CacheStreamControlChild::SerializeControl(PCacheReadStream* aReadStreamOut)
}
void
CacheStreamControlChild::SerializeFds(PCacheReadStream* aReadStreamOut,
CacheStreamControlChild::SerializeFds(CacheReadStream* aReadStreamOut,
const nsTArray<FileDescriptor>& aFds)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
@@ -97,7 +113,7 @@ CacheStreamControlChild::SerializeFds(PCacheReadStream* aReadStreamOut,
}
void
CacheStreamControlChild::DeserializeFds(const PCacheReadStream& aReadStream,
CacheStreamControlChild::DeserializeFds(const CacheReadStream& aReadStream,
nsTArray<FileDescriptor>& aFdsOut)
{
if (aReadStream.fds().type() !=
@@ -120,6 +136,15 @@ CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
unused << SendNoteClosed(aId);
// A stream has closed. If we delayed StartDestry() due to this stream
// being read, then we should check to see if any of the remaining streams
// are active. If none of our other streams have been read, then we can
// proceed with the shutdown now.
if (mDestroyDelayed && !HasEverBeenRead()) {
mDestroyDelayed = false;
RecvCloseAll();
}
}
#ifdef DEBUG
+4 -3
View File
@@ -31,14 +31,14 @@ public:
// StreamControl methods
virtual void
SerializeControl(PCacheReadStream* aReadStreamOut) override;
SerializeControl(CacheReadStream* aReadStreamOut) override;
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
SerializeFds(CacheReadStream* aReadStreamOut,
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
virtual void
DeserializeFds(const PCacheReadStream& aReadStream,
DeserializeFds(const CacheReadStream& aReadStream,
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
private:
@@ -56,6 +56,7 @@ private:
virtual bool RecvCloseAll() override;
bool mDestroyStarted;
bool mDestroyDelayed;
NS_DECL_OWNINGTHREAD
};
+4 -4
View File
@@ -8,7 +8,7 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/unused.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/StreamList.h"
#include "mozilla/ipc/FileDescriptorSetParent.h"
@@ -45,7 +45,7 @@ CacheStreamControlParent::~CacheStreamControlParent()
}
void
CacheStreamControlParent::SerializeControl(PCacheReadStream* aReadStreamOut)
CacheStreamControlParent::SerializeControl(CacheReadStream* aReadStreamOut)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
aReadStreamOut->controlChild() = nullptr;
@@ -53,7 +53,7 @@ CacheStreamControlParent::SerializeControl(PCacheReadStream* aReadStreamOut)
}
void
CacheStreamControlParent::SerializeFds(PCacheReadStream* aReadStreamOut,
CacheStreamControlParent::SerializeFds(CacheReadStream* aReadStreamOut,
const nsTArray<FileDescriptor>& aFds)
{
NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
@@ -73,7 +73,7 @@ CacheStreamControlParent::SerializeFds(PCacheReadStream* aReadStreamOut,
}
void
CacheStreamControlParent::DeserializeFds(const PCacheReadStream& aReadStream,
CacheStreamControlParent::DeserializeFds(const CacheReadStream& aReadStream,
nsTArray<FileDescriptor>& aFdsOut)
{
if (aReadStream.fds().type() !=
+3 -3
View File
@@ -32,14 +32,14 @@ public:
// StreamControl methods
virtual void
SerializeControl(PCacheReadStream* aReadStreamOut) override;
SerializeControl(CacheReadStream* aReadStreamOut) override;
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
SerializeFds(CacheReadStream* aReadStreamOut,
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) override;
virtual void
DeserializeFds(const PCacheReadStream& aReadStream,
DeserializeFds(const CacheReadStream& aReadStream,
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) override;
private:
+245
View File
@@ -0,0 +1,245 @@
/* 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 PCache;
include protocol PCachePushStream;
include protocol PCacheStreamControl;
include InputStreamParams;
using HeadersGuardEnum from "mozilla/dom/cache/IPCUtils.h";
using RequestCredentials from "mozilla/dom/cache/IPCUtils.h";
using RequestMode from "mozilla/dom/cache/IPCUtils.h";
using RequestCache from "mozilla/dom/cache/IPCUtils.h";
using RequestContext from "mozilla/dom/cache/IPCUtils.h";
using ResponseType from "mozilla/dom/cache/IPCUtils.h";
using mozilla::void_t from "ipc/IPCMessageUtils.h";
using struct nsID from "nsID.h";
namespace mozilla {
namespace dom {
namespace cache {
struct CacheQueryParams
{
bool ignoreSearch;
bool ignoreMethod;
bool ignoreVary;
bool prefixMatch;
bool cacheNameSet;
nsString cacheName;
};
struct CacheReadStream
{
nsID id;
OptionalInputStreamParams params;
OptionalFileDescriptorSet fds;
nullable PCacheStreamControl control;
nullable PCachePushStream pushStream;
};
union CacheReadStreamOrVoid
{
void_t;
CacheReadStream;
};
struct HeadersEntry
{
nsCString name;
nsCString value;
};
struct CacheRequest
{
nsCString method;
nsString url;
nsString urlWithoutQuery;
HeadersEntry[] headers;
HeadersGuardEnum headersGuard;
nsString referrer;
RequestMode mode;
RequestCredentials credentials;
CacheReadStreamOrVoid body;
uint32_t contentPolicyType;
RequestContext context;
RequestCache requestCache;
};
union CacheRequestOrVoid
{
void_t;
CacheRequest;
};
struct CacheResponse
{
ResponseType type;
nsString url;
uint32_t status;
nsCString statusText;
HeadersEntry[] headers;
HeadersGuardEnum headersGuard;
CacheReadStreamOrVoid body;
nsCString securityInfo;
};
union CacheResponseOrVoid
{
void_t;
CacheResponse;
};
struct CacheRequestResponse
{
CacheRequest request;
CacheResponse response;
};
struct CacheMatchArgs
{
CacheRequest request;
CacheQueryParams params;
};
struct CacheMatchAllArgs
{
CacheRequestOrVoid requestOrVoid;
CacheQueryParams params;
};
struct CacheAddAllArgs
{
CacheRequest[] requestList;
};
struct CachePutAllArgs
{
CacheRequestResponse[] requestResponseList;
};
struct CacheDeleteArgs
{
CacheRequest request;
CacheQueryParams params;
};
struct CacheKeysArgs
{
CacheRequestOrVoid requestOrVoid;
CacheQueryParams params;
};
struct StorageMatchArgs
{
CacheRequest request;
CacheQueryParams params;
};
struct StorageHasArgs
{
nsString key;
};
struct StorageOpenArgs
{
nsString key;
};
struct StorageDeleteArgs
{
nsString key;
};
struct StorageKeysArgs
{
};
union CacheOpArgs
{
CacheMatchArgs;
CacheMatchAllArgs;
CacheAddAllArgs;
CachePutAllArgs;
CacheDeleteArgs;
CacheKeysArgs;
StorageMatchArgs;
StorageHasArgs;
StorageOpenArgs;
StorageDeleteArgs;
StorageKeysArgs;
};
struct CacheMatchResult
{
CacheResponseOrVoid responseOrVoid;
};
struct CacheMatchAllResult
{
CacheResponse[] responseList;
};
struct CacheAddAllResult
{
};
struct CachePutAllResult
{
};
struct CacheDeleteResult
{
bool success;
};
struct CacheKeysResult
{
CacheRequest[] requestList;
};
struct StorageMatchResult
{
CacheResponseOrVoid responseOrVoid;
};
struct StorageHasResult
{
bool success;
};
struct StorageOpenResult
{
nullable PCache actor;
};
struct StorageDeleteResult
{
bool success;
};
struct StorageKeysResult
{
nsString[] keyList;
};
union CacheOpResult
{
void_t;
CacheMatchResult;
CacheMatchAllResult;
CacheAddAllResult;
CachePutAllResult;
CacheDeleteResult;
CacheKeysResult;
StorageMatchResult;
StorageHasResult;
StorageOpenResult;
StorageDeleteResult;
StorageKeysResult;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
+9 -1
View File
@@ -732,6 +732,7 @@ Context::Dispatch(nsIEventTarget* aTarget, Action* aAction)
MOZ_ASSERT(aTarget);
MOZ_ASSERT(aAction);
MOZ_ASSERT(mState != STATE_CONTEXT_CANCELED);
if (mState == STATE_CONTEXT_CANCELED) {
return;
} else if (mState == STATE_CONTEXT_INIT) {
@@ -766,11 +767,18 @@ Context::CancelAll()
AllowToClose();
}
bool
Context::IsCanceled() const
{
NS_ASSERT_OWNINGTHREAD(Context);
return mState == STATE_CONTEXT_CANCELED;
}
void
Context::Invalidate()
{
NS_ASSERT_OWNINGTHREAD(Context);
mManager->Invalidate();
mManager->NoteClosing();
CancelAll();
}
+3
View File
@@ -126,6 +126,9 @@ public:
// Only callable from the thread that created the Context.
void CancelAll();
// True if CancelAll() has been called.
bool IsCanceled() const;
// Like CancelAll(), but also marks the Manager as "invalid".
void Invalidate();
+4 -3
View File
@@ -14,6 +14,7 @@
#include "nsIFile.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "DBSchema.h"
#include "FileUtils.h"
@@ -146,7 +147,7 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
int32_t schemaVersion = 0;
rv = conn->GetSchemaVersion(&schemaVersion);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
if (schemaVersion > 0 && schemaVersion < DBSchema::kMaxWipeSchemaVersion) {
if (schemaVersion > 0 && schemaVersion < db::kMaxWipeSchemaVersion) {
conn = nullptr;
rv = WipeDatabase(dbFile, aDBDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -154,7 +155,7 @@ DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
rv = ss->OpenDatabaseWithFileURL(dbFileUrl, getter_AddRefs(conn));
}
rv = DBSchema::InitializeConnection(conn);
rv = db::InitializeConnection(conn);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
conn.forget(aConnOut);
@@ -169,7 +170,7 @@ DBAction::WipeDatabase(nsIFile* aDBFile, nsIFile* aDBDir)
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
// Delete the morgue as well.
rv = FileUtils::BodyDeleteDir(aDBDir);
rv = BodyDeleteDir(aDBDir);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
return rv;
-1
View File
@@ -8,7 +8,6 @@
#define mozilla_dom_cache_DBAction_h
#include "mozilla/dom/cache/Action.h"
#include "mozilla/dom/cache/CacheInitData.h"
#include "mozilla/nsRefPtr.h"
#include "nsString.h"
+143 -117
View File
@@ -8,8 +8,10 @@
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/SavedTypes.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "nsCOMPtr.h"
@@ -19,16 +21,21 @@
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#include "Types.h"
#include "nsIContentPolicy.h"
namespace mozilla {
namespace dom {
namespace cache {
namespace db {
const int32_t DBSchema::kMaxWipeSchemaVersion = 6;
const int32_t DBSchema::kLatestSchemaVersion = 6;
const int32_t DBSchema::kMaxEntriesPerStatement = 255;
const int32_t kMaxWipeSchemaVersion = 6;
namespace {
const int32_t kLatestSchemaVersion = 6;
const int32_t kMaxEntriesPerStatement = 255;
} // anonymous namespace
// If any of the static_asserts below fail, it means that you have changed
// the corresponding WebIDL enum in a way that may be incompatible with the
@@ -140,11 +147,48 @@ static_assert(nsIContentPolicy::TYPE_INVALID == 0 &&
nsIContentPolicy::TYPE_IMAGESET == 21,
"nsContentPolicytType values are as expected");
using mozilla::void_t;
namespace {
typedef int32_t EntryId;
static nsresult QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<EntryId>& aEntryIdListOut);
static nsresult QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
nsTArray<EntryId>& aEntryIdListOut,
uint32_t aMaxResults = UINT32_MAX);
static nsresult MatchByVaryHeader(mozIStorageConnection* aConn,
const CacheRequest& 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 CacheRequest& aRequest,
const nsID* aRequestBodyId,
const CacheResponse& 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);
} // anonymous namespace
// static
nsresult
DBSchema::CreateSchema(mozIStorageConnection* aConn)
CreateSchema(mozIStorageConnection* aConn)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -284,9 +328,8 @@ DBSchema::CreateSchema(mozIStorageConnection* aConn)
return rv;
}
// static
nsresult
DBSchema::InitializeConnection(mozIStorageConnection* aConn)
InitializeConnection(mozIStorageConnection* aConn)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -315,9 +358,8 @@ DBSchema::InitializeConnection(mozIStorageConnection* aConn)
return NS_OK;
}
// static
nsresult
DBSchema::CreateCache(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -345,10 +387,9 @@ DBSchema::CreateCache(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
return rv;
}
// static
nsresult
DBSchema::DeleteCache(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<nsID>& aDeletedBodyIdListOut)
DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<nsID>& aDeletedBodyIdListOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -379,10 +420,9 @@ DBSchema::DeleteCache(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::IsCacheOrphaned(mozIStorageConnection* aConn,
CacheId aCacheId, bool* aOrphanedOut)
IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
bool* aOrphanedOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -414,13 +454,12 @@ DBSchema::IsCacheOrphaned(mozIStorageConnection* aConn,
return rv;
}
// static
nsresult
DBSchema::CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut)
CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -446,19 +485,18 @@ DBSchema::CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams,
nsTArray<SavedResponse>& aSavedResponsesOut)
CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequestOrVoid& aRequestOrVoid,
const CacheQueryParams& aParams,
nsTArray<SavedResponse>& aSavedResponsesOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
nsresult rv;
nsAutoTArray<EntryId, 256> matches;
if (aRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t) {
if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
rv = QueryAll(aConn, aCacheId, matches);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
} else {
@@ -478,19 +516,18 @@ DBSchema::CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const nsID* aRequestBodyId,
const PCacheResponse& aResponse,
const nsID* aResponseBodyId,
nsTArray<nsID>& aDeletedBodyIdListOut)
CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const nsID* aRequestBodyId,
const CacheResponse& aResponse,
const nsID* aResponseBodyId,
nsTArray<nsID>& aDeletedBodyIdListOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
PCacheQueryParams params(false, false, false, false, false,
CacheQueryParams params(false, false, false, false, false,
NS_LITERAL_STRING(""));
nsAutoTArray<EntryId, 256> matches;
nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
@@ -506,12 +543,11 @@ DBSchema::CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -535,19 +571,18 @@ DBSchema::CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequestOrVoid& aRequestOrVoid,
const PCacheQueryParams& aParams,
nsTArray<SavedRequest>& aSavedRequestsOut)
CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequestOrVoid& aRequestOrVoid,
const CacheQueryParams& aParams,
nsTArray<SavedRequest>& aSavedRequestsOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
nsresult rv;
nsAutoTArray<EntryId, 256> matches;
if (aRequestOrVoid.type() == PCacheRequestOrVoid::Tvoid_t) {
if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
rv = QueryAll(aConn, aCacheId, matches);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
} else {
@@ -567,14 +602,13 @@ DBSchema::CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::StorageMatch(mozIStorageConnection* aConn,
Namespace aNamespace,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut)
StorageMatch(mozIStorageConnection* aConn,
Namespace aNamespace,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -639,11 +673,10 @@ DBSchema::StorageMatch(mozIStorageConnection* aConn,
return NS_OK;
}
// static
nsresult
DBSchema::StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, bool* aFoundCacheOut,
CacheId* aCacheIdOut)
StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, bool* aFoundCacheOut,
CacheId* aCacheIdOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -679,10 +712,9 @@ DBSchema::StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
return rv;
}
// static
nsresult
DBSchema::StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, CacheId aCacheId)
StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, CacheId aCacheId)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -708,10 +740,9 @@ DBSchema::StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
return rv;
}
// static
nsresult
DBSchema::StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey)
StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -734,10 +765,9 @@ DBSchema::StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
return rv;
}
// static
nsresult
DBSchema::StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
nsTArray<nsString>& aKeysOut)
StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
nsTArray<nsString>& aKeysOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -762,10 +792,11 @@ DBSchema::StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
return rv;
}
// static
namespace {
nsresult
DBSchema::QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<EntryId>& aEntryIdListOut)
QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<EntryId>& aEntryIdListOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -790,13 +821,12 @@ DBSchema::QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const PCacheQueryParams& aParams,
nsTArray<EntryId>& aEntryIdListOut,
uint32_t aMaxResults)
QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
nsTArray<EntryId>& aEntryIdListOut,
uint32_t aMaxResults)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -882,11 +912,10 @@ DBSchema::QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::MatchByVaryHeader(mozIStorageConnection* aConn,
const PCacheRequest& aRequest,
EntryId entryId, bool* aSuccessOut)
MatchByVaryHeader(mozIStorageConnection* aConn,
const CacheRequest& aRequest,
EntryId entryId, bool* aSuccessOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -927,7 +956,8 @@ DBSchema::MatchByVaryHeader(mozIStorageConnection* aConn,
rv = state->BindInt32Parameter(0, entryId);
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsRefPtr<InternalHeaders> cachedHeaders = new InternalHeaders(HeadersGuardEnum::None);
nsRefPtr<InternalHeaders> cachedHeaders =
new InternalHeaders(HeadersGuardEnum::None);
while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
nsAutoCString name;
@@ -944,7 +974,8 @@ DBSchema::MatchByVaryHeader(mozIStorageConnection* aConn,
}
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
nsRefPtr<InternalHeaders> queryHeaders = new InternalHeaders(aRequest.headers());
nsRefPtr<InternalHeaders> queryHeaders =
TypeUtils::ToInternalHeaders(aRequest.headers());
// Assume the vary headers match until we find a conflict
bool varyHeadersMatch = true;
@@ -993,12 +1024,11 @@ DBSchema::MatchByVaryHeader(mozIStorageConnection* aConn,
return rv;
}
// static
nsresult
DBSchema::DeleteEntries(mozIStorageConnection* aConn,
const nsTArray<EntryId>& aEntryIdList,
nsTArray<nsID>& aDeletedBodyIdListOut,
uint32_t aPos, int32_t aLen)
DeleteEntries(mozIStorageConnection* aConn,
const nsTArray<EntryId>& aEntryIdList,
nsTArray<nsID>& aDeletedBodyIdListOut,
uint32_t aPos, int32_t aLen)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -1082,13 +1112,12 @@ DBSchema::DeleteEntries(mozIStorageConnection* aConn,
return rv;
}
// static
nsresult
DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
const PCacheRequest& aRequest,
const nsID* aRequestBodyId,
const PCacheResponse& aResponse,
const nsID* aResponseBodyId)
InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const nsID* aRequestBodyId,
const CacheResponse& aResponse,
const nsID* aResponseBodyId)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -1209,7 +1238,7 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
), getter_AddRefs(state));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
const nsTArray<PHeadersEntry>& requestHeaders = aRequest.headers();
const nsTArray<HeadersEntry>& requestHeaders = aRequest.headers();
for (uint32_t i = 0; i < requestHeaders.Length(); ++i) {
rv = state->BindUTF8StringParameter(0, requestHeaders[i].name());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1233,7 +1262,7 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
), getter_AddRefs(state));
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
const nsTArray<PHeadersEntry>& responseHeaders = aResponse.headers();
const nsTArray<HeadersEntry>& responseHeaders = aResponse.headers();
for (uint32_t i = 0; i < responseHeaders.Length(); ++i) {
rv = state->BindUTF8StringParameter(0, responseHeaders[i].name());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1251,10 +1280,9 @@ DBSchema::InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
return rv;
}
// static
nsresult
DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
SavedResponse* aSavedResponseOut)
ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
SavedResponse* aSavedResponseOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -1334,7 +1362,7 @@ DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
PHeadersEntry header;
HeadersEntry header;
rv = state->GetUTF8String(0, header.name());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1348,10 +1376,9 @@ DBSchema::ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
return rv;
}
// static
nsresult
DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
SavedRequest* aSavedRequestOut)
ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
SavedRequest* aSavedRequestOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aConn);
@@ -1453,7 +1480,7 @@ DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
while (NS_SUCCEEDED(state->ExecuteStep(&hasMoreData)) && hasMoreData) {
PHeadersEntry header;
HeadersEntry header;
rv = state->GetUTF8String(0, header.name());
if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1467,11 +1494,10 @@ DBSchema::ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
return rv;
}
// static
void
DBSchema::AppendListParamsToQuery(nsACString& aQuery,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen)
AppendListParamsToQuery(nsACString& aQuery,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
@@ -1484,11 +1510,10 @@ DBSchema::AppendListParamsToQuery(nsACString& aQuery,
}
}
// static
nsresult
DBSchema::BindListParamsToQuery(mozIStorageStatement* aState,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen)
BindListParamsToQuery(mozIStorageStatement* aState,
const nsTArray<EntryId>& aEntryIdList,
uint32_t aPos, int32_t aLen)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
@@ -1499,9 +1524,8 @@ DBSchema::BindListParamsToQuery(mozIStorageStatement* aState,
return NS_OK;
}
// static
nsresult
DBSchema::BindId(mozIStorageStatement* aState, uint32_t aPos, const nsID* aId)
BindId(mozIStorageStatement* aState, uint32_t aPos, const nsID* aId)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aState);
@@ -1521,9 +1545,8 @@ DBSchema::BindId(mozIStorageStatement* aState, uint32_t aPos, const nsID* aId)
return rv;
}
// static
nsresult
DBSchema::ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
{
MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(aState);
@@ -1539,6 +1562,9 @@ DBSchema::ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
return rv;
}
} // anonymouns namespace
} // namespace db
} // namespace cache
} // namespace dom
} // namespace mozilla
+74 -103
View File
@@ -14,129 +14,100 @@
#include "nsTArrayForwardDeclare.h"
class mozIStorageConnection;
class mozIStorageStatement;
struct nsID;
namespace mozilla {
namespace dom {
namespace cache {
class PCacheQueryParams;
class PCacheRequest;
class PCacheRequestOrVoid;
class PCacheResponse;
class CacheQueryParams;
class CacheRequest;
class CacheRequestOrVoid;
class CacheResponse;
struct SavedRequest;
struct SavedResponse;
// TODO: remove static class and use functions in cache namespace (bug 1110485)
class DBSchema final
{
public:
static nsresult CreateSchema(mozIStorageConnection* aConn);
static nsresult InitializeConnection(mozIStorageConnection* aConn);
namespace db {
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);
nsresult
CreateSchema(mozIStorageConnection* aConn);
// TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
static nsresult IsCacheOrphaned(mozIStorageConnection* aConn,
CacheId aCacheId, bool* aOrphanedOut);
nsresult
InitializeConnection(mozIStorageConnection* aConn);
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);
nsresult
CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut);
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);
nsresult
DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
nsTArray<nsID>& aDeletedBodyIdListOut);
// We will wipe out databases with a schema versions less than this.
static const int32_t kMaxWipeSchemaVersion;
// TODO: Consider removing unused IsCacheOrphaned after writing cleanup code. (bug 1110446)
nsresult
IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
bool* aOrphanedOut);
private:
typedef int32_t EntryId;
nsresult
CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest, const CacheQueryParams& aParams,
bool* aFoundResponseOut, SavedResponse* aSavedResponseOut);
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);
nsresult
CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequestOrVoid& aRequestOrVoid,
const CacheQueryParams& aParams,
nsTArray<SavedResponse>& aSavedResponsesOut);
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);
nsresult
CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const nsID* aRequestBodyId,
const CacheResponse& aResponse,
const nsID* aResponseBodyId,
nsTArray<nsID>& aDeletedBodyIdListOut);
DBSchema() = delete;
~DBSchema() = delete;
nsresult
CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
nsTArray<nsID>& aDeletedBodyIdListOut,
bool* aSuccessOut);
static const int32_t kLatestSchemaVersion;
static const int32_t kMaxEntriesPerStatement;
};
nsresult
CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
const CacheRequestOrVoid& aRequestOrVoid,
const CacheQueryParams& aParams,
nsTArray<SavedRequest>& aSavedRequestsOut);
nsresult
StorageMatch(mozIStorageConnection* aConn,
Namespace aNamespace,
const CacheRequest& aRequest,
const CacheQueryParams& aParams,
bool* aFoundResponseOut,
SavedResponse* aSavedResponseOut);
nsresult
StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, bool* aFoundCacheOut,
CacheId* aCacheIdOut);
nsresult
StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey, CacheId aCacheId);
nsresult
StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
const nsAString& aKey);
nsresult
StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
nsTArray<nsString>& aKeysOut);
// We will wipe out databases with a schema versions less than this.
extern const int32_t kMaxWipeSchemaVersion;
} // namespace db
} // namespace cache
} // namespace dom
} // namespace mozilla
+35 -35
View File
@@ -16,7 +16,6 @@
#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"
@@ -98,9 +97,8 @@ private:
// static
nsresult
FetchPut::Create(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
FetchPut::Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
FetchPut** aFetchPutOut)
{
@@ -115,7 +113,7 @@ FetchPut::Create(Listener* aListener, Manager* aManager,
}
#endif
nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aRequestId, aCacheId,
nsRefPtr<FetchPut> ref = new FetchPut(aListener, aManager, aCacheId,
aRequests, aRequestStreams);
nsresult rv = ref->DispatchToMainThread();
@@ -133,13 +131,11 @@ FetchPut::ClearListener()
mListener = nullptr;
}
FetchPut::FetchPut(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
FetchPut::FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams)
: mListener(aListener)
, mManager(aManager)
, mRequestId(aRequestId)
, mCacheId(aCacheId)
, mInitiatingThread(NS_GetCurrentThread())
, mStateList(aRequests.Length())
@@ -151,7 +147,7 @@ FetchPut::FetchPut(Listener* aListener, Manager* aManager,
for (uint32_t i = 0; i < aRequests.Length(); ++i) {
State* s = mStateList.AppendElement();
s->mPCacheRequest = aRequests[i];
s->mCacheRequest = aRequests[i];
s->mRequestStream = aRequestStreams[i];
}
@@ -211,14 +207,14 @@ FetchPut::DoFetchOnMainThread()
nsCOMPtr<nsILoadGroup> loadGroup;
nsresult rv = NS_NewLoadGroup(getter_AddRefs(loadGroup), principal);
if (NS_WARN_IF(NS_FAILED(rv))) {
MaybeSetError(rv);
MaybeSetError(ErrorResult(rv));
MaybeCompleteOnMainThread();
return;
}
for (uint32_t i = 0; i < mStateList.Length(); ++i) {
nsRefPtr<InternalRequest> internalRequest =
ToInternalRequest(mStateList[i].mPCacheRequest);
ToInternalRequest(mStateList[i].mCacheRequest);
// If there is a stream we must clone it so that its still available
// to store in the cache later;
@@ -240,7 +236,7 @@ FetchPut::DoFetchOnMainThread()
mStateList[i].mFetchObserver = new FetchObserver(this);
rv = fetchDriver->Fetch(mStateList[i].mFetchObserver);
if (NS_WARN_IF(NS_FAILED(rv))) {
MaybeSetError(rv);
MaybeSetError(ErrorResult(rv));
mStateList[i].mFetchObserver = nullptr;
mPendingCount -= 1;
continue;
@@ -258,16 +254,16 @@ FetchPut::FetchComplete(FetchObserver* aObserver,
MOZ_ASSERT(NS_IsMainThread());
if (aInternalResponse->IsError() && !mResult.Failed()) {
MaybeSetError(NS_ERROR_FAILURE);
MaybeSetError(ErrorResult(NS_ERROR_FAILURE));
}
for (uint32_t i = 0; i < mStateList.Length(); ++i) {
if (mStateList[i].mFetchObserver == aObserver) {
ErrorResult rv;
ToPCacheResponseWithoutBody(mStateList[i].mPCacheResponse,
ToCacheResponseWithoutBody(mStateList[i].mCacheResponse,
*aInternalResponse, rv);
if (rv.Failed()) {
mResult = Move(rv);
MaybeSetError(Move(rv));
} else {
aInternalResponse->GetBody(getter_AddRefs(mStateList[i].mResponseStream));
}
@@ -316,27 +312,27 @@ FetchPut::DoPutOnWorkerThread()
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);
if (MatchInPutList(mStateList[i].mCacheRequest, putList)) {
MaybeSetError(ErrorResult(NS_ERROR_DOM_INVALID_STATE_ERR));
MaybeNotifyListener();
return;
}
CacheRequestResponse* entry = putList.AppendElement();
entry->request() = mStateList[i].mPCacheRequest;
entry->response() = mStateList[i].mPCacheResponse;
entry->request() = mStateList[i].mCacheRequest;
entry->response() = mStateList[i].mCacheResponse;
requestStreamList.AppendElement(mStateList[i].mRequestStream.forget());
responseStreamList.AppendElement(mStateList[i].mResponseStream.forget());
}
mStateList.Clear();
mManager->CachePutAll(this, mRequestId, mCacheId, putList, requestStreamList,
responseStreamList);
mManager->ExecutePutAll(this, mCacheId, putList, requestStreamList,
responseStreamList);
}
// static
bool
FetchPut::MatchInPutList(const PCacheRequest& aRequest,
FetchPut::MatchInPutList(const CacheRequest& aRequest,
const nsTArray<CacheRequestResponse>& aPutList)
{
// This method implements the SW spec QueryCache algorithm against an
@@ -351,11 +347,11 @@ FetchPut::MatchInPutList(const PCacheRequest& aRequest,
}
nsRefPtr<InternalHeaders> requestHeaders =
new InternalHeaders(aRequest.headers());
ToInternalHeaders(aRequest.headers());
for (uint32_t i = 0; i < aPutList.Length(); ++i) {
const PCacheRequest& cachedRequest = aPutList[i].request();
const PCacheResponse& cachedResponse = aPutList[i].response();
const CacheRequest& cachedRequest = aPutList[i].request();
const CacheResponse& cachedResponse = aPutList[i].response();
// If the URLs don't match, then just skip to the next entry.
if (aRequest.url() != cachedRequest.url()) {
@@ -363,10 +359,10 @@ FetchPut::MatchInPutList(const PCacheRequest& aRequest,
}
nsRefPtr<InternalHeaders> cachedRequestHeaders =
new InternalHeaders(cachedRequest.headers());
ToInternalHeaders(cachedRequest.headers());
nsRefPtr<InternalHeaders> cachedResponseHeaders =
new InternalHeaders(cachedResponse.headers());
ToInternalHeaders(cachedResponse.headers());
nsAutoTArray<nsCString, 16> varyHeaders;
ErrorResult rv;
@@ -426,20 +422,25 @@ FetchPut::MatchInPutList(const PCacheRequest& aRequest,
}
void
FetchPut::OnCachePutAll(RequestId aRequestId, nsresult aRv)
FetchPut::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList)
{
MOZ_ASSERT(mInitiatingThread == NS_GetCurrentThread());
MaybeSetError(aRv);
MOZ_ASSERT(aResult.type() == CacheOpResult::TCachePutAllResult);
MaybeSetError(Move(aRv));
MaybeNotifyListener();
}
void
FetchPut::MaybeSetError(nsresult aRv)
FetchPut::MaybeSetError(ErrorResult&& aRv)
{
if (mResult.Failed() || NS_SUCCEEDED(aRv)) {
if (mResult.Failed() || !aRv.Failed()) {
return;
}
mResult.Throw(aRv);
mResult = Move(aRv);
}
void
@@ -453,8 +454,7 @@ FetchPut::MaybeNotifyListener()
// object is removed from CacheParent::mFetchPutList, so make sure that
// doesn't happen until this method returns.
nsRefPtr<FetchPut> kungFuDeathGrip(this);
mListener->OnFetchPut(this, mRequestId, mResult);
mResult.ClearMessage(); // This may contain a TypeError.
mListener->OnFetchPut(this, Move(mResult));
}
nsIGlobalObject*
+17 -14
View File
@@ -11,7 +11,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/cache/Manager.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/Types.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/nsRefPtr.h"
@@ -40,13 +40,12 @@ public:
{
public:
virtual void
OnFetchPut(FetchPut* aFetchPut, RequestId aRequestId, const ErrorResult& aRv) = 0;
OnFetchPut(FetchPut* aFetchPut, ErrorResult&& aRv) = 0;
};
static nsresult
Create(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
Create(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams,
FetchPut** aFetchPutOut);
@@ -58,19 +57,18 @@ private:
friend class FetchObserver;
struct State
{
PCacheRequest mPCacheRequest;
CacheRequest mCacheRequest;
nsCOMPtr<nsIInputStream> mRequestStream;
nsRefPtr<FetchObserver> mFetchObserver;
PCacheResponse mPCacheResponse;
CacheResponse mCacheResponse;
nsCOMPtr<nsIInputStream> mResponseStream;
nsRefPtr<Request> mRequest;
nsRefPtr<Response> mResponse;
};
FetchPut(Listener* aListener, Manager* aManager,
RequestId aRequestId, CacheId aCacheId,
const nsTArray<PCacheRequest>& aRequests,
FetchPut(Listener* aListener, Manager* aManager, CacheId aCacheId,
const nsTArray<CacheRequest>& aRequests,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreams);
~FetchPut();
@@ -83,11 +81,17 @@ private:
void MaybeCompleteOnMainThread();
void DoPutOnWorkerThread();
static bool MatchInPutList(const PCacheRequest& aRequest,
static bool MatchInPutList(const CacheRequest& aRequest,
const nsTArray<CacheRequestResponse>& aPutList);
virtual void OnCachePutAll(RequestId aRequestId, nsresult aRv) override;
void MaybeSetError(nsresult aRv);
virtual void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList) override;
void MaybeSetError(ErrorResult&& aRv);
void MaybeNotifyListener();
// TypeUtils methods
@@ -101,7 +105,6 @@ private:
Listener* mListener;
nsRefPtr<Manager> mManager;
const RequestId mRequestId;
const CacheId mCacheId;
nsCOMPtr<nsIThread> mInitiatingThread;
nsTArray<State> mStateList;
+33 -18
View File
@@ -23,11 +23,24 @@ namespace cache {
using mozilla::dom::quota::FileInputStream;
using mozilla::dom::quota::FileOutputStream;
using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
using mozilla::unused;
namespace {
enum BodyFileType
{
BODY_FILE_FINAL,
BODY_FILE_TMP
};
nsresult
BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
nsIFile** aBodyFileOut);
} // anonymous namespace
// static
nsresult
FileUtils::BodyCreateDir(nsIFile* aBaseDir)
BodyCreateDir(nsIFile* aBaseDir)
{
MOZ_ASSERT(aBaseDir);
@@ -49,7 +62,7 @@ FileUtils::BodyCreateDir(nsIFile* aBaseDir)
// static
nsresult
FileUtils::BodyDeleteDir(nsIFile* aBaseDir)
BodyDeleteDir(nsIFile* aBaseDir)
{
MOZ_ASSERT(aBaseDir);
@@ -72,8 +85,7 @@ FileUtils::BodyDeleteDir(nsIFile* aBaseDir)
// static
nsresult
FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
nsIFile** aCacheDirOut)
BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aCacheDirOut);
@@ -107,11 +119,11 @@ FileUtils::BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
// static
nsresult
FileUtils::BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
nsIFile* aBaseDir, nsIInputStream* aSource,
void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut)
BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
nsIFile* aBaseDir, nsIInputStream* aSource,
void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aSource);
@@ -168,7 +180,7 @@ FileUtils::BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
// static
void
FileUtils::BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aCopyContext);
@@ -182,7 +194,7 @@ FileUtils::BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
// static
nsresult
FileUtils::BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
{
MOZ_ASSERT(aBaseDir);
@@ -206,8 +218,8 @@ FileUtils::BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
// static
nsresult
FileUtils::BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
const nsID& aId, nsIInputStream** aStreamOut)
BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
nsIInputStream** aStreamOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aStreamOut);
@@ -234,7 +246,7 @@ FileUtils::BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
// static
nsresult
FileUtils::BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
{
nsresult rv = NS_OK;
@@ -273,10 +285,11 @@ FileUtils::BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList)
return NS_OK;
}
// static
namespace {
nsresult
FileUtils::BodyIdToFile(nsIFile* aBaseDir, const nsID& aId,
BodyFileType aType, nsIFile** aBodyFileOut)
BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
nsIFile** aBodyFileOut)
{
MOZ_ASSERT(aBaseDir);
MOZ_ASSERT(aBodyFileOut);
@@ -304,6 +317,8 @@ FileUtils::BodyIdToFile(nsIFile* aBaseDir, const nsID& aId,
return rv;
}
} // anonymous namespace
} // namespace cache
} // namespace dom
} // namespace mozilla
+23 -38
View File
@@ -19,51 +19,36 @@ namespace mozilla {
namespace dom {
namespace cache {
// TODO: remove static class and use functions in cache namespace (bug 1110485)
class FileUtils final
{
public:
enum BodyFileType
{
BODY_FILE_FINAL,
BODY_FILE_TMP
};
nsresult
BodyCreateDir(nsIFile* aBaseDir);
static nsresult BodyCreateDir(nsIFile* aBaseDir);
// Note that this function can only be used during the initialization of the
// database. We're unlikely to be able to delete the DB successfully past
// that point due to the file being in use.
static nsresult BodyDeleteDir(nsIFile* aBaseDir);
static nsresult BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId,
nsIFile** aCacheDirOut);
// Note that this function can only be used during the initialization of the
// database. We're unlikely to be able to delete the DB successfully past
// that point due to the file being in use.
nsresult
BodyDeleteDir(nsIFile* aBaseDir);
static nsresult
BodyStartWriteStream(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
nsIInputStream* aSource, void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut);
nsresult
BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut);
static void
BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext);
nsresult
BodyStartWriteStream(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
nsIInputStream* aSource, void* aClosure,
nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
nsISupports** aCopyContextOut);
static nsresult
BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
void
BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext);
static nsresult
BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
nsIInputStream** aStreamOut);
nsresult
BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId);
static nsresult
BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
nsresult
BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
nsIInputStream** aStreamOut);
private:
static nsresult
BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
nsIFile** aBodyFileOut);
FileUtils() = delete;
~FileUtils() = delete;
};
nsresult
BodyDeleteFiles(nsIFile* aBaseDir, const nsTArray<nsID>& aIdList);
} // namespace cache
} // namespace dom
+37
View File
@@ -8,10 +8,47 @@
#define mozilla_dom_cache_IPCUtils_h
#include "ipc/IPCMessageUtils.h"
// Fix X11 header brain damage that conflicts with HeadersGuardEnum::None
#undef None
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#include "mozilla/dom/cache/Types.h"
namespace IPC {
template<>
struct ParamTraits<mozilla::dom::HeadersGuardEnum> :
public ContiguousEnumSerializer<mozilla::dom::HeadersGuardEnum,
mozilla::dom::HeadersGuardEnum::None,
mozilla::dom::HeadersGuardEnum::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestMode> :
public ContiguousEnumSerializer<mozilla::dom::RequestMode,
mozilla::dom::RequestMode::Same_origin,
mozilla::dom::RequestMode::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestCredentials> :
public ContiguousEnumSerializer<mozilla::dom::RequestCredentials,
mozilla::dom::RequestCredentials::Omit,
mozilla::dom::RequestCredentials::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestCache> :
public ContiguousEnumSerializer<mozilla::dom::RequestCache,
mozilla::dom::RequestCache::Default,
mozilla::dom::RequestCache::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestContext> :
public ContiguousEnumSerializer<mozilla::dom::RequestContext,
mozilla::dom::RequestContext::Audio,
mozilla::dom::RequestContext::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::ResponseType> :
public ContiguousEnumSerializer<mozilla::dom::ResponseType,
mozilla::dom::ResponseType::Basic,
mozilla::dom::ResponseType::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::cache::Namespace> :
public ContiguousEnumSerializer<mozilla::dom::cache::Namespace,
mozilla::dom::cache::DEFAULT_NAMESPACE,
+327 -348
View File
File diff suppressed because it is too large Load Diff
+62 -73
View File
@@ -7,11 +7,10 @@
#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 "mozilla/nsRefPtr.h"
#include "nsString.h"
#include "nsTArray.h"
@@ -22,12 +21,11 @@ namespace mozilla {
namespace dom {
namespace cache {
class CacheOpArgs;
class CacheOpResult;
class CacheRequestResponse;
class Context;
class ManagerId;
class PCacheQueryParams;
class PCacheRequest;
class PCacheRequestOrVoid;
struct SavedRequest;
struct SavedResponse;
class StreamList;
@@ -41,23 +39,25 @@ class StreamList;
// 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.
// The life cycle of Manager objects is somewhat complex. While code may
// hold a strong reference to the Manager, it will invalidate itself once it
// believes it has become completely idle. This is currently determined when
// all of the following conditions occur:
//
// 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.
// 1) There are no more Manager::Listener objects registered with the Manager
// by performing a Cache or Storage operation.
// 2) There are no more CacheId references noted via Manager::AddRefCacheId().
// 3) There are no more BodyId references noted via Manager::AddRefBodyId().
//
// In order to keep your Manager alive you should perform an operation to set
// a Listener, call AddRefCacheId(), or call AddRefBodyId().
//
// Even once a Manager becomes invalid, however, it may still continue to
// exist. This is allowed so that any in-progress Actions can gracefully
// complete.
//
// As an invariant, all Manager objects must cease all IO before shutdown. This
// is enforced by the ShutdownObserver. If content still holds references to
// is enforced by the Manager::Factory. If content still holds references to
// Cache DOM objects during shutdown, then all operations will begin rejecting.
class Manager final
{
@@ -84,30 +84,36 @@ public:
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) { }
// convenience routines
void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult);
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) { }
void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId);
void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const SavedResponse& aSavedResponse,
StreamList* aStreamList);
void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const nsTArray<SavedResponse>& aSavedResponseList,
StreamList* aStreamList);
void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList);
// interface to be implemented
virtual void
OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
CacheId aOpenedCacheId,
const nsTArray<SavedResponse>& aSavedResponseList,
const nsTArray<SavedRequest>& aSavedRequestList,
StreamList* aStreamList) { }
protected:
~Listener() { }
@@ -127,8 +133,8 @@ public:
// Marks the Manager "invalid". Once the Context completes no new operations
// will be permitted with this Manager. New actors will get a new Manager.
void Invalidate();
bool IsValid() const;
void NoteClosing();
bool IsClosing() const;
// If an actor represents a long term reference to a cache or body stream,
// then they must call AddRefCacheId() or AddRefBodyId(). This will
@@ -148,35 +154,15 @@ public:
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 ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
const CacheOpArgs& aOpArgs);
void ExecutePutAll(Listener* aListener, CacheId aCacheId,
const nsTArray<CacheRequestResponse>& aPutList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList);
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);
void ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
const CacheOpArgs& aOpArgs);
private:
class Factory;
@@ -199,6 +185,7 @@ private:
Manager(ManagerId* aManagerId, nsIThread* aIOThread);
~Manager();
void Init();
void Shutdown();
already_AddRefed<Context> CurrentContext();
@@ -209,6 +196,8 @@ private:
bool SetBodyIdOrphanedIfRefed(const nsID& aBodyId);
void NoteOrphanedBodyIdList(const nsTArray<nsID>& aDeletedBodyIdList);
void MaybeAllowContextToClose();
nsRefPtr<ManagerId> mManagerId;
nsCOMPtr<nsIThread> mIOThread;
@@ -260,7 +249,7 @@ private:
nsTArray<StreamList*> mStreamLists;
bool mShuttingDown;
bool mValid;
bool mClosing;
struct CacheIdRefCounter
{
+6 -21
View File
@@ -3,16 +3,13 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PBlob; // FIXME: bug 792908
include protocol PCacheOp;
include protocol PCachePushStream;
include PCacheTypes;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PBlob; // FIXME: bug 792908
include protocol PCacheStreamControl;
using mozilla::dom::cache::RequestId from "mozilla/dom/cache/Types.h";
using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
include "mozilla/dom/cache/IPCUtils.h";
include CacheTypes;
namespace mozilla {
namespace dom {
@@ -21,27 +18,15 @@ namespace cache {
protocol PCache
{
manager PBackground;
manages PCacheOp;
manages PCachePushStream;
parent:
PCacheOp(CacheOpArgs aOpArgs);
PCachePushStream();
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, ErrorResult aRv);
PutResponse(RequestId requestId, nsresult aRv);
DeleteResponse(RequestId requestId, nsresult aRv, bool success);
KeysResponse(RequestId requestId, nsresult aRv, PCacheRequest[] requests);
both:
__delete__();
};
+29
View File
@@ -0,0 +1,29 @@
/* 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 PCache;
include protocol PCachePushStream;
include protocol PCacheStorage;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include CacheTypes;
using mozilla::ErrorResult from "ipc/ErrorIPCUtils.h";
namespace mozilla {
namespace dom {
namespace cache {
protocol PCacheOp
{
manager PCache or PCacheStorage;
child:
__delete__(ErrorResult aRv, CacheOpResult aResult);
};
} // namespace cache
} // namespace dom
} // namespace mozilla
+6 -17
View File
@@ -3,14 +3,13 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include protocol PBackground;
include protocol PBlob; // FIXME: bug 792908
include protocol PCache;
include PCacheTypes;
include protocol PCacheOp;
include protocol PCacheStreamControl;
include protocol PFileDescriptorSet;
include protocol PBlob; // FIXME: bug 792908
include protocol PCacheStreamControl;
using mozilla::dom::cache::RequestId from "mozilla/dom/cache/IPCUtils.h";
include CacheTypes;
namespace mozilla {
namespace dom {
@@ -19,23 +18,13 @@ namespace cache {
protocol PCacheStorage
{
manager PBackground;
manages PCacheOp;
parent:
PCacheOp(CacheOpArgs aOpArgs);
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__();
};
-96
View File
@@ -1,96 +0,0 @@
/* 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 PCachePushStream;
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 RequestCache from "mozilla/dom/FetchIPCUtils.h";
using RequestContext 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;
nullable PCachePushStream pushStream;
};
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 contentPolicyType;
RequestContext context;
RequestCache requestCache;
};
union PCacheRequestOrVoid
{
void_t;
PCacheRequest;
};
struct PCacheResponse
{
ResponseType type;
nsString url;
uint32_t status;
nsCString statusText;
PHeadersEntry[] headers;
HeadersGuardEnum headersGuard;
PCacheReadStreamOrVoid body;
nsCString securityInfo;
};
union PCacheResponseOrVoid
{
void_t;
PCacheResponse;
};
struct CacheRequestResponse
{
PCacheRequest request;
PCacheResponse response;
};
} // namespace cache
} // namespace dom
} // namespace mozilla
+22 -16
View File
@@ -46,25 +46,35 @@ PrincipalVerifier::CreateAndDispatch(Listener* aListener,
}
void
PrincipalVerifier::ClearListener()
PrincipalVerifier::AddListener(Listener* aListener)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(mListener);
mListener = nullptr;
MOZ_ASSERT(aListener);
MOZ_ASSERT(!mListenerList.Contains(aListener));
mListenerList.AppendElement(aListener);
}
void
PrincipalVerifier::RemoveListener(Listener* aListener)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(aListener);
MOZ_ALWAYS_TRUE(mListenerList.RemoveElement(aListener));
}
PrincipalVerifier::PrincipalVerifier(Listener* aListener,
PBackgroundParent* aActor,
const PrincipalInfo& aPrincipalInfo)
: mListener(aListener)
, mActor(BackgroundParent::GetContentParent(aActor))
: mActor(BackgroundParent::GetContentParent(aActor))
, mPrincipalInfo(aPrincipalInfo)
, mInitiatingThread(NS_GetCurrentThread())
, mResult(NS_OK)
{
AssertIsOnBackgroundThread();
MOZ_ASSERT(mListener);
MOZ_ASSERT(mInitiatingThread);
MOZ_ASSERT(aListener);
mListenerList.AppendElement(aListener);
}
PrincipalVerifier::~PrincipalVerifier()
@@ -73,7 +83,7 @@ PrincipalVerifier::~PrincipalVerifier()
// threads, its a race to see which thread de-refs us last. Therefore
// we cannot guarantee which thread we destruct on.
MOZ_ASSERT(!mListener);
MOZ_ASSERT(mListenerList.IsEmpty());
// We should always be able to explicitly release the actor on the main
// thread.
@@ -172,17 +182,13 @@ 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;
ListenerList::ForwardIterator iter(mListenerList);
while (iter.HasMore()) {
iter.GetNext()->OnPrincipalVerified(mResult, mManagerId);
}
mListener->OnPrincipalVerified(mResult, mManagerId);
// The listener must clear their reference in OnPrincipalVerified()
MOZ_ASSERT(!mListener);
// The listener must clear its reference in OnPrincipalVerified()
MOZ_ASSERT(mListenerList.IsEmpty());
}
void
+9 -5
View File
@@ -9,6 +9,7 @@
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsThreadUtils.h"
#include "nsTObserverArray.h"
namespace mozilla {
@@ -26,7 +27,7 @@ class PrincipalVerifier 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
// for calling RemoveListener() on the PrincipalVerifier to clear the
// weak reference.
class Listener
{
@@ -38,9 +39,11 @@ public:
CreateAndDispatch(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo);
// The Listener must call ClearListener() when OnPrincipalVerified() is
void AddListener(Listener* aListener);
// The Listener must call RemoveListener() when OnPrincipalVerified() is
// called or when the Listener is destroyed.
void ClearListener();
void RemoveListener(Listener* aListener);
private:
PrincipalVerifier(Listener* aListener, mozilla::ipc::PBackgroundParent* aActor,
@@ -52,8 +55,9 @@ private:
void DispatchToInitiatingThread(nsresult aRv);
// Weak reference cleared by ClearListener()
Listener* mListener;
// Weak reference cleared by RemoveListener()
typedef nsTObserverArray<Listener*> ListenerList;
ListenerList mListenerList;
// set in originating thread at construction, but must be accessed and
// released on main thread
+37 -12
View File
@@ -9,7 +9,7 @@
#include "mozilla/unused.h"
#include "mozilla/dom/cache/CacheStreamControlChild.h"
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/ipc/FileDescriptor.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/SnappyUncompressInputStream.h"
@@ -34,10 +34,10 @@ public:
nsIInputStream* aStream);
void
Serialize(PCacheReadStreamOrVoid* aReadStreamOut);
Serialize(CacheReadStreamOrVoid* aReadStreamOut);
void
Serialize(PCacheReadStream* aReadStreamOut);
Serialize(CacheReadStream* aReadStreamOut);
// ReadStream::Controllable methods
virtual void
@@ -49,6 +49,9 @@ public:
virtual bool
MatchId(const nsID& aId) const override;
virtual bool
HasEverBeenRead() const override;
// Simulate nsIInputStream methods, but we don't actually inherit from it
NS_METHOD
Close();
@@ -102,6 +105,7 @@ private:
NumStates
};
Atomic<State> mState;
Atomic<bool> mHasEverBeenRead;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::ReadStream::Inner)
};
@@ -192,17 +196,17 @@ ReadStream::Inner::Inner(StreamControl* aControl, const nsID& aId,
}
void
ReadStream::Inner::Serialize(PCacheReadStreamOrVoid* aReadStreamOut)
ReadStream::Inner::Serialize(CacheReadStreamOrVoid* aReadStreamOut)
{
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
MOZ_ASSERT(aReadStreamOut);
PCacheReadStream stream;
CacheReadStream stream;
Serialize(&stream);
*aReadStreamOut = stream;
}
void
ReadStream::Inner::Serialize(PCacheReadStream* aReadStreamOut)
ReadStream::Inner::Serialize(CacheReadStream* aReadStreamOut)
{
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
MOZ_ASSERT(aReadStreamOut);
@@ -248,6 +252,13 @@ ReadStream::Inner::MatchId(const nsID& aId) const
return mId.Equals(aId);
}
bool
ReadStream::Inner::HasEverBeenRead() const
{
MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
return mHasEverBeenRead;
}
NS_IMETHODIMP
ReadStream::Inner::Close()
{
@@ -283,6 +294,8 @@ ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
Close();
}
mHasEverBeenRead = true;
return rv;
}
@@ -293,6 +306,10 @@ ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
// stream ops can happen on any thread
MOZ_ASSERT(aNumReadOut);
if (aCount) {
mHasEverBeenRead = true;
}
nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
aNumReadOut);
@@ -301,6 +318,14 @@ ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
Close();
}
// Verify bytes were actually read before marking as being ever read. For
// example, code can test if the stream supports ReadSegments() by calling
// this method with a dummy callback which doesn't read anything. We don't
// want to trigger on that.
if (*aNumReadOut) {
mHasEverBeenRead = true;
}
return rv;
}
@@ -390,18 +415,18 @@ NS_IMPL_ISUPPORTS(cache::ReadStream, nsIInputStream, ReadStream);
// static
already_AddRefed<ReadStream>
ReadStream::Create(const PCacheReadStreamOrVoid& aReadStreamOrVoid)
ReadStream::Create(const CacheReadStreamOrVoid& aReadStreamOrVoid)
{
if (aReadStreamOrVoid.type() == PCacheReadStreamOrVoid::Tvoid_t) {
if (aReadStreamOrVoid.type() == CacheReadStreamOrVoid::Tvoid_t) {
return nullptr;
}
return Create(aReadStreamOrVoid.get_PCacheReadStream());
return Create(aReadStreamOrVoid.get_CacheReadStream());
}
// static
already_AddRefed<ReadStream>
ReadStream::Create(const PCacheReadStream& aReadStream)
ReadStream::Create(const CacheReadStream& 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,
@@ -456,13 +481,13 @@ ReadStream::Create(PCacheStreamControlParent* aControl, const nsID& aId,
}
void
ReadStream::Serialize(PCacheReadStreamOrVoid* aReadStreamOut)
ReadStream::Serialize(CacheReadStreamOrVoid* aReadStreamOut)
{
mInner->Serialize(aReadStreamOut);
}
void
ReadStream::Serialize(PCacheReadStream* aReadStreamOut)
ReadStream::Serialize(CacheReadStream* aReadStreamOut)
{
mInner->Serialize(aReadStreamOut);
}
+9 -6
View File
@@ -21,8 +21,8 @@ namespace mozilla {
namespace dom {
namespace cache {
class PCacheReadStream;
class PCacheReadStreamOrVoid;
class CacheReadStream;
class CacheReadStreamOrVoid;
class PCacheStreamControlParent;
// IID for the dom::cache::ReadStream interface
@@ -63,6 +63,9 @@ public:
virtual bool
MatchId(const nsID& aId) const = 0;
virtual bool
HasEverBeenRead() const = 0;
NS_IMETHOD_(MozExternalRefCountType)
AddRef(void) = 0;
@@ -71,17 +74,17 @@ public:
};
static already_AddRefed<ReadStream>
Create(const PCacheReadStreamOrVoid& aReadStreamOrVoid);
Create(const CacheReadStreamOrVoid& aReadStreamOrVoid);
static already_AddRefed<ReadStream>
Create(const PCacheReadStream& aReadStream);
Create(const CacheReadStream& aReadStream);
static already_AddRefed<ReadStream>
Create(PCacheStreamControlParent* aControl, const nsID& aId,
nsIInputStream* aStream);
void Serialize(PCacheReadStreamOrVoid* aReadStreamOut);
void Serialize(PCacheReadStream* aReadStreamOut);
void Serialize(CacheReadStreamOrVoid* aReadStreamOut);
void Serialize(CacheReadStream* aReadStreamOut);
private:
class Inner;
+3 -3
View File
@@ -10,7 +10,7 @@
// 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/CacheTypes.h"
#include "mozilla/dom/cache/Types.h"
#include "nsCOMPtr.h"
#include "nsID.h"
@@ -23,7 +23,7 @@ namespace cache {
struct SavedRequest
{
SavedRequest() : mHasBodyId(false) { mValue.body() = void_t(); }
PCacheRequest mValue;
CacheRequest mValue;
bool mHasBodyId;
nsID mBodyId;
CacheId mCacheId;
@@ -32,7 +32,7 @@ struct SavedRequest
struct SavedResponse
{
SavedResponse() : mHasBodyId(false) { mValue.body() = void_t(); }
PCacheResponse mValue;
CacheResponse mValue;
bool mHasBodyId;
nsID mBodyId;
CacheId mCacheId;
+12
View File
@@ -84,6 +84,18 @@ StreamControl::CloseAllReadStreamsWithoutReporting()
}
}
bool
StreamControl::HasEverBeenRead() const
{
ReadStreamList::ForwardIterator iter(mReadStreamList);
while (iter.HasMore()) {
if (iter.GetNext()->HasEverBeenRead()) {
return true;
}
}
return false;
}
} // namespace cache
} // namespace dom
} // namespace mozilla
+7 -4
View File
@@ -20,7 +20,7 @@ namespace ipc {
namespace dom {
namespace cache {
class PCacheReadStream;
class CacheReadStream;
// Abstract class to help implement the stream control Child and Parent actors.
// This provides an interface to partly help with serialization of IPC types,
@@ -30,14 +30,14 @@ class StreamControl
public:
// abstract interface that must be implemented by child class
virtual void
SerializeControl(PCacheReadStream* aReadStreamOut) = 0;
SerializeControl(CacheReadStream* aReadStreamOut) = 0;
virtual void
SerializeFds(PCacheReadStream* aReadStreamOut,
SerializeFds(CacheReadStream* aReadStreamOut,
const nsTArray<mozilla::ipc::FileDescriptor>& aFds) = 0;
virtual void
DeserializeFds(const PCacheReadStream& aReadStream,
DeserializeFds(const CacheReadStream& aReadStream,
nsTArray<mozilla::ipc::FileDescriptor>& aFdsOut) = 0;
// inherited implementation of the ReadStream::Controllable list
@@ -68,6 +68,9 @@ protected:
void
CloseAllReadStreamsWithoutReporting();
bool
HasEverBeenRead() const;
// protected parts of the abstract interface
virtual void
NoteClosedAfterForget(const nsID& aId) = 0;
-151
View File
@@ -1,151 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/cache/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
View File
@@ -1,39 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_dom_cache_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
+56 -23
View File
@@ -12,7 +12,7 @@
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
#include "mozilla/dom/cache/CachePushStreamChild.h"
#include "mozilla/dom/cache/PCacheTypes.h"
#include "mozilla/dom/cache/CacheTypes.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
@@ -34,7 +34,9 @@ namespace {
using mozilla::ErrorResult;
using mozilla::unused;
using mozilla::void_t;
using mozilla::dom::cache::PCacheReadStream;
using mozilla::dom::InternalHeaders;
using mozilla::dom::cache::CacheReadStream;
using mozilla::dom::cache::HeadersEntry;
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::FileDescriptor;
using mozilla::ipc::PBackgroundChild;
@@ -130,7 +132,7 @@ HasVaryStar(mozilla::dom::InternalHeaders* aHeaders)
}
void
SerializeNormalStream(nsIInputStream* aStream, PCacheReadStream& aReadStreamOut)
SerializeNormalStream(nsIInputStream* aStream, CacheReadStream& aReadStreamOut)
{
nsAutoTArray<FileDescriptor, 4> fds;
SerializeInputStream(aStream, aReadStreamOut.params(), fds);
@@ -154,6 +156,20 @@ SerializeNormalStream(nsIInputStream* aStream, PCacheReadStream& aReadStreamOut)
}
}
void
ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
{
MOZ_ASSERT(aHeaders);
nsAutoTArray<InternalHeaders::Entry, 16> entryList;
aHeaders->GetEntries(entryList);
for (uint32_t i = 0; i < entryList.Length(); ++i) {
InternalHeaders::Entry& entry = entryList[i];
aOut.AppendElement(HeadersEntry(entry.mName, entry.mValue));
}
}
} // anonymous namespace
namespace mozilla {
@@ -206,10 +222,10 @@ TypeUtils::ToInternalRequest(const OwningRequestOrUSVString& aIn,
}
void
TypeUtils::ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction,
ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv)
{
MOZ_ASSERT(aIn);
@@ -245,7 +261,7 @@ TypeUtils::ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
nsRefPtr<InternalHeaders> headers = aIn->Headers();
MOZ_ASSERT(headers);
headers->GetPHeaders(aOut.headers());
ToHeadersEntryList(aOut.headers(), headers);
aOut.headersGuard() = headers->Guard();
aOut.mode() = aIn->Mode();
aOut.credentials() = aIn->GetCredentialsMode();
@@ -269,8 +285,8 @@ TypeUtils::ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
}
void
TypeUtils::ToPCacheResponseWithoutBody(PCacheResponse& aOut,
InternalResponse& aIn, ErrorResult& aRv)
TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
InternalResponse& aIn, ErrorResult& aRv)
{
aOut.type() = aIn.Type();
@@ -295,13 +311,13 @@ TypeUtils::ToPCacheResponseWithoutBody(PCacheResponse& aOut,
aRv.ThrowTypeError(MSG_RESPONSE_HAS_VARY_STAR);
return;
}
headers->GetPHeaders(aOut.headers());
ToHeadersEntryList(aOut.headers(), headers);
aOut.headersGuard() = headers->Guard();
aOut.securityInfo() = aIn.GetSecurityInfo();
}
void
TypeUtils::ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aRv)
TypeUtils::ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv)
{
if (aIn.BodyUsed()) {
aRv.ThrowTypeError(MSG_FETCH_BODY_CONSUMED_ERROR);
@@ -309,7 +325,7 @@ TypeUtils::ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aR
}
nsRefPtr<InternalResponse> ir = aIn.GetInternalResponse();
ToPCacheResponseWithoutBody(aOut, *ir, aRv);
ToCacheResponseWithoutBody(aOut, *ir, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@@ -328,8 +344,8 @@ TypeUtils::ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aR
// static
void
TypeUtils::ToPCacheQueryParams(PCacheQueryParams& aOut,
const CacheQueryOptions& aIn)
TypeUtils::ToCacheQueryParams(CacheQueryParams& aOut,
const CacheQueryOptions& aIn)
{
aOut.ignoreSearch() = aIn.mIgnoreSearch;
aOut.ignoreMethod() = aIn.mIgnoreMethod;
@@ -344,7 +360,7 @@ TypeUtils::ToPCacheQueryParams(PCacheQueryParams& aOut,
}
already_AddRefed<Response>
TypeUtils::ToResponse(const PCacheResponse& aIn)
TypeUtils::ToResponse(const CacheResponse& aIn)
{
if (aIn.type() == ResponseType::Error) {
nsRefPtr<InternalResponse> error = InternalResponse::NetworkError();
@@ -357,7 +373,7 @@ TypeUtils::ToResponse(const PCacheResponse& aIn)
ir->SetUrl(NS_ConvertUTF16toUTF8(aIn.url()));
nsRefPtr<InternalHeaders> internalHeaders =
new InternalHeaders(aIn.headers(), aIn.headersGuard());
ToInternalHeaders(aIn.headers(), aIn.headersGuard());
ErrorResult result;
ir->Headers()->SetGuard(aIn.headersGuard(), result);
MOZ_ASSERT(!result.Failed());
@@ -392,7 +408,7 @@ TypeUtils::ToResponse(const PCacheResponse& aIn)
}
already_AddRefed<InternalRequest>
TypeUtils::ToInternalRequest(const PCacheRequest& aIn)
TypeUtils::ToInternalRequest(const CacheRequest& aIn)
{
nsRefPtr<InternalRequest> internalRequest = new InternalRequest();
@@ -409,7 +425,7 @@ TypeUtils::ToInternalRequest(const PCacheRequest& aIn)
internalRequest->SetCacheMode(aIn.requestCache());
nsRefPtr<InternalHeaders> internalHeaders =
new InternalHeaders(aIn.headers(), aIn.headersGuard());
ToInternalHeaders(aIn.headers(), aIn.headersGuard());
ErrorResult result;
internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
MOZ_ASSERT(!result.Failed());
@@ -424,13 +440,30 @@ TypeUtils::ToInternalRequest(const PCacheRequest& aIn)
}
already_AddRefed<Request>
TypeUtils::ToRequest(const PCacheRequest& aIn)
TypeUtils::ToRequest(const CacheRequest& aIn)
{
nsRefPtr<InternalRequest> internalRequest = ToInternalRequest(aIn);
nsRefPtr<Request> request = new Request(GetGlobalObject(), internalRequest);
return request.forget();
}
// static
already_AddRefed<InternalHeaders>
TypeUtils::ToInternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
HeadersGuardEnum aGuard)
{
nsTArray<InternalHeaders::Entry> entryList(aHeadersEntryList.Length());
for (uint32_t i = 0; i < aHeadersEntryList.Length(); ++i) {
const HeadersEntry& headersEntry = aHeadersEntryList[i];
entryList.AppendElement(InternalHeaders::Entry(headersEntry.name(),
headersEntry.value()));
}
nsRefPtr<InternalHeaders> ref = new InternalHeaders(Move(entryList), aGuard);
return ref.forget();
}
void
TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
ErrorResult& aRv)
@@ -478,7 +511,7 @@ TypeUtils::ToInternalRequest(const nsAString& aIn, ErrorResult& aRv)
void
TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
PCacheReadStreamOrVoid* aStreamOut,
CacheReadStreamOrVoid* aStreamOut,
ErrorResult& aRv)
{
*aStreamOut = void_t();
@@ -493,7 +526,7 @@ TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
return;
}
PCacheReadStream readStream;
CacheReadStream readStream;
readStream.controlChild() = nullptr;
readStream.controlParent() = nullptr;
readStream.pushStreamChild() = nullptr;
@@ -517,7 +550,7 @@ TypeUtils::SerializeCacheStream(nsIInputStream* aStream,
void
TypeUtils::SerializePushStream(nsIInputStream* aStream,
PCacheReadStream& aReadStreamOut,
CacheReadStream& aReadStreamOut,
ErrorResult& aRv)
{
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
+24 -17
View File
@@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/InternalHeaders.h"
#include "nsError.h"
class nsIGlobalObject;
@@ -29,11 +30,12 @@ class Response;
namespace cache {
class CachePushStreamChild;
class PCacheQueryParams;
class PCacheReadStream;
class PCacheReadStreamOrVoid;
class PCacheRequest;
class PCacheResponse;
class CacheQueryParams;
class CacheReadStream;
class CacheReadStreamOrVoid;
class CacheRequest;
class CacheResponse;
class HeadersEntry;
class TypeUtils
{
@@ -77,28 +79,33 @@ public:
ErrorResult& aRv);
void
ToPCacheRequest(PCacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction, ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv);
ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
BodyAction aBodyAction, ReferrerAction aReferrerAction,
SchemeAction aSchemeAction, ErrorResult& aRv);
void
ToPCacheResponseWithoutBody(PCacheResponse& aOut, InternalResponse& aIn,
ErrorResult& aRv);
ToCacheResponseWithoutBody(CacheResponse& aOut, InternalResponse& aIn,
ErrorResult& aRv);
void
ToPCacheResponse(PCacheResponse& aOut, Response& aIn, ErrorResult& aRv);
ToCacheResponse(CacheResponse& aOut, Response& aIn, ErrorResult& aRv);
void
ToPCacheQueryParams(PCacheQueryParams& aOut, const CacheQueryOptions& aIn);
ToCacheQueryParams(CacheQueryParams& aOut, const CacheQueryOptions& aIn);
already_AddRefed<Response>
ToResponse(const PCacheResponse& aIn);
ToResponse(const CacheResponse& aIn);
already_AddRefed<InternalRequest>
ToInternalRequest(const PCacheRequest& aIn);
ToInternalRequest(const CacheRequest& aIn);
already_AddRefed<Request>
ToRequest(const PCacheRequest& aIn);
ToRequest(const CacheRequest& aIn);
// static methods
static already_AddRefed<InternalHeaders>
ToInternalHeaders(const nsTArray<HeadersEntry>& aHeadersEntryList,
HeadersGuardEnum aGuard = HeadersGuardEnum::None);
private:
void
@@ -109,11 +116,11 @@ private:
ToInternalRequest(const nsAString& aIn, ErrorResult& aRv);
void
SerializeCacheStream(nsIInputStream* aStream, PCacheReadStreamOrVoid* aStreamOut,
SerializeCacheStream(nsIInputStream* aStream, CacheReadStreamOrVoid* aStreamOut,
ErrorResult& aRv);
void
SerializePushStream(nsIInputStream* aStream, PCacheReadStream& aReadStreamOut,
SerializePushStream(nsIInputStream* aStream, CacheReadStream& aReadStreamOut,
ErrorResult& aRv);
};
+1 -3
View File
@@ -22,9 +22,7 @@ enum Namespace
CHROME_ONLY_NAMESPACE,
NUMBER_OF_NAMESPACES
};
typedef uintptr_t RequestId;
static const RequestId INVALID_REQUEST_ID = 0;
static const Namespace INVALID_NAMESPACE = NUMBER_OF_NAMESPACES;
typedef int64_t CacheId;
static const CacheId INVALID_CACHE_ID = -1;
+6 -4
View File
@@ -11,6 +11,8 @@ EXPORTS.mozilla.dom.cache += [
'AutoUtils.h',
'Cache.h',
'CacheChild.h',
'CacheOpChild.h',
'CacheOpParent.h',
'CacheParent.h',
'CachePushStreamChild.h',
'CachePushStreamParent.h',
@@ -35,7 +37,6 @@ EXPORTS.mozilla.dom.cache += [
'SavedTypes.h',
'StreamControl.h',
'StreamList.h',
'StreamUtils.h',
'Types.h',
'TypeUtils.h',
]
@@ -46,6 +47,8 @@ UNIFIED_SOURCES += [
'AutoUtils.cpp',
'Cache.cpp',
'CacheChild.cpp',
'CacheOpChild.cpp',
'CacheOpParent.cpp',
'CacheParent.cpp',
'CachePushStreamChild.cpp',
'CachePushStreamParent.cpp',
@@ -68,17 +71,16 @@ UNIFIED_SOURCES += [
'ReadStream.cpp',
'StreamControl.cpp',
'StreamList.cpp',
'StreamUtils.cpp',
'TypeUtils.cpp',
]
IPDL_SOURCES += [
'CacheInitData.ipdlh',
'CacheTypes.ipdlh',
'PCache.ipdl',
'PCacheOp.ipdl',
'PCachePushStream.ipdl',
'PCacheStorage.ipdl',
'PCacheStreamControl.ipdl',
'PCacheTypes.ipdlh',
]
include('/ipc/chromium/chromium-config.mozbuild')
-51
View File
@@ -1,51 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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_FetchIPCUtils_h
#define mozilla_dom_FetchIPCUtils_h
#include "ipc/IPCMessageUtils.h"
// Fix X11 header brain damage that conflicts with HeadersGuardEnum::None
#undef None
#include "mozilla/dom/HeadersBinding.h"
#include "mozilla/dom/Request.h"
#include "mozilla/dom/Response.h"
namespace IPC {
template<>
struct ParamTraits<mozilla::dom::HeadersGuardEnum> :
public ContiguousEnumSerializer<mozilla::dom::HeadersGuardEnum,
mozilla::dom::HeadersGuardEnum::None,
mozilla::dom::HeadersGuardEnum::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestMode> :
public ContiguousEnumSerializer<mozilla::dom::RequestMode,
mozilla::dom::RequestMode::Same_origin,
mozilla::dom::RequestMode::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestCredentials> :
public ContiguousEnumSerializer<mozilla::dom::RequestCredentials,
mozilla::dom::RequestCredentials::Omit,
mozilla::dom::RequestCredentials::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestCache> :
public ContiguousEnumSerializer<mozilla::dom::RequestCache,
mozilla::dom::RequestCache::Default,
mozilla::dom::RequestCache::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::RequestContext> :
public ContiguousEnumSerializer<mozilla::dom::RequestContext,
mozilla::dom::RequestContext::Audio,
mozilla::dom::RequestContext::EndGuard_> {};
template<>
struct ParamTraits<mozilla::dom::ResponseType> :
public ContiguousEnumSerializer<mozilla::dom::ResponseType,
mozilla::dom::ResponseType::Basic,
mozilla::dom::ResponseType::EndGuard_> {};
}
#endif // mozilla_dom_FetchIPCUtils_h
+2 -13
View File
@@ -7,7 +7,6 @@
#include "mozilla/dom/InternalHeaders.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/PHeaders.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
@@ -17,21 +16,11 @@
namespace mozilla {
namespace dom {
InternalHeaders::InternalHeaders(const nsTArray<PHeadersEntry>& aHeaders,
InternalHeaders::InternalHeaders(const nsTArray<Entry>&& aHeaders,
HeadersGuardEnum aGuard)
: mGuard(aGuard)
, mList(aHeaders)
{
for (uint32_t i = 0; i < aHeaders.Length(); ++i) {
mList.AppendElement(Entry(aHeaders[i].name(), aHeaders[i].value()));
}
}
void
InternalHeaders::GetPHeaders(nsTArray<PHeadersEntry>& aPHeadersOut) const
{
for (uint32_t i = 0; i < mList.Length(); ++i) {
aPHeadersOut.AppendElement(PHeadersEntry(mList[i].mName, mList[i].mValue));
}
}
void
+1 -5
View File
@@ -24,7 +24,6 @@ namespace dom {
template<typename T> class MozMap;
class HeadersOrByteStringSequenceSequenceOrByteStringMozMap;
class PHeadersEntry;
class InternalHeaders final
{
@@ -62,7 +61,7 @@ public:
MOZ_ASSERT(!result.Failed());
}
explicit InternalHeaders(const nsTArray<PHeadersEntry>& aHeaders,
explicit InternalHeaders(const nsTArray<Entry>&& aHeaders,
HeadersGuardEnum aGuard = HeadersGuardEnum::None);
void Append(const nsACString& aName, const nsACString& aValue,
@@ -91,9 +90,6 @@ public:
static already_AddRefed<InternalHeaders>
CORSHeaders(InternalHeaders* aHeaders);
void
GetPHeaders(nsTArray<PHeadersEntry>& aPHeadersOut) const;
void
GetEntries(nsTArray<InternalHeaders::Entry>& aEntries) const;
-15
View File
@@ -1,15 +0,0 @@
/* 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/. */
namespace mozilla {
namespace dom {
struct PHeadersEntry
{
nsCString name;
nsCString value;
};
} // namespace dom
} // namespace mozilla
-7
View File
@@ -7,7 +7,6 @@
EXPORTS.mozilla.dom += [
'Fetch.h',
'FetchDriver.h',
'FetchIPCUtils.h',
'Headers.h',
'InternalHeaders.h',
'InternalRequest.h',
@@ -27,12 +26,6 @@ UNIFIED_SOURCES += [
'Response.cpp',
]
IPDL_SOURCES += [
'PHeaders.ipdlh',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'../workers',
# For nsDataHandler.h
@@ -31,7 +31,7 @@ public:
// As required
nsIGlobalObject* GetParentObject() const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// setter and getter
void Register(RTCIdentityProvider& aIdp);
+13 -4
View File
@@ -60,12 +60,13 @@ nsPluginPlayPreviewInfo::GetWhitelist(nsACString& aWhitelist)
return NS_OK;
}
NS_IMETHODIMP
/* static */ nsresult
nsPluginPlayPreviewInfo::CheckWhitelist(const nsACString& aPageURI,
const nsACString& aObjectURI,
const nsACString& aWhitelist,
bool *_retval)
{
if (mWhitelist.Length() == 0) {
if (aWhitelist.Length() == 0) {
// Considering empty whitelist as '*' entry.
*_retval = true;
return NS_OK;
@@ -76,8 +77,8 @@ nsPluginPlayPreviewInfo::CheckWhitelist(const nsACString& aPageURI,
// where page_url and object_url pattern matches for aPageURI
// and aObjectURI, and performs matching as the same time.
nsACString::const_iterator start, end;
mWhitelist.BeginReading(start);
mWhitelist.EndReading(end);
aWhitelist.BeginReading(start);
aWhitelist.EndReading(end);
nsAutoCString pageURI(aPageURI);
nsAutoCString objectURI(aObjectURI);
@@ -143,3 +144,11 @@ nsPluginPlayPreviewInfo::CheckWhitelist(const nsACString& aPageURI,
*_retval = false;
return NS_OK;
}
NS_IMETHODIMP
nsPluginPlayPreviewInfo::CheckWhitelist(const nsACString& aPageURI,
const nsACString& aObjectURI,
bool *_retval)
{
return CheckWhitelist(aPageURI, aObjectURI, mWhitelist, _retval);
}
@@ -23,6 +23,18 @@ public:
const char* aWhitelist);
explicit nsPluginPlayPreviewInfo(const nsPluginPlayPreviewInfo* aSource);
/** This function checks aPageURI and aObjectURI against the whitelist
* specified in aWhitelist. This is public static function because this
* whitelist checking code needs to be accessed without any instances of
* nsIPluginPlayPreviewInfo. In particular, the Shumway whitelist is
* obtained directly from prefs and compared using this code for telemetry
* purposes.
*/
static nsresult CheckWhitelist(const nsACString& aPageURI,
const nsACString& aObjectURI,
const nsACString& aWhitelist,
bool *_retval);
nsCString mMimeType;
bool mIgnoreCTP;
nsCString mRedirectURL;
+38 -2
View File
@@ -7,6 +7,7 @@
#include "mozilla/DebugOnly.h"
#include <stdint.h> // for intptr_t
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "PluginInstanceParent.h"
#include "BrowserStreamParent.h"
@@ -21,6 +22,7 @@
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxSharedImageSurface.h"
#include "nsNetUtil.h"
#include "nsNPAPIPluginInstance.h"
#include "nsPluginInstanceOwner.h"
#include "nsFocusManager.h"
@@ -54,6 +56,10 @@ extern const wchar_t* kFlashFullscreenClass;
#include <ApplicationServices/ApplicationServices.h>
#endif // defined(XP_MACOSX)
// This is the pref used to determine whether to use Shumway on a Flash object
// (when Shumway is enabled).
static const char kShumwayWhitelistPref[] = "shumway.swf.whitelist";
using namespace mozilla::plugins;
using namespace mozilla::layers;
using namespace mozilla::gl;
@@ -104,6 +110,7 @@ PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent,
, mUseSurrogate(true)
, mNPP(npp)
, mNPNIface(npniface)
, mIsWhitelistedForShumway(false)
, mWindowType(NPWindowTypeWindow)
, mDrawingModel(kDefaultDrawingModel)
#if defined(OS_WIN)
@@ -143,9 +150,38 @@ PluginInstanceParent::~PluginInstanceParent()
}
bool
PluginInstanceParent::Init()
PluginInstanceParent::InitMetadata(const nsACString& aMimeType,
const nsACString& aSrcAttribute)
{
return true;
if (aSrcAttribute.IsEmpty()) {
return false;
}
// Ensure that the src attribute is absolute
nsRefPtr<nsPluginInstanceOwner> owner = GetOwner();
if (!owner) {
return false;
}
nsCOMPtr<nsIURI> baseUri(owner->GetBaseURI());
nsresult rv = NS_MakeAbsoluteURI(mSrcAttribute, aSrcAttribute, baseUri);
if (NS_FAILED(rv)) {
return false;
}
// Check the whitelist
nsAutoCString baseUrlSpec;
rv = baseUri->GetSpec(baseUrlSpec);
if (NS_FAILED(rv)) {
return false;
}
auto whitelist = Preferences::GetCString(kShumwayWhitelistPref);
// Empty whitelist is interpreted by CheckWhitelist as "allow everything,"
// which is not valid for our use case and should be treated as a failure.
if (whitelist.IsEmpty()) {
return false;
}
rv = nsPluginPlayPreviewInfo::CheckWhitelist(baseUrlSpec, mSrcAttribute,
whitelist,
&mIsWhitelistedForShumway);
return NS_SUCCEEDED(rv);
}
void
+22 -1
View File
@@ -68,7 +68,8 @@ public:
virtual ~PluginInstanceParent();
bool Init();
bool InitMetadata(const nsACString& aMimeType,
const nsACString& aSrcAttribute);
NPError Destroy();
virtual void ActorDestroy(ActorDestroyReason why) override;
@@ -271,6 +272,24 @@ public:
return mUseSurrogate;
}
void
GetSrcAttribute(nsACString& aOutput) const
{
aOutput = mSrcAttribute;
}
/**
* This function tells us whether this plugin instance would have been
* whitelisted for Shumway if Shumway had been enabled. This is being used
* for the purpose of gathering telemetry on Flash hangs that could
* potentially be avoided by using Shumway instead.
*/
bool
IsWhitelistedForShumway() const
{
return mIsWhitelistedForShumway;
}
virtual bool
AnswerPluginFocusChange(const bool& gotFocus) override;
@@ -323,6 +342,8 @@ private:
bool mUseSurrogate;
NPP mNPP;
const NPNetscapeFuncs* mNPNIface;
nsCString mSrcAttribute;
bool mIsWhitelistedForShumway;
NPWindowType mWindowType;
int16_t mDrawingModel;
nsAutoPtr<mozilla::layers::CompositionNotifySink> mNotifySink;
+166 -12
View File
@@ -33,8 +33,10 @@
#include "prsystem.h"
#include "GoannaProfiler.h"
#include "nsPluginTags.h"
#include "nsUnicharUtils.h"
#ifdef XP_WIN
#include "mozilla/plugins/PluginSurfaceParent.h"
#include "mozilla/widget/AudioSession.h"
#include "nsWindowsHelpers.h"
#include "PluginHangUIParent.h"
@@ -502,6 +504,7 @@ PluginModuleParent::PluginModuleParent(bool aIsChrome)
, mNPPIface(nullptr)
, mPlugin(nullptr)
, mTaskFactory(this)
, mIsFlashPlugin(false)
, mIsStartingAsync(false)
, mNPInitialized(false)
, mAsyncNewRv(NS_ERROR_NOT_INITIALIZED)
@@ -543,17 +546,21 @@ PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32
, mPluginId(aPluginId)
, mChromeTaskFactory(this)
, mHangAnnotationFlags(0)
, mHangAnnotatorMutex("PluginModuleChromeParent::mHangAnnotatorMutex")
#ifdef XP_WIN
, mPluginCpuUsageOnHang()
, mHangUIParent(nullptr)
, mHangUIEnabled(true)
, mIsTimerReset(true)
#ifdef MOZ_CRASHREPORTER
, mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex")
, mCrashReporter(nullptr)
#endif
#endif
, mInitOnAsyncConnect(false)
, mAsyncInitRv(NS_ERROR_NOT_INITIALIZED)
, mAsyncInitError(NPERR_NO_ERROR)
, mContentParent(nullptr)
, mIsFlashPlugin(false)
{
NS_ASSERTION(mSubprocess, "Out of memory!");
sInstantiated = true;
@@ -736,6 +743,119 @@ GetProcessCpuUsage(const InfallibleTArray<base::ProcessHandle>& processHandles,
#endif // #ifdef XP_WIN
void
PluginModuleChromeParent::OnEnteredCall()
{
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
MOZ_ASSERT(protocol);
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mProtocolCallStack.AppendElement(protocol);
}
void
PluginModuleChromeParent::OnExitedCall()
{
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
}
void
PluginModuleChromeParent::OnEnteredSyncSend()
{
mozilla::ipc::IProtocol* protocol = GetInvokingProtocol();
MOZ_ASSERT(protocol);
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
mProtocolCallStack.AppendElement(protocol);
}
void
PluginModuleChromeParent::OnExitedSyncSend()
{
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
MOZ_ASSERT(!mProtocolCallStack.IsEmpty());
mProtocolCallStack.RemoveElementAt(mProtocolCallStack.Length() - 1);
}
/**
* This function converts the topmost routing id on the call stack (as recorded
* by the MessageChannel) into a pointer to a IProtocol object.
*/
mozilla::ipc::IProtocol*
PluginModuleChromeParent::GetInvokingProtocol()
{
int32_t routingId = GetIPCChannel()->GetTopmostMessageRoutingId();
// Nothing being routed. No protocol. Just return nullptr.
if (routingId == MSG_ROUTING_NONE) {
return nullptr;
}
// If routingId is MSG_ROUTING_CONTROL then we're dealing with control
// messages that were initiated by the topmost managing protocol, ie. this.
if (routingId == MSG_ROUTING_CONTROL) {
return this;
}
// Otherwise we can look up the protocol object by the routing id.
mozilla::ipc::IProtocol* protocol = Lookup(routingId);
return protocol;
}
/**
* This function examines the IProtocol object parameter and converts it into
* the PluginInstanceParent object that is associated with that protocol, if
* any. Since PluginInstanceParent manages subprotocols, this function needs
* to determine whether |aProtocol| is a subprotocol, and if so it needs to
* obtain the protocol's manager.
*
* This function needs to be updated if the subprotocols are modified in
* PPluginInstance.ipdl.
*/
PluginInstanceParent*
PluginModuleChromeParent::GetManagingInstance(mozilla::ipc::IProtocol* aProtocol)
{
MOZ_ASSERT(aProtocol);
mozilla::ipc::MessageListener* listener =
static_cast<mozilla::ipc::MessageListener*>(aProtocol);
switch (listener->GetProtocolTypeId()) {
case PPluginInstanceMsgStart:
// In this case, aProtocol is the instance itself. Just cast it.
return static_cast<PluginInstanceParent*>(aProtocol);
case PPluginBackgroundDestroyerMsgStart: {
PPluginBackgroundDestroyerParent* actor =
static_cast<PPluginBackgroundDestroyerParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
case PPluginScriptableObjectMsgStart: {
PPluginScriptableObjectParent* actor =
static_cast<PPluginScriptableObjectParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
case PBrowserStreamMsgStart: {
PBrowserStreamParent* actor =
static_cast<PBrowserStreamParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
case PPluginStreamMsgStart: {
PPluginStreamParent* actor =
static_cast<PPluginStreamParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
case PStreamNotifyMsgStart: {
PStreamNotifyParent* actor =
static_cast<PStreamNotifyParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
#ifdef XP_WIN
case PPluginSurfaceMsgStart: {
PPluginSurfaceParent* actor =
static_cast<PPluginSurfaceParent*>(aProtocol);
return static_cast<PluginInstanceParent*>(actor->Manager());
}
#endif
default:
return nullptr;
}
}
void
PluginModuleChromeParent::EnteredCxxStack()
{
@@ -778,6 +898,24 @@ PluginModuleChromeParent::AnnotateHang(mozilla::HangMonitor::HangAnnotations& aA
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginName"), mPluginName);
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginVersion"),
mPluginVersion);
if (mIsFlashPlugin) {
bool isWhitelistedForShumway = false;
{ // Scope for lock
mozilla::MutexAutoLock lock(mHangAnnotatorMutex);
if (!mProtocolCallStack.IsEmpty()) {
mozilla::ipc::IProtocol* topProtocol =
mProtocolCallStack.LastElement();
PluginInstanceParent* instance =
GetManagingInstance(topProtocol);
if (instance) {
isWhitelistedForShumway =
instance->IsWhitelistedForShumway();
}
}
}
aAnnotations.AddAnnotation(NS_LITERAL_STRING("pluginIsWhitelistedForShumway"),
isWhitelistedForShumway);
}
}
}
@@ -849,8 +987,7 @@ PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop)
}
bool
PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
nsACString& aPluginVersion)
PluginModuleParent::GetPluginDetails()
{
nsRefPtr<nsPluginHost> host = nsPluginHost::GetInst();
if (!host) {
@@ -860,8 +997,9 @@ PluginModuleParent::GetPluginDetails(nsACString& aPluginName,
if (!pluginTag) {
return false;
}
aPluginName = pluginTag->mName;
aPluginVersion = pluginTag->mVersion;
mPluginName = pluginTag->mName;
mPluginVersion = pluginTag->mVersion;
mIsFlashPlugin = pluginTag->mIsFlashPlugin;
return true;
}
@@ -1892,7 +2030,7 @@ PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
}
if (mPluginName.IsEmpty()) {
GetPluginDetails(mPluginName, mPluginVersion);
GetPluginDetails();
/** mTimeBlocked measures the time that the main thread has been blocked
* on plugin module initialization. As implemented, this is the sum of
* plugin-container launch + toolhelp32 snapshot + NP_Initialize.
@@ -1922,6 +2060,15 @@ PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance,
return NS_PLUGIN_INIT_PENDING;
}
class nsCaseInsensitiveUTF8StringArrayComparator
{
public:
template<class A, class B>
bool Equals(const A& a, const B& b) const {
return a.Equals(b.get(), nsCaseInsensitiveUTF8StringComparator());
}
};
nsresult
PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
uint16_t mode,
@@ -1929,13 +2076,20 @@ PluginModuleParent::NPP_NewInternal(NPMIMEType pluginType, NPP instance,
InfallibleTArray<nsCString>& values,
NPSavedData* saved, NPError* error)
{
PluginInstanceParent* parentInstance =
new PluginInstanceParent(this, instance,
nsDependentCString(pluginType), mNPNIface);
nsCaseInsensitiveUTF8StringArrayComparator comparator;
NS_NAMED_LITERAL_CSTRING(srcAttributeName, "src");
auto srcAttributeIndex = names.IndexOf(srcAttributeName, 0, comparator);
nsAutoCString srcAttribute;
if (srcAttributeIndex != names.NoIndex) {
srcAttribute = values[srcAttributeIndex];
}
if (!parentInstance->Init()) {
delete parentInstance;
return NS_ERROR_FAILURE;
nsDependentCString strPluginType(pluginType);
PluginInstanceParent* parentInstance =
new PluginInstanceParent(this, instance, strPluginType, mNPNIface);
if (mIsFlashPlugin) {
parentInstance->InitMetadata(strPluginType, srcAttribute);
}
// Release the surrogate reference that was in pdata
+13 -3
View File
@@ -9,7 +9,7 @@
#include "base/process.h"
#include "mozilla/FileUtils.h"
#include "mozilla/HangMonitor.h"
#include "mozilla/HangAnnotations.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/plugins/ScopedMethodFactory.h"
#include "mozilla/plugins/PluginProcessParent.h"
@@ -261,6 +261,7 @@ protected:
TimeDuration mTimeBlocked;
nsCString mPluginName;
nsCString mPluginVersion;
bool mIsFlashPlugin;
#ifdef MOZ_X11
// Dup of plugin's X socket, used to scope its resources to this
@@ -269,7 +270,7 @@ protected:
#endif
bool
GetPluginDetails(nsACString& aPluginName, nsACString& aPluginVersion);
GetPluginDetails();
friend class mozilla::plugins::PluginAsyncSurrogate;
@@ -358,6 +359,11 @@ class PluginModuleChromeParent
void CachedSettingChanged();
void OnEnteredCall() override;
void OnExitedCall() override;
void OnEnteredSyncSend() override;
void OnExitedSyncSend() override;
private:
virtual void
EnteredCxxStack() override;
@@ -365,6 +371,9 @@ private:
void
ExitedCxxStack() override;
mozilla::ipc::IProtocol* GetInvokingProtocol();
PluginInstanceParent* GetManagingInstance(mozilla::ipc::IProtocol* aProtocol);
virtual void
AnnotateHang(mozilla::HangMonitor::HangAnnotations& aAnnotations) override;
@@ -417,6 +426,8 @@ private:
kHangUIDontShow = (1u << 3)
};
Atomic<uint32_t> mHangAnnotationFlags;
mozilla::Mutex mHangAnnotatorMutex;
InfallibleTArray<mozilla::ipc::IProtocol*> mProtocolCallStack;
#ifdef XP_WIN
InfallibleTArray<float> mPluginCpuUsageOnHang;
PluginHangUIParent *mHangUIParent;
@@ -469,7 +480,6 @@ private:
NPError mAsyncInitError;
dom::ContentParent* mContentParent;
nsCOMPtr<nsIObserver> mOfflineObserver;
bool mIsFlashPlugin;
bool mIsBlocklisted;
static bool sInstantiated;
};
+16
View File
@@ -193,6 +193,11 @@ public:
*name = mMessageName;
}
int32_t GetRoutingId() const
{
return mMessageRoutingId;
}
private:
const char* mMessageName;
int32_t mMessageRoutingId;
@@ -1826,6 +1831,17 @@ MessageChannel::DumpInterruptStack(const char* const pfx) const
}
}
int32_t
MessageChannel::GetTopmostMessageRoutingId() const
{
MOZ_ASSERT(MessageLoop::current() == mWorkerLoop);
if (mCxxStackFrames.empty()) {
return MSG_ROUTING_NONE;
}
const InterruptFrame& frame = mCxxStackFrames.back();
return frame.GetRoutingId();
}
bool
ParentProcessIsBlocked()
{
+8
View File
@@ -135,6 +135,14 @@ class MessageChannel : HasResultCodes
return !mCxxStackFrames.empty();
}
/**
* This function is used by hang annotation code to determine which IPDL
* actor is highest in the call stack at the time of the hang. It should
* be called from the main thread when a sync or intr message is about to
* be sent.
*/
int32_t GetTopmostMessageRoutingId() const;
void FlushPendingInterruptQueue();
// Unsound_IsClosed and Unsound_NumQueuedMessages are safe to call from any
+15 -5
View File
@@ -5160,6 +5160,11 @@ static bool
CheckInternalCall(FunctionCompiler& f, ParseNode* callNode, PropertyName* calleeName,
RetType retType, MDefinition** def, Type* type)
{
if (!f.canCall()) {
return f.fail(callNode, "call expressions may not be nested inside heap expressions "
"when the module contains a change-heap function");
}
FunctionCompiler::Call call(f, callNode, retType);
if (!CheckCallArgs(f, callNode, CheckIsVarType, &call))
@@ -5205,6 +5210,11 @@ CheckFuncPtrTableAgainstExisting(ModuleCompiler& m, ParseNode* usepn,
static bool
CheckFuncPtrCall(FunctionCompiler& f, ParseNode* callNode, RetType retType, MDefinition** def, Type* type)
{
if (!f.canCall()) {
return f.fail(callNode, "function-pointer call expressions may not be nested inside heap "
"expressions when the module contains a change-heap function");
}
ParseNode* callee = CallCallee(callNode);
ParseNode* tableNode = ElemBase(callee);
ParseNode* indexExpr = ElemIndex(callee);
@@ -5264,6 +5274,11 @@ static bool
CheckFFICall(FunctionCompiler& f, ParseNode* callNode, unsigned ffiIndex, RetType retType,
MDefinition** def, Type* type)
{
if (!f.canCall()) {
return f.fail(callNode, "FFI call expressions may not be nested inside heap "
"expressions when the module contains a change-heap function");
}
PropertyName* calleeName = CallCallee(callNode)->name();
if (retType == RetType::Float)
@@ -6130,11 +6145,6 @@ CheckCoercedCall(FunctionCompiler& f, ParseNode* call, RetType retType, MDefinit
{
JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
if (!f.canCall()) {
return f.fail(call, "call expressions may not be nested inside heap expressions when "
"the module contains a change-heap function");
}
if (IsNumericLiteral(f.m(), call)) {
AsmJSNumLit literal = ExtractNumericLiteral(f.m(), call);
MDefinition* result = f.constant(literal);
+8 -1
View File
@@ -147,13 +147,18 @@ assertAsmTypeFail('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'function ch(b2) { if
// Tests for no calls in heap index expressions
const SETUP = USE_ASM + IMPORT2 + 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true }';
const CHANGE_FUN = 'function ch(b2) { if(len(b2) & 0xffffff || len(b2) <= 0xffffff || len(b2) > 0x80000000) return false; i8=new I8(b2); i32=new I32(b2); b=b2; return true }';
const SETUP = USE_ASM + IMPORT2 + 'var imul=glob.Math.imul; var ffi=ffis.ffi;' + CHANGE_FUN;
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { i32[0] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(imul(i,i)|0) >> 2] = 0 } return f');
asmCompile('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (imul(i,i)|0) } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(ffi()|0) >> 2] } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(TBL[i&0]()|0) >> 2] } function g() { return 0 } var TBL=[g]; return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[(g()|0) >> 2] = 0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = g()|0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i32[(g()|0)>>2] >> 2] } function g() { return 0 } return f');
@@ -162,6 +167,8 @@ assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[((i32[i>>2]|0) + (g()|0)) >> 2] = 0 } function g() { return 0 } return f');
assertAsmTypeFail('glob', 'ffis', 'b', SETUP + 'function f() { var i = 0; i32[i >> 2] = (i32[i>>2]|0) + (g()|0) } function g() { return 0 } return f');
if (isSimdAvailable() && typeof SIMD !== 'undefined')
asmCompile('glob', 'ffis', 'b', USE_ASM + IMPORT2 + 'var i4 = glob.SIMD.int32x4; var add = i4.add;' + CHANGE_FUN + 'function f(i) { i=i|0; i32[i4(i,1,2,i).x >> 2]; i32[add(i4(0,0,0,0),i4(1,1,1,1)).x >> 2]; } return f');
// Tests for constant heap accesses when change-heap is used
+19 -1
View File
@@ -83,6 +83,9 @@ CollectLaterSiblings(nsISupports* aElement,
struct RestyleEnumerateData : RestyleTracker::Hints {
nsRefPtr<dom::Element> mElement;
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
UniquePtr<ProfilerBacktrace> mBacktrace;
#endif
};
struct RestyleCollector {
@@ -140,7 +143,9 @@ CollectRestyles(nsISupports* aElement,
currentRestyle->mElement = element;
currentRestyle->mRestyleHint = aData->mRestyleHint;
currentRestyle->mChangeHint = aData->mChangeHint;
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
currentRestyle->mBacktrace = Move(aData->mBacktrace);
#endif
#ifdef RESTYLE_LOGGING
collector->count++;
#endif
@@ -305,6 +310,12 @@ RestyleTracker::DoProcessRestyles()
continue;
}
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
Maybe<GoannaProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
}
#endif
ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint);
AddRestyleRootsIfAwaitingRestyle(data->mDescendants);
}
@@ -340,6 +351,13 @@ RestyleTracker::DoProcessRestyles()
FrameTagToString(currentRestyle->mElement).get(),
index++, collector.count);
LOG_RESTYLE_INDENT();
#if defined(MOZ_ENABLE_PROFILER_SPS) && !defined(MOZILLA_XPCOMRT_API)
Maybe<GoannaProfilerTracingRAII> profilerRAII;
if (profiler_feature_active("restyle")) {
profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
}
#endif
ProcessOneRestyle(currentRestyle->mElement,
currentRestyle->mRestyleHint,
currentRestyle->mChangeHint);
+15 -2
View File
@@ -16,6 +16,11 @@
#include "nsContainerFrame.h"
#include "mozilla/SplayTree.h"
#include "mozilla/RestyleLogging.h"
#include "GoannaProfiler.h"
#if defined(MOZ_ENABLE_PROFILER_SPS)
#include "ProfilerBacktrace.h"
#endif
namespace mozilla {
@@ -291,6 +296,9 @@ public:
// that we called AddPendingRestyle for and found the element this is
// the RestyleData for as its nearest restyle root.
nsTArray<nsRefPtr<Element>> mDescendants;
#if defined(MOZ_ENABLE_PROFILER_SPS)
UniquePtr<ProfilerBacktrace> mBacktrace;
#endif
};
/**
@@ -388,8 +396,13 @@ RestyleTracker::AddPendingRestyleToTable(Element* aElement,
}
if (!existingData) {
mPendingRestyles.Put(aElement,
new RestyleData(aRestyleHint, aMinChangeHint));
RestyleData* rd = new RestyleData(aRestyleHint, aMinChangeHint);
#if defined(MOZ_ENABLE_PROFILER_SPS)
if (profiler_feature_active("restyle")) {
rd->mBacktrace.reset(profiler_get_backtrace());
}
#endif
mPendingRestyles.Put(aElement, rd);
return false;
}
+2 -2
View File
@@ -141,7 +141,7 @@ MOZ_ReportAssertionFailure(const char* aStr, const char* aFilename, int aLine)
aStr, aFilename, aLine);
#else
fprintf(stderr, "Assertion failure: %s, at %s:%d\n", aStr, aFilename, aLine);
#ifdef MOZ_DUMP_ASSERTION_STACK
#if defined (MOZ_DUMP_ASSERTION_STACK) && !defined(MOZILLA_XPCOMRT_API)
nsTraceRefcnt::WalkTheStack(stderr);
#endif
fflush(stderr);
@@ -157,7 +157,7 @@ MOZ_ReportCrash(const char* aStr, const char* aFilename, int aLine)
"Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
#else
fprintf(stderr, "Hit MOZ_CRASH(%s) at %s:%d\n", aStr, aFilename, aLine);
#ifdef MOZ_DUMP_ASSERTION_STACK
#if defined(MOZ_DUMP_ASSERTION_STACK) && !defined(MOZILLA_XPCOMRT_API)
nsTraceRefcnt::WalkTheStack(stderr);
#endif
fflush(stderr);
+1
View File
@@ -22,6 +22,7 @@
#endif
#if defined(MOZILLA_INTERNAL_API) && \
!defined(MOZILLA_XPCOMRT_API) && \
(defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
#define MOZ_REFCOUNTED_LEAK_CHECKING
#endif
+1 -1
View File
@@ -89,7 +89,7 @@
* symbols. We add the weak attribute to the import version of the MFBT API
* macros to exploit this.
*/
# if defined(MOZ_GLUE_IN_PROGRAM)
# if defined(MOZ_GLUE_IN_PROGRAM) && !defined(MOZILLA_XPCOMRT_API)
# define MFBT_API __attribute__((weak)) MOZ_IMPORT_API
# define MFBT_DATA __attribute__((weak)) MOZ_IMPORT_DATA
# else
@@ -108,10 +108,10 @@ function testHistogram(histogramId, expectedNonZeroRanges) {
* the test data that will be used by the following tests.
*/
add_task(function test_initialize() {
let oldCanRecord = Services.telemetry.canRecord;
Services.telemetry.canRecord = true;
let oldCanRecord = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
do_register_cleanup(function () {
Services.telemetry.canRecord = oldCanRecord;
Services.telemetry.canRecordExtended = oldCanRecord;
});
let uniqueNumber = 1;
@@ -4338,6 +4338,12 @@
"extended_statistics_ok": true,
"description": "Number of histograms with total count low errors"
},
"TELEMETRY_DISCARDED_CONTENT_PINGS_COUNT": {
"alert_emails": ["perf-telemetry-alerts@mozilla.com"],
"expires_in_version": "never",
"kind": "count",
"description": "Count of discarded content payloads."
},
"TELEMETRY_FILES_EVICTED": {
"alert_emails": ["perf-telemetry-alerts@mozilla.com", "rvitillo@mozilla.com"],
"expires_in_version": "never",
+322 -84
View File
@@ -41,6 +41,7 @@
#include "nsTHashtable.h"
#include "nsHashKeys.h"
#include "nsBaseHashtable.h"
#include "nsClassHashtable.h"
#include "nsXULAppAPI.h"
#include "nsReadableUtils.h"
#include "nsThreadUtils.h"
@@ -53,6 +54,7 @@
#include "nsReadableUtils.h"
#include "plstr.h"
#include "nsAppDirectoryServiceDefs.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/ProcessedStack.h"
#include "mozilla/Mutex.h"
#include "mozilla/FileUtils.h"
@@ -78,6 +80,7 @@ using base::LinearHistogram;
using base::StatisticsRecorder;
const char KEYED_HISTOGRAM_NAME_SEPARATOR[] = "#";
const char SUBSESSION_HISTOGRAM_PREFIX[] = "sub#";
enum reflectStatus {
REFLECT_OK,
@@ -234,7 +237,7 @@ public:
*/
struct AnnotationInfo {
AnnotationInfo(uint32_t aHangIndex,
UniquePtr<HangAnnotations> aAnnotations)
HangAnnotationsPtr aAnnotations)
: mHangIndex(aHangIndex)
, mAnnotations(Move(aAnnotations))
{}
@@ -250,7 +253,7 @@ public:
return *this;
}
uint32_t mHangIndex;
UniquePtr<HangAnnotations> mAnnotations;
HangAnnotationsPtr mAnnotations;
private:
// Force move constructor
@@ -260,7 +263,7 @@ public:
size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
void AddHang(const Telemetry::ProcessedStack& aStack, uint32_t aDuration,
int32_t aSystemUptime, int32_t aFirefoxUptime,
UniquePtr<HangAnnotations> aAnnotations);
HangAnnotationsPtr aAnnotations);
uint32_t GetDuration(unsigned aIndex) const;
int32_t GetSystemUptime(unsigned aIndex) const;
int32_t GetFirefoxUptime(unsigned aIndex) const;
@@ -289,7 +292,7 @@ HangReports::AddHang(const Telemetry::ProcessedStack& aStack,
uint32_t aDuration,
int32_t aSystemUptime,
int32_t aFirefoxUptime,
UniquePtr<HangAnnotations> aAnnotations) {
HangAnnotationsPtr aAnnotations) {
HangInfo info = { aDuration, aSystemUptime, aFirefoxUptime };
mHangInfo.push_back(info);
if (aAnnotations) {
@@ -639,7 +642,8 @@ class TelemetryImpl final
public:
void InitMemoryReporter();
static bool CanRecord();
static bool CanRecordBase();
static bool CanRecordExtended();
static already_AddRefed<nsITelemetry> CreateTelemetryInstance();
static void ShutdownTelemetry();
static void RecordSlowStatement(const nsACString &sql, const nsACString &dbName,
@@ -685,6 +689,10 @@ private:
nsresult GetHistogramByName(const nsACString &name, Histogram **ret);
bool ShouldReflectHistogram(Histogram *h);
void IdentifyCorruptHistograms(StatisticsRecorder::Histograms &hs);
nsresult CreateHistogramSnapshots(JSContext *cx,
JS::MutableHandle<JS::Value> ret,
bool subsession,
bool clearSubsession);
typedef StatisticsRecorder::Histograms::iterator HistogramIterator;
struct AddonHistogramInfo {
@@ -710,7 +718,8 @@ private:
typedef nsBaseHashtableET<nsDepCharHashKey, Telemetry::ID> CharPtrEntryType;
typedef AutoHashtable<CharPtrEntryType> HistogramMapType;
HistogramMapType mHistogramMap;
bool mCanRecord;
bool mCanRecordBase;
bool mCanRecordExtended;
static TelemetryImpl *sTelemetry;
AutoHashtable<SlowSQLEntryType> mPrivateSQL;
AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
@@ -757,18 +766,22 @@ public:
KeyedHistogram(const nsACString &name, const nsACString &expiration,
uint32_t histogramType, uint32_t min, uint32_t max,
uint32_t bucketCount);
nsresult GetHistogram(const nsCString& name, Histogram** histogram);
Histogram* GetHistogram(const nsCString& name);
nsresult GetHistogram(const nsCString& name, Histogram** histogram, bool subsession);
Histogram* GetHistogram(const nsCString& name, bool subsession);
uint32_t GetHistogramType() const { return mHistogramType; }
nsresult GetDataset(uint32_t* dataset) const;
nsresult GetJSKeys(JSContext* cx, JS::CallArgs& args);
nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj);
void Clear();
nsresult GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
bool subsession, bool clearSubsession);
nsresult Add(const nsCString& key, uint32_t aSample);
void Clear(bool subsession);
private:
typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry;
typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType;
KeyedHistogramMapType mHistogramMap;
KeyedHistogramMapType mSubsessionMap;
struct ReflectKeysArgs {
JSContext* jsContext;
@@ -971,6 +984,89 @@ GetHistogramByEnumId(Telemetry::ID id, Histogram **ret)
return NS_OK;
}
/**
* This clones a histogram |existing| with the id |existingId| to a
* new histogram with the name |newName|.
* For simplicity this is limited to registered histograms.
*/
Histogram*
CloneHistogram(const nsACString& newName, Telemetry::ID existingId,
Histogram& existing)
{
const TelemetryHistogram &info = gHistograms[existingId];
Histogram *clone = nullptr;
nsresult rv;
rv = HistogramGet(PromiseFlatCString(newName).get(), info.expiration(),
info.histogramType, existing.declared_min(),
existing.declared_max(), existing.bucket_count(),
true, &clone);
if (NS_FAILED(rv)) {
return nullptr;
}
Histogram::SampleSet ss;
existing.SnapshotSample(&ss);
clone->AddSampleSet(ss);
return clone;
}
/**
* This clones a histogram with the id |existingId| to a new histogram
* with the name |newName|.
* For simplicity this is limited to registered histograms.
*/
Histogram*
CloneHistogram(const nsACString& newName, Telemetry::ID existingId)
{
Histogram *existing = nullptr;
nsresult rv = GetHistogramByEnumId(existingId, &existing);
if (NS_FAILED(rv)) {
return nullptr;
}
return CloneHistogram(newName, existingId, *existing);
}
Histogram*
GetSubsessionHistogram(Histogram& existing)
{
Telemetry::ID id;
nsresult rv = TelemetryImpl::GetHistogramEnumId(existing.histogram_name().c_str(), &id);
if (NS_FAILED(rv) || gHistograms[id].keyed) {
return nullptr;
}
static Histogram* subsession[Telemetry::HistogramCount] = {};
if (subsession[id]) {
return subsession[id];
}
NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX);
nsDependentCString existingName(gHistograms[id].id());
if (StringBeginsWith(existingName, prefix)) {
return nullptr;
}
nsCString subsessionName(prefix);
subsessionName.Append(existingName);
subsession[id] = CloneHistogram(subsessionName, id, existing);
return subsession[id];
}
nsresult
HistogramAdd(Histogram& histogram, int32_t value)
{
histogram.Add(value);
if (Histogram* subsession = GetSubsessionHistogram(histogram)) {
subsession->Add(value);
}
return NS_OK;
}
bool
FillRanges(JSContext *cx, JS::Handle<JSObject*> array, Histogram *h)
{
@@ -1069,6 +1165,7 @@ JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
}
Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
MOZ_ASSERT(h);
Histogram::ClassType type = h->histogram_type();
int32_t value = 1;
@@ -1089,8 +1186,8 @@ JSHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
}
}
if (TelemetryImpl::CanRecord()) {
h->Add(value);
if (TelemetryImpl::CanRecordExtended()) {
HistogramAdd(*h, value);
}
return true;
@@ -1132,8 +1229,28 @@ JSHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
return false;
}
bool onlySubsession = false;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() >= 1) {
if (!args[0].isBoolean()) {
JS_ReportError(cx, "Not a boolean");
return false;
}
onlySubsession = JS::ToBoolean(args[0]);
}
Histogram *h = static_cast<Histogram*>(JS_GetPrivate(obj));
h->Clear();
MOZ_ASSERT(h);
if(!onlySubsession) {
h->Clear();
}
if (Histogram* subsession = GetSubsessionHistogram(*h)) {
subsession->Clear();
}
return true;
}
@@ -1223,17 +1340,7 @@ JSKeyedHistogram_Add(JSContext *cx, unsigned argc, JS::Value *vp)
}
}
Histogram* h = nullptr;
nsresult rv = keyed->GetHistogram(NS_ConvertUTF16toUTF8(key), &h);
if (NS_FAILED(rv)) {
JS_ReportError(cx, "Failed to get histogram");
return false;
}
if (TelemetryImpl::CanRecord()) {
h->Add(value);
}
keyed->Add(NS_ConvertUTF16toUTF8(key), value);
return true;
}
@@ -1255,7 +1362,8 @@ JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp)
}
bool
JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
KeyedHistogram_SnapshotImpl(JSContext *cx, unsigned argc, JS::Value *vp,
bool subsession, bool clearSubsession)
{
JSObject *obj = JS_THIS_OBJECT(cx, vp);
if (!obj) {
@@ -1276,7 +1384,7 @@ JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
return false;
}
if (!NS_SUCCEEDED(keyed->GetJSSnapshot(cx, snapshot))) {
if (!NS_SUCCEEDED(keyed->GetJSSnapshot(cx, snapshot, subsession, clearSubsession))) {
JS_ReportError(cx, "Failed to reflect keyed histograms");
return false;
}
@@ -1292,7 +1400,7 @@ JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
}
Histogram* h = nullptr;
nsresult rv = keyed->GetHistogram(NS_ConvertUTF16toUTF8(key), &h);
nsresult rv = keyed->GetHistogram(NS_ConvertUTF16toUTF8(key), &h, subsession);
if (NS_FAILED(rv)) {
JS_ReportError(cx, "Failed to get histogram");
return false;
@@ -1317,6 +1425,29 @@ JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
}
}
bool
JSKeyedHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp)
{
return KeyedHistogram_SnapshotImpl(cx, argc, vp, false, false);
}
bool
JSKeyedHistogram_SubsessionSnapshot(JSContext *cx, unsigned argc, JS::Value *vp)
{
return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, false);
}
bool
JSKeyedHistogram_SnapshotSubsessionAndClear(JSContext *cx, unsigned argc, JS::Value *vp)
{
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() != 0) {
JS_ReportError(cx, "No key arguments supported for snapshotSubsessionAndClear");
}
return KeyedHistogram_SnapshotImpl(cx, argc, vp, true, true);
}
bool
JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
{
@@ -1330,7 +1461,19 @@ JSKeyedHistogram_Clear(JSContext *cx, unsigned argc, JS::Value *vp)
return false;
}
keyed->Clear();
bool onlySubsession = false;
JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
if (args.length() >= 1) {
if (!(args[0].isNumber() || args[0].isBoolean())) {
JS_ReportError(cx, "Not a boolean");
return false;
}
onlySubsession = JS::ToBoolean(args[0]);
}
keyed->Clear(onlySubsession);
return true;
}
@@ -1371,6 +1514,8 @@ WrapAndReturnKeyedHistogram(KeyedHistogram *h, JSContext *cx, JS::MutableHandle<
return NS_ERROR_FAILURE;
if (!(JS_DefineFunction(cx, obj, "add", JSKeyedHistogram_Add, 2, 0)
&& JS_DefineFunction(cx, obj, "snapshot", JSKeyedHistogram_Snapshot, 1, 0)
&& JS_DefineFunction(cx, obj, "subsessionSnapshot", JSKeyedHistogram_SubsessionSnapshot, 1, 0)
&& JS_DefineFunction(cx, obj, "snapshotSubsessionAndClear", JSKeyedHistogram_SnapshotSubsessionAndClear, 0, 0)
&& JS_DefineFunction(cx, obj, "keys", JSKeyedHistogram_Keys, 0, 0)
&& JS_DefineFunction(cx, obj, "clear", JSKeyedHistogram_Clear, 0, 0)
&& JS_DefineFunction(cx, obj, "dataset", JSKeyedHistogram_Dataset, 0, 0))) {
@@ -1568,7 +1713,7 @@ TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback)
// We make this check so that GetShutdownTimeFileName() doesn't get
// called; calling that function without telemetry enabled violates
// assumptions that the write-the-shutdown-timestamp machinery makes.
if (!Telemetry::CanRecord()) {
if (!Telemetry::CanRecordExtended()) {
mCachedTelemetryData = true;
aCallback->Complete();
return NS_OK;
@@ -1622,7 +1767,10 @@ TelemetryImpl::AsyncFetchTelemetryData(nsIFetchTelemetryDataCallback *aCallback)
TelemetryImpl::TelemetryImpl():
mHistogramMap(Telemetry::HistogramCount),
mCanRecord(XRE_GetProcessType() == GoannaProcessType_Default),
mCanRecordBase(XRE_GetProcessType() == GoannaProcessType_Default ||
XRE_GetProcessType() == GoannaProcessType_Content),
mCanRecordExtended(XRE_GetProcessType() == GoannaProcessType_Default ||
XRE_GetProcessType() == GoannaProcessType_Content),
mHashMutex("Telemetry::mHashMutex"),
mHangReportsMutex("Telemetry::mHangReportsMutex"),
mCachedTelemetryData(false),
@@ -1658,6 +1806,7 @@ mFailedLockCount(0)
mTrackedDBs.MarkImmutable();
#endif
// Create registered keyed histograms
for (size_t i = 0; i < ArrayLength(gHistograms); ++i) {
const TelemetryHistogram& h = gHistograms[i];
if (!h.keyed) {
@@ -1834,25 +1983,12 @@ TelemetryImpl::HistogramFrom(const nsACString &name, const nsACString &existing_
if (NS_FAILED(rv)) {
return rv;
}
const TelemetryHistogram &p = gHistograms[id];
Histogram *existing;
rv = GetHistogramByEnumId(id, &existing);
if (NS_FAILED(rv)) {
return rv;
Histogram* clone = CloneHistogram(name, id);
if (!clone) {
return NS_ERROR_FAILURE;
}
Histogram *clone;
rv = HistogramGet(PromiseFlatCString(name).get(), p.expiration(),
p.histogramType, existing->declared_min(),
existing->declared_max(), existing->bucket_count(),
true, &clone);
if (NS_FAILED(rv))
return rv;
Histogram::SampleSet ss;
existing->SnapshotSample(&ss);
clone->AddSampleSet(ss);
return WrapAndReturnHistogram(clone, cx, ret);
}
@@ -2035,8 +2171,11 @@ TelemetryImpl::UnregisterAddonHistograms(const nsACString &id)
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
nsresult
TelemetryImpl::CreateHistogramSnapshots(JSContext *cx,
JS::MutableHandle<JS::Value> ret,
bool subsession,
bool clearSubsession)
{
JS::Rooted<JSObject*> root_obj(cx, JS_NewPlainObject(cx));
if (!root_obj)
@@ -2077,6 +2216,14 @@ TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value>
continue;
}
Histogram* original = h;
if (subsession) {
h = GetSubsessionHistogram(*h);
if (!h) {
continue;
}
}
hobj = JS_NewPlainObject(cx);
if (!hobj) {
return NS_ERROR_FAILURE;
@@ -2090,15 +2237,33 @@ TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value>
case REFLECT_FAILURE:
return NS_ERROR_FAILURE;
case REFLECT_OK:
if (!JS_DefineProperty(cx, root_obj, h->histogram_name().c_str(), hobj,
JSPROP_ENUMERATE)) {
if (!JS_DefineProperty(cx, root_obj, original->histogram_name().c_str(),
hobj, JSPROP_ENUMERATE)) {
return NS_ERROR_FAILURE;
}
}
if (subsession && clearSubsession) {
h->Clear();
}
}
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::GetHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret)
{
return CreateHistogramSnapshots(cx, ret, false, false);
}
NS_IMETHODIMP
TelemetryImpl::SnapshotSubsessionHistograms(bool clearSubsession,
JSContext *cx,
JS::MutableHandle<JS::Value> ret)
{
return CreateHistogramSnapshots(cx, ret, true, clearSubsession);
}
bool
TelemetryImpl::CreateHistogramForAddon(const nsACString &name,
AddonHistogramInfo &info)
@@ -2204,7 +2369,7 @@ TelemetryImpl::KeyedHistogramsReflector(const nsACString& key, nsAutoPtr<KeyedHi
return PL_DHASH_STOP;
}
if (!NS_SUCCEEDED(entry->GetJSSnapshot(cx, snapshot))) {
if (!NS_SUCCEEDED(entry->GetJSSnapshot(cx, snapshot, false, false))) {
return PL_DHASH_STOP;
}
@@ -2357,9 +2522,9 @@ TelemetryImpl::GetChromeHangs(JSContext *cx, JS::MutableHandle<JS::Value> ret)
if (!jsAnnotation) {
return NS_ERROR_FAILURE;
}
nsAutoPtr<HangAnnotations::Enumerator> annotationsEnum;
if (!annotationInfo[iterIndex].mAnnotations->GetEnumerator(
annotationsEnum.StartAssignment())) {
UniquePtr<HangAnnotations::Enumerator> annotationsEnum =
annotationInfo[iterIndex].mAnnotations->GetEnumerator();
if (!annotationsEnum) {
return NS_ERROR_FAILURE;
}
nsAutoString key;
@@ -2722,24 +2887,56 @@ TelemetryImpl::GetKeyedHistogramById(const nsACString &name)
}
NS_IMETHODIMP
TelemetryImpl::GetCanRecord(bool *ret) {
*ret = mCanRecord;
TelemetryImpl::GetCanRecordBase(bool *ret) {
*ret = mCanRecordBase;
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::SetCanRecord(bool canRecord) {
mCanRecord = !!canRecord;
TelemetryImpl::SetCanRecordBase(bool canRecord) {
mCanRecordBase = canRecord;
return NS_OK;
}
/**
* Indicates if Telemetry can record base data (FHR data). This is true if the
* FHR data reporting service or self-support are enabled.
*
* In the unlikely event that adding a new base probe is needed, please check the data
* collection wiki at https://wiki.mozilla.org/Firefox/Data_Collection and talk to the
* Telemetry team.
*/
bool
TelemetryImpl::CanRecord() {
return !sTelemetry || sTelemetry->mCanRecord;
TelemetryImpl::CanRecordBase() {
return !sTelemetry || sTelemetry->mCanRecordBase;
}
NS_IMETHODIMP
TelemetryImpl::GetCanSend(bool *ret) {
TelemetryImpl::GetCanRecordExtended(bool *ret) {
*ret = mCanRecordExtended;
return NS_OK;
}
NS_IMETHODIMP
TelemetryImpl::SetCanRecordExtended(bool canRecord) {
mCanRecordExtended = canRecord;
return NS_OK;
}
/**
* Indicates if Telemetry is allowed to record extended data. Returns false if the user
* hasn't opted into "extended Telemetry" on the Release channel, when the user has
* explicitly opted out of Telemetry on Nightly/Aurora/Beta or if manually set to false
* during tests.
* If the returned value is false, gathering of extended telemetry statistics is disabled.
*/
bool
TelemetryImpl::CanRecordExtended() {
return !sTelemetry || sTelemetry->mCanRecordExtended;
}
NS_IMETHODIMP
TelemetryImpl::GetIsOfficialTelemetry(bool *ret) {
#if defined(MOZILLA_OFFICIAL) && defined(MOZ_TELEMETRY_REPORTING)
*ret = true;
#else
@@ -2934,7 +3131,7 @@ TelemetryImpl::RecordSlowStatement(const nsACString &sql,
const nsACString &dbName,
uint32_t delay)
{
if (!sTelemetry || !sTelemetry->mCanRecord)
if (!sTelemetry || !sTelemetry->mCanRecordExtended)
return;
bool isFirefoxDB = sTelemetry->mTrackedDBs.Contains(dbName);
@@ -3069,7 +3266,7 @@ RecordShutdownStartTimeStamp() {
recorded = true;
#endif
if (!Telemetry::CanRecord())
if (!Telemetry::CanRecordExtended())
return;
gRecordedShutdownStartTime = TimeStamp::Now();
@@ -3120,13 +3317,13 @@ Accumulate(ID aHistogram, uint32_t aSample)
{
return;
/*
if (!TelemetryImpl::CanRecord()) {
if (!TelemetryImpl::CanRecordExtended()) {
return;
}
Histogram *h;
nsresult rv = GetHistogramByEnumId(aHistogram, &h);
if (NS_SUCCEEDED(rv))
h->Add(aSample);
HistogramAdd(*h, aSample);
*/
}
@@ -3135,7 +3332,7 @@ Accumulate(ID aID, const nsCString& aKey, uint32_t aSample)
{
return;
/*
if (!TelemetryImpl::CanRecord()) {
if (!TelemetryImpl::CanRecordExtended()) {
return;
}
@@ -3143,10 +3340,7 @@ return;
KeyedHistogram* keyed = TelemetryImpl::GetKeyedHistogramById(nsDependentCString(th.id()));
MOZ_ASSERT(keyed);
Histogram* histogram = keyed->GetHistogram(aKey);
if (histogram) {
histogram->Add(aSample);
}
keyed->Add(aKey, aSample);
*/
}
@@ -3155,7 +3349,7 @@ Accumulate(const char* name, uint32_t sample)
{
return;
/*
if (!TelemetryImpl::CanRecord()) {
if (!TelemetryImpl::CanRecordExtended()) {
return;
}
ID id;
@@ -3167,7 +3361,7 @@ return;
Histogram *h;
rv = GetHistogramByEnumId(id, &h);
if (NS_SUCCEEDED(rv)) {
h->Add(sample);
HistogramAdd(*h, sample);
}
*/
}
@@ -3183,9 +3377,15 @@ return;
}
bool
CanRecord()
CanRecordBase()
{
return false; // TelemetryImpl::CanRecord();
return false; // TelemetryImpl::CanRecordBase();
}
bool
CanRecordExtended()
{
return false; // TelemetryImpl::CanRecordExtended();
}
base::Histogram*
@@ -3388,6 +3588,7 @@ KeyedHistogram::KeyedHistogram(const nsACString &name, const nsACString &expirat
uint32_t histogramType, uint32_t min, uint32_t max,
uint32_t bucketCount)
: mHistogramMap()
, mSubsessionMap()
, mName(name)
, mExpiration(expiration)
, mHistogramType(histogramType)
@@ -3398,15 +3599,21 @@ KeyedHistogram::KeyedHistogram(const nsACString &name, const nsACString &expirat
}
nsresult
KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram)
KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram,
bool subsession)
{
KeyedHistogramEntry* entry = mHistogramMap.GetEntry(key);
KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
KeyedHistogramEntry* entry = map.GetEntry(key);
if (entry) {
*histogram = entry->mData;
return NS_OK;
}
nsCString histogramName = mName;
nsCString histogramName;
if (subsession) {
histogramName.Append(SUBSESSION_HISTOGRAM_PREFIX);
}
histogramName.Append(mName);
histogramName.Append(KEYED_HISTOGRAM_NAME_SEPARATOR);
histogramName.Append(key);
@@ -3422,7 +3629,7 @@ KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram)
h->SetFlags(Histogram::kExtendedStatisticsFlag);
*histogram = h;
entry = mHistogramMap.PutEntry(key);
entry = map.PutEntry(key);
if (MOZ_UNLIKELY(!entry)) {
return NS_ERROR_OUT_OF_MEMORY;
}
@@ -3432,10 +3639,10 @@ KeyedHistogram::GetHistogram(const nsCString& key, Histogram** histogram)
}
Histogram*
KeyedHistogram::GetHistogram(const nsCString& key)
KeyedHistogram::GetHistogram(const nsCString& key, bool subsession)
{
Histogram* h = nullptr;
if (NS_FAILED(GetHistogram(key, &h))) {
if (NS_FAILED(GetHistogram(key, &h, subsession))) {
return nullptr;
}
return h;
@@ -3464,9 +3671,34 @@ KeyedHistogram::ClearHistogramEnumerator(KeyedHistogramEntry* entry, void*)
return PL_DHASH_NEXT;
}
void
KeyedHistogram::Clear()
nsresult
KeyedHistogram::Add(const nsCString& key, uint32_t sample)
{
if (!TelemetryImpl::CanRecordExtended()) {
return NS_OK;
}
Histogram* histogram = GetHistogram(key, false);
Histogram* subsession = GetHistogram(key, true);
MOZ_ASSERT(histogram && subsession);
if (!histogram || !subsession) {
return NS_ERROR_FAILURE;
}
histogram->Add(sample);
subsession->Add(sample);
return NS_OK;
}
void
KeyedHistogram::Clear(bool onlySubsession)
{
mSubsessionMap.EnumerateEntries(&KeyedHistogram::ClearHistogramEnumerator, nullptr);
mSubsessionMap.Clear();
if (onlySubsession) {
return;
}
mHistogramMap.EnumerateEntries(&KeyedHistogram::ClearHistogramEnumerator, nullptr);
mHistogramMap.Clear();
}
@@ -3532,11 +3764,17 @@ KeyedHistogram::ReflectKeyedHistogram(KeyedHistogramEntry* entry, JSContext* cx,
}
nsresult
KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj)
KeyedHistogram::GetJSSnapshot(JSContext* cx, JS::Handle<JSObject*> obj,
bool subsession, bool clearSubsession)
{
if (!mHistogramMap.ReflectIntoJS(&KeyedHistogram::ReflectKeyedHistogram, cx, obj)) {
KeyedHistogramMapType& map = subsession ? mSubsessionMap : mHistogramMap;
if (!map.ReflectIntoJS(&KeyedHistogram::ReflectKeyedHistogram, cx, obj)) {
return NS_ERROR_FAILURE;
}
if (subsession && clearSubsession) {
Clear(true);
}
return NS_OK;
}
+8 -4
View File
@@ -174,11 +174,15 @@ private:
};
/**
* Indicates whether Telemetry recording is turned on. This is intended
* to guard calls to Accumulate when the statistic being recorded is
* expensive to compute.
* Indicates whether Telemetry base data recording is turned on. Added for future uses.
*/
bool CanRecord();
bool CanRecordBase();
/**
* Indicates whether Telemetry extended data recording is turned on. This is intended
* to guard calls to Accumulate when the statistic being recorded is expensive to compute.
*/
bool CanRecordExtended();
/**
* Records slow SQL statements for Telemetry reporting.
@@ -0,0 +1,143 @@
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = [
"TelemetryEnvironment",
];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Log.jsm");
const LOGGER_NAME = "Toolkit.Telemetry";
this.TelemetryEnvironment = {
_shutdown: true,
// A map of (sync) listeners that will be called on environment changes.
_changeListeners: new Map(),
// Async task for collecting the environment data.
_collectTask: null,
_doNotify: false,
/**
* Initialize TelemetryEnvironment.
*/
init: function () {
if (!this._shutdown) {
this._log.error("init - Already initialized");
return;
}
this._configureLog();
this._log.trace("init");
this._shutdown = false;
},
/**
* Shutdown TelemetryEnvironment.
* @return Promise<> that is resolved when shutdown is finished.
*/
shutdown: Task.async(function* () {
if (this._shutdown) {
if (this._log) {
this._log.error("shutdown - Already shut down");
} else {
Cu.reportError("TelemetryEnvironment.shutdown - Already shut down");
}
return;
}
this._log.trace("shutdown");
this._shutdown = true;
this._changeListeners.clear();
yield this._collectTask;
}),
_configureLog: function () {
if (this._log) {
return;
}
this._log = Log.repository.getLoggerWithMessagePrefix(
LOGGER_NAME, "TelemetryEnvironment::");
},
/**
* Register a listener for environment changes.
* It's fine to call this on an unitialized TelemetryEnvironment.
* @param name The name of the listener - good for debugging purposes.
* @param listener A JS callback function.
*/
registerChangeListener: function (name, listener) {
this._configureLog();
this._log.trace("registerChangeListener for " + name);
if (this._shutdown) {
this._log.warn("registerChangeListener - already shutdown")
return;
}
this._changeListeners.set(name, listener);
},
/**
* Unregister from listening to environment changes.
* It's fine to call this on an unitialized TelemetryEnvironment.
* @param name The name of the listener to remove.
*/
unregisterChangeListener: function (name) {
this._configureLog();
this._log.trace("unregisterChangeListener for " + name);
if (this._shutdown) {
this._log.warn("registerChangeListener - already shutdown")
return;
}
this._changeListeners.delete(name);
},
/**
* Get the environment data in object form.
* @return Promise<Object> Resolved with the data on success, otherwise rejected.
*/
getEnvironmentData: function() {
if (this._shutdown) {
this._log.error("getEnvironmentData - Already shut down");
return Promise.reject("Already shutdown");
}
this._log.trace("getEnvironmentData");
if (this._collectTask) {
return this._collectTask;
}
this._collectTask = this._doGetEnvironmentData();
let clear = () => this._collectTask = null;
this._collectTask.then(clear, clear);
return this._collectTask;
},
_doGetEnvironmentData: Task.async(function* () {
this._log.trace("getEnvironmentData");
return {};
}),
_onEnvironmentChange: function (what) {
this._log.trace("_onEnvironmentChange for " + what);
if (this._shutdown) {
this._log.trace("_onEnvironmentChange - Already shut down.");
return;
}
for (let [name, listener] of this._changeListeners) {
try {
this._log.debug("_onEnvironmentChange - calling " + name);
listener();
} catch (e) {
this._log.warning("_onEnvironmentChange - listener " + name + " caught error", e);
}
}
},
};
@@ -244,13 +244,9 @@ this.TelemetryFile = {
*
* @return {iterator}
*/
popPendingPings: function*(reason) {
popPendingPings: function*() {
while (pendingPings.length > 0) {
let data = pendingPings.pop();
// Send persisted pings to the test URL too.
if (reason == "test-ping") {
data.reason = reason;
}
yield data;
}
},
@@ -263,7 +259,7 @@ this.TelemetryFile = {
///// Utility functions
function pingFilePath(ping) {
return OS.Path.join(TelemetryFile.pingDirectoryPath, ping.slug);
return OS.Path.join(TelemetryFile.pingDirectoryPath, ping.id);
}
function getPingDirectory() {
+489 -74
View File
@@ -30,10 +30,14 @@ const PREF_LOG_DUMP = PREF_BRANCH_LOG + "dump";
const PREF_CACHED_CLIENTID = PREF_BRANCH + "cachedClientID"
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
const PING_FORMAT_VERSION = 2;
// Delay before intializing telemetry (ms)
const TELEMETRY_DELAY = 60000;
// Delay before initializing telemetry if we're testing (ms)
const TELEMETRY_TEST_DELAY = 100;
// The number of days to keep pings serialised on the disk in case of failures.
const DEFAULT_RETENTION_DAYS = 14;
XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
"@mozilla.org/base/telemetry;1",
@@ -46,6 +50,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
"resource://gre/modules/TelemetryLog.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ThirdPartyCookieProbe",
"resource://gre/modules/ThirdPartyCookieProbe.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryEnvironment",
"resource://gre/modules/TelemetryEnvironment.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
"resource://gre/modules/UpdateChannel.jsm");
/**
* Setup Telemetry logging. This function also gets called when loggin related
@@ -108,6 +116,14 @@ this.TelemetryPing = Object.freeze({
Impl._clientID = null;
return this.setup();
},
/**
* Used only for testing purposes.
*/
shutdown: function() {
return Impl.shutdown(true);
},
/**
* Used only for testing purposes.
*/
@@ -131,9 +147,105 @@ this.TelemetryPing = Object.freeze({
/**
* Send payloads to the server.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} [aOptions] Options object.
* @param {Number} [aOptions.retentionDays=14] The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} [aOptions.addClientId=false] true if the ping should contain the client
* id, false otherwise.
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @returns {Promise} A promise that resolves when the ping is sent.
*/
send: function(aReason, aPingPayload) {
return Impl.send(aReason, aPingPayload);
send: function(aType, aPayload, aOptions = {}) {
let options = aOptions;
options.retentionDays = aOptions.retentionDays || DEFAULT_RETENTION_DAYS;
options.addClientId = aOptions.addClientId || false;
options.addEnvironment = aOptions.addEnvironment || false;
return Impl.send(aType, aPayload, options);
},
/**
* Add the ping to the pending ping list and save all pending pings.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} [aOptions] Options object.
* @param {Number} [aOptions.retentionDays=14] The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} [aOptions.addClientId=false] true if the ping should contain the client
* id, false otherwise.
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @returns {Promise} A promise that resolves when the pings are saved.
*/
savePendingPings: function(aType, aPayload, aOptions = {}) {
let options = aOptions;
options.retentionDays = aOptions.retentionDays || DEFAULT_RETENTION_DAYS;
options.addClientId = aOptions.addClientId || false;
options.addEnvironment = aOptions.addEnvironment || false;
return Impl.savePendingPings(aType, aPayload, options);
},
/**
* Save a ping to disk.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} [aOptions] Options object.
* @param {Number} [aOptions.retentionDays=14] The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} [aOptions.addClientId=false] true if the ping should contain the client
* id, false otherwise.
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @param {Boolean} [aOptions.overwrite=false] true overwrites a ping with the same name,
* if found.
*
* @returns {Promise} A promise that resolves when the ping is saved to disk.
*/
savePing: function(aType, aPayload, aOptions = {}) {
let options = aOptions;
options.retentionDays = aOptions.retentionDays || DEFAULT_RETENTION_DAYS;
options.addClientId = aOptions.addClientId || false;
options.addEnvironment = aOptions.addEnvironment || false;
options.overwrite = aOptions.overwrite || false;
return Impl.savePing(aType, aPayload, options);
},
/**
* Only used for testing. Saves a ping to disk and return the ping id once done.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} [aOptions] Options object.
* @param {Number} [aOptions.retentionDays=14] The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} [aOptions.addClientId=false] true if the ping should contain the client
* id, false otherwise.
* @param {Boolean} [aOptions.addEnvironment=false] true if the ping should contain the
* environment data.
* @param {Boolean} [aOptions.overwrite=false] true overwrites a ping with the same name,
* if found.
* @param {String} [aOptions.filePath] The path to save the ping to. Will save to default
* ping location if not provided.
*
* @returns {Promise<Integer>} A promise that resolves with the ping id when the ping is
* saved to disk.
*/
testSavePingToFile: function(aType, aPayload, aOptions = {}) {
let options = aOptions;
options.retentionDays = aOptions.retentionDays || DEFAULT_RETENTION_DAYS;
options.addClientId = aOptions.addClientId || false;
options.addEnvironment = aOptions.addEnvironment || false;
options.overwrite = aOptions.overwrite || false;
return Impl.testSavePingToFile(aType, aPayload, options);
},
/**
@@ -144,23 +256,109 @@ this.TelemetryPing = Object.freeze({
get clientID() {
return Impl.clientID;
},
/**
* The AsyncShutdown.Barrier to synchronize with TelemetryPing shutdown.
*/
get shutdown() {
return Impl._shutdownBarrier.client;
},
});
let Impl = {
_initialized: false,
_initStarted: false, // Whether we started setting up TelemetryPing.
_log: null,
_prevValues: {},
// The previous build ID, if this is the first run with a new build.
// Undefined if this is not the first run, or the previous build ID is unknown.
_previousBuildID: undefined,
_clientID: null,
// A task performing delayed initialization
_delayedInitTask: null,
popPayloads: function popPayloads(reason, externalPayload) {
_shutdownBarrier: new AsyncShutdown.Barrier("TelemetryPing: Waiting for clients."),
/**
* Get the data for the "application" section of the ping.
*/
_getApplicationSection: function() {
// Querying architecture and update channel can throw. Make sure to recover and null
// those fields.
let arch = null;
try {
arch = Services.sysinfo.get("arch");
} catch (e) {
this._log.trace("assemblePing - Unable to get system architecture.", e);
}
let updateChannel = null;
try {
updateChannel = UpdateChannel.get();
} catch (e) {
this._log.trace("assemblePing - Unable to get update channel.", e);
}
return {
architecture: arch,
buildId: Services.appinfo.appBuildID,
name: Services.appinfo.name,
version: Services.appinfo.version,
vendor: Services.appinfo.vendor,
platformVersion: Services.appinfo.platformVersion,
xpcomAbi: Services.appinfo.XPCOMABI,
channel: updateChannel,
};
},
/**
* Assemble a complete ping following the common ping format specification.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} aOptions Options object.
* @param {Boolean} aOptions.addClientId true if the ping should contain the client
* id, false otherwise.
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
* environment data.
*
* @returns Promise<Object> A promise that resolves when the ping is completely assembled.
*/
assemblePing: function assemblePing(aType, aPayload, aOptions = {}) {
this._log.trace("assemblePing - Type " + aType + ", Server " + this._server +
", aOptions " + JSON.stringify(aOptions));
// Fill the common ping fields.
let pingData = {
type: aType,
id: generateUUID(),
creationDate: (new Date()).toISOString(),
version: PING_FORMAT_VERSION,
application: this._getApplicationSection(),
payload: aPayload,
};
if (aOptions.addClientId) {
pingData.clientId = this._clientID;
}
if (aOptions.addEnvironment) {
return TelemetryEnvironment.getEnvironmentData().then(environment => {
pingData.environment = environment;
return pingData;
},
error => {
this._log.error("assemblePing - Rejection", error);
});
}
return Promise.resolve(pingData);
},
popPayloads: function popPayloads() {
this._log.trace("popPayloads");
function payloadIter() {
if (externalPayload && reason != "overdue-flush") {
yield externalPayload;
}
let iterator = TelemetryFile.popPendingPings(reason);
let iterator = TelemetryFile.popPendingPings();
for (let data of iterator) {
yield data;
}
@@ -178,29 +376,142 @@ let Impl = {
},
/**
* Send data to the server. Record success/send-time in histograms
* Build a complete ping and send data to the server. Record success/send-time in
* histograms.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} aOptions Options object.
* @param {Number} aOptions.retentionDays The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} aOptions.addClientId true if the ping should contain the client id,
* false otherwise.
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
* environment data.
*
* @returns {Promise} A promise that resolves when the ping is sent.
*/
send: function send(reason, aPayload) {
this._log.trace("send - Reason " + reason + ", Server " + this._server);
return this.sendPingsFromIterator(this._server, reason,
Iterator(this.popPayloads(reason, aPayload)));
send: function send(aType, aPayload, aOptions) {
this._log.trace("send - Type " + aType + ", Server " + this._server +
", aOptions " + JSON.stringify(aOptions));
return this.assemblePing(aType, aPayload, aOptions)
.then(pingData => {
// Once ping is assembled, send it along with the persisted ping in the backlog.
let p = [
// Persist the ping if sending it fails.
this.doPing(pingData, false)
.catch(() => TelemetryFile.savePing(pingData, true)),
this.sendPersistedPings(),
];
return Promise.all(p);
},
error => this._log.error("send - Rejection", error));
},
sendPingsFromIterator: function sendPingsFromIterator(server, reason, i) {
let p = [data for (data in i)].map((data) =>
this.doPing(server, data).then(null, () => TelemetryFile.savePing(data, true)));
/**
* Send the persisted pings to the server.
*/
sendPersistedPings: function sendPersistedPings() {
this._log.trace("sendPersistedPings");
let pingsIterator = Iterator(this.popPayloads());
let p = [data for (data in pingsIterator)].map(data => this.doPing(data, true));
return Promise.all(p);
},
finishPingRequest: function finishPingRequest(success, startTime, ping) {
/**
* Saves all the pending pings, plus the passed one, to disk.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} aOptions Options object.
* @param {Number} aOptions.retentionDays The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} aOptions.addClientId true if the ping should contain the client id,
* false otherwise.
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
* environment data.
*
* @returns {Promise} A promise that resolves when all the pings are saved to disk.
*/
savePendingPings: function savePendingPings(aType, aPayload, aOptions) {
this._log.trace("savePendingPings - Type " + aType + ", Server " + this._server +
", aOptions " + JSON.stringify(aOptions));
return this.assemblePing(aType, aPayload, aOptions)
.then(pingData => TelemetryFile.savePendingPings(pingData),
error => this._log.error("savePendingPings - Rejection", error));
},
/**
* Save a ping to disk.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} aOptions Options object.
* @param {Number} aOptions.retentionDays The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} aOptions.addClientId true if the ping should contain the client id,
* false otherwise.
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
* environment data.
* @param {Boolean} aOptions.overwrite true overwrites a ping with the same name, if found.
*
* @returns {Promise} A promise that resolves when the ping is saved to disk.
*/
savePing: function savePing(aType, aPayload, aOptions) {
this._log.trace("savePing - Type " + aType + ", Server " + this._server +
", aOptions " + JSON.stringify(aOptions));
return this.assemblePing(aType, aPayload, aOptions)
.then(pingData => TelemetryFile.savePing(pingData, aOptions.overwrite),
error => this._log.error("savePing - Rejection", error));
},
/**
* Save a ping to disk and return the ping id when done.
*
* @param {String} aType The type of the ping.
* @param {Object} aPayload The actual data payload for the ping.
* @param {Object} aOptions Options object.
* @param {Number} aOptions.retentionDays The number of days to keep the ping on disk
* if sending fails.
* @param {Boolean} aOptions.addClientId true if the ping should contain the client id,
* false otherwise.
* @param {Boolean} aOptions.addEnvironment true if the ping should contain the
* environment data.
* @param {Boolean} aOptions.overwrite true overwrites a ping with the same name, if found.
* @param {String} [aOptions.filePath] The path to save the ping to. Will save to default
* ping location if not provided.
*
* @returns {Promise} A promise that resolves with the ping id when the ping is saved to
* disk.
*/
testSavePingToFile: function testSavePingToFile(aType, aPayload, aOptions) {
this._log.trace("testSavePingToFile - Type " + aType + ", Server " + this._server +
", aOptions " + JSON.stringify(aOptions));
return this.assemblePing(aType, aPayload, aOptions)
.then(pingData => {
if (aOptions.filePath) {
return TelemetryFile.savePingToFile(pingData, aOptions.filePath, aOptions.overwrite)
.then(() => { return pingData.id; });
} else {
return TelemetryFile.savePing(pingData, aOptions.overwrite)
.then(() => { return pingData.id; });
}
}, error => this._log.error("testSavePing - Rejection", error));
},
finishPingRequest: function finishPingRequest(success, startTime, ping, isPersisted) {
this._log.trace("finishPingRequest - Success " + success + ", Persisted " + isPersisted);
let hping = Telemetry.getHistogramById("TELEMETRY_PING");
let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS");
hsuccess.add(success);
hping.add(new Date() - startTime);
if (success) {
if (success && isPersisted) {
return TelemetryFile.cleanupPingFile(ping);
} else {
return Promise.resolve();
@@ -208,23 +519,20 @@ let Impl = {
},
submissionPath: function submissionPath(ping) {
let slug;
if (!ping) {
slug = this._uuid;
} else {
let info = ping.payload.info;
let pathComponents = [ping.slug, info.reason, info.appName,
info.appVersion, info.appUpdateChannel,
info.appBuildID];
slug = pathComponents.join("/");
}
let app = ping.application;
// We insert the Ping id in the URL to simplify server handling of duplicated
// pings.
let pathComponents = [ping.id, ping.type, app.name, app.version,
app.channel, app.buildId];
let slug = pathComponents.join("/");
return "/submit/telemetry/" + slug;
},
doPing: function doPing(server, ping) {
this._log.trace("doPing - Server " + server);
doPing: function doPing(ping, isPersisted) {
this._log.trace("doPing - Server " + this._server + ", Persisted " + isPersisted);
let deferred = Promise.defer();
let url = server + this.submissionPath(ping);
let url = this._server + this.submissionPath(ping);
let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Ci.nsIXMLHttpRequest);
request.mozBackgroundRequest = true;
@@ -235,14 +543,22 @@ let Impl = {
let startTime = new Date();
function handler(success) {
let handleCompletion = event => {
if (success) {
deferred.resolve();
} else {
deferred.reject(event);
}
};
return function(event) {
this.finishPingRequest(success, startTime, ping).then(() => {
if (success) {
deferred.resolve();
} else {
deferred.reject(event);
}
});
this.finishPingRequest(success, startTime, ping, isPersisted)
.then(() => handleCompletion(event),
error => {
this._log.error("doPing - Request Success " + success + ", Error " +
error);
handleCompletion(event);
});
};
}
request.addEventListener("error", handler(false).bind(this), false);
@@ -252,7 +568,7 @@ let Impl = {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
let utf8Payload = converter.ConvertFromUnicode(JSON.stringify(ping.payload));
let utf8Payload = converter.ConvertFromUnicode(JSON.stringify(ping));
utf8Payload += converter.Finish();
let payloadStream = Cc["@mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
@@ -289,13 +605,22 @@ let Impl = {
* Perform telemetry initialization for either chrome or content process.
*/
enableTelemetryRecording: function enableTelemetryRecording(testing) {
// Enable base Telemetry recording, if needed.
#if !defined(MOZ_WIDGET_ANDROID)
Telemetry.canRecordBase =
Preferences.get("datareporting.healthreport.service.enabled", false) ||
Preferences.get("browser.selfsupport.enabled", false);
#else
// FHR recording is always "enabled" on Android (data upload is not).
Telemetry.canRecordBase = true;
#endif
#ifdef MOZILLA_OFFICIAL
if (!Telemetry.canSend && !testing) {
if (!Telemetry.isOfficialTelemetry && !testing) {
// We can't send data; no point in initializing observers etc.
// Only do this for official builds so that e.g. developer builds
// still enable Telemetry based on prefs.
Telemetry.canRecord = false;
Telemetry.canRecordExtended = false;
this._log.config("enableTelemetryRecording - Can't send data, disabling Telemetry recording.");
return false;
}
@@ -303,10 +628,10 @@ let Impl = {
let enabled = Preferences.get(PREF_ENABLED, false);
this._server = Preferences.get(PREF_SERVER, undefined);
if (!enabled) {
// Turn off local telemetry if telemetry is disabled.
// This may change once about:telemetry is added.
Telemetry.canRecord = false;
if (!enabled || !Telemetry.canRecordBase) {
// Turn off extended telemetry recording if disabled by preferences or if base/telemetry
// telemetry recording is off.
Telemetry.canRecordExtended = false;
this._log.config("enableTelemetryRecording - Telemetry is disabled, turning off Telemetry recording.");
return false;
}
@@ -316,14 +641,32 @@ let Impl = {
/**
* Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry.
*
* This delayed initialization means TelemetryPing init can be in the following states:
* 1) setupTelemetry was never called
* or it was called and
* 2) _delayedInitTask was scheduled, but didn't run yet.
* 3) _delayedInitTask is currently running.
* 4) _delayedInitTask finished running and is nulled out.
*/
setupTelemetry: function setupTelemetry(testing) {
this._initStarted = true;
if (testing && !this._log) {
this._log = Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX);
}
this._log.trace("setupTelemetry");
if (this._delayedInitTask) {
this._log.error("setupTelemetry - init task already running");
return this._delayedInitTask;
}
if (this._initialized && !testing) {
this._log.error("setupTelemetry - already initialized");
return Promise.resolve();
}
// Initialize some probes that are kept in their own modules
this._thirdPartyCookies = new ThirdPartyCookieProbe();
this._thirdPartyCookies.init();
@@ -343,42 +686,91 @@ let Impl = {
// run various late initializers. Otherwise our gathered memory
// footprint and other numbers would be too optimistic.
let deferred = Promise.defer();
let delayedTask = new DeferredTask(function* () {
this._initialized = true;
this._delayedInitTask = new DeferredTask(function* () {
try {
this._initialized = true;
yield TelemetryFile.loadSavedPings();
// If we have any TelemetryPings lying around, we'll be aggressive
// and try to send them all off ASAP.
if (TelemetryFile.pingsOverdue > 0) {
this._log.trace("setupChromeProcess - Sending " + TelemetryFile.pingsOverdue +
" overdue pings now.");
// It doesn't really matter what we pass to this.send as a reason,
// since it's never sent to the server. All that this.send does with
// the reason is check to make sure it's not a test-ping.
yield this.send("overdue-flush");
yield TelemetryEnvironment.init();
yield TelemetryFile.loadSavedPings();
// If we have any TelemetryPings lying around, we'll be aggressive
// and try to send them all off ASAP.
if (TelemetryFile.pingsOverdue > 0) {
this._log.trace("setupChromeProcess - Sending " + TelemetryFile.pingsOverdue +
" overdue pings now.");
// It doesn't really matter what we pass to this.send as a reason,
// since it's never sent to the server. All that this.send does with
// the reason is check to make sure it's not a test-ping.
yield this.sendPersistedPings();
}
if ("@mozilla.org/datareporting/service;1" in Cc) {
let drs = Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
this._clientID = yield drs.getClientID();
// Update cached client id.
Preferences.set(PREF_CACHED_CLIENTID, this._clientID);
} else {
// Nuke potentially cached client id.
Preferences.reset(PREF_CACHED_CLIENTID);
}
Telemetry.asyncFetchTelemetryData(function () {});
deferred.resolve();
} catch (e) {
deferred.reject(e);
} finally {
this._delayedInitTask = null;
}
if ("@mozilla.org/datareporting/service;1" in Cc) {
let drs = Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
this._clientID = yield drs.getClientID();
// Update cached client id.
Preferences.set(PREF_CACHED_CLIENTID, this._clientID);
} else {
// Nuke potentially cached client id.
Preferences.reset(PREF_CACHED_CLIENTID);
}
Telemetry.asyncFetchTelemetryData(function () {});
deferred.resolve();
}.bind(this), testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY);
delayedTask.arm();
AsyncShutdown.sendTelemetry.addBlocker("TelemetryPing: shutting down",
() => this.shutdown(),
() => this._getState());
this._delayedInitTask.arm();
return deferred.promise;
},
shutdown: function() {
this._log.trace("shutdown");
let cleanup = () => {
if (!this._initialized) {
return;
}
let reset = () => {
this._initialized = false;
this._initStarted = false;
};
return this._shutdownBarrier.wait().then(
() => TelemetryEnvironment.shutdown().then(reset, reset));
};
// We can be in one the following states here:
// 1) setupTelemetry was never called
// or it was called and
// 2) _delayedInitTask was scheduled, but didn't run yet.
// 3) _delayedInitTask is running now.
// 4) _delayedInitTask finished running already.
// This handles 1).
if (!this._initStarted) {
return Promise.resolve();
}
// This handles 4).
if (!this._delayedInitTask) {
// We already ran the delayed initialization.
return cleanup();
}
// This handles 2) and 3).
this._delayedInitTask.disarm();
return this._delayedInitTask.finalize().then(cleanup);
},
/**
* This observer drives telemetry.
*/
@@ -412,4 +804,27 @@ let Impl = {
get clientID() {
return this._clientID;
},
/**
* Get an object describing the current state of this module for AsyncShutdown diagnostics.
*/
_getState: function() {
return {
initialized: this._initialized,
initStarted: this._initStarted,
haveDelayedInitTask: !!this._delayedInitTask,
};
},
/**
* This tells TelemetryPing to uninitialize and save any pending pings.
* @param testing Optional. If true, always saves the ping whether Telemetry
* can send pings or not, which is used for testing.
*/
shutdown: function(testing = false) {
this.uninstall();
if (Telemetry.canSend || testing) {
return this.savePendingPings();
}
},
};
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,197 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
/* 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_BackgroundHangTelemetry_h
#define mozilla_BackgroundHangTelemetry_h
#include "mozilla/Array.h"
#include "mozilla/Assertions.h"
#include "mozilla/HangAnnotations.h"
#include "mozilla/Move.h"
#include "mozilla/Mutex.h"
#include "mozilla/PodOperations.h"
#include "mozilla/Vector.h"
#include "nsString.h"
#include "prinrval.h"
namespace mozilla {
namespace Telemetry {
static const size_t kTimeHistogramBuckets = 8 * sizeof(PRIntervalTime);
/* TimeHistogram is an efficient histogram that puts time durations into
exponential (base 2) buckets; times are accepted in PRIntervalTime and
stored in milliseconds. */
class TimeHistogram : public mozilla::Array<uint32_t, kTimeHistogramBuckets>
{
public:
TimeHistogram()
{
mozilla::PodArrayZero(*this);
}
// Get minimum (inclusive) range of bucket in milliseconds
uint32_t GetBucketMin(size_t aBucket) const {
MOZ_ASSERT(aBucket < ArrayLength(*this));
return (1u << aBucket) & ~1u; // Bucket 0 starts at 0, not 1
}
// Get maximum (inclusive) range of bucket in milliseconds
uint32_t GetBucketMax(size_t aBucket) const {
MOZ_ASSERT(aBucket < ArrayLength(*this));
return (1u << (aBucket + 1u)) - 1u;
}
void Add(PRIntervalTime aTime);
};
/* HangStack stores an array of const char pointers,
with optional internal storage for strings. */
class HangStack : public mozilla::Vector<const char*, 8>
{
private:
typedef mozilla::Vector<const char*, 8> Base;
// Stack entries can either be a static const char*
// or a pointer to within this buffer.
mozilla::Vector<char, 0> mBuffer;
public:
HangStack() { }
HangStack(HangStack&& aOther)
: Base(mozilla::Move(aOther))
, mBuffer(mozilla::Move(aOther.mBuffer))
{
}
bool operator==(const HangStack& aOther) const {
for (size_t i = 0; i < length(); i++) {
if (!IsSameAsEntry(operator[](i), aOther[i])) {
return false;
}
}
return true;
}
bool operator!=(const HangStack& aOther) const {
return !operator==(aOther);
}
void clear() {
Base::clear();
mBuffer.clear();
}
bool IsInBuffer(const char* aEntry) const {
return aEntry >= mBuffer.begin() && aEntry < mBuffer.end();
}
bool IsSameAsEntry(const char* aEntry, const char* aOther) const {
// If the entry came from the buffer, we need to compare its content;
// otherwise we only need to compare its pointer.
return IsInBuffer(aEntry) ? !strcmp(aEntry, aOther) : (aEntry == aOther);
}
size_t AvailableBufferSize() const {
return mBuffer.capacity() - mBuffer.length();
}
bool EnsureBufferCapacity(size_t aCapacity) {
// aCapacity is the minimal capacity and Vector may make the actual
// capacity larger, in which case we want to use up all the space.
return mBuffer.reserve(aCapacity) &&
mBuffer.reserve(mBuffer.capacity());
}
const char* InfallibleAppendViaBuffer(const char* aText, size_t aLength);
const char* AppendViaBuffer(const char* aText, size_t aLength);
};
/* A hang histogram consists of a stack associated with the
hang, along with a time histogram of the hang times. */
class HangHistogram : public TimeHistogram
{
private:
static uint32_t GetHash(const HangStack& aStack);
HangStack mStack;
// Native stack that corresponds to the pseudostack in mStack
HangStack mNativeStack;
// Use a hash to speed comparisons
const uint32_t mHash;
// Annotations attributed to this stack
HangMonitor::HangAnnotationsVector mAnnotations;
public:
explicit HangHistogram(HangStack&& aStack)
: mStack(mozilla::Move(aStack))
, mHash(GetHash(mStack))
{
}
HangHistogram(HangHistogram&& aOther)
: TimeHistogram(mozilla::Move(aOther))
, mStack(mozilla::Move(aOther.mStack))
, mNativeStack(mozilla::Move(aOther.mNativeStack))
, mHash(mozilla::Move(aOther.mHash))
, mAnnotations(mozilla::Move(aOther.mAnnotations))
{
}
bool operator==(const HangHistogram& aOther) const;
bool operator!=(const HangHistogram& aOther) const
{
return !operator==(aOther);
}
const HangStack& GetStack() const {
return mStack;
}
HangStack& GetNativeStack() {
return mNativeStack;
}
const HangStack& GetNativeStack() const {
return mNativeStack;
}
const HangMonitor::HangAnnotationsVector& GetAnnotations() const {
return mAnnotations;
}
void Add(PRIntervalTime aTime, HangMonitor::HangAnnotationsPtr aAnnotations) {
TimeHistogram::Add(aTime);
if (aAnnotations) {
mAnnotations.append(Move(aAnnotations));
}
}
};
/* Thread hang stats consist of
- thread name
- time histogram of all task run times
- hang histograms of individual hangs
- annotations for each hang
*/
class ThreadHangStats
{
private:
nsAutoCString mName;
public:
TimeHistogram mActivity;
mozilla::Vector<HangHistogram, 4> mHangs;
explicit ThreadHangStats(const char* aName)
: mName(aName)
{
}
ThreadHangStats(ThreadHangStats&& aOther)
: mName(mozilla::Move(aOther.mName))
, mActivity(mozilla::Move(aOther.mActivity))
, mHangs(mozilla::Move(aOther.mHangs))
{
}
const char* GetName() const {
return mName.get();
}
};
} // namespace Telemetry
} // namespace mozilla
#endif // mozilla_BackgroundHangTelemetry_h
@@ -23,7 +23,7 @@ Structure::
reason: <string>, // what triggered this ping: "saved-session", "environment-change", "shutdown", ...
revision: <string>, // the Histograms.json revision
timezoneOffset: <number>, // time-zone offset from UTC, in minutes, for the current locale
previousBuildId: <string>,
previousBuildId: <string>, // null if this is the first run, or the previous build ID is unknown
sessionId: <uuid>, // random session id, shared by subsessions
subsessionId: <uuid>, // random subsession id
@@ -34,7 +34,7 @@ Structure::
profileSubsessionCounter: <number>, // the running no. of all subsessions for the whole profile life time
sessionStartDate: <ISO date>, // daily precision
subsessionStartDate: <ISO date>, // daily precision
subsessionStartDate: <ISO date>, // daily precision, ISO date in local time
subsessionLength: <number>, // the subsession length in seconds
},
+1
View File
@@ -27,6 +27,7 @@ EXTRA_COMPONENTS += [
]
EXTRA_JS_MODULES += [
'TelemetryEnvironment.jsm',
'TelemetryFile.jsm',
'TelemetryLog.jsm',
'TelemetryStopwatch.jsm',
+31 -6
View File
@@ -12,7 +12,7 @@ interface nsIFetchTelemetryDataCallback : nsISupports
void complete();
};
[scriptable, uuid(c782cf96-7f44-45ac-8d76-e0d1b174e562)]
[scriptable, uuid(68e82b42-cad2-411f-857b-b64ea9377929)]
interface nsITelemetry : nsISupports
{
/**
@@ -39,7 +39,7 @@ interface nsITelemetry : nsISupports
const unsigned long DATASET_RELEASE_CHANNEL_OPTIN = 1;
/*
/**
* An object containing a snapshot from all of the currently registered histograms.
* { name1: {data1}, name2:{data2}...}
* where data is consists of the following properties:
@@ -55,6 +55,14 @@ interface nsITelemetry : nsISupports
[implicit_jscontext]
readonly attribute jsval histogramSnapshots;
/**
* Get a snapshot of the internally duplicated subsession histograms.
* @param clear Whether to clear out the subsession histograms after snapshotting.
* @return An object as histogramSnapshots, except this contains the internally duplicated histograms for subsession telemetry.
*/
[implicit_jscontext]
jsval snapshotSubsessionHistograms([optional] in boolean clear);
/**
* The amount of time, in milliseconds, that the last session took
* to shutdown. Reads as 0 to indicate failure.
@@ -226,14 +234,31 @@ interface nsITelemetry : nsISupports
jsval getKeyedHistogramById(in ACString id);
/**
* Set this to false to disable gathering of telemetry statistics.
* A flag indicating if Telemetry can record base data (FHR data). This is true if the
* FHR data reporting service or self-support are enabled.
*
* In the unlikely event that adding a new base probe is needed, please check the data
* collection wiki at https://wiki.mozilla.org/Firefox/Data_Collection and talk to the
* Telemetry team.
*/
attribute boolean canRecord;
attribute boolean canRecordBase;
/**
* A flag indicating whether Telemetry can submit official results.
* A flag indicating if Telemetry is allowed to record extended data. Returns false if
* the user hasn't opted into "extended Telemetry" on the Release channel, when the
* user has explicitly opted out of Telemetry on Nightly/Aurora/Beta or if manually
* set to false during tests.
*
* Set this to false in tests to disable gathering of extended telemetry statistics.
*/
readonly attribute boolean canSend;
attribute boolean canRecordExtended;
/**
* A flag indicating whether Telemetry can submit official results (for base or extended
* data). This is true on official builds with built in support for Mozilla Telemetry
* reporting.
*/
readonly attribute boolean isOfficialTelemetry;
/** Addon telemetry hooks */
@@ -58,7 +58,17 @@ function createAppInfo(id, name, version, platformVersion) {
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
}
// Fake setTimeout and clearTimeout for the daily timer in tests for controllable behavior.
function fakeDailyTimers(set, clear) {
let session = Components.utils.import("resource://gre/modules/TelemetrySession.jsm");
session.Policy.setDailyTimeout = set;
session.Policy.clearDailyTimeout = clear;
}
// Set logging preferences for all the tests.
Services.prefs.setCharPref("toolkit.telemetry.log.level", "Trace");
Services.prefs.setBoolPref("toolkit.telemetry.log.dump", true);
TelemetryPing.initLogging();
// Avoid timers interrupting test behavior.
fakeDailyTimers(() => {}, () => {});
@@ -0,0 +1,72 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
function run_test() {
do_test_pending();
do_get_profile();
run_next_test();
}
function isRejected(promise) {
return new Promise((resolve, reject) => {
promise.then(() => resolve(false), () => resolve(true));
});
}
add_task(function* test_initAndShutdown() {
// Check that init and shutdown work properly.
TelemetryEnvironment.init();
yield TelemetryEnvironment.shutdown();
TelemetryEnvironment.init();
yield TelemetryEnvironment.shutdown();
// A double init should be silently handled.
TelemetryEnvironment.init();
TelemetryEnvironment.init();
// getEnvironmentData should return a sane result.
let data = yield TelemetryEnvironment.getEnvironmentData();
Assert.ok(!!data);
// The change listener registration should silently fail after shutdown.
yield TelemetryEnvironment.shutdown();
TelemetryEnvironment.registerChangeListener("foo", () => {});
TelemetryEnvironment.unregisterChangeListener("foo");
// Shutting down again should be ignored.
yield TelemetryEnvironment.shutdown();
// Getting the environment data should reject after shutdown.
Assert.ok(yield isRejected(TelemetryEnvironment.getEnvironmentData()));
});
add_task(function* test_changeNotify() {
TelemetryEnvironment.init();
// Register some listeners
let results = new Array(4).fill(false);
for (let i=0; i<results.length; ++i) {
let k = i;
TelemetryEnvironment.registerChangeListener("test"+k, () => results[k] = true);
}
// Trigger environment change notifications.
// TODO: test with proper environment changes, not directly.
TelemetryEnvironment._onEnvironmentChange("foo");
Assert.ok(results.every(val => val), "All change listeners should have been notified.");
results.fill(false);
TelemetryEnvironment._onEnvironmentChange("bar");
Assert.ok(results.every(val => val), "All change listeners should have been notified.");
// Unregister listeners
for (let i=0; i<4; ++i) {
TelemetryEnvironment.unregisterChangeListener("test"+i);
}
});
add_task(function*() {
do_test_finished();
});
@@ -1,3 +1,4 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
@@ -168,7 +169,6 @@ function checkPayloadInfo(payload, reason) {
// get rid of the non-deterministic field
const expected_info = {
OS: "XPCShell",
appID: "xpcshell@tests.mozilla.org",
appVersion: "1",
appName: "XPCShell",
appBuildID: "2007010101",
@@ -182,7 +182,6 @@ function checkPayloadInfo(payload, reason) {
do_check_eq(payload.info.reason, reason);
do_check_true("appUpdateChannel" in payload.info);
do_check_true("locale" in payload.info);
do_check_true("revision" in payload.info);
if (Services.appinfo.isOfficial) {
do_check_true(payload.info.revision.startsWith("http"));
@@ -194,24 +193,6 @@ function checkPayloadInfo(payload, reason) {
do_check_neq(payload.clientID, null);
do_check_eq(payload.clientID, gDataReportingClientID);
}
try {
// If we've not got nsIGfxInfoDebug, then this will throw and stop us doing
// this test.
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
let isOSX = ("nsILocalFileMac" in Components.interfaces);
if (isWindows || isOSX) {
do_check_true("adapterVendorID" in payload.info);
do_check_true("adapterDeviceID" in payload.info);
if (isWindows) {
do_check_true("adapterSubsysID" in payload.info);
}
}
}
catch (x) {
}
}
function checkPayload(request, payload, reason, successfulPings) {
@@ -362,17 +343,6 @@ function checkPayload(request, payload, reason, successfulPings) {
Assert.deepEqual(expected_keyed_count, keyedHistograms[TELEMETRY_TEST_KEYED_COUNT]);
}
function dummyTheme(id) {
return {
id: id,
name: Math.random().toString(),
headerURL: "http://lwttest.invalid/a.png",
footerURL: "http://lwttest.invalid/b.png",
textcolor: Math.random().toString(),
accentcolor: Math.random().toString()
};
}
// A fake plugin host for testing flash version telemetry
let PluginHost = {
getPluginTags: function(countRef) {
@@ -435,13 +405,6 @@ function write_fake_failedprofilelocks_file() {
function run_test() {
do_test_pending();
try {
let gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfoDebug);
gfxInfo.spoofVendorID("0xabcd");
gfxInfo.spoofDeviceID("0x1234");
} catch (x) {
// If we can't test gfxInfo, that's fine, we'll note it later.
}
// Addon manager needs a profile directory
do_get_profile();
@@ -495,7 +458,6 @@ function actualTest() {
.QueryInterface(Ci.nsITimerCallback);
gInternalManager.observe(null, "addons-startup", null);
LightweightThemeManager.currentTheme = dummyTheme("1234");
// fake plugin host for consistent flash version data
registerFakePluginHost();
@@ -606,6 +568,173 @@ add_task(function* test_saveLoadPing() {
}
});
add_task(function* test_checkSubsession() {
const COUNT_ID = "TELEMETRY_TEST_COUNT";
const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
const count = Telemetry.getHistogramById(COUNT_ID);
const keyed = Telemetry.getKeyedHistogramById(KEYED_ID);
const registeredIds =
new Set(Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []));
const stableHistograms = new Set([
"TELEMETRY_TEST_FLAG",
"TELEMETRY_TEST_COUNT",
"TELEMETRY_TEST_RELEASE_OPTOUT",
"TELEMETRY_TEST_RELEASE_OPTIN",
"STARTUP_CRASH_DETECTED",
]);
const stableKeyedHistograms = new Set([
"TELEMETRY_TEST_KEYED_FLAG",
"TELEMETRY_TEST_KEYED_COUNT",
"TELEMETRY_TEST_KEYED_RELEASE_OPTIN",
"TELEMETRY_TEST_KEYED_RELEASE_OPTOUT",
]);
// Compare the two sets of histograms.
// The "subsession" histograms should match the registered
// "classic" histograms. However, histograms can change
// between us collecting the different payloads, so we only
// check for deep equality on known stable histograms.
checkHistograms = (classic, subsession) => {
for (let id of Object.keys(classic)) {
if (!registeredIds.has(id)) {
continue;
}
Assert.ok(id in subsession);
if (stableHistograms.has(id)) {
Assert.deepEqual(classic[id],
subsession[id]);
} else {
Assert.equal(classic[id].histogram_type,
subsession[id].histogram_type);
}
}
};
// Same as above, except for keyed histograms.
checkKeyedHistograms = (classic, subsession) => {
for (let id of Object.keys(classic)) {
if (!registeredIds.has(id)) {
continue;
}
Assert.ok(id in subsession);
if (stableKeyedHistograms.has(id)) {
Assert.deepEqual(classic[id],
subsession[id]);
}
}
};
// Both classic and subsession payload histograms should start the same.
// The payloads should be identical for now except for the reason.
count.clear();
keyed.clear();
let classic = TelemetrySession.getPayload();
let subsession = TelemetrySession.getPayload("environment-change");
Assert.equal(classic.info.reason, "gather-payload");
Assert.equal(subsession.info.reason, "environment-change");
Assert.ok(!(COUNT_ID in classic.histograms));
Assert.ok(!(COUNT_ID in subsession.histograms));
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.deepEqual(classic.keyedHistograms[KEYED_ID], {});
Assert.deepEqual(subsession.keyedHistograms[KEYED_ID], {});
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Adding values should get picked up in both.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Values should still reset properly.
count.clear();
keyed.clear();
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(!(COUNT_ID in classic.histograms));
Assert.ok(!(COUNT_ID in subsession.histograms));
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.deepEqual(classic.keyedHistograms[KEYED_ID], {});
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Adding values should get picked up in both.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// We should be able to reset only the subsession histograms.
count.clear(true);
keyed.clear(true);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(subsession.histograms[COUNT_ID].sum, 0);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
Assert.deepEqual(subsession.keyedHistograms[KEYED_ID], {});
// Adding values should get picked up in both again.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 2);
Assert.equal(subsession.histograms[COUNT_ID].sum, 1);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 2);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 2);
Assert.equal(subsession.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(subsession.keyedHistograms[KEYED_ID]["b"].sum, 1);
});
// Checks that an expired histogram file is deleted when loaded.
add_task(function* test_runOldPingFile() {
let histogramsFile = getSavedHistogramsFile("old-histograms.dat");
@@ -17,12 +17,12 @@ const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
Cu.import("resource://gre/modules/osfile.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
let {OS: {File, Path, Constants}} = Cu.import("resource://gre/modules/osfile.jsm", {});
@@ -53,70 +53,66 @@ let gCreatedPings = 0;
let gSeenPings = 0;
/**
* Creates some TelemetrySession pings for the current session and
* saves them to disk. Each ping gets a unique ID slug based on
* an incrementor.
* Creates some Telemetry pings for the and saves them to disk. Each ping gets a
* unique ID based on an incrementor.
*
* @param aNum the number of pings to create.
* @param aAge the age in milliseconds to offset from now. A value
* of 10 would make the ping 10ms older than now, for
* example.
* @param {Array} aPingInfos An array of ping type objects. Each entry must be an
* object containing a "num" field for the number of pings to create and
* an "age" field. The latter representing the age in milliseconds to offset
* from now. A value of 10 would make the ping 10ms older than now, for
* example.
* @returns Promise
* @resolve an Array with the created pings.
* @resolve an Array with the created pings ids.
*/
function createSavedPings(aNum, aAge) {
return Task.spawn(function*(){
let pings = [];
let age = Date.now() - aAge;
let createSavedPings = Task.async(function* (aPingInfos) {
let pingIds = [];
let now = Date.now();
for (let i = 0; i < aNum; ++i) {
let payload = TelemetrySession.getPayload();
let ping = { slug: "test-ping-" + gCreatedPings, reason: "test", payload: payload };
yield TelemetryFile.savePing(ping);
if (aAge) {
for (let type in aPingInfos) {
let num = aPingInfos[type].num;
let age = now - aPingInfos[type].age;
for (let i = 0; i < num; ++i) {
let pingId = yield TelemetryPing.testSavePingToFile("test-ping", {}, { overwrite: true });
if (aPingInfos[type].age) {
// savePing writes to the file synchronously, so we're good to
// modify the lastModifedTime now.
let file = getSavePathForPing(ping);
yield File.setDates(file, null, age);
let filePath = getSavePathForPingId(pingId);
yield File.setDates(filePath, null, age);
}
gCreatedPings++;
pings.push(ping);
pingIds.push(pingId);
}
return pings;
});
}
}
return pingIds;
});
/**
* Deletes locally saved pings in aPings if they
* exist.
* Deletes locally saved pings if they exist.
*
* @param aPings an Array of pings to delete.
* @param aPingIds an Array of ping ids to delete.
* @returns Promise
*/
function clearPings(aPings) {
return Task.spawn(function*() {
for (let ping of aPings) {
let path = getSavePathForPing(ping);
yield File.remove(path);
}
});
}
let clearPings = Task.async(function* (aPingIds) {
for (let pingId of aPingIds) {
let filePath = getSavePathForPingId(pingId);
yield File.remove(filePath);
}
});
/**
* Returns a handle for the file that aPing should be
* Returns a handle for the file that a ping should be
* stored in locally.
*
* @returns path
*/
function getSavePathForPing(aPing) {
return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPing.slug);
function getSavePathForPingId(aPingId) {
return Path.join(Constants.Path.profileDir, PING_SAVE_FOLDER, aPingId);
}
/**
* Check if the number of TelemetrySession pings received by the
* HttpServer is not equal to aExpectedNum.
* Check if the number of Telemetry pings received by the HttpServer is not equal
* to aExpectedNum.
*
* @param aExpectedNum the number of pings we expect to receive.
*/
@@ -125,30 +121,28 @@ function assertReceivedPings(aExpectedNum) {
}
/**
* Throws if any pings in aPings is saved locally.
* Throws if any pings with the id in aPingIds is saved locally.
*
* @param aPings an Array of pings to check.
* @param aPingIds an Array of pings ids to check.
* @returns Promise
*/
function assertNotSaved(aPings) {
return Task.spawn(function*() {
let saved = 0;
for (let ping of aPings) {
let file = getSavePathForPing(ping);
if (yield File.exists()) {
saved++;
}
let assertNotSaved = Task.async(function* (aPingIds) {
let saved = 0;
for (let id of aPingIds) {
let filePath = getSavePathForPingId(id);
if (yield File.exists(filePath)) {
saved++;
}
if (saved > 0) {
do_throw("Found " + saved + " unexpected saved pings.");
}
});
}
}
if (saved > 0) {
do_throw("Found " + saved + " unexpected saved pings.");
}
});
/**
* Our handler function for the HttpServer that simply
* increments the gSeenPings global when it successfully
* receives and decodes a TelemetrySession payload.
* receives and decodes a Telemetry payload.
*
* @param aRequest the HTTP request sent from HttpServer.
*/
@@ -174,7 +168,6 @@ function stopHttpServer() {
* Reset Telemetry state.
*/
function resetTelemetry() {
TelemetrySession.uninstall();
// Quick and dirty way to clear TelemetryFile's pendingPings
// collection, and put it back in its initial state.
let gen = TelemetryFile.popPendingPings();
@@ -189,10 +182,6 @@ function startTelemetry() {
return TelemetryPing.setup();
}
function startTelemetrySession() {
return TelemetrySession.setup();
}
function run_test() {
gHttpServer.registerPrefixHandler("/submit/telemetry/", pingHandler);
gHttpServer.start(-1);
@@ -209,13 +198,26 @@ function run_test() {
run_next_test();
}
/**
* Setup the tests by making sure the ping storage directory is available, otherwise
* |TelemetryPing.testSaveDirectoryToFile| could fail.
*/
add_task(function* setupEnvironment() {
yield TelemetryPing.setup();
let directory = TelemetryFile.pingDirectoryPath;
yield File.makeDir(directory, { ignoreExisting: true, unixMode: OS.Constants.S_IRWXU });
yield resetTelemetry();
});
/**
* Test that pings that are considered too old are just chucked out
* immediately and never sent.
*/
add_task(function* test_expired_pings_are_deleted() {
yield startTelemetrySession();
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
let pingTypes = [{ num: EXPIRED_PINGS, age: EXPIRED_PING_FILE_AGE }];
let expiredPings = yield createSavedPings(pingTypes);
yield startTelemetry();
assertReceivedPings(0);
yield assertNotSaved(expiredPings);
@@ -226,8 +228,8 @@ add_task(function* test_expired_pings_are_deleted() {
* Test that really recent pings are not sent on Telemetry initialization.
*/
add_task(function* test_recent_pings_not_sent() {
yield startTelemetrySession();
let recentPings = yield createSavedPings(RECENT_PINGS);
let pingTypes = [{ num: RECENT_PINGS }];
let recentPings = yield createSavedPings(pingTypes);
yield startTelemetry();
assertReceivedPings(0);
yield resetTelemetry();
@@ -238,17 +240,20 @@ add_task(function* test_recent_pings_not_sent() {
* Test that only the most recent LRU_PINGS pings are kept at startup.
*/
add_task(function* test_most_recent_pings_kept() {
yield startTelemetrySession();
let head = yield createSavedPings(LRU_PINGS);
let tail = yield createSavedPings(3, ONE_MINUTE_MS);
let pings = head.concat(tail);
let pingTypes = [
{ num: LRU_PINGS },
{ num: 3, age: ONE_MINUTE_MS },
];
let pings = yield createSavedPings(pingTypes);
let head = pings.slice(0, LRU_PINGS);
let tail = pings.slice(-3);
yield startTelemetry();
let gen = TelemetryFile.popPendingPings();
for (let item of gen) {
for (let p of tail) {
do_check_neq(p.slug, item.slug);
for (let id of tail) {
do_check_neq(id, item.id);
}
}
@@ -263,10 +268,15 @@ add_task(function* test_most_recent_pings_kept() {
* should just be deleted.
*/
add_task(function* test_overdue_pings_trigger_send() {
yield startTelemetrySession();
let recentPings = yield createSavedPings(RECENT_PINGS);
let expiredPings = yield createSavedPings(EXPIRED_PINGS, EXPIRED_PING_FILE_AGE);
let overduePings = yield createSavedPings(OVERDUE_PINGS, OVERDUE_PING_FILE_AGE);
let pingTypes = [
{ num: RECENT_PINGS },
{ num: EXPIRED_PINGS, age: EXPIRED_PING_FILE_AGE },
{ num: OVERDUE_PINGS, age: OVERDUE_PING_FILE_AGE },
];
let pings = yield createSavedPings(pingTypes);
let recentPings = pings.slice(0, RECENT_PINGS);
let expiredPings = pings.slice(RECENT_PINGS, RECENT_PINGS + EXPIRED_PINGS);
let overduePings = pings.slice(-OVERDUE_PINGS);
yield startTelemetry();
assertReceivedPings(TOTAL_EXPECTED_PINGS);
@@ -0,0 +1,953 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
*/
/* This testcase triggers two telemetry pings.
*
* Telemetry code keeps histograms of past telemetry pings. The first
* ping populates these histograms. One of those histograms is then
* checked in the second request.
*/
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://testing-common/httpd.js", this);
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/LightweightThemeManager.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://gre/modules/TelemetryPing.jsm", this);
Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
Cu.import("resource://gre/modules/TelemetryFile.jsm", this);
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
Cu.import("resource://gre/modules/Promise.jsm", this);
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/osfile.jsm", this);
const PING_FORMAT_VERSION = 2;
const PING_TYPE_MAIN = "main";
const REASON_SAVED_SESSION = "saved-session";
const REASON_TEST_PING = "test-ping";
const REASON_DAILY = "daily";
const REASON_ENVIRONMENT_CHANGE = "environment-change";
const PLATFORM_VERSION = "1.9.2";
const APP_VERSION = "1";
const APP_ID = "xpcshell@tests.mozilla.org";
const APP_NAME = "XPCShell";
const IGNORE_HISTOGRAM = "test::ignore_me";
const IGNORE_HISTOGRAM_TO_CLONE = "MEMORY_HEAP_ALLOCATED";
const IGNORE_CLONED_HISTOGRAM = "test::ignore_me_also";
const ADDON_NAME = "Telemetry test addon";
const ADDON_HISTOGRAM = "addon-histogram";
// Add some unicode characters here to ensure that sending them works correctly.
const SHUTDOWN_TIME = 10000;
const FAILED_PROFILE_LOCK_ATTEMPTS = 2;
// Constants from prio.h for nsIFileOutputStream.init
const PR_WRONLY = 0x2;
const PR_CREATE_FILE = 0x8;
const PR_TRUNCATE = 0x20;
const RW_OWNER = parseInt("0600", 8);
const NUMBER_OF_THREADS_TO_LAUNCH = 30;
let gNumberOfThreadsLaunched = 0;
const SEC_IN_ONE_DAY = 24 * 60 * 60;
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
const PREF_BRANCH = "toolkit.telemetry.";
const PREF_ENABLED = PREF_BRANCH + "enabled";
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
const PREF_FHR_SERVICE_ENABLED = "datareporting.healthreport.service.enabled";
const HAS_DATAREPORTINGSERVICE = "@mozilla.org/datareporting/service;1" in Cc;
const SESSION_RECORDER_EXPECTED = HAS_DATAREPORTINGSERVICE &&
Preferences.get(PREF_FHR_SERVICE_ENABLED, true);
const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
let gHttpServer = new HttpServer();
let gServerStarted = false;
let gRequestIterator = null;
let gDataReportingClientID = null;
XPCOMUtils.defineLazyGetter(this, "gDatareportingService",
() => Cc["@mozilla.org/datareporting/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject);
function sendPing() {
TelemetrySession.gatherStartup();
if (gServerStarted) {
TelemetryPing.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
return TelemetrySession.testPing();
} else {
TelemetryPing.setServer("http://doesnotexist");
return TelemetrySession.testPing();
}
}
function wrapWithExceptionHandler(f) {
function wrapper(...args) {
try {
f(...args);
} catch (ex if typeof(ex) == 'object') {
dump("Caught exception: " + ex.message + "\n");
dump(ex.stack);
do_test_finished();
}
}
return wrapper;
}
function fakeNow(date) {
let session = Cu.import("resource://gre/modules/TelemetrySession.jsm");
session.Policy.now = () => new Date(date.getTime());
}
function futureDate(date, offset) {
return new Date(date.getTime() + offset);
}
function fakeNow(date) {
let session = Cu.import("resource://gre/modules/TelemetrySession.jsm");
session.Policy.now = () => date;
}
function registerPingHandler(handler) {
gHttpServer.registerPrefixHandler("/submit/telemetry/",
wrapWithExceptionHandler(handler));
}
function setupTestData() {
Telemetry.newHistogram(IGNORE_HISTOGRAM, "never", Telemetry.HISTOGRAM_BOOLEAN);
Telemetry.histogramFrom(IGNORE_CLONED_HISTOGRAM, IGNORE_HISTOGRAM_TO_CLONE);
Services.startup.interrupted = true;
Telemetry.registerAddonHistogram(ADDON_NAME, ADDON_HISTOGRAM,
Telemetry.HISTOGRAM_LINEAR,
1, 5, 6);
let h1 = Telemetry.getAddonHistogram(ADDON_NAME, ADDON_HISTOGRAM);
h1.add(1);
let h2 = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT");
h2.add();
let k1 = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT");
k1.add("a");
k1.add("a");
k1.add("b");
}
function getSavedPingFile(basename) {
let tmpDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
let pingFile = tmpDir.clone();
pingFile.append(basename);
if (pingFile.exists()) {
pingFile.remove(true);
}
do_register_cleanup(function () {
try {
pingFile.remove(true);
} catch (e) {
}
});
return pingFile;
}
function decodeRequestPayload(request) {
let s = request.bodyInputStream;
let payload = null;
let decoder = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON)
if (request.getHeader("content-encoding") == "gzip") {
let observer = {
buffer: "",
onStreamComplete: function(loader, context, status, length, result) {
this.buffer = String.fromCharCode.apply(this, result);
}
};
let scs = Cc["@mozilla.org/streamConverters;1"]
.getService(Ci.nsIStreamConverterService);
let listener = Cc["@mozilla.org/network/stream-loader;1"]
.createInstance(Ci.nsIStreamLoader);
listener.init(observer);
let converter = scs.asyncConvertData("gzip", "uncompressed",
listener, null);
converter.onStartRequest(null, null);
converter.onDataAvailable(null, null, s, 0, s.available());
converter.onStopRequest(null, null, null);
let unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
unicodeConverter.charset = "UTF-8";
let utf8string = unicodeConverter.ConvertToUnicode(observer.buffer);
utf8string += unicodeConverter.Finish();
payload = decoder.decode(utf8string);
} else {
payload = decoder.decodeFromStream(s, s.available());
}
return payload;
}
function checkPingFormat(aPing, aType, aHasClientId, aHasEnvironment) {
const MANDATORY_PING_FIELDS = [
"type", "id", "creationDate", "version", "application", "payload"
];
const APPLICATION_TEST_DATA = {
buildId: "2007010101",
name: APP_NAME,
version: APP_VERSION,
vendor: "Mozilla",
platformVersion: PLATFORM_VERSION,
xpcomAbi: "noarch-spidermonkey",
};
// Check that the ping contains all the mandatory fields.
for (let f of MANDATORY_PING_FIELDS) {
Assert.ok(f in aPing, f + "must be available.");
}
Assert.equal(aPing.type, aType, "The ping must have the correct type.");
Assert.equal(aPing.version, PING_FORMAT_VERSION, "The ping must have the correct version.");
// Test the application section.
for (let f in APPLICATION_TEST_DATA) {
Assert.equal(aPing.application[f], APPLICATION_TEST_DATA[f],
f + " must have the correct value.");
}
// We can't check the values for channel and architecture. Just make
// sure they are in.
Assert.ok("architecture" in aPing.application,
"The application section must have an architecture field.");
Assert.ok("channel" in aPing.application,
"The application section must have a channel field.");
// Check the clientId and environment fields, as needed.
Assert.equal("clientId" in aPing, aHasClientId);
Assert.equal("environment" in aPing, aHasEnvironment);
}
function checkPayload(payload, reason, successfulPings) {
Assert.ok(payload.simpleMeasurements.uptime >= 0);
Assert.equal(payload.simpleMeasurements.startupInterrupted, 1);
Assert.equal(payload.simpleMeasurements.shutdownDuration, SHUTDOWN_TIME);
Assert.equal(payload.simpleMeasurements.savedPings, 1);
Assert.ok("maximalNumberOfConcurrentThreads" in payload.simpleMeasurements);
Assert.ok(payload.simpleMeasurements.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched);
let activeTicks = payload.simpleMeasurements.activeTicks;
Assert.ok(SESSION_RECORDER_EXPECTED ? activeTicks >= 0 : activeTicks == -1);
Assert.equal(payload.simpleMeasurements.failedProfileLockCount,
FAILED_PROFILE_LOCK_ATTEMPTS);
let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
let failedProfileLocksFile = profileDirectory.clone();
failedProfileLocksFile.append("Telemetry.FailedProfileLocks.txt");
Assert.ok(!failedProfileLocksFile.exists());
let isWindows = ("@mozilla.org/windows-registry-key;1" in Components.classes);
if (isWindows) {
Assert.ok(payload.simpleMeasurements.startupSessionRestoreReadBytes > 0);
Assert.ok(payload.simpleMeasurements.startupSessionRestoreWriteBytes > 0);
}
const TELEMETRY_PING = "TELEMETRY_PING";
const TELEMETRY_SUCCESS = "TELEMETRY_SUCCESS";
const TELEMETRY_TEST_FLAG = "TELEMETRY_TEST_FLAG";
const TELEMETRY_TEST_COUNT = "TELEMETRY_TEST_COUNT";
const TELEMETRY_TEST_KEYED_FLAG = "TELEMETRY_TEST_KEYED_FLAG";
const TELEMETRY_TEST_KEYED_COUNT = "TELEMETRY_TEST_KEYED_COUNT";
const READ_SAVED_PING_SUCCESS = "READ_SAVED_PING_SUCCESS";
Assert.ok(TELEMETRY_PING in payload.histograms);
Assert.ok(READ_SAVED_PING_SUCCESS in payload.histograms);
Assert.ok(TELEMETRY_TEST_FLAG in payload.histograms);
Assert.ok(TELEMETRY_TEST_COUNT in payload.histograms);
let rh = Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []);
for (let name of rh) {
if (/SQLITE/.test(name) && name in payload.histograms) {
let histogramName = ("STARTUP_" + name);
Assert.ok(histogramName in payload.histograms, histogramName + " must be available.");
}
}
Assert.ok(!(IGNORE_HISTOGRAM in payload.histograms));
Assert.ok(!(IGNORE_CLONED_HISTOGRAM in payload.histograms));
// Flag histograms should automagically spring to life.
const expected_flag = {
range: [1, 2],
bucket_count: 3,
histogram_type: 3,
values: {0:1, 1:0},
sum: 0,
sum_squares_lo: 0,
sum_squares_hi: 0
};
let flag = payload.histograms[TELEMETRY_TEST_FLAG];
Assert.equal(uneval(flag), uneval(expected_flag));
// We should have a test count.
const expected_count = {
range: [1, 2],
bucket_count: 3,
histogram_type: 4,
values: {0:1, 1:0},
sum: 1,
sum_squares_lo: 1,
sum_squares_hi: 0,
};
let count = payload.histograms[TELEMETRY_TEST_COUNT];
Assert.equal(uneval(count), uneval(expected_count));
// There should be one successful report from the previous telemetry ping.
const expected_tc = {
range: [1, 2],
bucket_count: 3,
histogram_type: 2,
values: {0:2, 1:successfulPings, 2:0},
sum: successfulPings,
sum_squares_lo: successfulPings,
sum_squares_hi: 0
};
let tc = payload.histograms[TELEMETRY_SUCCESS];
Assert.equal(uneval(tc), uneval(expected_tc));
let h = payload.histograms[READ_SAVED_PING_SUCCESS];
Assert.equal(h.values[0], 1);
// The ping should include data from memory reporters. We can't check that
// this data is correct, because we can't control the values returned by the
// memory reporters. But we can at least check that the data is there.
//
// It's important to check for the presence of reporters with a mix of units,
// because TelemetryPing has separate logic for each one. But we can't
// currently check UNITS_COUNT_CUMULATIVE or UNITS_PERCENTAGE because
// Telemetry doesn't touch a memory reporter with these units that's
// available on all platforms.
Assert.ok('MEMORY_JS_GC_HEAP' in payload.histograms); // UNITS_BYTES
Assert.ok('MEMORY_JS_COMPARTMENTS_SYSTEM' in payload.histograms); // UNITS_COUNT
// We should have included addon histograms.
Assert.ok("addonHistograms" in payload);
Assert.ok(ADDON_NAME in payload.addonHistograms);
Assert.ok(ADDON_HISTOGRAM in payload.addonHistograms[ADDON_NAME]);
Assert.ok(("mainThread" in payload.slowSQL) &&
("otherThreads" in payload.slowSQL));
// Check keyed histogram payload.
Assert.ok("keyedHistograms" in payload);
let keyedHistograms = payload.keyedHistograms;
Assert.ok(TELEMETRY_TEST_KEYED_FLAG in keyedHistograms);
Assert.ok(TELEMETRY_TEST_KEYED_COUNT in keyedHistograms);
Assert.deepEqual({}, keyedHistograms[TELEMETRY_TEST_KEYED_FLAG]);
const expected_keyed_count = {
"a": {
range: [1, 2],
bucket_count: 3,
histogram_type: 4,
values: {0:2, 1:0},
sum: 2,
sum_squares_lo: 2,
sum_squares_hi: 0,
},
"b": {
range: [1, 2],
bucket_count: 3,
histogram_type: 4,
values: {0:1, 1:0},
sum: 1,
sum_squares_lo: 1,
sum_squares_hi: 0,
},
};
Assert.deepEqual(expected_keyed_count, keyedHistograms[TELEMETRY_TEST_KEYED_COUNT]);
}
function writeStringToFile(file, contents) {
let ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
ostream.init(file, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
RW_OWNER, ostream.DEFER_OPEN);
ostream.write(contents, contents.length);
ostream.QueryInterface(Ci.nsISafeOutputStream).finish();
ostream.close();
}
function write_fake_shutdown_file() {
let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
let file = profileDirectory.clone();
file.append("Telemetry.ShutdownTime.txt");
let contents = "" + SHUTDOWN_TIME;
writeStringToFile(file, contents);
}
function write_fake_failedprofilelocks_file() {
let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsIFile);
let file = profileDirectory.clone();
file.append("Telemetry.FailedProfileLocks.txt");
let contents = "" + FAILED_PROFILE_LOCK_ATTEMPTS;
writeStringToFile(file, contents);
}
function run_test() {
do_test_pending();
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
Services.prefs.setBoolPref(PREF_ENABLED, true);
Services.prefs.setBoolPref(PREF_FHR_UPLOAD_ENABLED, true);
// Send the needed startup notifications to the datareporting service
// to ensure that it has been initialized.
if (HAS_DATAREPORTINGSERVICE) {
gDatareportingService.observe(null, "app-startup", null);
gDatareportingService.observe(null, "profile-after-change", null);
}
// Make it look like we've previously failed to lock a profile a couple times.
write_fake_failedprofilelocks_file();
// Make it look like we've shutdown before.
write_fake_shutdown_file();
let currentMaxNumberOfThreads = Telemetry.maximalNumberOfConcurrentThreads;
do_check_true(currentMaxNumberOfThreads > 0);
// Try to augment the maximal number of threads currently launched
let threads = [];
try {
for (let i = 0; i < currentMaxNumberOfThreads + 10; ++i) {
threads.push(Services.tm.newThread(0));
}
} catch (ex) {
// If memory is too low, it is possible that not all threads will be launched.
}
gNumberOfThreadsLaunched = threads.length;
do_check_true(Telemetry.maximalNumberOfConcurrentThreads >= gNumberOfThreadsLaunched);
do_register_cleanup(function() {
threads.forEach(function(thread) {
thread.shutdown();
});
});
Telemetry.asyncFetchTelemetryData(wrapWithExceptionHandler(run_next_test));
}
add_task(function* asyncSetup() {
yield TelemetrySession.setup();
yield TelemetryPing.setup();
if (HAS_DATAREPORTINGSERVICE) {
// force getSessionRecorder()==undefined to check the payload's activeTicks
gDatareportingService.simulateNoSessionRecorder();
}
// When no DRS or no DRS.getSessionRecorder(), activeTicks should be -1.
do_check_eq(TelemetrySession.getPayload().simpleMeasurements.activeTicks, -1);
if (HAS_DATAREPORTINGSERVICE) {
// Restore normal behavior for getSessionRecorder()
gDatareportingService.simulateRestoreSessionRecorder();
gDataReportingClientID = yield gDatareportingService.getClientID();
// We should have cached the client id now. Lets confirm that by
// checking the client id before the async ping setup is finished.
let promisePingSetup = TelemetryPing.reset();
do_check_eq(TelemetryPing.clientID, gDataReportingClientID);
yield promisePingSetup;
}
});
// Ensures that expired histograms are not part of the payload.
add_task(function* test_expiredHistogram() {
let histogram_id = "FOOBAR";
let dummy = Telemetry.newHistogram(histogram_id, "30", Telemetry.HISTOGRAM_EXPONENTIAL, 1, 2, 3);
dummy.add(1);
do_check_eq(TelemetrySession.getPayload()["histograms"][histogram_id], undefined);
do_check_eq(TelemetrySession.getPayload()["histograms"]["TELEMETRY_TEST_EXPIRED"], undefined);
});
// Checks that an invalid histogram file is deleted if TelemetryFile fails to parse it.
add_task(function* test_runInvalidJSON() {
let pingFile = getSavedPingFile("invalid-histograms.dat");
writeStringToFile(pingFile, "this.is.invalid.JSON");
do_check_true(pingFile.exists());
yield TelemetryFile.testLoadHistograms(pingFile);
do_check_false(pingFile.exists());
});
// Sends a ping to a non existing server. If we remove this test, we won't get
// all the histograms we need in the main ping.
add_task(function* test_noServerPing() {
yield sendPing();
// We need two pings in order to make sure STARTUP_MEMORY_STORAGE_SQLIE histograms
// are initialised. See bug 1131585.
yield sendPing();
});
// Checks that a sent ping is correctly received by a dummy http server.
add_task(function* test_simplePing() {
gHttpServer.start(-1);
gServerStarted = true;
gRequestIterator = Iterator(new Request());
yield sendPing();
let request = yield gRequestIterator.next();
let ping = decodeRequestPayload(request);
checkPingFormat(ping, PING_TYPE_MAIN, true, true);
});
// Saves the current session histograms, reloads them, performs a ping
// and checks that the dummy http server received both the previously
// saved histograms and the new ones.
add_task(function* test_saveLoadPing() {
let histogramsFile = getSavedPingFile("saved-histograms.dat");
setupTestData();
yield TelemetrySession.testSaveHistograms(histogramsFile);
yield TelemetryFile.testLoadHistograms(histogramsFile);
yield sendPing();
// Get requests received by dummy server.
let request1 = yield gRequestIterator.next();
let request2 = yield gRequestIterator.next();
Assert.equal(request1.getHeader("content-type"), "application/json; charset=UTF-8",
"The request must have the correct content-type.");
Assert.equal(request2.getHeader("content-type"), "application/json; charset=UTF-8",
"The request must have the correct content-type.");
// We decode both requests to check for the |reason|.
let ping1 = decodeRequestPayload(request1);
let ping2 = decodeRequestPayload(request2);
checkPingFormat(ping1, PING_TYPE_MAIN, true, true);
checkPingFormat(ping2, PING_TYPE_MAIN, true, true);
// Check we have the correct two requests. Ordering is not guaranteed.
if (ping1.payload.info.reason === REASON_TEST_PING) {
// Until we change MainPing according to bug 1120982, common ping payload
// will contain another nested payload.
checkPayload(ping1.payload, REASON_TEST_PING, 1);
checkPayload(ping2.payload, REASON_SAVED_SESSION, 1);
} else {
checkPayload(ping1.payload, REASON_SAVED_SESSION, 1);
checkPayload(ping2.payload, REASON_TEST_PING, 1);
}
});
add_task(function* test_checkSubsession() {
let now = new Date(2020, 1, 1, 12, 0, 0);
let expectedDate = new Date(2020, 1, 1, 0, 0, 0);
fakeNow(now);
TelemetrySession.setup();
const COUNT_ID = "TELEMETRY_TEST_COUNT";
const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
const count = Telemetry.getHistogramById(COUNT_ID);
const keyed = Telemetry.getKeyedHistogramById(KEYED_ID);
const registeredIds =
new Set(Telemetry.registeredHistograms(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN, []));
const stableHistograms = new Set([
"TELEMETRY_TEST_FLAG",
"TELEMETRY_TEST_COUNT",
"TELEMETRY_TEST_RELEASE_OPTOUT",
"TELEMETRY_TEST_RELEASE_OPTIN",
"STARTUP_CRASH_DETECTED",
]);
const stableKeyedHistograms = new Set([
"TELEMETRY_TEST_KEYED_FLAG",
"TELEMETRY_TEST_KEYED_COUNT",
"TELEMETRY_TEST_KEYED_RELEASE_OPTIN",
"TELEMETRY_TEST_KEYED_RELEASE_OPTOUT",
]);
// Compare the two sets of histograms.
// The "subsession" histograms should match the registered
// "classic" histograms. However, histograms can change
// between us collecting the different payloads, so we only
// check for deep equality on known stable histograms.
checkHistograms = (classic, subsession) => {
for (let id of Object.keys(classic)) {
if (!registeredIds.has(id)) {
continue;
}
Assert.ok(id in subsession);
if (stableHistograms.has(id)) {
Assert.deepEqual(classic[id],
subsession[id]);
} else {
Assert.equal(classic[id].histogram_type,
subsession[id].histogram_type);
}
}
};
// Same as above, except for keyed histograms.
checkKeyedHistograms = (classic, subsession) => {
for (let id of Object.keys(classic)) {
if (!registeredIds.has(id)) {
continue;
}
Assert.ok(id in subsession);
if (stableKeyedHistograms.has(id)) {
Assert.deepEqual(classic[id],
subsession[id]);
}
}
};
// Both classic and subsession payload histograms should start the same.
// The payloads should be identical for now except for the reason.
count.clear();
keyed.clear();
let classic = TelemetrySession.getPayload();
let subsession = TelemetrySession.getPayload("environment-change");
Assert.equal(classic.info.reason, "gather-payload");
Assert.equal(subsession.info.reason, "environment-change");
Assert.ok(!(COUNT_ID in classic.histograms));
Assert.ok(!(COUNT_ID in subsession.histograms));
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.deepEqual(classic.keyedHistograms[KEYED_ID], {});
Assert.deepEqual(subsession.keyedHistograms[KEYED_ID], {});
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Adding values should get picked up in both.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Values should still reset properly.
count.clear();
keyed.clear();
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(!(COUNT_ID in classic.histograms));
Assert.ok(!(COUNT_ID in subsession.histograms));
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.deepEqual(classic.keyedHistograms[KEYED_ID], {});
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// Adding values should get picked up in both.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// We should be able to reset only the subsession histograms.
// First check that "snapshot and clear" still returns the old state...
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change", true);
let subsessionStartDate = new Date(classic.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString());
subsessionStartDate = new Date(subsession.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), expectedDate.toISOString());
checkHistograms(classic.histograms, subsession.histograms);
checkKeyedHistograms(classic.keyedHistograms, subsession.keyedHistograms);
// ... then check that the next snapshot shows the subsession
// histograms got reset.
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 1);
Assert.equal(subsession.histograms[COUNT_ID].sum, 0);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 1);
Assert.deepEqual(subsession.keyedHistograms[KEYED_ID], {});
// Adding values should get picked up in both again.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
classic = TelemetrySession.getPayload();
subsession = TelemetrySession.getPayload("environment-change");
Assert.ok(COUNT_ID in classic.histograms);
Assert.ok(COUNT_ID in subsession.histograms);
Assert.equal(classic.histograms[COUNT_ID].sum, 2);
Assert.equal(subsession.histograms[COUNT_ID].sum, 1);
Assert.ok(KEYED_ID in classic.keyedHistograms);
Assert.ok(KEYED_ID in subsession.keyedHistograms);
Assert.equal(classic.keyedHistograms[KEYED_ID]["a"].sum, 2);
Assert.equal(classic.keyedHistograms[KEYED_ID]["b"].sum, 2);
Assert.equal(subsession.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(subsession.keyedHistograms[KEYED_ID]["b"].sum, 1);
});
add_task(function* test_dailyCollection() {
let now = new Date(2030, 1, 1, 12, 0, 0);
let nowDay = new Date(2030, 1, 1, 0, 0, 0);
let timerCallback = null;
let timerDelay = null;
gRequestIterator = Iterator(new Request());
fakeNow(now);
fakeDailyTimers((callback, timeout) => {
dump("fake setDailyTimeout(" + callback + ", " + timeout + ")\n");
timerCallback = callback;
timerDelay = timeout;
return 1;
}, () => {});
// Init and check timer.
yield TelemetrySession.setup();
TelemetryPing.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
Assert.ok(!!timerCallback);
Assert.ok(Number.isFinite(timerDelay));
let timerDate = futureDate(now, timerDelay);
let expectedDate = futureDate(nowDay, MS_IN_ONE_DAY);
Assert.equal(timerDate.toISOString(), expectedDate.toISOString());
// Set histograms to expected state.
const COUNT_ID = "TELEMETRY_TEST_COUNT";
const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
const count = Telemetry.getHistogramById(COUNT_ID);
const keyed = Telemetry.getKeyedHistogramById(KEYED_ID);
count.clear();
keyed.clear();
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
keyed.add("b", 1);
// Trigger and collect daily ping.
yield timerCallback();
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
let subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());
Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["b"].sum, 2);
// Trigger and collect another ping. The histograms should be reset.
yield timerCallback();
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());
Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0);
Assert.deepEqual(ping.payload.keyedHistograms[KEYED_ID], {});
// Trigger and collect another daily ping, with the histograms being set again.
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
yield timerCallback();
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.payload.info.reason, REASON_DAILY);
subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());
Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["b"].sum, 1);
});
add_task(function* test_environmentChange() {
let now = new Date(2040, 1, 1, 12, 0, 0);
let nowDay = new Date(2040, 1, 1, 0, 0, 0);
let timerCallback = null;
let timerDelay = null;
gRequestIterator = Iterator(new Request());
fakeNow(now);
fakeDailyTimers(() => {}, () => {});
const PREF_TEST = "toolkit.telemetry.test.pref1";
Preferences.reset(PREF_TEST);
let prefsToWatch = {};
prefsToWatch[PREF_TEST] = TelemetryEnvironment.RECORD_PREF_VALUE;
// Setup.
yield TelemetrySession.setup();
TelemetryPing.setServer("http://localhost:" + gHttpServer.identity.primaryPort);
TelemetryEnvironment._watchPreferences(prefsToWatch);
// Set histograms to expected state.
const COUNT_ID = "TELEMETRY_TEST_COUNT";
const KEYED_ID = "TELEMETRY_TEST_KEYED_COUNT";
const count = Telemetry.getHistogramById(COUNT_ID);
const keyed = Telemetry.getKeyedHistogramById(KEYED_ID);
count.clear();
keyed.clear();
count.add(1);
keyed.add("a", 1);
keyed.add("b", 1);
// Trigger and collect environment-change ping.
Preferences.set(PREF_TEST, 1);
let request = yield gRequestIterator.next();
Assert.ok(!!request);
let ping = decodeRequestPayload(request);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 1);
Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE);
let subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());
Assert.equal(ping.payload.histograms[COUNT_ID].sum, 1);
Assert.equal(ping.payload.keyedHistograms[KEYED_ID]["a"].sum, 1);
// Trigger and collect another ping. The histograms should be reset.
Preferences.set(PREF_TEST, 2);
request = yield gRequestIterator.next();
Assert.ok(!!request);
ping = decodeRequestPayload(request);
Assert.equal(ping.type, PING_TYPE_MAIN);
Assert.equal(ping.environment.settings.userPrefs[PREF_TEST], 2);
Assert.equal(ping.payload.info.reason, REASON_ENVIRONMENT_CHANGE);
subsessionStartDate = new Date(ping.payload.info.subsessionStartDate);
Assert.equal(subsessionStartDate.toISOString(), nowDay.toISOString());
Assert.equal(ping.payload.histograms[COUNT_ID].sum, 0);
Assert.deepEqual(ping.payload.keyedHistograms[KEYED_ID], {});
});
// Checks that an expired histogram file is deleted when loaded.
add_task(function* test_runOldPingFile() {
let histogramsFile = getSavedPingFile("old-histograms.dat");
yield TelemetrySession.testSaveHistograms(histogramsFile);
do_check_true(histogramsFile.exists());
let mtime = histogramsFile.lastModifiedTime;
histogramsFile.lastModifiedTime = mtime - (14 * 24 * 60 * 60 * 1000 + 60000); // 14 days, 1m
yield TelemetryFile.testLoadHistograms(histogramsFile);
do_check_false(histogramsFile.exists());
});
add_task(function* test_savedSessionClientID() {
// Assure that we store the ping properly when saving sessions on shutdown.
// We make the TelemetrySession shutdown to trigger a session save.
const dir = TelemetryFile.pingDirectoryPath;
yield OS.File.removeDir(dir, {ignoreAbsent: true});
yield OS.File.makeDir(dir);
yield TelemetrySession.shutdown();
yield TelemetryFile.loadSavedPings();
Assert.equal(TelemetryFile.pingsLoaded, 1);
let ping = TelemetryFile.popPendingPings().next();
Assert.equal(ping.value.clientId, gDataReportingClientID);
});
add_task(function* stopServer(){
gHttpServer.stop(do_test_finished);
});
// An iterable sequence of http requests
function Request() {
let defers = [];
let current = 0;
function RequestIterator() {}
// Returns a promise that resolves to the next http request
RequestIterator.prototype.next = function() {
let deferred = defers[current++];
return deferred.promise;
}
this.__iterator__ = function(){
return new RequestIterator();
}
registerPingHandler((request, response) => {
let deferred = defers[defers.length - 1];
defers.push(Promise.defer());
deferred.resolve(request);
});
defers.push(Promise.defer());
}
@@ -355,10 +355,10 @@ function test_addons() {
function test_privateMode() {
var h = Telemetry.newHistogram("test::private_mode_boolean", "never", Telemetry.HISTOGRAM_BOOLEAN);
var orig = h.snapshot();
Telemetry.canRecord = false;
Telemetry.canRecordExtended = false;
h.add(1);
do_check_eq(uneval(orig), uneval(h.snapshot()));
Telemetry.canRecord = true;
Telemetry.canRecordExtended = true;
h.add(1);
do_check_neq(uneval(orig), uneval(h.snapshot()));
}
@@ -391,7 +391,7 @@ function numberRange(lower, upper)
function test_keyed_boolean_histogram()
{
const KEYED_ID = "test::keyed::boolean";
KEYS = ["key"+(i+1) for (i of numberRange(0, 2))];
let KEYS = ["key"+(i+1) for (i of numberRange(0, 2))];
KEYS.push("漢語");
let histogramBase = {
"min": 1,
@@ -605,6 +605,134 @@ function test_datasets()
Assert.ok(registered.has("TELEMETRY_TEST_KEYED_RELEASE_OPTOUT"));
}
function test_subsession() {
const ID = "TELEMETRY_TEST_COUNT";
const FLAG = "TELEMETRY_TEST_FLAG";
let h = Telemetry.getHistogramById(ID);
let flag = Telemetry.getHistogramById(FLAG);
// Both original and duplicate should start out the same.
h.clear();
let snapshot = Telemetry.histogramSnapshots;
let subsession = Telemetry.snapshotSubsessionHistograms();
Assert.ok(!(ID in snapshot));
Assert.ok(!(ID in subsession));
// They should instantiate and pick-up the count.
h.add(1);
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.ok(ID in snapshot);
Assert.ok(ID in subsession);
Assert.equal(snapshot[ID].sum, 1);
Assert.equal(subsession[ID].sum, 1);
// They should still reset properly.
h.clear();
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.ok(!(ID in snapshot));
Assert.ok(!(ID in subsession));
// Both should instantiate and pick-up the count.
h.add(1);
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.equal(snapshot[ID].sum, 1);
Assert.equal(subsession[ID].sum, 1);
// Check that we are able to only reset the duplicate histogram.
h.clear(true);
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.ok(ID in snapshot);
Assert.ok(ID in subsession);
Assert.equal(snapshot[ID].sum, 1);
Assert.equal(subsession[ID].sum, 0);
// Both should register the next count.
h.add(1);
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.equal(snapshot[ID].sum, 2);
Assert.equal(subsession[ID].sum, 1);
// Retrieve a subsession snapshot and pass the flag to
// clear subsession histograms too.
h.clear();
flag.clear();
h.add(1);
flag.add(1);
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms(true);
Assert.ok(ID in snapshot);
Assert.ok(ID in subsession);
Assert.ok(FLAG in snapshot);
Assert.ok(FLAG in subsession);
Assert.equal(snapshot[ID].sum, 1);
Assert.equal(subsession[ID].sum, 1);
Assert.equal(snapshot[FLAG].sum, 1);
Assert.equal(subsession[FLAG].sum, 1);
// The next subsesssion snapshot should show the histograms
// got reset.
snapshot = Telemetry.histogramSnapshots;
subsession = Telemetry.snapshotSubsessionHistograms();
Assert.ok(ID in snapshot);
Assert.ok(ID in subsession);
Assert.ok(FLAG in snapshot);
Assert.ok(FLAG in subsession);
Assert.equal(snapshot[ID].sum, 1);
Assert.equal(subsession[ID].sum, 0);
Assert.equal(snapshot[FLAG].sum, 1);
Assert.equal(subsession[FLAG].sum, 0);
}
function test_keyed_subsession() {
let h = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG");
const KEY = "foo";
// Both original and subsession should start out the same.
h.clear();
Assert.ok(!(KEY in h.snapshot()));
Assert.ok(!(KEY in h.subsessionSnapshot()));
Assert.equal(h.snapshot(KEY).sum, 0);
Assert.equal(h.subsessionSnapshot(KEY).sum, 0);
// Both should register the flag.
h.add(KEY, 1);
Assert.ok(KEY in h.snapshot());
Assert.ok(KEY in h.subsessionSnapshot());
Assert.equal(h.snapshot(KEY).sum, 1);
Assert.equal(h.subsessionSnapshot(KEY).sum, 1);
// Check that we are able to only reset the subsession histogram.
h.clear(true);
Assert.ok(KEY in h.snapshot());
Assert.ok(!(KEY in h.subsessionSnapshot()));
Assert.equal(h.snapshot(KEY).sum, 1);
Assert.equal(h.subsessionSnapshot(KEY).sum, 0);
// Setting the flag again should make both match again.
h.add(KEY, 1);
Assert.ok(KEY in h.snapshot());
Assert.ok(KEY in h.subsessionSnapshot());
Assert.equal(h.snapshot(KEY).sum, 1);
Assert.equal(h.subsessionSnapshot(KEY).sum, 1);
// Check that "snapshot and clear" works properly.
let snapshot = h.snapshot();
let subsession = h.snapshotSubsessionAndClear();
Assert.ok(KEY in snapshot);
Assert.ok(KEY in subsession);
Assert.equal(snapshot[KEY].sum, 1);
Assert.equal(subsession[KEY].sum, 1);
subsession = h.subsessionSnapshot();
Assert.ok(!(KEY in subsession));
Assert.equal(h.subsessionSnapshot(KEY).sum, 0);
}
function generateUUID() {
let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
// strip {}
@@ -640,4 +768,6 @@ function run_test()
test_expired_histogram();
test_keyed_histogram();
test_datasets();
test_subsession();
test_keyed_subsession();
}
@@ -4,6 +4,7 @@ tail =
skip-if = toolkit == 'gonk'
[test_nsITelemetry.js]
[test_TelemetryEnvironment.js]
[test_TelemetryFlagClear.js]
[test_TelemetryLateWrites.js]
[test_TelemetryLockCount.js]
@@ -17,5 +18,6 @@ skip-if = toolkit == 'gonk'
[test_ThirdPartyCookieProbe.js]
[test_TelemetrySendOldPings.js]
skip-if = debug == true || os == "android" # Disabled due to intermittent orange on Android
[test_TelemetrySession.js]
[test_ThreadHangStats.js]
run-sequentially = Bug 1046307, test can fail intermittently when CPU load is high
@@ -389,7 +389,7 @@ void
nsTerminator::StartWriter()
{
if (!Telemetry::CanRecord()) {
if (!Telemetry::CanRecordExtended()) {
return;
}
nsCOMPtr<nsIFile> profLD;
@@ -473,7 +473,7 @@ nsTerminator::UpdateHeartbeat(const char* aTopic)
void
nsTerminator::UpdateTelemetry()
{
if (!Telemetry::CanRecord() || !gWriteReady) {
if (!Telemetry::CanRecordExtended() || !gWriteReady) {
return;
}
@@ -65,7 +65,7 @@ add_task(function* actualTest() {
do_check_true(simpleMeasurements.bar > 1); // bar was included
do_check_eq(undefined, simpleMeasurements.baz); // baz wasn't included since it wasn't added
yield TelemetrySession.shutdown();
yield TelemetrySession.shutdown(false);
do_test_finished();
});

Some files were not shown because too many files have changed in this diff Show More