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