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