Merge mozilla-central to autoland. a=merge CLOSED TREE
[gecko.git] / dom / fs / api / FileSystemHandle.cpp
blob79151920c08d18c82648c9d8e249045b1fe0ab9e
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"
24 #include "nsString.h"
25 #include "prio.h"
26 #include "private/pprio.h"
27 #include "xpcpublic.h"
29 namespace mozilla::dom {
31 namespace {
33 bool ConstructHandleMetadata(JSContext* aCx, nsIGlobalObject* aGlobal,
34 JSStructuredCloneReader* aReader,
35 const bool aDirectory,
36 fs::FileSystemEntryMetadata& aMetadata) {
37 using namespace mozilla::dom::fs;
39 EntryId entryId;
40 if (!entryId.SetLength(32u, fallible)) {
41 return false;
44 if (!JS_ReadBytes(aReader, static_cast<void*>(entryId.BeginWriting()), 32u)) {
45 return false;
48 Name name;
49 if (!StructuredCloneHolder::ReadString(aReader, name)) {
50 return false;
53 mozilla::ipc::PrincipalInfo storageKey;
54 if (!nsJSPrincipals::ReadPrincipalInfo(aReader, storageKey)) {
55 return false;
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()));
64 return false;
67 LOG_VERBOSE(("Deserializing %s", NS_ConvertUTF16toUTF8(name).get()));
69 aMetadata = fs::FileSystemEntryMetadata(entryId, name, aDirectory);
70 return true;
73 } // namespace
75 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemHandle)
76 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
77 NS_INTERFACE_MAP_ENTRY(nsISupports)
78 NS_INTERFACE_MAP_END
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)
98 : mGlobal(aGlobal),
99 mManager(aManager),
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);
114 // WebIDL Interface
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()) {
124 return nullptr;
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()) {
167 return nullptr;
170 fs::FileSystemChildMetadata newMetadata;
171 newMetadata.parentId() = aParentId;
172 newMetadata.childName() = aName;
173 if (!aParentId.IsEmpty()) {
174 mRequestHandler->MoveEntry(mManager, this, &mMetadata, newMetadata, promise,
175 aError);
176 } else {
177 mRequestHandler->RenameEntry(mManager, this, &mMetadata,
178 newMetadata.childName(), promise, aError);
180 if (aError.Failed()) {
181 return nullptr;
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) {
189 // XXX Fix entryId!
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()));
200 RefPtr(this));
202 return promise.forget();
205 // [Serializable] implementation
207 // static
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),
216 sizeof(uint32_t))) {
217 return nullptr;
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();
232 return nullptr;
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)))) {
243 return false;
246 if (NS_WARN_IF(!JS_WriteBytes(
247 aWriter, static_cast<const void*>(mMetadata.entryId().get()),
248 mMetadata.entryId().Length()))) {
249 return false;
252 if (!StructuredCloneHolder::WriteString(aWriter, mMetadata.entryName())) {
253 return false;
256 // Needed to make sure the destination nsIGlobalObject is from the same
257 // origin/principal
258 QM_TRY_INSPECT(const auto& storageKey, mGlobal->GetStorageKey(), false);
260 return nsJSPrincipals::WritePrincipalInfo(aWriter, storageKey);
263 // static
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,
271 metadata)) {
272 return nullptr;
275 RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
276 if (!storageManager) {
277 return nullptr;
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();
290 // static
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,
299 metadata)) {
300 return nullptr;
303 RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
304 if (!storageManager) {
305 return nullptr;
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