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/SlicedInputStream.h"
8 #include "mozilla/dom/ContentParent.h"
9 #include "mozilla/StaticMutex.h"
10 #include "mozilla/StaticPtr.h"
11 #include "nsIPropertyBag2.h"
12 #include "nsStreamUtils.h"
13 #include "RemoteLazyInputStreamParent.h"
14 #include "RemoteLazyInputStreamStorage.h"
15
16 namespace mozilla {
17
18 using namespace hal;
19
20 namespace {
21 StaticMutex gMutex;
22 StaticRefPtr<RemoteLazyInputStreamStorage> gStorage;
23 } // namespace
24
25 NS_INTERFACE_MAP_BEGIN(RemoteLazyInputStreamStorage)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIObserver)26 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
27 NS_INTERFACE_MAP_ENTRY(nsIObserver)
28 NS_INTERFACE_MAP_END
29
30 NS_IMPL_ADDREF(RemoteLazyInputStreamStorage)
31 NS_IMPL_RELEASE(RemoteLazyInputStreamStorage)
32
33 /* static */
34 Result<RefPtr<RemoteLazyInputStreamStorage>, nsresult>
35 RemoteLazyInputStreamStorage::Get() {
36 mozilla::StaticMutexAutoLock lock(gMutex);
37 if (gStorage) {
38 RefPtr<RemoteLazyInputStreamStorage> storage = gStorage;
39 return storage;
40 }
41
42 return Err(NS_ERROR_NOT_INITIALIZED);
43 }
44
45 /* static */
Initialize()46 void RemoteLazyInputStreamStorage::Initialize() {
47 mozilla::StaticMutexAutoLock lock(gMutex);
48 MOZ_ASSERT(!gStorage);
49
50 gStorage = new RemoteLazyInputStreamStorage();
51
52 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
53 if (obs) {
54 obs->AddObserver(gStorage, "xpcom-shutdown", false);
55 obs->AddObserver(gStorage, "ipc:content-shutdown", false);
56 }
57 }
58
59 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)60 RemoteLazyInputStreamStorage::Observe(nsISupports* aSubject, const char* aTopic,
61 const char16_t* aData) {
62 if (!strcmp(aTopic, "xpcom-shutdown")) {
63 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
64 if (obs) {
65 obs->RemoveObserver(this, "xpcom-shutdown");
66 obs->RemoveObserver(this, "ipc:content-shutdown");
67 }
68
69 mozilla::StaticMutexAutoLock lock(gMutex);
70 gStorage = nullptr;
71 return NS_OK;
72 }
73
74 MOZ_ASSERT(!strcmp(aTopic, "ipc:content-shutdown"));
75
76 nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
77 if (NS_WARN_IF(!props)) {
78 return NS_ERROR_FAILURE;
79 }
80
81 uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
82 props->GetPropertyAsUint64(u"childID"_ns, &childID);
83 if (NS_WARN_IF(childID == CONTENT_PROCESS_ID_UNKNOWN)) {
84 return NS_ERROR_FAILURE;
85 }
86
87 mozilla::StaticMutexAutoLock lock(gMutex);
88
89 for (auto iter = mStorage.Iter(); !iter.Done(); iter.Next()) {
90 if (iter.Data()->mChildID == childID) {
91 iter.Remove();
92 }
93 }
94
95 return NS_OK;
96 }
97
AddStream(nsIInputStream * aInputStream,const nsID & aID,uint64_t aSize,uint64_t aChildID)98 void RemoteLazyInputStreamStorage::AddStream(nsIInputStream* aInputStream,
99 const nsID& aID, uint64_t aSize,
100 uint64_t aChildID) {
101 MOZ_ASSERT(aInputStream);
102
103 UniquePtr<StreamData> data = MakeUnique<StreamData>();
104 data->mInputStream = aInputStream;
105 data->mChildID = aChildID;
106 data->mSize = aSize;
107
108 mozilla::StaticMutexAutoLock lock(gMutex);
109 mStorage.InsertOrUpdate(aID, std::move(data));
110 }
111
ForgetStream(const nsID & aID)112 nsCOMPtr<nsIInputStream> RemoteLazyInputStreamStorage::ForgetStream(
113 const nsID& aID) {
114 UniquePtr<StreamData> entry;
115
116 mozilla::StaticMutexAutoLock lock(gMutex);
117 mStorage.Remove(aID, &entry);
118
119 if (!entry) {
120 return nullptr;
121 }
122
123 return std::move(entry->mInputStream);
124 }
125
HasStream(const nsID & aID)126 bool RemoteLazyInputStreamStorage::HasStream(const nsID& aID) {
127 mozilla::StaticMutexAutoLock lock(gMutex);
128 StreamData* data = mStorage.Get(aID);
129 return !!data;
130 }
131
GetStream(const nsID & aID,uint64_t aStart,uint64_t aLength,nsIInputStream ** aInputStream)132 void RemoteLazyInputStreamStorage::GetStream(const nsID& aID, uint64_t aStart,
133 uint64_t aLength,
134 nsIInputStream** aInputStream) {
135 *aInputStream = nullptr;
136
137 nsCOMPtr<nsIInputStream> inputStream;
138 uint64_t size;
139
140 // NS_CloneInputStream cannot be called when the mutex is locked because it
141 // can, recursively call GetStream() in case the child actor lives on the
142 // parent process.
143 {
144 mozilla::StaticMutexAutoLock lock(gMutex);
145 StreamData* data = mStorage.Get(aID);
146 if (!data) {
147 return;
148 }
149
150 inputStream = data->mInputStream;
151 size = data->mSize;
152 }
153
154 MOZ_ASSERT(inputStream);
155
156 // We cannot return always the same inputStream because not all of them are
157 // able to be reused. Better to clone them.
158
159 nsCOMPtr<nsIInputStream> clonedStream;
160 nsCOMPtr<nsIInputStream> replacementStream;
161
162 nsresult rv = NS_CloneInputStream(inputStream, getter_AddRefs(clonedStream),
163 getter_AddRefs(replacementStream));
164 if (NS_WARN_IF(NS_FAILED(rv))) {
165 return;
166 }
167
168 if (replacementStream) {
169 mozilla::StaticMutexAutoLock lock(gMutex);
170 StreamData* data = mStorage.Get(aID);
171 // data can be gone in the meantime.
172 if (!data) {
173 return;
174 }
175
176 data->mInputStream = replacementStream;
177 }
178
179 // Now it's the right time to apply a slice if needed.
180 if (aStart > 0 || aLength < size) {
181 clonedStream =
182 new SlicedInputStream(clonedStream.forget(), aStart, aLength);
183 }
184
185 clonedStream.forget(aInputStream);
186 }
187
StoreCallback(const nsID & aID,RemoteLazyInputStreamParentCallback * aCallback)188 void RemoteLazyInputStreamStorage::StoreCallback(
189 const nsID& aID, RemoteLazyInputStreamParentCallback* aCallback) {
190 MOZ_ASSERT(aCallback);
191
192 mozilla::StaticMutexAutoLock lock(gMutex);
193 StreamData* data = mStorage.Get(aID);
194 if (data) {
195 MOZ_ASSERT(!data->mCallback);
196 data->mCallback = aCallback;
197 }
198 }
199
200 already_AddRefed<RemoteLazyInputStreamParentCallback>
TakeCallback(const nsID & aID)201 RemoteLazyInputStreamStorage::TakeCallback(const nsID& aID) {
202 mozilla::StaticMutexAutoLock lock(gMutex);
203 StreamData* data = mStorage.Get(aID);
204 if (!data) {
205 return nullptr;
206 }
207
208 RefPtr<RemoteLazyInputStreamParentCallback> callback;
209 data->mCallback.swap(callback);
210 return callback.forget();
211 }
212
213 } // namespace mozilla
214