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"
18 #include "nsIGlobalObject.h"
19 #include "nsQueryObject.h"
22 namespace mozilla::dom::indexedDB
{
24 struct MOZ_STACK_CLASS MutableFileData final
{
28 MOZ_COUNTED_DEFAULT_CTOR(MutableFileData
)
30 MOZ_COUNTED_DTOR(MutableFileData
)
33 struct MOZ_STACK_CLASS BlobOrFileData final
{
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
;
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
,
61 if (!JS_ReadBytes(aReader
, &length
, sizeof(uint32_t))) {
62 NS_WARNING("Failed to read length!");
65 length
= NativeEndian::swapFromLittleEndian(length
);
67 if (!aString
.SetLength(length
, fallible
)) {
68 NS_WARNING("Out of memory?");
71 char* const buffer
= aString
.BeginWriting();
73 if (!JS_ReadBytes(aReader
, buffer
, length
)) {
74 NS_WARNING("Failed to read type!");
81 bool ReadFileHandle(JSStructuredCloneReader
* aReader
,
82 MutableFileData
* aRetval
) {
83 static_assert(SCTAG_DOM_MUTABLEFILE
== 0xFFFF8004, "Update me!");
84 MOZ_ASSERT(aReader
&& aRetval
);
87 if (!StructuredCloneReadString(aReader
, type
)) {
90 CopyUTF8toUTF16(type
, aRetval
->type
);
93 if (!StructuredCloneReadString(aReader
, name
)) {
96 CopyUTF8toUTF16(name
, aRetval
->name
);
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,
109 MOZ_ASSERT(aTag
== SCTAG_DOM_FILE
||
110 aTag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
||
111 aTag
== SCTAG_DOM_BLOB
);
117 if (NS_WARN_IF(!JS_ReadBytes(aReader
, &size
, sizeof(uint64_t)))) {
121 aRetval
->size
= NativeEndian::swapFromLittleEndian(size
);
124 if (NS_WARN_IF(!StructuredCloneReadString(aReader
, type
))) {
128 CopyUTF8toUTF16(type
, aRetval
->type
);
131 if (aTag
== SCTAG_DOM_BLOB
) {
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
;
142 if (NS_WARN_IF(!JS_ReadBytes(aReader
, &lastModifiedDate
,
143 sizeof(lastModifiedDate
)))) {
146 lastModifiedDate
= NativeEndian::swapFromLittleEndian(lastModifiedDate
);
149 aRetval
->lastModifiedDate
= lastModifiedDate
;
152 if (NS_WARN_IF(!StructuredCloneReadString(aReader
, name
))) {
156 CopyUTF8toUTF16(name
, aRetval
->name
);
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
))) {
171 aRetval
->bytecodeIndex
= bytecodeIndex
;
172 aRetval
->compiledIndex
= compiledIndex
;
177 template <typename StructuredCloneFile
>
178 class ValueDeserializationHelper
;
180 class ValueDeserializationHelperBase
{
182 static bool CreateAndWrapWasmModule(JSContext
* aCx
,
183 const StructuredCloneFileBase
& aFile
,
184 const WasmModuleData
& aData
,
185 JS::MutableHandle
<JSObject
*> aResult
) {
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
)) {
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
) {
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
)) {
219 if (aData
.tag
== SCTAG_DOM_BLOB
) {
220 blob
->Impl()->SetLazyData(VoidString(), aData
.type
, aData
.size
,
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
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
)) {
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();
251 return WrapAsJSObject(aCx
, file
, aResult
);
256 class ValueDeserializationHelper
<StructuredCloneFileParent
>
257 : public ValueDeserializationHelperBase
{
259 static bool CreateAndWrapMutableFile(JSContext
* aCx
,
260 StructuredCloneFileParent
& aFile
,
261 const MutableFileData
& aData
,
262 JS::MutableHandle
<JSObject
*> aResult
) {
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
)) {
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
);
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();
318 const auto impl
= MakeRefPtr
<FileBlobImpl
>(file
);
319 impl
->SetFileId(aFile
.FileInfo().Id());
320 return File::Create(global
, impl
);
325 class ValueDeserializationHelper
<StructuredCloneFileChild
>
326 : public ValueDeserializationHelperBase
{
328 static bool CreateAndWrapMutableFile(JSContext
* aCx
,
329 StructuredCloneFileChild
& aFile
,
330 const MutableFileData
& aData
,
331 JS::MutableHandle
<JSObject
*> aResult
) {
333 MOZ_ASSERT(aFile
.Type() == StructuredCloneFileBase::eMutableFile
);
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");
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
))) {
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!");
389 const auto& file
= files
[data
.bytecodeIndex
];
391 if (NS_WARN_IF(!ValueDeserializationHelper
<StructuredCloneFile
>::
392 CreateAndWrapWasmModule(aCx
, file
, data
, &result
))) {
399 if (aData
>= aCloneReadInfo
->Files().Length()) {
400 MOZ_ASSERT(false, "Bad index value!");
404 auto& file
= aCloneReadInfo
->MutableFile(aData
);
406 if (aTag
== SCTAG_DOM_MUTABLEFILE
) {
407 MutableFileData data
;
408 if (NS_WARN_IF(!ReadFileHandle(aReader
, &data
))) {
412 if (NS_WARN_IF(!ValueDeserializationHelper
<StructuredCloneFile
>::
413 CreateAndWrapMutableFile(aCx
, file
, data
, &result
))) {
421 if (NS_WARN_IF(!ReadBlobOrFile(aReader
, aTag
, &data
))) {
425 if (NS_WARN_IF(!ValueDeserializationHelper
<
426 StructuredCloneFile
>::CreateAndWrapBlobOrFile(aCx
, aDatabase
,
435 return StructuredCloneHolder::ReadFullySerializableObjects(aCx
, aReader
, aTag
,
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