1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/cache/StreamList.h"
10 #include "mozilla/dom/cache/CacheStreamControlParent.h"
11 #include "mozilla/dom/cache/Context.h"
12 #include "mozilla/dom/cache/Manager.h"
13 #include "nsIInputStream.h"
15 namespace mozilla::dom::cache
{
19 auto MatchById(const nsID
& aId
) {
20 return [aId
](const auto& entry
) { return entry
.mId
== aId
; };
25 StreamList::StreamList(SafeRefPtr
<Manager
> aManager
,
26 SafeRefPtr
<Context
> aContext
)
27 : mManager(std::move(aManager
)),
28 mContext(std::move(aContext
)),
29 mCacheId(INVALID_CACHE_ID
),
30 mStreamControl(nullptr),
32 MOZ_DIAGNOSTIC_ASSERT(mManager
);
33 mContext
->AddActivity(*this);
36 Manager
& StreamList::GetManager() const {
37 MOZ_DIAGNOSTIC_ASSERT(mManager
);
41 bool StreamList::ShouldOpenStreamFor(const nsID
& aId
) const {
42 NS_ASSERT_OWNINGTHREAD(StreamList
);
44 return std::any_of(mList
.cbegin(), mList
.cend(), MatchById(aId
));
47 void StreamList::SetStreamControl(CacheStreamControlParent
* aStreamControl
) {
48 NS_ASSERT_OWNINGTHREAD(StreamList
);
49 MOZ_DIAGNOSTIC_ASSERT(aStreamControl
);
51 // For cases where multiple streams are serialized for a single list
52 // then the control will get passed multiple times. This is ok, but
53 // it should be the same control each time.
55 MOZ_DIAGNOSTIC_ASSERT(aStreamControl
== mStreamControl
);
59 mStreamControl
= aStreamControl
;
60 mStreamControl
->SetStreamList(SafeRefPtrFromThis());
63 void StreamList::RemoveStreamControl(CacheStreamControlParent
* aStreamControl
) {
64 NS_ASSERT_OWNINGTHREAD(StreamList
);
65 MOZ_DIAGNOSTIC_ASSERT(mStreamControl
);
66 MOZ_DIAGNOSTIC_ASSERT(mStreamControl
== aStreamControl
);
67 mStreamControl
= nullptr;
70 void StreamList::Activate(CacheId aCacheId
) {
71 NS_ASSERT_OWNINGTHREAD(StreamList
);
72 MOZ_DIAGNOSTIC_ASSERT(!mActivated
);
73 MOZ_DIAGNOSTIC_ASSERT(mCacheId
== INVALID_CACHE_ID
);
76 mManager
->AddRefCacheId(mCacheId
);
77 mManager
->AddStreamList(*this);
79 for (uint32_t i
= 0; i
< mList
.Length(); ++i
) {
80 mManager
->AddRefBodyId(mList
[i
].mId
);
84 void StreamList::Add(const nsID
& aId
, nsCOMPtr
<nsIInputStream
>&& aStream
) {
85 // All streams should be added on IO thread before we set the stream
86 // control on the owning IPC thread.
87 MOZ_DIAGNOSTIC_ASSERT(!mStreamControl
);
89 // Removal of the stream will be triggered when the stream is closed,
90 // which happens only once, so let's ensure nobody adds us twice.
91 MOZ_ASSERT_DEBUG_OR_FUZZING(
92 std::find_if(mList
.begin(), mList
.end(), MatchById(aId
)) == mList
.end());
94 mList
.EmplaceBack(aId
, std::move(aStream
));
97 already_AddRefed
<nsIInputStream
> StreamList::Extract(const nsID
& aId
) {
98 NS_ASSERT_OWNINGTHREAD(StreamList
);
100 const auto it
= std::find_if(mList
.begin(), mList
.end(), MatchById(aId
));
102 // Note that if the stream has not been opened with OpenMode::Eager we will
103 // find it nullptr here and it will have to be opened by the consumer.
105 return it
!= mList
.end() ? it
->mStream
.forget() : nullptr;
108 void StreamList::NoteClosed(const nsID
& aId
) {
109 NS_ASSERT_OWNINGTHREAD(StreamList
);
111 const auto it
= std::find_if(mList
.begin(), mList
.end(), MatchById(aId
));
112 if (it
!= mList
.end()) {
113 MOZ_ASSERT(!it
->mStream
, "We expect to find mStream already extracted.");
114 mList
.RemoveElementAt(it
);
115 mManager
->ReleaseBodyId(aId
);
118 if (mList
.IsEmpty() && mStreamControl
) {
119 mStreamControl
->Shutdown();
123 void StreamList::NoteClosedAll() {
124 NS_ASSERT_OWNINGTHREAD(StreamList
);
125 for (uint32_t i
= 0; i
< mList
.Length(); ++i
) {
126 mManager
->ReleaseBodyId(mList
[i
].mId
);
130 if (mStreamControl
) {
131 mStreamControl
->Shutdown();
135 void StreamList::CloseAll() {
136 NS_ASSERT_OWNINGTHREAD(StreamList
);
138 if (mStreamControl
&& mStreamControl
->CanSend()) {
139 auto* streamControl
= std::exchange(mStreamControl
, nullptr);
141 streamControl
->CloseAll();
143 mStreamControl
= std::exchange(streamControl
, nullptr);
145 mStreamControl
->Shutdown();
147 // We cannot interact with the child, let's just clear our lists of
148 // streams to unblock shutdown.
149 if (NS_WARN_IF(mStreamControl
)) {
150 // TODO: Check if this case is actually possible. We might see a late
151 // delivery of the CSCP::ActorDestroy? What would that mean for CanSend?
152 mStreamControl
->LostIPCCleanup(SafeRefPtrFromThis());
153 mStreamControl
= nullptr;
160 void StreamList::Cancel() {
161 NS_ASSERT_OWNINGTHREAD(StreamList
);
165 bool StreamList::MatchesCacheId(CacheId aCacheId
) const {
166 NS_ASSERT_OWNINGTHREAD(StreamList
);
167 return aCacheId
== mCacheId
;
170 void StreamList::DoStringify(nsACString
& aData
) {
171 aData
.Append("StreamList "_ns
+ kStringifyStartInstance
+
174 IntToCString(static_cast<uint64_t>(mList
.Length())) +
175 kStringifyDelimiter
+
177 "Activated:"_ns
+ IntToCString(mActivated
) + ")"_ns
+
178 kStringifyDelimiter
+
180 "Manager:"_ns
+ IntToCString(static_cast<bool>(mManager
)));
182 aData
.Append(" "_ns
);
183 mManager
->Stringify(aData
);
185 aData
.Append(kStringifyDelimiter
+
187 "Context:"_ns
+ IntToCString(static_cast<bool>(mContext
)));
189 aData
.Append(" "_ns
);
190 mContext
->Stringify(aData
);
192 aData
.Append(kStringifyEndInstance
);
195 StreamList::~StreamList() {
196 NS_ASSERT_OWNINGTHREAD(StreamList
);
197 MOZ_DIAGNOSTIC_ASSERT(!mStreamControl
);
199 mManager
->RemoveStreamList(*this);
200 for (uint32_t i
= 0; i
< mList
.Length(); ++i
) {
201 mManager
->ReleaseBodyId(mList
[i
].mId
);
203 mManager
->ReleaseCacheId(mCacheId
);
205 mContext
->RemoveActivity(*this);
208 } // namespace mozilla::dom::cache