Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef nsDOMFile_h__
7 #define nsDOMFile_h__
9 #include "mozilla/Attributes.h"
10 #include "nsICharsetDetectionObserver.h"
11 #include "nsIFile.h"
12 #include "nsIDOMFile.h"
13 #include "nsIDOMFileList.h"
14 #include "nsIInputStream.h"
15 #include "nsIJSNativeInitializer.h"
16 #include "nsIMutable.h"
17 #include "nsCOMArray.h"
18 #include "nsCOMPtr.h"
19 #include "nsString.h"
20 #include "nsIXMLHttpRequest.h"
21 #include "nsAutoPtr.h"
22 #include "nsFileStreams.h"
23 #include "nsTemporaryFileInputStream.h"
25 #include "mozilla/GuardObjects.h"
26 #include "mozilla/LinkedList.h"
27 #include <stdint.h>
28 #include "mozilla/StaticMutex.h"
29 #include "mozilla/StaticPtr.h"
30 #include "mozilla/dom/DOMError.h"
31 #include "mozilla/dom/indexedDB/FileInfo.h"
32 #include "mozilla/dom/indexedDB/FileManager.h"
33 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
34 #include "nsWrapperCache.h"
35 #include "nsCycleCollectionParticipant.h"
37 class nsIFile;
38 class nsIInputStream;
39 class nsIClassInfo;
41 class nsDOMFileBase : public nsIDOMFile,
42 public nsIXHRSendable,
43 public nsIMutable
44 {
45 public:
46 typedef mozilla::dom::indexedDB::FileInfo FileInfo;
48 virtual already_AddRefed<nsIDOMBlob>
49 CreateSlice(uint64_t aStart, uint64_t aLength,
50 const nsAString& aContentType) = 0;
52 virtual const nsTArray<nsCOMPtr<nsIDOMBlob> >*
53 GetSubBlobs() const { return nullptr; }
55 NS_DECL_NSIDOMBLOB
56 NS_DECL_NSIDOMFILE
57 NS_DECL_NSIXHRSENDABLE
58 NS_DECL_NSIMUTABLE
60 void
61 SetLazyData(const nsAString& aName, const nsAString& aContentType,
62 uint64_t aLength, uint64_t aLastModifiedDate)
63 {
64 NS_ASSERTION(aLength, "must have length");
66 mName = aName;
67 mContentType = aContentType;
68 mLength = aLength;
69 mLastModificationDate = aLastModifiedDate;
70 mIsFile = !aName.IsVoid();
71 }
73 bool IsSizeUnknown() const
74 {
75 return mLength == UINT64_MAX;
76 }
78 bool IsDateUnknown() const
79 {
80 return mIsFile && mLastModificationDate == UINT64_MAX;
81 }
83 protected:
84 nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
85 uint64_t aLength, uint64_t aLastModifiedDate)
86 : mIsFile(true), mImmutable(false), mContentType(aContentType),
87 mName(aName), mStart(0), mLength(aLength), mLastModificationDate(aLastModifiedDate)
88 {
89 // Ensure non-null mContentType by default
90 mContentType.SetIsVoid(false);
91 }
93 nsDOMFileBase(const nsAString& aName, const nsAString& aContentType,
94 uint64_t aLength)
95 : mIsFile(true), mImmutable(false), mContentType(aContentType),
96 mName(aName), mStart(0), mLength(aLength), mLastModificationDate(UINT64_MAX)
97 {
98 // Ensure non-null mContentType by default
99 mContentType.SetIsVoid(false);
100 }
102 nsDOMFileBase(const nsAString& aContentType, uint64_t aLength)
103 : mIsFile(false), mImmutable(false), mContentType(aContentType),
104 mStart(0), mLength(aLength), mLastModificationDate(UINT64_MAX)
105 {
106 // Ensure non-null mContentType by default
107 mContentType.SetIsVoid(false);
108 }
110 nsDOMFileBase(const nsAString& aContentType, uint64_t aStart,
111 uint64_t aLength)
112 : mIsFile(false), mImmutable(false), mContentType(aContentType),
113 mStart(aStart), mLength(aLength), mLastModificationDate(UINT64_MAX)
114 {
115 NS_ASSERTION(aLength != UINT64_MAX,
116 "Must know length when creating slice");
117 // Ensure non-null mContentType by default
118 mContentType.SetIsVoid(false);
119 }
121 virtual ~nsDOMFileBase() {}
123 virtual bool IsStoredFile() const
124 {
125 return false;
126 }
128 virtual bool IsWholeFile() const
129 {
130 NS_NOTREACHED("Should only be called on dom blobs backed by files!");
131 return false;
132 }
134 virtual bool IsSnapshot() const
135 {
136 return false;
137 }
139 FileInfo* GetFileInfo() const
140 {
141 NS_ASSERTION(IsStoredFile(), "Should only be called on stored files!");
142 NS_ASSERTION(!mFileInfos.IsEmpty(), "Must have at least one file info!");
144 return mFileInfos.ElementAt(0);
145 }
147 bool mIsFile;
148 bool mImmutable;
150 nsString mContentType;
151 nsString mName;
152 nsString mPath; // The path relative to a directory chosen by the user
154 uint64_t mStart;
155 uint64_t mLength;
157 uint64_t mLastModificationDate;
159 // Protected by IndexedDatabaseManager::FileMutex()
160 nsTArray<nsRefPtr<FileInfo> > mFileInfos;
161 };
163 class nsDOMFile : public nsDOMFileBase
164 {
165 public:
166 nsDOMFile(const nsAString& aName, const nsAString& aContentType,
167 uint64_t aLength, uint64_t aLastModifiedDate)
168 : nsDOMFileBase(aName, aContentType, aLength, aLastModifiedDate)
169 { }
171 nsDOMFile(const nsAString& aName, const nsAString& aContentType,
172 uint64_t aLength)
173 : nsDOMFileBase(aName, aContentType, aLength)
174 { }
176 nsDOMFile(const nsAString& aContentType, uint64_t aLength)
177 : nsDOMFileBase(aContentType, aLength)
178 { }
180 nsDOMFile(const nsAString& aContentType, uint64_t aStart, uint64_t aLength)
181 : nsDOMFileBase(aContentType, aStart, aLength)
182 { }
184 NS_DECL_THREADSAFE_ISUPPORTS
185 };
187 class nsDOMFileCC : public nsDOMFileBase
188 {
189 public:
190 nsDOMFileCC(const nsAString& aName, const nsAString& aContentType,
191 uint64_t aLength)
192 : nsDOMFileBase(aName, aContentType, aLength)
193 { }
195 nsDOMFileCC(const nsAString& aContentType, uint64_t aLength)
196 : nsDOMFileBase(aContentType, aLength)
197 { }
199 nsDOMFileCC(const nsAString& aContentType, uint64_t aStart, uint64_t aLength)
200 : nsDOMFileBase(aContentType, aStart, aLength)
201 { }
203 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
205 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMFileCC, nsIDOMFile)
206 };
208 class nsDOMFileFile : public nsDOMFile
209 {
210 public:
211 // Create as a file
212 nsDOMFileFile(nsIFile *aFile)
213 : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX),
214 mFile(aFile), mWholeFile(true), mStoredFile(false)
215 {
216 NS_ASSERTION(mFile, "must have file");
217 // Lazily get the content type and size
218 mContentType.SetIsVoid(true);
219 mFile->GetLeafName(mName);
220 }
222 nsDOMFileFile(nsIFile *aFile, FileInfo *aFileInfo)
223 : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX),
224 mFile(aFile), mWholeFile(true), mStoredFile(true)
225 {
226 NS_ASSERTION(mFile, "must have file");
227 NS_ASSERTION(aFileInfo, "must have file info");
228 // Lazily get the content type and size
229 mContentType.SetIsVoid(true);
230 mFile->GetLeafName(mName);
232 mFileInfos.AppendElement(aFileInfo);
233 }
235 // Create as a file
236 nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
237 uint64_t aLength, nsIFile *aFile)
238 : nsDOMFile(aName, aContentType, aLength, UINT64_MAX),
239 mFile(aFile), mWholeFile(true), mStoredFile(false)
240 {
241 NS_ASSERTION(mFile, "must have file");
242 }
244 nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
245 uint64_t aLength, nsIFile *aFile, uint64_t aLastModificationDate)
246 : nsDOMFile(aName, aContentType, aLength, aLastModificationDate),
247 mFile(aFile), mWholeFile(true), mStoredFile(false)
248 {
249 NS_ASSERTION(mFile, "must have file");
250 }
252 // Create as a file with custom name
253 nsDOMFileFile(nsIFile *aFile, const nsAString& aName,
254 const nsAString& aContentType)
255 : nsDOMFile(aName, aContentType, UINT64_MAX, UINT64_MAX),
256 mFile(aFile), mWholeFile(true), mStoredFile(false)
257 {
258 NS_ASSERTION(mFile, "must have file");
259 if (aContentType.IsEmpty()) {
260 // Lazily get the content type and size
261 mContentType.SetIsVoid(true);
262 }
263 }
265 // Create as a stored file
266 nsDOMFileFile(const nsAString& aName, const nsAString& aContentType,
267 uint64_t aLength, nsIFile* aFile,
268 FileInfo* aFileInfo)
269 : nsDOMFile(aName, aContentType, aLength, UINT64_MAX),
270 mFile(aFile), mWholeFile(true), mStoredFile(true)
271 {
272 NS_ASSERTION(mFile, "must have file");
273 mFileInfos.AppendElement(aFileInfo);
274 }
276 // Create as a stored blob
277 nsDOMFileFile(const nsAString& aContentType, uint64_t aLength,
278 nsIFile* aFile, FileInfo* aFileInfo)
279 : nsDOMFile(aContentType, aLength),
280 mFile(aFile), mWholeFile(true), mStoredFile(true)
281 {
282 NS_ASSERTION(mFile, "must have file");
283 mFileInfos.AppendElement(aFileInfo);
284 }
286 // Create as a file to be later initialized
287 nsDOMFileFile()
288 : nsDOMFile(EmptyString(), EmptyString(), UINT64_MAX, UINT64_MAX),
289 mWholeFile(true), mStoredFile(false)
290 {
291 // Lazily get the content type and size
292 mContentType.SetIsVoid(true);
293 mName.SetIsVoid(true);
294 }
296 // Overrides
297 NS_IMETHOD GetSize(uint64_t* aSize) MOZ_OVERRIDE;
298 NS_IMETHOD GetType(nsAString& aType) MOZ_OVERRIDE;
299 NS_IMETHOD GetLastModifiedDate(JSContext* cx, JS::MutableHandle<JS::Value> aLastModifiedDate) MOZ_OVERRIDE;
300 NS_IMETHOD GetMozLastModifiedDate(uint64_t* aLastModifiedDate) MOZ_OVERRIDE;
301 NS_IMETHOD GetMozFullPathInternal(nsAString& aFullPath) MOZ_OVERRIDE;
302 NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
304 void SetPath(const nsAString& aFullPath);
306 protected:
307 // Create slice
308 nsDOMFileFile(const nsDOMFileFile* aOther, uint64_t aStart, uint64_t aLength,
309 const nsAString& aContentType)
310 : nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
311 mFile(aOther->mFile), mWholeFile(false),
312 mStoredFile(aOther->mStoredFile)
313 {
314 NS_ASSERTION(mFile, "must have file");
315 mImmutable = aOther->mImmutable;
317 if (mStoredFile) {
318 FileInfo* fileInfo;
320 using mozilla::dom::indexedDB::IndexedDatabaseManager;
322 if (IndexedDatabaseManager::IsClosed()) {
323 fileInfo = aOther->GetFileInfo();
324 }
325 else {
326 mozilla::MutexAutoLock lock(IndexedDatabaseManager::FileMutex());
327 fileInfo = aOther->GetFileInfo();
328 }
330 mFileInfos.AppendElement(fileInfo);
331 }
332 }
334 virtual already_AddRefed<nsIDOMBlob>
335 CreateSlice(uint64_t aStart, uint64_t aLength,
336 const nsAString& aContentType) MOZ_OVERRIDE;
338 virtual bool IsStoredFile() const MOZ_OVERRIDE
339 {
340 return mStoredFile;
341 }
343 virtual bool IsWholeFile() const MOZ_OVERRIDE
344 {
345 return mWholeFile;
346 }
348 nsCOMPtr<nsIFile> mFile;
349 bool mWholeFile;
350 bool mStoredFile;
351 };
353 /**
354 * This class may be used off the main thread, and in particular, its
355 * constructor and destructor may not run on the same thread. Be careful!
356 */
357 class nsDOMMemoryFile : public nsDOMFile
358 {
359 public:
360 // Create as file
361 nsDOMMemoryFile(void *aMemoryBuffer,
362 uint64_t aLength,
363 const nsAString& aName,
364 const nsAString& aContentType)
365 : nsDOMFile(aName, aContentType, aLength, UINT64_MAX),
366 mDataOwner(new DataOwner(aMemoryBuffer, aLength))
367 {
368 NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
369 }
371 // Create as blob
372 nsDOMMemoryFile(void *aMemoryBuffer,
373 uint64_t aLength,
374 const nsAString& aContentType)
375 : nsDOMFile(aContentType, aLength),
376 mDataOwner(new DataOwner(aMemoryBuffer, aLength))
377 {
378 NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
379 }
381 NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
383 NS_IMETHOD_(bool) IsMemoryFile(void) MOZ_OVERRIDE;
385 protected:
386 // Create slice
387 nsDOMMemoryFile(const nsDOMMemoryFile* aOther, uint64_t aStart,
388 uint64_t aLength, const nsAString& aContentType)
389 : nsDOMFile(aContentType, aOther->mStart + aStart, aLength),
390 mDataOwner(aOther->mDataOwner)
391 {
392 NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
393 mImmutable = aOther->mImmutable;
394 }
395 virtual already_AddRefed<nsIDOMBlob>
396 CreateSlice(uint64_t aStart, uint64_t aLength,
397 const nsAString& aContentType) MOZ_OVERRIDE;
399 // These classes need to see DataOwner.
400 friend class DataOwnerAdapter;
401 friend class nsDOMMemoryFileDataOwnerMemoryReporter;
403 class DataOwner MOZ_FINAL : public mozilla::LinkedListElement<DataOwner> {
404 public:
405 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataOwner)
406 DataOwner(void* aMemoryBuffer, uint64_t aLength)
407 : mData(aMemoryBuffer)
408 , mLength(aLength)
409 {
410 mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
412 if (!sDataOwners) {
413 sDataOwners = new mozilla::LinkedList<DataOwner>();
414 EnsureMemoryReporterRegistered();
415 }
416 sDataOwners->insertBack(this);
417 }
419 private:
420 // Private destructor, to discourage deletion outside of Release():
421 ~DataOwner() {
422 mozilla::StaticMutexAutoLock lock(sDataOwnerMutex);
424 remove();
425 if (sDataOwners->isEmpty()) {
426 // Free the linked list if it's empty.
427 sDataOwners = nullptr;
428 }
430 moz_free(mData);
431 }
433 public:
434 static void EnsureMemoryReporterRegistered();
436 // sDataOwners and sMemoryReporterRegistered may only be accessed while
437 // holding sDataOwnerMutex! You also must hold the mutex while touching
438 // elements of the linked list that DataOwner inherits from.
439 static mozilla::StaticMutex sDataOwnerMutex;
440 static mozilla::StaticAutoPtr<mozilla::LinkedList<DataOwner> > sDataOwners;
441 static bool sMemoryReporterRegistered;
443 void* mData;
444 uint64_t mLength;
445 };
447 // Used when backed by a memory store
448 nsRefPtr<DataOwner> mDataOwner;
449 };
451 class nsDOMFileList MOZ_FINAL : public nsIDOMFileList,
452 public nsWrapperCache
453 {
454 public:
455 nsDOMFileList(nsISupports *aParent) : mParent(aParent)
456 {
457 SetIsDOMBinding();
458 }
460 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
461 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMFileList)
463 NS_DECL_NSIDOMFILELIST
465 virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
467 nsISupports* GetParentObject()
468 {
469 return mParent;
470 }
472 void Disconnect()
473 {
474 mParent = nullptr;
475 }
477 bool Append(nsIDOMFile *aFile) { return mFiles.AppendObject(aFile); }
479 bool Remove(uint32_t aIndex) { return mFiles.RemoveObjectAt(aIndex); }
480 void Clear() { return mFiles.Clear(); }
482 static nsDOMFileList* FromSupports(nsISupports* aSupports)
483 {
484 #ifdef DEBUG
485 {
486 nsCOMPtr<nsIDOMFileList> list_qi = do_QueryInterface(aSupports);
488 // If this assertion fires the QI implementation for the object in
489 // question doesn't use the nsIDOMFileList pointer as the nsISupports
490 // pointer. That must be fixed, or we'll crash...
491 NS_ASSERTION(list_qi == static_cast<nsIDOMFileList*>(aSupports),
492 "Uh, fix QI!");
493 }
494 #endif
496 return static_cast<nsDOMFileList*>(aSupports);
497 }
499 nsIDOMFile* Item(uint32_t aIndex)
500 {
501 return mFiles.SafeObjectAt(aIndex);
502 }
503 nsIDOMFile* IndexedGetter(uint32_t aIndex, bool& aFound)
504 {
505 aFound = aIndex < static_cast<uint32_t>(mFiles.Count());
506 return aFound ? mFiles.ObjectAt(aIndex) : nullptr;
507 }
508 uint32_t Length()
509 {
510 return mFiles.Count();
511 }
513 private:
514 nsCOMArray<nsIDOMFile> mFiles;
515 nsISupports *mParent;
516 };
518 class MOZ_STACK_CLASS nsDOMFileInternalUrlHolder {
519 public:
520 nsDOMFileInternalUrlHolder(nsIDOMBlob* aFile, nsIPrincipal* aPrincipal
521 MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
522 ~nsDOMFileInternalUrlHolder();
523 nsAutoString mUrl;
524 private:
525 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
526 };
527 // This class would take the ownership of aFD and the caller must not close it.
528 class nsDOMTemporaryFileBlob : public nsDOMFile
529 {
530 public:
531 nsDOMTemporaryFileBlob(PRFileDesc* aFD, uint64_t aStartPos, uint64_t aLength,
532 const nsAString& aContentType)
533 : nsDOMFile(aContentType, aLength),
534 mLength(aLength),
535 mStartPos(aStartPos),
536 mContentType(aContentType)
537 {
538 mFileDescOwner = new nsTemporaryFileInputStream::FileDescOwner(aFD);
539 }
541 ~nsDOMTemporaryFileBlob() { }
542 NS_IMETHOD GetInternalStream(nsIInputStream**) MOZ_OVERRIDE;
544 protected:
545 nsDOMTemporaryFileBlob(const nsDOMTemporaryFileBlob* aOther, uint64_t aStart, uint64_t aLength,
546 const nsAString& aContentType)
547 : nsDOMFile(aContentType, aLength),
548 mLength(aLength),
549 mStartPos(aStart),
550 mFileDescOwner(aOther->mFileDescOwner),
551 mContentType(aContentType) { }
553 virtual already_AddRefed<nsIDOMBlob>
554 CreateSlice(uint64_t aStart, uint64_t aLength,
555 const nsAString& aContentType) MOZ_OVERRIDE;
557 private:
558 uint64_t mLength;
559 uint64_t mStartPos;
560 nsRefPtr<nsTemporaryFileInputStream::FileDescOwner> mFileDescOwner;
561 nsString mContentType;
562 };
564 #endif