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 "FileSystemHandle.h"
9 #include "FileSystemDirectoryHandle.h"
10 #include "FileSystemFileHandle.h"
11 #include "fs/FileSystemRequestHandler.h"
12 #include "js/StructuredClone.h"
13 #include "mozilla/ErrorResult.h"
14 #include "mozilla/dom/FileSystemHandleBinding.h"
15 #include "mozilla/dom/FileSystemLog.h"
16 #include "mozilla/dom/FileSystemManager.h"
17 #include "mozilla/dom/Promise-inl.h"
18 #include "mozilla/dom/Promise.h"
19 #include "mozilla/dom/StorageManager.h"
20 #include "mozilla/dom/StructuredCloneHolder.h"
21 #include "mozilla/dom/quota/QuotaCommon.h"
22 #include "mozilla/ipc/PBackgroundSharedTypes.h"
23 #include "nsJSPrincipals.h"
26 #include "private/pprio.h"
27 #include "xpcpublic.h"
29 namespace mozilla::dom
{
33 bool ConstructHandleMetadata(JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
34 JSStructuredCloneReader
* aReader
,
35 const bool aDirectory
,
36 fs::FileSystemEntryMetadata
& aMetadata
) {
37 using namespace mozilla::dom::fs
;
40 if (!entryId
.SetLength(32u, fallible
)) {
44 if (!JS_ReadBytes(aReader
, static_cast<void*>(entryId
.BeginWriting()), 32u)) {
49 if (!StructuredCloneHolder::ReadString(aReader
, name
)) {
53 mozilla::ipc::PrincipalInfo storageKey
;
54 if (!nsJSPrincipals::ReadPrincipalInfo(aReader
, storageKey
)) {
58 QM_TRY_UNWRAP(auto hasEqualStorageKey
,
59 aGlobal
->HasEqualStorageKey(storageKey
), false);
61 if (!hasEqualStorageKey
) {
62 LOG(("Blocking deserialization of %s due to cross-origin",
63 NS_ConvertUTF16toUTF8(name
).get()));
67 LOG_VERBOSE(("Deserializing %s", NS_ConvertUTF16toUTF8(name
).get()));
69 aMetadata
= fs::FileSystemEntryMetadata(entryId
, name
, aDirectory
);
75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemHandle
)
76 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
77 NS_INTERFACE_MAP_ENTRY(nsISupports
)
80 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle
)
81 NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle
)
83 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemHandle
)
84 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemHandle
)
85 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal
)
86 // Don't unlink mManager!
87 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemHandle
)
90 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal
)
91 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager
)
92 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
94 FileSystemHandle::FileSystemHandle(
95 nsIGlobalObject
* aGlobal
, RefPtr
<FileSystemManager
>& aManager
,
96 const fs::FileSystemEntryMetadata
& aMetadata
,
97 fs::FileSystemRequestHandler
* aRequestHandler
)
100 mMetadata(aMetadata
),
101 mRequestHandler(aRequestHandler
) {
102 MOZ_ASSERT(!mMetadata
.entryId().IsEmpty());
105 // WebIDL Boilerplate
107 nsIGlobalObject
* FileSystemHandle::GetParentObject() const { return mGlobal
; }
109 JSObject
* FileSystemHandle::WrapObject(JSContext
* aCx
,
110 JS::Handle
<JSObject
*> aGivenProto
) {
111 return FileSystemHandle_Binding::Wrap(aCx
, this, aGivenProto
);
116 void FileSystemHandle::GetName(nsAString
& aResult
) {
117 aResult
= mMetadata
.entryName();
120 already_AddRefed
<Promise
> FileSystemHandle::IsSameEntry(
121 FileSystemHandle
& aOther
, ErrorResult
& aError
) const {
122 RefPtr
<Promise
> promise
= Promise::Create(GetParentObject(), aError
);
123 if (aError
.Failed()) {
127 // Handles the case of "dir = createdir foo; removeEntry(foo); file =
128 // createfile foo; issameentry(dir, file)"
129 const bool result
= mMetadata
.entryId().Equals(aOther
.mMetadata
.entryId()) &&
130 Kind() == aOther
.Kind();
131 promise
->MaybeResolve(result
);
133 return promise
.forget();
136 already_AddRefed
<Promise
> FileSystemHandle::Move(const nsAString
& aName
,
137 ErrorResult
& aError
) {
138 LOG(("Move %s to %s", NS_ConvertUTF16toUTF8(mMetadata
.entryName()).get(),
139 NS_ConvertUTF16toUTF8(aName
).get()));
141 fs::EntryId parent
; // empty means same directory
142 return Move(parent
, aName
, aError
);
145 already_AddRefed
<Promise
> FileSystemHandle::Move(
146 FileSystemDirectoryHandle
& aParent
, ErrorResult
& aError
) {
147 LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata
.entryName()).get(),
148 NS_ConvertUTF16toUTF8(aParent
.mMetadata
.entryName()).get(),
149 NS_ConvertUTF16toUTF8(mMetadata
.entryName()).get()));
150 return Move(aParent
, mMetadata
.entryName(), aError
);
153 already_AddRefed
<Promise
> FileSystemHandle::Move(
154 FileSystemDirectoryHandle
& aParent
, const nsAString
& aName
,
155 ErrorResult
& aError
) {
156 LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata
.entryName()).get(),
157 NS_ConvertUTF16toUTF8(aParent
.mMetadata
.entryName()).get(),
158 NS_ConvertUTF16toUTF8(aName
).get()));
159 return Move(aParent
.mMetadata
.entryId(), aName
, aError
);
162 already_AddRefed
<Promise
> FileSystemHandle::Move(const fs::EntryId
& aParentId
,
163 const nsAString
& aName
,
164 ErrorResult
& aError
) {
165 RefPtr
<Promise
> promise
= Promise::Create(GetParentObject(), aError
);
166 if (aError
.Failed()) {
170 fs::FileSystemChildMetadata newMetadata
;
171 newMetadata
.parentId() = aParentId
;
172 newMetadata
.childName() = aName
;
173 if (!aParentId
.IsEmpty()) {
174 mRequestHandler
->MoveEntry(mManager
, this, &mMetadata
, newMetadata
, promise
,
177 mRequestHandler
->RenameEntry(mManager
, this, &mMetadata
,
178 newMetadata
.childName(), promise
, aError
);
180 if (aError
.Failed()) {
184 // Other handles to this will be broken, and the spec is ok with this, but we
185 // need to update our EntryId and name
186 promise
->AddCallbacksWithCycleCollectedArgs(
187 [newMetadata
](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
188 ErrorResult
& aRv
, FileSystemHandle
* aHandle
) {
190 LOG(("Changing FileSystemHandle name from %s to %s",
191 NS_ConvertUTF16toUTF8(aHandle
->mMetadata
.entryName()).get(),
192 NS_ConvertUTF16toUTF8(newMetadata
.childName()).get()));
193 aHandle
->mMetadata
.entryName() = newMetadata
.childName();
195 [](JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
, ErrorResult
& aRv
,
196 FileSystemHandle
* aHandle
) {
197 LOG(("reject of move for %s",
198 NS_ConvertUTF16toUTF8(aHandle
->mMetadata
.entryName()).get()));
202 return promise
.forget();
205 // [Serializable] implementation
208 already_AddRefed
<FileSystemHandle
> FileSystemHandle::ReadStructuredClone(
209 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
210 JSStructuredCloneReader
* aReader
) {
211 LOG_VERBOSE(("Reading File/DirectoryHandle"));
213 uint32_t kind
= UINT32_MAX
;
215 if (!JS_ReadBytes(aReader
, reinterpret_cast<void*>(&kind
),
220 if (kind
== static_cast<uint32_t>(FileSystemHandleKind::Directory
)) {
221 RefPtr
<FileSystemHandle
> result
=
222 FileSystemHandle::ConstructDirectoryHandle(aCx
, aGlobal
, aReader
);
223 return result
.forget();
226 if (kind
== static_cast<uint32_t>(FileSystemHandleKind::File
)) {
227 RefPtr
<FileSystemHandle
> result
=
228 FileSystemHandle::ConstructFileHandle(aCx
, aGlobal
, aReader
);
229 return result
.forget();
235 bool FileSystemHandle::WriteStructuredClone(
236 JSContext
* aCx
, JSStructuredCloneWriter
* aWriter
) const {
237 LOG_VERBOSE(("Writing File/DirectoryHandle"));
238 MOZ_ASSERT(mMetadata
.entryId().Length() == 32);
240 auto kind
= static_cast<uint32_t>(Kind());
241 if (NS_WARN_IF(!JS_WriteBytes(aWriter
, static_cast<void*>(&kind
),
242 sizeof(uint32_t)))) {
246 if (NS_WARN_IF(!JS_WriteBytes(
247 aWriter
, static_cast<const void*>(mMetadata
.entryId().get()),
248 mMetadata
.entryId().Length()))) {
252 if (!StructuredCloneHolder::WriteString(aWriter
, mMetadata
.entryName())) {
256 // Needed to make sure the destination nsIGlobalObject is from the same
258 QM_TRY_INSPECT(const auto& storageKey
, mGlobal
->GetStorageKey(), false);
260 return nsJSPrincipals::WritePrincipalInfo(aWriter
, storageKey
);
264 already_AddRefed
<FileSystemFileHandle
> FileSystemHandle::ConstructFileHandle(
265 JSContext
* aCx
, nsIGlobalObject
* aGlobal
,
266 JSStructuredCloneReader
* aReader
) {
267 LOG(("Reading FileHandle"));
269 fs::FileSystemEntryMetadata metadata
;
270 if (!ConstructHandleMetadata(aCx
, aGlobal
, aReader
, /* aDirectory */ false,
275 RefPtr
<StorageManager
> storageManager
= aGlobal
->GetStorageManager();
276 if (!storageManager
) {
280 // Note that the actor may not exist or may not be connected yet.
281 RefPtr
<FileSystemManager
> fileSystemManager
=
282 storageManager
->GetFileSystemManager();
284 RefPtr
<FileSystemFileHandle
> fsHandle
=
285 new FileSystemFileHandle(aGlobal
, fileSystemManager
, metadata
);
287 return fsHandle
.forget();
291 already_AddRefed
<FileSystemDirectoryHandle
>
292 FileSystemHandle::ConstructDirectoryHandle(JSContext
* aCx
,
293 nsIGlobalObject
* aGlobal
,
294 JSStructuredCloneReader
* aReader
) {
295 LOG(("Reading DirectoryHandle"));
297 fs::FileSystemEntryMetadata metadata
;
298 if (!ConstructHandleMetadata(aCx
, aGlobal
, aReader
, /* aDirectory */ true,
303 RefPtr
<StorageManager
> storageManager
= aGlobal
->GetStorageManager();
304 if (!storageManager
) {
308 // Note that the actor may not exist or may not be connected yet.
309 RefPtr
<FileSystemManager
> fileSystemManager
=
310 storageManager
->GetFileSystemManager();
312 RefPtr
<FileSystemDirectoryHandle
> fsHandle
=
313 new FileSystemDirectoryHandle(aGlobal
, fileSystemManager
, metadata
);
315 return fsHandle
.forget();
318 } // namespace mozilla::dom