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 #ifndef mozilla_dom_MemoryBlobImpl_h
8 #define mozilla_dom_MemoryBlobImpl_h
9 
10 #include "mozilla/dom/BaseBlobImpl.h"
11 #include "mozilla/LinkedList.h"
12 #include "mozilla/StaticMutex.h"
13 #include "mozilla/StaticPtr.h"
14 #include "nsCOMPtr.h"
15 #include "nsICloneableInputStream.h"
16 #include "nsIInputStream.h"
17 #include "nsIIPCSerializableInputStream.h"
18 #include "nsISeekableStream.h"
19 
20 namespace mozilla {
21 namespace dom {
22 
23 class MemoryBlobImpl final : public BaseBlobImpl {
24  public:
25   NS_INLINE_DECL_REFCOUNTING_INHERITED(MemoryBlobImpl, BaseBlobImpl)
26 
27   // File constructor.
28   static already_AddRefed<MemoryBlobImpl> CreateWithLastModifiedNow(
29       void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
30       const nsAString& aContentType, bool aCrossOriginIsolated);
31 
32   // File constructor with custom lastModified attribue value. You should
33   // probably use CreateWithLastModifiedNow() instead of this one.
34   static already_AddRefed<MemoryBlobImpl> CreateWithCustomLastModified(
35       void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
36       const nsAString& aContentType, int64_t aLastModifiedDate);
37 
38   // Blob constructor.
MemoryBlobImpl(void * aMemoryBuffer,uint64_t aLength,const nsAString & aContentType)39   MemoryBlobImpl(void* aMemoryBuffer, uint64_t aLength,
40                  const nsAString& aContentType)
41       : BaseBlobImpl(aContentType, aLength),
42         mDataOwner(new DataOwner(aMemoryBuffer, aLength)) {
43     MOZ_ASSERT(mDataOwner && mDataOwner->mData, "must have data");
44   }
45 
46   void CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv) override;
47 
48   already_AddRefed<BlobImpl> CreateSlice(uint64_t aStart, uint64_t aLength,
49                                          const nsAString& aContentType,
50                                          ErrorResult& aRv) override;
51 
IsMemoryFile()52   bool IsMemoryFile() const override { return true; }
53 
GetAllocationSize()54   size_t GetAllocationSize() const override { return mLength; }
55 
GetAllocationSize(FallibleTArray<BlobImpl * > & aVisitedBlobImpls)56   size_t GetAllocationSize(
57       FallibleTArray<BlobImpl*>& aVisitedBlobImpls) const override {
58     return GetAllocationSize();
59   }
60 
GetBlobImplType(nsAString & aBlobImplType)61   void GetBlobImplType(nsAString& aBlobImplType) const override {
62     aBlobImplType = u"MemoryBlobImpl"_ns;
63   }
64 
65   class DataOwner final : public mozilla::LinkedListElement<DataOwner> {
66    public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOwner)67     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOwner)
68     DataOwner(void* aMemoryBuffer, uint64_t aLength)
69         : mData(aMemoryBuffer), mLength(aLength) {
70       mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
71 
72       if (!sDataOwners) {
73         sDataOwners = new mozilla::LinkedList<DataOwner>();
74         EnsureMemoryReporterRegistered();
75       }
76       sDataOwners->insertBack(this);
77     }
78 
79    private:
80     // Private destructor, to discourage deletion outside of Release():
~DataOwner()81     ~DataOwner() {
82       mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
83 
84       remove();
85       if (sDataOwners->isEmpty()) {
86         // Free the linked list if it's empty.
87         sDataOwners = nullptr;
88       }
89 
90       free(mData);
91     }
92 
93    public:
94     static void EnsureMemoryReporterRegistered();
95 
96     // sDataOwners and sMemoryReporterRegistered may only be accessed while
97     // holding sDataOwnerMutex!  You also must hold the mutex while touching
98     // elements of the linked list that DataOwner inherits from.
99     static mozilla::StaticMutex sDataOwnerMutex;
100     static mozilla::StaticAutoPtr<mozilla::LinkedList<DataOwner> > sDataOwners;
101     static bool sMemoryReporterRegistered;
102 
103     void* mData;
104     uint64_t mLength;
105   };
106 
107   class DataOwnerAdapter final : public nsIInputStream,
108                                  public nsISeekableStream,
109                                  public nsIIPCSerializableInputStream,
110                                  public nsICloneableInputStream {
111     typedef MemoryBlobImpl::DataOwner DataOwner;
112 
113    public:
114     static nsresult Create(DataOwner* aDataOwner, uint32_t aStart,
115                            uint32_t aLength, nsIInputStream** _retval);
116 
117     NS_DECL_THREADSAFE_ISUPPORTS
118     NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
119 
120     // These are mandatory.
121     NS_FORWARD_NSIINPUTSTREAM(mStream->)
122     NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
123     NS_FORWARD_NSITELLABLESTREAM(mSeekableStream->)
124     NS_FORWARD_NSICLONEABLEINPUTSTREAM(mCloneableInputStream->)
125 
126    private:
127     ~DataOwnerAdapter() = default;
128 
DataOwnerAdapter(DataOwner * aDataOwner,nsIInputStream * aStream,uint32_t aLength)129     DataOwnerAdapter(DataOwner* aDataOwner, nsIInputStream* aStream,
130                      uint32_t aLength)
131         : mDataOwner(aDataOwner),
132           mStream(aStream),
133           mSeekableStream(do_QueryInterface(aStream)),
134           mSerializableInputStream(do_QueryInterface(aStream)),
135           mCloneableInputStream(do_QueryInterface(aStream)),
136           mLength(aLength) {
137       MOZ_ASSERT(
138           mSeekableStream && mSerializableInputStream && mCloneableInputStream,
139           "Somebody gave us the wrong stream!");
140     }
141 
142     template <typename M>
143     void SerializeInternal(mozilla::ipc::InputStreamParams& aParams,
144                            FileDescriptorArray& aFileDescriptors,
145                            bool aDelayedStart, uint32_t aMaxSize,
146                            uint32_t* aSizeUsed, M* aManager);
147 
148     RefPtr<DataOwner> mDataOwner;
149     nsCOMPtr<nsIInputStream> mStream;
150     nsCOMPtr<nsISeekableStream> mSeekableStream;
151     nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream;
152     nsCOMPtr<nsICloneableInputStream> mCloneableInputStream;
153     uint32_t mLength;
154   };
155 
156  private:
157   // File constructor.
MemoryBlobImpl(void * aMemoryBuffer,uint64_t aLength,const nsAString & aName,const nsAString & aContentType,int64_t aLastModifiedDate)158   MemoryBlobImpl(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
159                  const nsAString& aContentType, int64_t aLastModifiedDate)
160       : BaseBlobImpl(aName, aContentType, aLength, aLastModifiedDate),
161         mDataOwner(new DataOwner(aMemoryBuffer, aLength)) {
162     MOZ_ASSERT(mDataOwner && mDataOwner->mData, "must have data");
163   }
164 
165   // Create slice
MemoryBlobImpl(const MemoryBlobImpl * aOther,uint64_t aStart,uint64_t aLength,const nsAString & aContentType)166   MemoryBlobImpl(const MemoryBlobImpl* aOther, uint64_t aStart,
167                  uint64_t aLength, const nsAString& aContentType)
168       : BaseBlobImpl(aContentType, aOther->mStart + aStart, aLength),
169         mDataOwner(aOther->mDataOwner) {
170     MOZ_ASSERT(mDataOwner && mDataOwner->mData, "must have data");
171   }
172 
173   ~MemoryBlobImpl() = default;
174 
175   // Used when backed by a memory store
176   RefPtr<DataOwner> mDataOwner;
177 };
178 
179 }  // namespace dom
180 }  // namespace mozilla
181 
182 #endif  // mozilla_dom_MemoryBlobImpl_h
183