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 "MemoryBlobImpl.h"
8 #include "mozilla/ipc/InputStreamUtils.h"
9 #include "mozilla/IntegerPrintfMacros.h"
10 #include "mozilla/SHA1.h"
11 #include "nsIMemoryReporter.h"
12 #include "nsMemory.h"
13 #include "nsPrintfCString.h"
14 #include "nsRFPService.h"
15 #include "nsStringStream.h"
16 #include "prtime.h"
17
18 namespace mozilla::dom {
19
20 NS_IMPL_ADDREF(MemoryBlobImpl::DataOwnerAdapter)
NS_IMPL_RELEASE(MemoryBlobImpl::DataOwnerAdapter)21 NS_IMPL_RELEASE(MemoryBlobImpl::DataOwnerAdapter)
22
23 NS_INTERFACE_MAP_BEGIN(MemoryBlobImpl::DataOwnerAdapter)
24 NS_INTERFACE_MAP_ENTRY(nsIInputStream)
25 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
26 NS_INTERFACE_MAP_ENTRY(nsITellableStream)
27 NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
28 NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
29 mSerializableInputStream)
30 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
31 NS_INTERFACE_MAP_END
32
33 // static
34 already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithCustomLastModified(
35 void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
36 const nsAString& aContentType, int64_t aLastModifiedDate) {
37 RefPtr<MemoryBlobImpl> blobImpl = new MemoryBlobImpl(
38 aMemoryBuffer, aLength, aName, aContentType, aLastModifiedDate);
39 return blobImpl.forget();
40 }
41
42 // static
CreateWithLastModifiedNow(void * aMemoryBuffer,uint64_t aLength,const nsAString & aName,const nsAString & aContentType,bool aCrossOriginIsolated)43 already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithLastModifiedNow(
44 void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
45 const nsAString& aContentType, bool aCrossOriginIsolated) {
46 int64_t lastModificationDate = nsRFPService::ReduceTimePrecisionAsUSecs(
47 PR_Now(), 0,
48 /* aIsSystemPrincipal */ false, aCrossOriginIsolated);
49 return CreateWithCustomLastModified(aMemoryBuffer, aLength, aName,
50 aContentType, lastModificationDate);
51 }
52
Create(DataOwner * aDataOwner,uint32_t aStart,uint32_t aLength,nsIInputStream ** _retval)53 nsresult MemoryBlobImpl::DataOwnerAdapter::Create(DataOwner* aDataOwner,
54 uint32_t aStart,
55 uint32_t aLength,
56 nsIInputStream** _retval) {
57 nsresult rv;
58 MOZ_ASSERT(aDataOwner, "Uh ...");
59
60 nsCOMPtr<nsIInputStream> stream;
61
62 rv = NS_NewByteInputStream(
63 getter_AddRefs(stream),
64 Span(static_cast<const char*>(aDataOwner->mData) + aStart, aLength),
65 NS_ASSIGNMENT_DEPEND);
66 NS_ENSURE_SUCCESS(rv, rv);
67
68 NS_ADDREF(*_retval = new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, stream,
69 aLength));
70
71 return NS_OK;
72 }
73
Serialize(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,mozilla::ipc::ChildToParentStreamActorManager * aManager)74 void MemoryBlobImpl::DataOwnerAdapter::Serialize(
75 mozilla::ipc::InputStreamParams& aParams,
76 FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
77 uint32_t aMaxSize, uint32_t* aSizeUsed,
78 mozilla::ipc::ChildToParentStreamActorManager* aManager) {
79 SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
80 aSizeUsed, aManager);
81 }
82
Serialize(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,mozilla::ipc::ParentToChildStreamActorManager * aManager)83 void MemoryBlobImpl::DataOwnerAdapter::Serialize(
84 mozilla::ipc::InputStreamParams& aParams,
85 FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
86 uint32_t aMaxSize, uint32_t* aSizeUsed,
87 mozilla::ipc::ParentToChildStreamActorManager* aManager) {
88 SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
89 aSizeUsed, aManager);
90 }
91
92 template <typename M>
SerializeInternal(mozilla::ipc::InputStreamParams & aParams,FileDescriptorArray & aFileDescriptors,bool aDelayedStart,uint32_t aMaxSize,uint32_t * aSizeUsed,M * aManager)93 void MemoryBlobImpl::DataOwnerAdapter::SerializeInternal(
94 mozilla::ipc::InputStreamParams& aParams,
95 FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
96 uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
97 MOZ_ASSERT(aSizeUsed);
98
99 // If we'd be serializing the underlying nsStringInputStream as a pipe,
100 // serialize ourselves as a pipe directly instead to avoid the string copy in
101 // nsStringInputStream's serialize method. Otherwise we'll delegate and
102 // serialize as a string internally.
103 if (mLength >= aMaxSize) {
104 *aSizeUsed = 0;
105 mozilla::ipc::InputStreamHelper::SerializeInputStreamAsPipe(
106 this, aParams, aDelayedStart, aManager);
107 return;
108 }
109
110 mSerializableInputStream->Serialize(aParams, aFileDescriptors, aDelayedStart,
111 aMaxSize, aSizeUsed, aManager);
112 }
113
Deserialize(const mozilla::ipc::InputStreamParams &,const FileDescriptorArray &)114 bool MemoryBlobImpl::DataOwnerAdapter::Deserialize(
115 const mozilla::ipc::InputStreamParams&, const FileDescriptorArray&) {
116 MOZ_CRASH("This method should never be called");
117 return false;
118 }
119
CreateSlice(uint64_t aStart,uint64_t aLength,const nsAString & aContentType,ErrorResult & aRv)120 already_AddRefed<BlobImpl> MemoryBlobImpl::CreateSlice(
121 uint64_t aStart, uint64_t aLength, const nsAString& aContentType,
122 ErrorResult& aRv) {
123 RefPtr<BlobImpl> impl =
124 new MemoryBlobImpl(this, aStart, aLength, aContentType);
125 return impl.forget();
126 }
127
CreateInputStream(nsIInputStream ** aStream,ErrorResult & aRv)128 void MemoryBlobImpl::CreateInputStream(nsIInputStream** aStream,
129 ErrorResult& aRv) {
130 if (mLength >= INT32_MAX) {
131 aRv.Throw(NS_ERROR_FAILURE);
132 return;
133 }
134
135 aRv = MemoryBlobImpl::DataOwnerAdapter::Create(mDataOwner, mStart, mLength,
136 aStream);
137 }
138
139 /* static */
140 StaticMutex MemoryBlobImpl::DataOwner::sDataOwnerMutex;
141
142 /* static */ StaticAutoPtr<LinkedList<MemoryBlobImpl::DataOwner>>
143 MemoryBlobImpl::DataOwner::sDataOwners;
144
145 /* static */
146 bool MemoryBlobImpl::DataOwner::sMemoryReporterRegistered = false;
147
148 MOZ_DEFINE_MALLOC_SIZE_OF(MemoryFileDataOwnerMallocSizeOf)
149
150 class MemoryBlobImplDataOwnerMemoryReporter final : public nsIMemoryReporter {
151 ~MemoryBlobImplDataOwnerMemoryReporter() = default;
152
153 public:
154 NS_DECL_THREADSAFE_ISUPPORTS
155
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)156 NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
157 nsISupports* aData, bool aAnonymize) override {
158 typedef MemoryBlobImpl::DataOwner DataOwner;
159
160 StaticMutexAutoLock lock(DataOwner::sDataOwnerMutex);
161
162 if (!DataOwner::sDataOwners) {
163 return NS_OK;
164 }
165
166 const size_t LARGE_OBJECT_MIN_SIZE = 8 * 1024;
167 size_t smallObjectsTotal = 0;
168
169 for (DataOwner* owner = DataOwner::sDataOwners->getFirst(); owner;
170 owner = owner->getNext()) {
171 size_t size = MemoryFileDataOwnerMallocSizeOf(owner->mData);
172
173 if (size < LARGE_OBJECT_MIN_SIZE) {
174 smallObjectsTotal += size;
175 } else {
176 SHA1Sum sha1;
177 sha1.update(owner->mData, owner->mLength);
178 uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
179 sha1.finish(digest);
180
181 nsAutoCString digestString;
182 for (size_t i = 0; i < sizeof(digest); i++) {
183 digestString.AppendPrintf("%02x", digest[i]);
184 }
185
186 aHandleReport->Callback(
187 /* process */ ""_ns,
188 nsPrintfCString(
189 "explicit/dom/memory-file-data/large/file(length=%" PRIu64
190 ", sha1=%s)",
191 owner->mLength,
192 aAnonymize ? "<anonymized>" : digestString.get()),
193 KIND_HEAP, UNITS_BYTES, size,
194 nsPrintfCString(
195 "Memory used to back a memory file of length %" PRIu64
196 " bytes. The file "
197 "has a sha1 of %s.\n\n"
198 "Note that the allocator may round up a memory file's length "
199 "-- "
200 "that is, an N-byte memory file may take up more than N bytes "
201 "of "
202 "memory.",
203 owner->mLength, digestString.get()),
204 aData);
205 }
206 }
207
208 if (smallObjectsTotal > 0) {
209 aHandleReport->Callback(
210 /* process */ ""_ns, "explicit/dom/memory-file-data/small"_ns,
211 KIND_HEAP, UNITS_BYTES, smallObjectsTotal,
212 nsPrintfCString(
213 "Memory used to back small memory files (i.e. those taking up "
214 "less "
215 "than %zu bytes of memory each).\n\n"
216 "Note that the allocator may round up a memory file's length -- "
217 "that is, an N-byte memory file may take up more than N bytes of "
218 "memory.",
219 LARGE_OBJECT_MIN_SIZE),
220 aData);
221 }
222
223 return NS_OK;
224 }
225 };
226
NS_IMPL_ISUPPORTS(MemoryBlobImplDataOwnerMemoryReporter,nsIMemoryReporter)227 NS_IMPL_ISUPPORTS(MemoryBlobImplDataOwnerMemoryReporter, nsIMemoryReporter)
228
229 /* static */
230 void MemoryBlobImpl::DataOwner::EnsureMemoryReporterRegistered() {
231 sDataOwnerMutex.AssertCurrentThreadOwns();
232 if (sMemoryReporterRegistered) {
233 return;
234 }
235
236 RegisterStrongMemoryReporter(new MemoryBlobImplDataOwnerMemoryReporter());
237
238 sMemoryReporterRegistered = true;
239 }
240
241 } // namespace mozilla::dom
242