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/URLSearchParams.h"
15 #include "mozilla/dom/WorkerScope.h"
16 #include "MainThreadUtils.h"
19 #include "nsIGlobalObject.h"
20 #include "nsQueryObject.h"
23 namespace mozilla::dom::indexedDB
{
25 struct MOZ_STACK_CLASS MutableFileData final
{
29 MOZ_COUNTED_DEFAULT_CTOR(MutableFileData
)
31 MOZ_COUNTED_DTOR(MutableFileData
)
34 struct MOZ_STACK_CLASS BlobOrFileData final
{
39 int64_t lastModifiedDate
= INT64_MAX
;
41 MOZ_COUNTED_DEFAULT_CTOR(BlobOrFileData
)
43 MOZ_COUNTED_DTOR(BlobOrFileData
)
46 struct MOZ_STACK_CLASS WasmModuleData final
{
47 uint32_t bytecodeIndex
;
48 uint32_t compiledIndex
;
51 explicit WasmModuleData(uint32_t aFlags
)
52 : bytecodeIndex(0), compiledIndex(0), flags(aFlags
) {
53 MOZ_COUNT_CTOR(WasmModuleData
);
56 MOZ_COUNTED_DTOR(WasmModuleData
)
59 bool StructuredCloneReadString(JSStructuredCloneReader
* aReader
,
62 if (!JS_ReadBytes(aReader
, &length
, sizeof(uint32_t))) {
63 NS_WARNING("Failed to read length!");
66 length
= NativeEndian::swapFromLittleEndian(length
);
68 if (!aString
.SetLength(length
, fallible
)) {
69 NS_WARNING("Out of memory?");
72 char* const buffer
= aString
.BeginWriting();
74 if (!JS_ReadBytes(aReader
, buffer
, length
)) {
75 NS_WARNING("Failed to read type!");
82 bool ReadFileHandle(JSStructuredCloneReader
* aReader
,
83 MutableFileData
* aRetval
) {
84 static_assert(SCTAG_DOM_MUTABLEFILE
== 0xFFFF8004, "Update me!");
85 MOZ_ASSERT(aReader
&& aRetval
);
88 if (!StructuredCloneReadString(aReader
, type
)) {
91 CopyUTF8toUTF16(type
, aRetval
->type
);
94 if (!StructuredCloneReadString(aReader
, name
)) {
97 CopyUTF8toUTF16(name
, aRetval
->name
);
102 bool ReadBlobOrFile(JSStructuredCloneReader
* aReader
, uint32_t aTag
,
103 BlobOrFileData
* aRetval
) {
104 static_assert(SCTAG_DOM_BLOB
== 0xffff8001 &&
105 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
== 0xffff8002 &&
106 SCTAG_DOM_FILE
== 0xffff8005,
110 MOZ_ASSERT(aTag
== SCTAG_DOM_FILE
||
111 aTag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
||
112 aTag
== SCTAG_DOM_BLOB
);
118 if (NS_WARN_IF(!JS_ReadBytes(aReader
, &size
, sizeof(uint64_t)))) {
122 aRetval
->size
= NativeEndian::swapFromLittleEndian(size
);
125 if (NS_WARN_IF(!StructuredCloneReadString(aReader
, type
))) {
129 CopyUTF8toUTF16(type
, aRetval
->type
);
132 if (aTag
== SCTAG_DOM_BLOB
) {
136 MOZ_ASSERT(aTag
== SCTAG_DOM_FILE
||
137 aTag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
);
139 int64_t lastModifiedDate
;
140 if (aTag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
) {
141 lastModifiedDate
= INT64_MAX
;
143 if (NS_WARN_IF(!JS_ReadBytes(aReader
, &lastModifiedDate
,
144 sizeof(lastModifiedDate
)))) {
147 lastModifiedDate
= NativeEndian::swapFromLittleEndian(lastModifiedDate
);
150 aRetval
->lastModifiedDate
= lastModifiedDate
;
153 if (NS_WARN_IF(!StructuredCloneReadString(aReader
, name
))) {
157 CopyUTF8toUTF16(name
, aRetval
->name
);
162 bool ReadWasmModule(JSStructuredCloneReader
* aReader
, WasmModuleData
* aRetval
) {
163 static_assert(SCTAG_DOM_WASM_MODULE
== 0xFFFF8006, "Update me!");
164 MOZ_ASSERT(aReader
&& aRetval
);
166 uint32_t bytecodeIndex
;
167 uint32_t compiledIndex
;
168 if (NS_WARN_IF(!JS_ReadUint32Pair(aReader
, &bytecodeIndex
, &compiledIndex
))) {
172 aRetval
->bytecodeIndex
= bytecodeIndex
;
173 aRetval
->compiledIndex
= compiledIndex
;
178 template <typename StructuredCloneFile
>
179 class ValueDeserializationHelper
;
181 class ValueDeserializationHelperBase
{
183 static bool CreateAndWrapWasmModule(JSContext
* aCx
,
184 const StructuredCloneFileBase
& aFile
,
185 const WasmModuleData
& aData
,
186 JS::MutableHandle
<JSObject
*> aResult
) {
188 MOZ_ASSERT(aFile
.Type() == StructuredCloneFileBase::eWasmBytecode
);
190 // Both on the parent and child side, just create a plain object here,
191 // support for de-serialization of WebAssembly.Modules has been removed in
192 // bug 1561876. Full removal is tracked in bug 1487479.
194 JS::Rooted
<JSObject
*> obj(aCx
, JS_NewPlainObject(aCx
));
195 if (NS_WARN_IF(!obj
)) {
203 template <typename StructuredCloneFile
>
204 static bool CreateAndWrapBlobOrFile(JSContext
* aCx
, IDBDatabase
* aDatabase
,
205 const StructuredCloneFile
& aFile
,
206 const BlobOrFileData
& aData
,
207 JS::MutableHandle
<JSObject
*> aResult
) {
209 MOZ_ASSERT(aData
.tag
== SCTAG_DOM_FILE
||
210 aData
.tag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
||
211 aData
.tag
== SCTAG_DOM_BLOB
);
212 MOZ_ASSERT(aFile
.Type() == StructuredCloneFileBase::eBlob
);
214 const auto blob
= ValueDeserializationHelper
<StructuredCloneFile
>::GetBlob(
215 aCx
, aDatabase
, aFile
);
216 if (NS_WARN_IF(!blob
)) {
220 if (aData
.tag
== SCTAG_DOM_BLOB
) {
221 blob
->Impl()->SetLazyData(VoidString(), aData
.type
, aData
.size
,
223 MOZ_ASSERT(!blob
->IsFile());
225 // XXX The comment below is somewhat confusing, since it seems to imply
226 // that this branch is only executed when called from ActorsParent, but
227 // it's executed from both the parent and the child side code.
229 // ActorsParent sends here a kind of half blob and half file wrapped into
230 // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a
231 // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a
232 // Blob (see the previous assert), but 'blob' still has the WebIDL DOM
234 // Before exposing it to content, we must recreate a DOM Blob object.
236 const RefPtr
<Blob
> exposedBlob
=
237 Blob::Create(blob
->GetParentObject(), blob
->Impl());
238 if (NS_WARN_IF(!exposedBlob
)) {
242 return WrapAsJSObject(aCx
, exposedBlob
, aResult
);
245 blob
->Impl()->SetLazyData(aData
.name
, aData
.type
, aData
.size
,
246 aData
.lastModifiedDate
* PR_USEC_PER_MSEC
);
248 MOZ_ASSERT(blob
->IsFile());
249 const RefPtr
<File
> file
= blob
->ToFile();
252 return WrapAsJSObject(aCx
, file
, aResult
);
257 class ValueDeserializationHelper
<StructuredCloneFileParent
>
258 : public ValueDeserializationHelperBase
{
260 static bool CreateAndWrapMutableFile(JSContext
* aCx
,
261 StructuredCloneFileParent
& aFile
,
262 const MutableFileData
& aData
,
263 JS::MutableHandle
<JSObject
*> aResult
) {
265 MOZ_ASSERT(aFile
.Type() == StructuredCloneFileBase::eBlob
);
267 // We are in an IDB SQLite schema upgrade where we don't care about a real
268 // 'MutableFile', but we just care of having a proper |mType| flag.
270 aFile
.MutateType(StructuredCloneFileBase::eMutableFile
);
272 // Just make a dummy object.
273 JS::Rooted
<JSObject
*> obj(aCx
, JS_NewPlainObject(aCx
));
275 if (NS_WARN_IF(!obj
)) {
283 static RefPtr
<Blob
> GetBlob(JSContext
* aCx
, IDBDatabase
* aDatabase
,
284 const StructuredCloneFileParent
& aFile
) {
285 // This is chrome code, so there is no parent, but still we want to set a
286 // correct parent for the new File object.
287 const auto global
= [aDatabase
, aCx
]() -> nsCOMPtr
<nsIGlobalObject
> {
288 if (NS_IsMainThread()) {
289 if (aDatabase
&& aDatabase
->GetParentObject()) {
290 return aDatabase
->GetParentObject();
292 return xpc::CurrentNativeGlobal(aCx
);
294 const WorkerPrivate
* const workerPrivate
=
295 GetCurrentThreadWorkerPrivate();
296 MOZ_ASSERT(workerPrivate
);
298 WorkerGlobalScope
* const globalScope
= workerPrivate
->GlobalScope();
299 MOZ_ASSERT(globalScope
);
301 return do_QueryObject(globalScope
);
306 // We do not have an mBlob but do have a DatabaseFileInfo.
308 // If we are creating an index, we do need a real-looking Blob/File instance
309 // because the index's key path can reference their properties. Rather than
310 // create a fake-looking object, create a real Blob.
312 // If we are in a schema upgrade, we don't strictly need that, but we do not
313 // need to optimize for that, and create it anyway.
314 const nsCOMPtr
<nsIFile
> file
= aFile
.FileInfo().GetFileForFileInfo();
319 const auto impl
= MakeRefPtr
<FileBlobImpl
>(file
);
320 impl
->SetFileId(aFile
.FileInfo().Id());
321 return File::Create(global
, impl
);
326 class ValueDeserializationHelper
<StructuredCloneFileChild
>
327 : public ValueDeserializationHelperBase
{
329 static bool CreateAndWrapMutableFile(JSContext
* aCx
,
330 StructuredCloneFileChild
& aFile
,
331 const MutableFileData
& aData
,
332 JS::MutableHandle
<JSObject
*> aResult
) {
334 MOZ_ASSERT(aFile
.Type() == StructuredCloneFileBase::eMutableFile
);
339 static RefPtr
<Blob
> GetBlob(JSContext
* aCx
, IDBDatabase
* aDatabase
,
340 const StructuredCloneFileChild
& aFile
) {
341 if (aFile
.HasBlob()) {
342 return aFile
.BlobPtr();
345 MOZ_CRASH("Expected a StructuredCloneFile with a Blob");
351 template <typename StructuredCloneReadInfo
>
352 JSObject
* CommonStructuredCloneReadCallback(
353 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
354 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, uint32_t aData
,
355 StructuredCloneReadInfo
* aCloneReadInfo
, IDBDatabase
* aDatabase
) {
356 // We need to statically assert that our tag values are what we expect
357 // so that if people accidentally change them they notice.
358 static_assert(SCTAG_DOM_BLOB
== 0xffff8001 &&
359 SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
== 0xffff8002 &&
360 SCTAG_DOM_MUTABLEFILE
== 0xffff8004 &&
361 SCTAG_DOM_FILE
== 0xffff8005 &&
362 SCTAG_DOM_WASM_MODULE
== 0xffff8006 &&
363 SCTAG_DOM_URLSEARCHPARAMS
== 0xffff8014,
364 "You changed our structured clone tag values and just ate "
365 "everyone's IndexedDB data. I hope you are happy.");
367 if (aTag
== SCTAG_DOM_URLSEARCHPARAMS
) {
368 // Protect the result from a moving GC in ~RefPtr.
369 JS::Rooted
<JSObject
*> result(aCx
);
372 // Scope for the RefPtr below.
374 nsIGlobalObject
* global
= xpc::CurrentNativeGlobal(aCx
);
379 RefPtr
<URLSearchParams
> params
= new URLSearchParams(global
);
383 if (!JS_ReadUint32Pair(aReader
, ¶mCount
, &zero
)) {
389 for (uint32_t index
= 0; index
< paramCount
; index
++) {
390 if (!StructuredCloneHolder::ReadString(aReader
, key
) ||
391 !StructuredCloneHolder::ReadString(aReader
, value
)) {
394 params
->Append(key
, value
);
397 if (!WrapAsJSObject(aCx
, params
, &result
)) {
405 using StructuredCloneFile
=
406 typename
StructuredCloneReadInfo::StructuredCloneFile
;
408 if (aTag
== SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE
||
409 aTag
== SCTAG_DOM_BLOB
|| aTag
== SCTAG_DOM_FILE
||
410 aTag
== SCTAG_DOM_MUTABLEFILE
|| aTag
== SCTAG_DOM_WASM_MODULE
) {
411 JS::Rooted
<JSObject
*> result(aCx
);
413 if (aTag
== SCTAG_DOM_WASM_MODULE
) {
414 WasmModuleData
data(aData
);
415 if (NS_WARN_IF(!ReadWasmModule(aReader
, &data
))) {
419 MOZ_ASSERT(data
.compiledIndex
== data
.bytecodeIndex
+ 1);
420 MOZ_ASSERT(!data
.flags
);
422 const auto& files
= aCloneReadInfo
->Files();
423 if (data
.bytecodeIndex
>= files
.Length() ||
424 data
.compiledIndex
>= files
.Length()) {
425 MOZ_ASSERT(false, "Bad index value!");
429 const auto& file
= files
[data
.bytecodeIndex
];
431 if (NS_WARN_IF(!ValueDeserializationHelper
<StructuredCloneFile
>::
432 CreateAndWrapWasmModule(aCx
, file
, data
, &result
))) {
439 if (aData
>= aCloneReadInfo
->Files().Length()) {
440 MOZ_ASSERT(false, "Bad index value!");
444 auto& file
= aCloneReadInfo
->MutableFile(aData
);
446 if (aTag
== SCTAG_DOM_MUTABLEFILE
) {
447 MutableFileData data
;
448 if (NS_WARN_IF(!ReadFileHandle(aReader
, &data
))) {
452 if (NS_WARN_IF(!ValueDeserializationHelper
<StructuredCloneFile
>::
453 CreateAndWrapMutableFile(aCx
, file
, data
, &result
))) {
461 if (NS_WARN_IF(!ReadBlobOrFile(aReader
, aTag
, &data
))) {
465 if (NS_WARN_IF(!ValueDeserializationHelper
<
466 StructuredCloneFile
>::CreateAndWrapBlobOrFile(aCx
, aDatabase
,
475 return StructuredCloneHolder::ReadFullySerializableObjects(aCx
, aReader
, aTag
,
479 template JSObject
* CommonStructuredCloneReadCallback(
480 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
481 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, uint32_t aData
,
482 StructuredCloneReadInfoChild
* aCloneReadInfo
, IDBDatabase
* aDatabase
);
484 template JSObject
* CommonStructuredCloneReadCallback(
485 JSContext
* aCx
, JSStructuredCloneReader
* aReader
,
486 const JS::CloneDataPolicy
& aCloneDataPolicy
, uint32_t aTag
, uint32_t aData
,
487 StructuredCloneReadInfoParent
* aCloneReadInfo
, IDBDatabase
* aDatabase
);
488 } // namespace mozilla::dom::indexedDB