1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/cache/StreamList.h"
#include <algorithm>
#include "mozilla/dom/cache/CacheStreamControlParent.h"
#include "mozilla/dom/cache/Context.h"
#include "mozilla/dom/cache/Manager.h"
#include "nsIInputStream.h"
namespace mozilla::dom::cache {
namespace {
auto MatchById(const nsID& aId) {
return [aId](const auto& entry) { return entry.mId == aId; };
}
} // namespace
StreamList::StreamList(SafeRefPtr<Manager> aManager,
SafeRefPtr<Context> aContext)
: mManager(std::move(aManager)),
mContext(std::move(aContext)),
mCacheId(INVALID_CACHE_ID),
mStreamControl(nullptr),
mActivated(false) {
MOZ_DIAGNOSTIC_ASSERT(mManager);
mContext->AddActivity(*this);
}
Manager& StreamList::GetManager() const {
MOZ_DIAGNOSTIC_ASSERT(mManager);
return *mManager;
}
bool StreamList::ShouldOpenStreamFor(const nsID& aId) const {
NS_ASSERT_OWNINGTHREAD(StreamList);
return std::any_of(mList.cbegin(), mList.cend(), MatchById(aId));
}
void StreamList::SetStreamControl(CacheStreamControlParent* aStreamControl) {
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(aStreamControl);
// For cases where multiple streams are serialized for a single list
// then the control will get passed multiple times. This is ok, but
// it should be the same control each time.
if (mStreamControl) {
MOZ_DIAGNOSTIC_ASSERT(aStreamControl == mStreamControl);
return;
}
mStreamControl = aStreamControl;
mStreamControl->SetStreamList(SafeRefPtrFromThis());
}
void StreamList::RemoveStreamControl(CacheStreamControlParent* aStreamControl) {
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(mStreamControl);
MOZ_DIAGNOSTIC_ASSERT(mStreamControl == aStreamControl);
mStreamControl = nullptr;
}
void StreamList::Activate(CacheId aCacheId) {
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(!mActivated);
MOZ_DIAGNOSTIC_ASSERT(mCacheId == INVALID_CACHE_ID);
mActivated = true;
mCacheId = aCacheId;
mManager->AddRefCacheId(mCacheId);
mManager->AddStreamList(*this);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->AddRefBodyId(mList[i].mId);
}
}
void StreamList::Add(const nsID& aId, nsCOMPtr<nsIInputStream>&& aStream) {
// All streams should be added on IO thread before we set the stream
// control on the owning IPC thread.
MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
// Removal of the stream will be triggered when the stream is closed,
// which happens only once, so let's ensure nobody adds us twice.
MOZ_ASSERT_DEBUG_OR_FUZZING(
std::find_if(mList.begin(), mList.end(), MatchById(aId)) == mList.end());
mList.EmplaceBack(aId, std::move(aStream));
}
already_AddRefed<nsIInputStream> StreamList::Extract(const nsID& aId) {
NS_ASSERT_OWNINGTHREAD(StreamList);
const auto it = std::find_if(mList.begin(), mList.end(), MatchById(aId));
// Note that if the stream has not been opened with OpenMode::Eager we will
// find it nullptr here and it will have to be opened by the consumer.
return it != mList.end() ? it->mStream.forget() : nullptr;
}
void StreamList::NoteClosed(const nsID& aId) {
NS_ASSERT_OWNINGTHREAD(StreamList);
const auto it = std::find_if(mList.begin(), mList.end(), MatchById(aId));
if (it != mList.end()) {
MOZ_ASSERT(!it->mStream, "We expect to find mStream already extracted.");
mList.RemoveElementAt(it);
mManager->ReleaseBodyId(aId);
}
if (mList.IsEmpty() && mStreamControl) {
mStreamControl->Shutdown();
}
}
void StreamList::NoteClosedAll() {
NS_ASSERT_OWNINGTHREAD(StreamList);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId);
}
mList.Clear();
if (mStreamControl) {
mStreamControl->Shutdown();
}
}
void StreamList::CloseAll() {
NS_ASSERT_OWNINGTHREAD(StreamList);
if (mStreamControl && mStreamControl->CanSend()) {
// CloseAll will kick off everything needed for shutdown.
// mStreamControl may go away immediately or async.
mStreamControl->CloseAll();
} else {
// We cannot interact with the child, let's just clear our lists of
// streams to unblock shutdown.
if (NS_WARN_IF(mStreamControl)) {
// TODO: Check if this case is actually possible. We might see a late
// delivery of the CSCP::ActorDestroy? What would that mean for CanSend?
mStreamControl->LostIPCCleanup(SafeRefPtrFromThis());
mStreamControl = nullptr;
} else {
NoteClosedAll();
}
}
}
void StreamList::Cancel() {
NS_ASSERT_OWNINGTHREAD(StreamList);
CloseAll();
}
bool StreamList::MatchesCacheId(CacheId aCacheId) const {
NS_ASSERT_OWNINGTHREAD(StreamList);
return aCacheId == mCacheId;
}
void StreamList::DoStringify(nsACString& aData) {
aData.Append("StreamList "_ns + kStringifyStartInstance +
//
"List:"_ns +
IntToCString(static_cast<uint64_t>(mList.Length())) +
kStringifyDelimiter +
//
"Activated:"_ns + IntToCString(mActivated) + ")"_ns +
kStringifyDelimiter +
//
"Manager:"_ns + IntToCString(static_cast<bool>(mManager)));
if (mManager) {
aData.Append(" "_ns);
mManager->Stringify(aData);
}
aData.Append(kStringifyDelimiter +
//
"Context:"_ns + IntToCString(static_cast<bool>(mContext)));
if (mContext) {
aData.Append(" "_ns);
mContext->Stringify(aData);
}
aData.Append(kStringifyEndInstance);
}
StreamList::~StreamList() {
NS_ASSERT_OWNINGTHREAD(StreamList);
MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
if (mActivated) {
mManager->RemoveStreamList(*this);
for (uint32_t i = 0; i < mList.Length(); ++i) {
mManager->ReleaseBodyId(mList[i].mId);
}
mManager->ReleaseCacheId(mCacheId);
}
mContext->RemoveActivity(*this);
}
} // namespace mozilla::dom::cache
|