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