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