summaryrefslogtreecommitdiffstats
path: root/dom/filesystem/FileSystemTaskBase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/filesystem/FileSystemTaskBase.cpp')
-rw-r--r--dom/filesystem/FileSystemTaskBase.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/dom/filesystem/FileSystemTaskBase.cpp b/dom/filesystem/FileSystemTaskBase.cpp
new file mode 100644
index 0000000000..34cfd8f018
--- /dev/null
+++ b/dom/filesystem/FileSystemTaskBase.cpp
@@ -0,0 +1,246 @@
+/* -*- 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 "mozilla/dom/FileSystemTaskBase.h"
+
+#include "nsNetCID.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemBase.h"
+#include "mozilla/dom/FileSystemRequestParent.h"
+#include "mozilla/dom/FileSystemUtils.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/Unused.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla::dom {
+
+namespace {
+
+nsresult FileSystemErrorFromNsError(const nsresult& aErrorValue) {
+ uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
+ if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
+ module == NS_ERROR_MODULE_DOM_FILE || module == NS_ERROR_MODULE_DOM) {
+ return aErrorValue;
+ }
+
+ switch (aErrorValue) {
+ case NS_OK:
+ return NS_OK;
+
+ case NS_ERROR_FILE_INVALID_PATH:
+ case NS_ERROR_FILE_UNRECOGNIZED_PATH:
+ return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
+
+ case NS_ERROR_FILE_DESTINATION_NOT_DIR:
+ return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
+
+ case NS_ERROR_FILE_ACCESS_DENIED:
+ case NS_ERROR_FILE_DIR_NOT_EMPTY:
+ return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
+
+ case NS_ERROR_FILE_NOT_FOUND:
+ case NS_ERROR_NOT_AVAILABLE:
+ return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
+
+ case NS_ERROR_FILE_ALREADY_EXISTS:
+ return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
+
+ case NS_ERROR_FILE_NOT_DIRECTORY:
+ return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
+
+ case NS_ERROR_UNEXPECTED:
+ default:
+ return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
+ }
+}
+
+nsresult DispatchToIOThread(nsIRunnable* aRunnable) {
+ MOZ_ASSERT(aRunnable);
+
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ MOZ_ASSERT(target);
+
+ return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
+
+} // anonymous namespace
+
+/**
+ * FileSystemTaskBase class
+ */
+
+FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
+ FileSystemBase* aFileSystem)
+ : mErrorValue(NS_OK),
+ mFileSystem(aFileSystem),
+ mGlobalObject(aGlobalObject) {
+ MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
+ aFileSystem->AssertIsOnOwningThread();
+ MOZ_ASSERT(aGlobalObject);
+}
+
+FileSystemTaskChildBase::~FileSystemTaskChildBase() {
+ mFileSystem->AssertIsOnOwningThread();
+}
+
+FileSystemBase* FileSystemTaskChildBase::GetFileSystem() const {
+ mFileSystem->AssertIsOnOwningThread();
+ return mFileSystem.get();
+}
+
+void FileSystemTaskChildBase::Start() {
+ mFileSystem->AssertIsOnOwningThread();
+
+ mozilla::ipc::PBackgroundChild* actor =
+ mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!actor)) {
+ // We are probably shutting down.
+ return;
+ }
+
+ nsAutoString serialization;
+ mFileSystem->SerializeDOMPath(serialization);
+
+ ErrorResult rv;
+ FileSystemParams params = GetRequestParams(serialization, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return;
+ }
+
+ actor->SendPFileSystemRequestConstructor(this, params);
+}
+
+void FileSystemTaskChildBase::SetRequestResult(
+ const FileSystemResponseValue& aValue) {
+ mFileSystem->AssertIsOnOwningThread();
+
+ if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
+ FileSystemErrorResponse r = aValue;
+ mErrorValue = r.error();
+ } else {
+ ErrorResult rv;
+ SetSuccessRequestResult(aValue, rv);
+ mErrorValue = rv.StealNSResult();
+ }
+}
+
+mozilla::ipc::IPCResult FileSystemTaskChildBase::Recv__delete__(
+ const FileSystemResponseValue& aValue) {
+ mFileSystem->AssertIsOnOwningThread();
+
+ SetRequestResult(aValue);
+ HandlerCallback();
+ return IPC_OK();
+}
+
+void FileSystemTaskChildBase::SetError(const nsresult& aErrorValue) {
+ mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
+
+/**
+ * FileSystemTaskParentBase class
+ */
+
+FileSystemTaskParentBase::FileSystemTaskParentBase(
+ FileSystemBase* aFileSystem, const FileSystemParams& aParam,
+ FileSystemRequestParent* aParent)
+ : Runnable("dom::FileSystemTaskParentBase"),
+ mErrorValue(NS_OK),
+ mFileSystem(aFileSystem),
+ mRequestParent(aParent),
+ mBackgroundEventTarget(GetCurrentSerialEventTarget()) {
+ MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
+ MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
+ MOZ_ASSERT(aParent);
+ MOZ_ASSERT(mBackgroundEventTarget);
+ mozilla::ipc::AssertIsOnBackgroundThread();
+}
+
+FileSystemTaskParentBase::~FileSystemTaskParentBase() {
+ // This task can be released on different threads because we dispatch it (as
+ // runnable) to main-thread, I/O and then back to the PBackground thread.
+ NS_ProxyRelease("FileSystemTaskParentBase::mFileSystem",
+ mBackgroundEventTarget, mFileSystem.forget());
+ NS_ProxyRelease("FileSystemTaskParentBase::mRequestParent",
+ mBackgroundEventTarget, mRequestParent.forget());
+}
+
+void FileSystemTaskParentBase::Start() {
+ mozilla::ipc::AssertIsOnBackgroundThread();
+ mFileSystem->AssertIsOnOwningThread();
+
+ DebugOnly<nsresult> rv = DispatchToIOThread(this);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchToIOThread failed");
+}
+
+void FileSystemTaskParentBase::HandleResult() {
+ mozilla::ipc::AssertIsOnBackgroundThread();
+ mFileSystem->AssertIsOnOwningThread();
+
+ if (mFileSystem->IsShutdown()) {
+ return;
+ }
+
+ MOZ_ASSERT(mRequestParent);
+ (void)mRequestParent->Send__delete__(mRequestParent, GetRequestResult());
+}
+
+FileSystemResponseValue FileSystemTaskParentBase::GetRequestResult() const {
+ mozilla::ipc::AssertIsOnBackgroundThread();
+ mFileSystem->AssertIsOnOwningThread();
+
+ if (HasError()) {
+ return FileSystemErrorResponse(mErrorValue);
+ }
+
+ ErrorResult rv;
+ FileSystemResponseValue value = GetSuccessRequestResult(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return FileSystemErrorResponse(rv.StealNSResult());
+ }
+
+ return value;
+}
+
+void FileSystemTaskParentBase::SetError(const nsresult& aErrorValue) {
+ mErrorValue = FileSystemErrorFromNsError(aErrorValue);
+}
+
+NS_IMETHODIMP
+FileSystemTaskParentBase::Run() {
+ // This method can run in 2 different threads. Here why:
+ // 1. We are are on the I/O thread and we call IOWork().
+ // 2. After step 1, it returns back to the PBackground thread.
+
+ // Run I/O thread tasks
+ if (!mozilla::ipc::IsOnBackgroundThread()) {
+ nsresult rv = IOWork();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ SetError(rv);
+ }
+
+ // Let's go back to PBackground thread to finish the work.
+ rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+ }
+
+ // If we are here, it's because the I/O work has been done and we have to
+ // handle the result back via IPC.
+ mozilla::ipc::AssertIsOnBackgroundThread();
+ HandleResult();
+ return NS_OK;
+}
+
+} // namespace mozilla::dom