Backed out 22 changesets (bug 1839396) for causing build bustages on js/Printer.h...
[gecko.git] / dom / indexedDB / IndexedDatabase.cpp
blob8717ac7894d1530326f164b49ea4149d9a4b1fb3
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/. */
7 #include "mozilla/dom/IndexedDatabase.h"
8 #include "IndexedDatabaseInlines.h"
10 #include "IDBDatabase.h"
12 #include "mozilla/dom/FileBlobImpl.h"
13 #include "mozilla/dom/StructuredCloneTags.h"
14 #include "mozilla/dom/WorkerScope.h"
15 #include "MainThreadUtils.h"
16 #include "jsapi.h"
17 #include "nsIFile.h"
18 #include "nsIGlobalObject.h"
19 #include "nsQueryObject.h"
20 #include "nsString.h"
22 namespace mozilla::dom::indexedDB {
23 namespace {
24 struct MOZ_STACK_CLASS MutableFileData final {
25 nsString type;
26 nsString name;
28 MOZ_COUNTED_DEFAULT_CTOR(MutableFileData)
30 MOZ_COUNTED_DTOR(MutableFileData)
33 struct MOZ_STACK_CLASS BlobOrFileData final {
34 uint32_t tag = 0;
35 uint64_t size = 0;
36 nsString type;
37 nsString name;
38 int64_t lastModifiedDate = INT64_MAX;
40 MOZ_COUNTED_DEFAULT_CTOR(BlobOrFileData)
42 MOZ_COUNTED_DTOR(BlobOrFileData)
45 struct MOZ_STACK_CLASS WasmModuleData final {
46 uint32_t bytecodeIndex;
47 uint32_t compiledIndex;
48 uint32_t flags;
50 explicit WasmModuleData(uint32_t aFlags)
51 : bytecodeIndex(0), compiledIndex(0), flags(aFlags) {
52 MOZ_COUNT_CTOR(WasmModuleData);
55 MOZ_COUNTED_DTOR(WasmModuleData)
58 bool StructuredCloneReadString(JSStructuredCloneReader* aReader,
59 nsCString& aString) {
60 uint32_t length;
61 if (!JS_ReadBytes(aReader, &length, sizeof(uint32_t))) {
62 NS_WARNING("Failed to read length!");
63 return false;
65 length = NativeEndian::swapFromLittleEndian(length);
67 if (!aString.SetLength(length, fallible)) {
68 NS_WARNING("Out of memory?");
69 return false;
71 char* const buffer = aString.BeginWriting();
73 if (!JS_ReadBytes(aReader, buffer, length)) {
74 NS_WARNING("Failed to read type!");
75 return false;
78 return true;
81 bool ReadFileHandle(JSStructuredCloneReader* aReader,
82 MutableFileData* aRetval) {
83 static_assert(SCTAG_DOM_MUTABLEFILE == 0xFFFF8004, "Update me!");
84 MOZ_ASSERT(aReader && aRetval);
86 nsCString type;
87 if (!StructuredCloneReadString(aReader, type)) {
88 return false;
90 CopyUTF8toUTF16(type, aRetval->type);
92 nsCString name;
93 if (!StructuredCloneReadString(aReader, name)) {
94 return false;
96 CopyUTF8toUTF16(name, aRetval->name);
98 return true;
101 bool ReadBlobOrFile(JSStructuredCloneReader* aReader, uint32_t aTag,
102 BlobOrFileData* aRetval) {
103 static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
104 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
105 SCTAG_DOM_FILE == 0xffff8005,
106 "Update me!");
108 MOZ_ASSERT(aReader);
109 MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
110 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
111 aTag == SCTAG_DOM_BLOB);
112 MOZ_ASSERT(aRetval);
114 aRetval->tag = aTag;
116 uint64_t size;
117 if (NS_WARN_IF(!JS_ReadBytes(aReader, &size, sizeof(uint64_t)))) {
118 return false;
121 aRetval->size = NativeEndian::swapFromLittleEndian(size);
123 nsCString type;
124 if (NS_WARN_IF(!StructuredCloneReadString(aReader, type))) {
125 return false;
128 CopyUTF8toUTF16(type, aRetval->type);
130 // Blobs are done.
131 if (aTag == SCTAG_DOM_BLOB) {
132 return true;
135 MOZ_ASSERT(aTag == SCTAG_DOM_FILE ||
136 aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE);
138 int64_t lastModifiedDate;
139 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE) {
140 lastModifiedDate = INT64_MAX;
141 } else {
142 if (NS_WARN_IF(!JS_ReadBytes(aReader, &lastModifiedDate,
143 sizeof(lastModifiedDate)))) {
144 return false;
146 lastModifiedDate = NativeEndian::swapFromLittleEndian(lastModifiedDate);
149 aRetval->lastModifiedDate = lastModifiedDate;
151 nsCString name;
152 if (NS_WARN_IF(!StructuredCloneReadString(aReader, name))) {
153 return false;
156 CopyUTF8toUTF16(name, aRetval->name);
158 return true;
161 bool ReadWasmModule(JSStructuredCloneReader* aReader, WasmModuleData* aRetval) {
162 static_assert(SCTAG_DOM_WASM_MODULE == 0xFFFF8006, "Update me!");
163 MOZ_ASSERT(aReader && aRetval);
165 uint32_t bytecodeIndex;
166 uint32_t compiledIndex;
167 if (NS_WARN_IF(!JS_ReadUint32Pair(aReader, &bytecodeIndex, &compiledIndex))) {
168 return false;
171 aRetval->bytecodeIndex = bytecodeIndex;
172 aRetval->compiledIndex = compiledIndex;
174 return true;
177 template <typename StructuredCloneFile>
178 class ValueDeserializationHelper;
180 class ValueDeserializationHelperBase {
181 public:
182 static bool CreateAndWrapWasmModule(JSContext* aCx,
183 const StructuredCloneFileBase& aFile,
184 const WasmModuleData& aData,
185 JS::MutableHandle<JSObject*> aResult) {
186 MOZ_ASSERT(aCx);
187 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eWasmBytecode);
189 // Both on the parent and child side, just create a plain object here,
190 // support for de-serialization of WebAssembly.Modules has been removed in
191 // bug 1561876. Full removal is tracked in bug 1487479.
193 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
194 if (NS_WARN_IF(!obj)) {
195 return false;
198 aResult.set(obj);
199 return true;
202 template <typename StructuredCloneFile>
203 static bool CreateAndWrapBlobOrFile(JSContext* aCx, IDBDatabase* aDatabase,
204 const StructuredCloneFile& aFile,
205 const BlobOrFileData& aData,
206 JS::MutableHandle<JSObject*> aResult) {
207 MOZ_ASSERT(aCx);
208 MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE ||
209 aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
210 aData.tag == SCTAG_DOM_BLOB);
211 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
213 const auto blob = ValueDeserializationHelper<StructuredCloneFile>::GetBlob(
214 aCx, aDatabase, aFile);
215 if (NS_WARN_IF(!blob)) {
216 return false;
219 if (aData.tag == SCTAG_DOM_BLOB) {
220 blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size,
221 INT64_MAX);
222 MOZ_ASSERT(!blob->IsFile());
224 // XXX The comment below is somewhat confusing, since it seems to imply
225 // that this branch is only executed when called from ActorsParent, but
226 // it's executed from both the parent and the child side code.
228 // ActorsParent sends here a kind of half blob and half file wrapped into
229 // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
230 // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
231 // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
232 // File wrapping.
233 // Before exposing it to content, we must recreate a DOM Blob object.
235 const RefPtr<Blob> exposedBlob =
236 Blob::Create(blob->GetParentObject(), blob->Impl());
237 if (NS_WARN_IF(!exposedBlob)) {
238 return false;
241 return WrapAsJSObject(aCx, exposedBlob, aResult);
244 blob->Impl()->SetLazyData(aData.name, aData.type, aData.size,
245 aData.lastModifiedDate * PR_USEC_PER_MSEC);
247 MOZ_ASSERT(blob->IsFile());
248 const RefPtr<File> file = blob->ToFile();
249 MOZ_ASSERT(file);
251 return WrapAsJSObject(aCx, file, aResult);
255 template <>
256 class ValueDeserializationHelper<StructuredCloneFileParent>
257 : public ValueDeserializationHelperBase {
258 public:
259 static bool CreateAndWrapMutableFile(JSContext* aCx,
260 StructuredCloneFileParent& aFile,
261 const MutableFileData& aData,
262 JS::MutableHandle<JSObject*> aResult) {
263 MOZ_ASSERT(aCx);
264 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eBlob);
266 // We are in an IDB SQLite schema upgrade where we don't care about a real
267 // 'MutableFile', but we just care of having a proper |mType| flag.
269 aFile.MutateType(StructuredCloneFileBase::eMutableFile);
271 // Just make a dummy object.
272 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
274 if (NS_WARN_IF(!obj)) {
275 return false;
278 aResult.set(obj);
279 return true;
282 static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
283 const StructuredCloneFileParent& aFile) {
284 // This is chrome code, so there is no parent, but still we want to set a
285 // correct parent for the new File object.
286 const auto global = [aDatabase, aCx]() -> nsCOMPtr<nsIGlobalObject> {
287 if (NS_IsMainThread()) {
288 if (aDatabase && aDatabase->GetParentObject()) {
289 return aDatabase->GetParentObject();
291 return xpc::CurrentNativeGlobal(aCx);
293 const WorkerPrivate* const workerPrivate =
294 GetCurrentThreadWorkerPrivate();
295 MOZ_ASSERT(workerPrivate);
297 WorkerGlobalScope* const globalScope = workerPrivate->GlobalScope();
298 MOZ_ASSERT(globalScope);
300 return do_QueryObject(globalScope);
301 }();
303 MOZ_ASSERT(global);
305 // We do not have an mBlob but do have a DatabaseFileInfo.
307 // If we are creating an index, we do need a real-looking Blob/File instance
308 // because the index's key path can reference their properties. Rather than
309 // create a fake-looking object, create a real Blob.
311 // If we are in a schema upgrade, we don't strictly need that, but we do not
312 // need to optimize for that, and create it anyway.
313 const nsCOMPtr<nsIFile> file = aFile.FileInfo().GetFileForFileInfo();
314 if (!file) {
315 return nullptr;
318 const auto impl = MakeRefPtr<FileBlobImpl>(file);
319 impl->SetFileId(aFile.FileInfo().Id());
320 return File::Create(global, impl);
324 template <>
325 class ValueDeserializationHelper<StructuredCloneFileChild>
326 : public ValueDeserializationHelperBase {
327 public:
328 static bool CreateAndWrapMutableFile(JSContext* aCx,
329 StructuredCloneFileChild& aFile,
330 const MutableFileData& aData,
331 JS::MutableHandle<JSObject*> aResult) {
332 MOZ_ASSERT(aCx);
333 MOZ_ASSERT(aFile.Type() == StructuredCloneFileBase::eMutableFile);
335 return false;
338 static RefPtr<Blob> GetBlob(JSContext* aCx, IDBDatabase* aDatabase,
339 const StructuredCloneFileChild& aFile) {
340 if (aFile.HasBlob()) {
341 return aFile.BlobPtr();
344 MOZ_CRASH("Expected a StructuredCloneFile with a Blob");
348 } // namespace
350 template <typename StructuredCloneReadInfo>
351 JSObject* CommonStructuredCloneReadCallback(
352 JSContext* aCx, JSStructuredCloneReader* aReader,
353 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
354 StructuredCloneReadInfo* aCloneReadInfo, IDBDatabase* aDatabase) {
355 // We need to statically assert that our tag values are what we expect
356 // so that if people accidentally change them they notice.
357 static_assert(SCTAG_DOM_BLOB == 0xffff8001 &&
358 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE == 0xffff8002 &&
359 SCTAG_DOM_MUTABLEFILE == 0xffff8004 &&
360 SCTAG_DOM_FILE == 0xffff8005 &&
361 SCTAG_DOM_WASM_MODULE == 0xffff8006,
362 "You changed our structured clone tag values and just ate "
363 "everyone's IndexedDB data. I hope you are happy.");
365 using StructuredCloneFile =
366 typename StructuredCloneReadInfo::StructuredCloneFile;
368 if (aTag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE ||
369 aTag == SCTAG_DOM_BLOB || aTag == SCTAG_DOM_FILE ||
370 aTag == SCTAG_DOM_MUTABLEFILE || aTag == SCTAG_DOM_WASM_MODULE) {
371 JS::Rooted<JSObject*> result(aCx);
373 if (aTag == SCTAG_DOM_WASM_MODULE) {
374 WasmModuleData data(aData);
375 if (NS_WARN_IF(!ReadWasmModule(aReader, &data))) {
376 return nullptr;
379 MOZ_ASSERT(data.compiledIndex == data.bytecodeIndex + 1);
380 MOZ_ASSERT(!data.flags);
382 const auto& files = aCloneReadInfo->Files();
383 if (data.bytecodeIndex >= files.Length() ||
384 data.compiledIndex >= files.Length()) {
385 MOZ_ASSERT(false, "Bad index value!");
386 return nullptr;
389 const auto& file = files[data.bytecodeIndex];
391 if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
392 CreateAndWrapWasmModule(aCx, file, data, &result))) {
393 return nullptr;
396 return result;
399 if (aData >= aCloneReadInfo->Files().Length()) {
400 MOZ_ASSERT(false, "Bad index value!");
401 return nullptr;
404 auto& file = aCloneReadInfo->MutableFile(aData);
406 if (aTag == SCTAG_DOM_MUTABLEFILE) {
407 MutableFileData data;
408 if (NS_WARN_IF(!ReadFileHandle(aReader, &data))) {
409 return nullptr;
412 if (NS_WARN_IF(!ValueDeserializationHelper<StructuredCloneFile>::
413 CreateAndWrapMutableFile(aCx, file, data, &result))) {
414 return nullptr;
417 return result;
420 BlobOrFileData data;
421 if (NS_WARN_IF(!ReadBlobOrFile(aReader, aTag, &data))) {
422 return nullptr;
425 if (NS_WARN_IF(!ValueDeserializationHelper<
426 StructuredCloneFile>::CreateAndWrapBlobOrFile(aCx, aDatabase,
427 file, data,
428 &result))) {
429 return nullptr;
432 return result;
435 return StructuredCloneHolder::ReadFullySerializableObjects(aCx, aReader, aTag,
436 true);
439 template JSObject* CommonStructuredCloneReadCallback(
440 JSContext* aCx, JSStructuredCloneReader* aReader,
441 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
442 StructuredCloneReadInfoChild* aCloneReadInfo, IDBDatabase* aDatabase);
444 template JSObject* CommonStructuredCloneReadCallback(
445 JSContext* aCx, JSStructuredCloneReader* aReader,
446 const JS::CloneDataPolicy& aCloneDataPolicy, uint32_t aTag, uint32_t aData,
447 StructuredCloneReadInfoParent* aCloneReadInfo, IDBDatabase* aDatabase);
448 } // namespace mozilla::dom::indexedDB