summaryrefslogtreecommitdiffstats
path: root/dom/fs/api/FileSystemHandle.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/fs/api/FileSystemHandle.cpp319
1 files changed, 319 insertions, 0 deletions
diff --git a/dom/fs/api/FileSystemHandle.cpp b/dom/fs/api/FileSystemHandle.cpp
new file mode 100644
index 0000000000..beca83d0bf
--- /dev/null
+++ b/dom/fs/api/FileSystemHandle.cpp
@@ -0,0 +1,319 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FileSystemHandle.h"
+
+#include "FileSystemDirectoryHandle.h"
+#include "FileSystemFileHandle.h"
+#include "fs/FileSystemRequestHandler.h"
+#include "js/StructuredClone.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/dom/FileSystemHandleBinding.h"
+#include "mozilla/dom/FileSystemLog.h"
+#include "mozilla/dom/FileSystemManager.h"
+#include "mozilla/dom/Promise-inl.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/StorageManager.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "mozilla/dom/quota/QuotaCommon.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsJSPrincipals.h"
+#include "nsString.h"
+#include "prio.h"
+#include "private/pprio.h"
+#include "xpcpublic.h"
+
+namespace mozilla::dom {
+
+namespace {
+
+bool ConstructHandleMetadata(JSContext* aCx, nsIGlobalObject* aGlobal,
+ JSStructuredCloneReader* aReader,
+ const bool aDirectory,
+ fs::FileSystemEntryMetadata& aMetadata) {
+ using namespace mozilla::dom::fs;
+
+ EntryId entryId;
+ if (!entryId.SetLength(32u, fallible)) {
+ return false;
+ }
+
+ if (!JS_ReadBytes(aReader, static_cast<void*>(entryId.BeginWriting()), 32u)) {
+ return false;
+ }
+
+ Name name;
+ if (!StructuredCloneHolder::ReadString(aReader, name)) {
+ return false;
+ }
+
+ mozilla::ipc::PrincipalInfo storageKey;
+ if (!nsJSPrincipals::ReadPrincipalInfo(aReader, storageKey)) {
+ return false;
+ }
+
+ QM_TRY_UNWRAP(auto hasEqualStorageKey,
+ aGlobal->HasEqualStorageKey(storageKey), false);
+
+ if (!hasEqualStorageKey) {
+ LOG(("Blocking deserialization of %s due to cross-origin",
+ NS_ConvertUTF16toUTF8(name).get()));
+ return false;
+ }
+
+ LOG_VERBOSE(("Deserializing %s", NS_ConvertUTF16toUTF8(name).get()));
+
+ aMetadata = fs::FileSystemEntryMetadata(entryId, name, aDirectory);
+ return true;
+}
+
+} // namespace
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemHandle)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FileSystemHandle)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemHandle)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+ // Don't unlink mManager!
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemHandle)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+FileSystemHandle::FileSystemHandle(
+ nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
+ const fs::FileSystemEntryMetadata& aMetadata,
+ fs::FileSystemRequestHandler* aRequestHandler)
+ : mGlobal(aGlobal),
+ mManager(aManager),
+ mMetadata(aMetadata),
+ mRequestHandler(aRequestHandler) {
+ MOZ_ASSERT(!mMetadata.entryId().IsEmpty());
+}
+
+// WebIDL Boilerplate
+
+nsIGlobalObject* FileSystemHandle::GetParentObject() const { return mGlobal; }
+
+JSObject* FileSystemHandle::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return FileSystemHandle_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+// WebIDL Interface
+
+void FileSystemHandle::GetName(nsAString& aResult) {
+ aResult = mMetadata.entryName();
+}
+
+already_AddRefed<Promise> FileSystemHandle::IsSameEntry(
+ FileSystemHandle& aOther, ErrorResult& aError) const {
+ RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ // Handles the case of "dir = createdir foo; removeEntry(foo); file =
+ // createfile foo; issameentry(dir, file)"
+ const bool result = mMetadata.entryId().Equals(aOther.mMetadata.entryId()) &&
+ Kind() == aOther.Kind();
+ promise->MaybeResolve(result);
+
+ return promise.forget();
+}
+
+already_AddRefed<Promise> FileSystemHandle::Move(const nsAString& aName,
+ ErrorResult& aError) {
+ LOG(("Move %s to %s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(aName).get()));
+
+ fs::EntryId parent; // empty means same directory
+ return Move(parent, aName, aError);
+}
+
+already_AddRefed<Promise> FileSystemHandle::Move(
+ FileSystemDirectoryHandle& aParent, ErrorResult& aError) {
+ LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(aParent.mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(mMetadata.entryName()).get()));
+ return Move(aParent, mMetadata.entryName(), aError);
+}
+
+already_AddRefed<Promise> FileSystemHandle::Move(
+ FileSystemDirectoryHandle& aParent, const nsAString& aName,
+ ErrorResult& aError) {
+ LOG(("Move %s to %s/%s", NS_ConvertUTF16toUTF8(mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(aParent.mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(aName).get()));
+ return Move(aParent.mMetadata.entryId(), aName, aError);
+}
+
+already_AddRefed<Promise> FileSystemHandle::Move(const fs::EntryId& aParentId,
+ const nsAString& aName,
+ ErrorResult& aError) {
+ RefPtr<Promise> promise = Promise::Create(GetParentObject(), aError);
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ fs::Name name(aName);
+ if (!aParentId.IsEmpty()) {
+ fs::FileSystemChildMetadata newMetadata;
+ newMetadata.parentId() = aParentId;
+ newMetadata.childName() = aName;
+ mRequestHandler->MoveEntry(mManager, this, mMetadata, newMetadata, promise,
+ aError);
+ } else {
+ mRequestHandler->RenameEntry(mManager, this, mMetadata, name, promise,
+ aError);
+ }
+ if (aError.Failed()) {
+ return nullptr;
+ }
+
+ // Other handles to this will be broken, and the spec is ok with this, but we
+ // need to update our EntryId and name
+ promise->AddCallbacksWithCycleCollectedArgs(
+ [name](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
+ FileSystemHandle* aHandle) {
+ // XXX Fix entryId!
+ LOG(("Changing FileSystemHandle name from %s to %s",
+ NS_ConvertUTF16toUTF8(aHandle->mMetadata.entryName()).get(),
+ NS_ConvertUTF16toUTF8(name).get()));
+ aHandle->mMetadata.entryName() = name;
+ },
+ [](JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv,
+ FileSystemHandle* aHandle) {
+ LOG(("reject of move for %s",
+ NS_ConvertUTF16toUTF8(aHandle->mMetadata.entryName()).get()));
+ },
+ RefPtr(this));
+
+ return promise.forget();
+}
+
+// [Serializable] implementation
+
+// static
+already_AddRefed<FileSystemHandle> FileSystemHandle::ReadStructuredClone(
+ JSContext* aCx, nsIGlobalObject* aGlobal,
+ JSStructuredCloneReader* aReader) {
+ LOG_VERBOSE(("Reading File/DirectoryHandle"));
+
+ uint32_t kind = static_cast<uint32_t>(FileSystemHandleKind::EndGuard_);
+
+ if (!JS_ReadBytes(aReader, reinterpret_cast<void*>(&kind),
+ sizeof(uint32_t))) {
+ return nullptr;
+ }
+
+ if (kind == static_cast<uint32_t>(FileSystemHandleKind::Directory)) {
+ RefPtr<FileSystemHandle> result =
+ FileSystemHandle::ConstructDirectoryHandle(aCx, aGlobal, aReader);
+ return result.forget();
+ }
+
+ if (kind == static_cast<uint32_t>(FileSystemHandleKind::File)) {
+ RefPtr<FileSystemHandle> result =
+ FileSystemHandle::ConstructFileHandle(aCx, aGlobal, aReader);
+ return result.forget();
+ }
+
+ return nullptr;
+}
+
+bool FileSystemHandle::WriteStructuredClone(
+ JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
+ LOG_VERBOSE(("Writing File/DirectoryHandle"));
+ MOZ_ASSERT(mMetadata.entryId().Length() == 32);
+
+ auto kind = static_cast<uint32_t>(Kind());
+ if (NS_WARN_IF(!JS_WriteBytes(aWriter, static_cast<void*>(&kind),
+ sizeof(uint32_t)))) {
+ return false;
+ }
+
+ if (NS_WARN_IF(!JS_WriteBytes(
+ aWriter, static_cast<const void*>(mMetadata.entryId().get()),
+ mMetadata.entryId().Length()))) {
+ return false;
+ }
+
+ if (!StructuredCloneHolder::WriteString(aWriter, mMetadata.entryName())) {
+ return false;
+ }
+
+ // Needed to make sure the destination nsIGlobalObject is from the same
+ // origin/principal
+ QM_TRY_INSPECT(const auto& storageKey, mGlobal->GetStorageKey(), false);
+
+ return nsJSPrincipals::WritePrincipalInfo(aWriter, storageKey);
+}
+
+// static
+already_AddRefed<FileSystemFileHandle> FileSystemHandle::ConstructFileHandle(
+ JSContext* aCx, nsIGlobalObject* aGlobal,
+ JSStructuredCloneReader* aReader) {
+ LOG(("Reading FileHandle"));
+
+ fs::FileSystemEntryMetadata metadata;
+ if (!ConstructHandleMetadata(aCx, aGlobal, aReader, /* aDirectory */ false,
+ metadata)) {
+ return nullptr;
+ }
+
+ RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
+ if (!storageManager) {
+ return nullptr;
+ }
+
+ // Note that the actor may not exist or may not be connected yet.
+ RefPtr<FileSystemManager> fileSystemManager =
+ storageManager->GetFileSystemManager();
+
+ RefPtr<FileSystemFileHandle> fsHandle =
+ new FileSystemFileHandle(aGlobal, fileSystemManager, metadata);
+
+ return fsHandle.forget();
+}
+
+// static
+already_AddRefed<FileSystemDirectoryHandle>
+FileSystemHandle::ConstructDirectoryHandle(JSContext* aCx,
+ nsIGlobalObject* aGlobal,
+ JSStructuredCloneReader* aReader) {
+ LOG(("Reading DirectoryHandle"));
+
+ fs::FileSystemEntryMetadata metadata;
+ if (!ConstructHandleMetadata(aCx, aGlobal, aReader, /* aDirectory */ true,
+ metadata)) {
+ return nullptr;
+ }
+
+ RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
+ if (!storageManager) {
+ return nullptr;
+ }
+
+ // Note that the actor may not exist or may not be connected yet.
+ RefPtr<FileSystemManager> fileSystemManager =
+ storageManager->GetFileSystemManager();
+
+ RefPtr<FileSystemDirectoryHandle> fsHandle =
+ new FileSystemDirectoryHandle(aGlobal, fileSystemManager, metadata);
+
+ return fsHandle.forget();
+}
+
+} // namespace mozilla::dom