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 file,
5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "IDBMutableFile.h"
8 
9 #include "ActorsChild.h"
10 #include "FileInfo.h"
11 #include "IDBDatabase.h"
12 #include "IDBFactory.h"
13 #include "IDBFileHandle.h"
14 #include "IDBFileRequest.h"
15 #include "IndexedDatabaseManager.h"
16 #include "MainThreadUtils.h"
17 #include "mozilla/Assertions.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/dom/File.h"
20 #include "mozilla/dom/IDBMutableFileBinding.h"
21 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
22 #include "mozilla/dom/quota/FileStreams.h"
23 #include "mozilla/dom/quota/QuotaManager.h"
24 #include "mozilla/ipc/BackgroundUtils.h"
25 #include "mozilla/ipc/PBackgroundSharedTypes.h"
26 #include "nsContentUtils.h"
27 #include "nsDebug.h"
28 #include "nsError.h"
29 #include "nsIInputStream.h"
30 #include "nsIPrincipal.h"
31 #include "ReportInternalError.h"
32 
33 namespace mozilla {
34 namespace dom {
35 
36 using namespace mozilla::dom::indexedDB;
37 using namespace mozilla::dom::quota;
38 
IDBMutableFile(IDBDatabase * aDatabase,BackgroundMutableFileChild * aActor,const nsAString & aName,const nsAString & aType)39 IDBMutableFile::IDBMutableFile(IDBDatabase* aDatabase,
40                                BackgroundMutableFileChild* aActor,
41                                const nsAString& aName, const nsAString& aType)
42     : DOMEventTargetHelper(aDatabase),
43       mDatabase(aDatabase),
44       mBackgroundActor(aActor),
45       mName(aName),
46       mType(aType),
47       mInvalidated(false) {
48   MOZ_ASSERT(aDatabase);
49   aDatabase->AssertIsOnOwningThread();
50   MOZ_ASSERT(aActor);
51 
52   mDatabase->NoteLiveMutableFile(this);
53 }
54 
~IDBMutableFile()55 IDBMutableFile::~IDBMutableFile() {
56   AssertIsOnOwningThread();
57 
58   mDatabase->NoteFinishedMutableFile(this);
59 
60   if (mBackgroundActor) {
61     mBackgroundActor->SendDeleteMeInternal();
62     MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
63   }
64 }
65 
66 #ifdef DEBUG
67 
AssertIsOnOwningThread() const68 void IDBMutableFile::AssertIsOnOwningThread() const {
69   MOZ_ASSERT(mDatabase);
70   mDatabase->AssertIsOnOwningThread();
71 }
72 
73 #endif  // DEBUG
74 
GetFileId() const75 int64_t IDBMutableFile::GetFileId() const {
76   AssertIsOnOwningThread();
77 
78   int64_t fileId;
79   if (!mBackgroundActor ||
80       NS_WARN_IF(!mBackgroundActor->SendGetFileId(&fileId))) {
81     return -1;
82   }
83 
84   return fileId;
85 }
86 
Invalidate()87 void IDBMutableFile::Invalidate() {
88   AssertIsOnOwningThread();
89   MOZ_ASSERT(!mInvalidated);
90 
91   mInvalidated = true;
92 
93   AbortFileHandles();
94 }
95 
RegisterFileHandle(IDBFileHandle * aFileHandle)96 void IDBMutableFile::RegisterFileHandle(IDBFileHandle* aFileHandle) {
97   AssertIsOnOwningThread();
98   MOZ_ASSERT(aFileHandle);
99   aFileHandle->AssertIsOnOwningThread();
100   MOZ_ASSERT(!mFileHandles.Contains(aFileHandle));
101 
102   mFileHandles.PutEntry(aFileHandle);
103 }
104 
UnregisterFileHandle(IDBFileHandle * aFileHandle)105 void IDBMutableFile::UnregisterFileHandle(IDBFileHandle* aFileHandle) {
106   AssertIsOnOwningThread();
107   MOZ_ASSERT(aFileHandle);
108   aFileHandle->AssertIsOnOwningThread();
109   MOZ_ASSERT(mFileHandles.Contains(aFileHandle));
110 
111   mFileHandles.RemoveEntry(aFileHandle);
112 }
113 
AbortFileHandles()114 void IDBMutableFile::AbortFileHandles() {
115   AssertIsOnOwningThread();
116 
117   class MOZ_STACK_CLASS Helper final {
118    public:
119     static void AbortFileHandles(
120         nsTHashtable<nsPtrHashKey<IDBFileHandle>>& aTable) {
121       if (!aTable.Count()) {
122         return;
123       }
124 
125       nsTArray<RefPtr<IDBFileHandle>> fileHandlesToAbort;
126       fileHandlesToAbort.SetCapacity(aTable.Count());
127 
128       for (auto iter = aTable.Iter(); !iter.Done(); iter.Next()) {
129         IDBFileHandle* fileHandle = iter.Get()->GetKey();
130         MOZ_ASSERT(fileHandle);
131 
132         fileHandle->AssertIsOnOwningThread();
133 
134         if (!fileHandle->IsDone()) {
135           fileHandlesToAbort.AppendElement(iter.Get()->GetKey());
136         }
137       }
138       MOZ_ASSERT(fileHandlesToAbort.Length() <= aTable.Count());
139 
140       if (fileHandlesToAbort.IsEmpty()) {
141         return;
142       }
143 
144       for (RefPtr<IDBFileHandle>& fileHandle : fileHandlesToAbort) {
145         MOZ_ASSERT(fileHandle);
146 
147         fileHandle->Abort();
148       }
149     }
150   };
151 
152   Helper::AbortFileHandles(mFileHandles);
153 }
154 
Database() const155 IDBDatabase* IDBMutableFile::Database() const {
156   AssertIsOnOwningThread();
157 
158   return mDatabase;
159 }
160 
Open(FileMode aMode,ErrorResult & aError)161 already_AddRefed<IDBFileHandle> IDBMutableFile::Open(FileMode aMode,
162                                                      ErrorResult& aError) {
163   AssertIsOnOwningThread();
164 
165   if (QuotaManager::IsShuttingDown() || mDatabase->IsClosed() || !GetOwner()) {
166     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
167     return nullptr;
168   }
169 
170   RefPtr<IDBFileHandle> fileHandle = IDBFileHandle::Create(this, aMode);
171   if (NS_WARN_IF(!fileHandle)) {
172     aError.Throw(NS_ERROR_DOM_FILEHANDLE_UNKNOWN_ERR);
173     return nullptr;
174   }
175 
176   BackgroundFileHandleChild* actor = new BackgroundFileHandleChild(fileHandle);
177 
178   MOZ_ALWAYS_TRUE(
179       mBackgroundActor->SendPBackgroundFileHandleConstructor(actor, aMode));
180 
181   fileHandle->SetBackgroundActor(actor);
182 
183   return fileHandle.forget();
184 }
185 
GetFile(ErrorResult & aError)186 already_AddRefed<DOMRequest> IDBMutableFile::GetFile(ErrorResult& aError) {
187   RefPtr<IDBFileHandle> fileHandle = Open(FileMode::Readonly, aError);
188   if (NS_WARN_IF(aError.Failed())) {
189     return nullptr;
190   }
191 
192   FileRequestGetFileParams params;
193 
194   RefPtr<IDBFileRequest> request =
195       IDBFileRequest::Create(fileHandle, /* aWrapAsDOMRequest */ true);
196 
197   fileHandle->StartRequest(request, params);
198 
199   return request.forget();
200 }
201 
202 NS_IMPL_ADDREF_INHERITED(IDBMutableFile, DOMEventTargetHelper)
203 NS_IMPL_RELEASE_INHERITED(IDBMutableFile, DOMEventTargetHelper)
204 
205 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBMutableFile)
206 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
207 
208 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBMutableFile)
209 
210 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBMutableFile,
211                                                   DOMEventTargetHelper)
212   tmp->AssertIsOnOwningThread();
213   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDatabase)
214 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
215 
216 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBMutableFile,
217                                                 DOMEventTargetHelper)
218   tmp->AssertIsOnOwningThread();
219 
220   // Don't unlink mDatabase!
221 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
222 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)223 JSObject* IDBMutableFile::WrapObject(JSContext* aCx,
224                                      JS::Handle<JSObject*> aGivenProto) {
225   return IDBMutableFileBinding::Wrap(aCx, this, aGivenProto);
226 }
227 
228 }  // namespace dom
229 }  // namespace mozilla
230