tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

FileSystemTaskBase.cpp (8054B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/FileSystemTaskBase.h"
      8 
      9 #include "mozilla/dom/File.h"
     10 #include "mozilla/dom/FileSystemBase.h"
     11 #include "mozilla/dom/FileSystemRequestParent.h"
     12 #include "mozilla/dom/FileSystemUtils.h"
     13 #include "mozilla/dom/Promise.h"
     14 #include "mozilla/ipc/BackgroundChild.h"
     15 #include "mozilla/ipc/BackgroundParent.h"
     16 #include "mozilla/ipc/PBackgroundChild.h"
     17 #include "nsNetCID.h"
     18 #include "nsProxyRelease.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 namespace {
     23 
     24 nsresult FileSystemErrorFromNsError(const nsresult& aErrorValue) {
     25  uint16_t module = NS_ERROR_GET_MODULE(aErrorValue);
     26  if (module == NS_ERROR_MODULE_DOM_FILESYSTEM ||
     27      module == NS_ERROR_MODULE_DOM_FILE || module == NS_ERROR_MODULE_DOM) {
     28    return aErrorValue;
     29  }
     30 
     31  switch (aErrorValue) {
     32    case NS_OK:
     33      return NS_OK;
     34 
     35    case NS_ERROR_FILE_INVALID_PATH:
     36    case NS_ERROR_FILE_UNRECOGNIZED_PATH:
     37      return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
     38 
     39    case NS_ERROR_FILE_DESTINATION_NOT_DIR:
     40      return NS_ERROR_DOM_FILESYSTEM_INVALID_MODIFICATION_ERR;
     41 
     42    case NS_ERROR_FILE_ACCESS_DENIED:
     43    case NS_ERROR_FILE_DIR_NOT_EMPTY:
     44      return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
     45 
     46    case NS_ERROR_FILE_NOT_FOUND:
     47    case NS_ERROR_NOT_AVAILABLE:
     48      return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
     49 
     50    case NS_ERROR_FILE_ALREADY_EXISTS:
     51      return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
     52 
     53    case NS_ERROR_FILE_NOT_DIRECTORY:
     54      return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
     55 
     56    case NS_ERROR_UNEXPECTED:
     57    default:
     58      return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
     59  }
     60 }
     61 
     62 nsresult DispatchToIOThread(nsIRunnable* aRunnable) {
     63  MOZ_ASSERT(aRunnable);
     64 
     65  nsCOMPtr<nsIEventTarget> target =
     66      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
     67  MOZ_ASSERT(target);
     68 
     69  return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
     70 }
     71 
     72 }  // anonymous namespace
     73 
     74 /**
     75 * FileSystemTaskBase class
     76 */
     77 
     78 FileSystemTaskChildBase::FileSystemTaskChildBase(nsIGlobalObject* aGlobalObject,
     79                                                 FileSystemBase* aFileSystem)
     80    : mErrorValue(NS_OK),
     81      mFileSystem(aFileSystem),
     82      mGlobalObject(aGlobalObject) {
     83  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
     84  aFileSystem->AssertIsOnOwningThread();
     85  MOZ_ASSERT(aGlobalObject);
     86 }
     87 
     88 FileSystemTaskChildBase::~FileSystemTaskChildBase() {
     89  mFileSystem->AssertIsOnOwningThread();
     90 }
     91 
     92 FileSystemBase* FileSystemTaskChildBase::GetFileSystem() const {
     93  mFileSystem->AssertIsOnOwningThread();
     94  return mFileSystem.get();
     95 }
     96 
     97 void FileSystemTaskChildBase::Start() {
     98  mFileSystem->AssertIsOnOwningThread();
     99 
    100  mozilla::ipc::PBackgroundChild* actor =
    101      mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
    102  if (NS_WARN_IF(!actor)) {
    103    // We are probably shutting down.
    104    return;
    105  }
    106 
    107  nsAutoString serialization;
    108  mFileSystem->SerializeDOMPath(serialization);
    109 
    110  ErrorResult rv;
    111  FileSystemParams params = GetRequestParams(serialization, rv);
    112  if (NS_WARN_IF(rv.Failed())) {
    113    rv.SuppressException();
    114    return;
    115  }
    116 
    117  actor->SendPFileSystemRequestConstructor(this, params);
    118 }
    119 
    120 void FileSystemTaskChildBase::SetRequestResult(
    121    const FileSystemResponseValue& aValue) {
    122  mFileSystem->AssertIsOnOwningThread();
    123 
    124  if (aValue.type() == FileSystemResponseValue::TFileSystemErrorResponse) {
    125    FileSystemErrorResponse r = aValue;
    126    mErrorValue = r.error();
    127  } else {
    128    ErrorResult rv;
    129    SetSuccessRequestResult(aValue, rv);
    130    mErrorValue = rv.StealNSResult();
    131  }
    132 }
    133 
    134 mozilla::ipc::IPCResult FileSystemTaskChildBase::Recv__delete__(
    135    const FileSystemResponseValue& aValue) {
    136  mFileSystem->AssertIsOnOwningThread();
    137 
    138  SetRequestResult(aValue);
    139  HandlerCallback();
    140  return IPC_OK();
    141 }
    142 
    143 void FileSystemTaskChildBase::SetError(const nsresult& aErrorValue) {
    144  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
    145 }
    146 
    147 /**
    148 * FileSystemTaskParentBase class
    149 */
    150 
    151 FileSystemTaskParentBase::FileSystemTaskParentBase(
    152    FileSystemBase* aFileSystem, const FileSystemParams& aParam,
    153    FileSystemRequestParent* aParent)
    154    : Runnable("dom::FileSystemTaskParentBase"),
    155      mErrorValue(NS_OK),
    156      mFileSystem(aFileSystem),
    157      mRequestParent(aParent),
    158      mBackgroundEventTarget(GetCurrentSerialEventTarget()) {
    159  MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
    160  MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
    161  MOZ_ASSERT(aParent);
    162  MOZ_ASSERT(mBackgroundEventTarget);
    163  mozilla::ipc::AssertIsOnBackgroundThread();
    164 }
    165 
    166 FileSystemTaskParentBase::~FileSystemTaskParentBase() {
    167  // This task can be released on different threads because we dispatch it (as
    168  // runnable) to main-thread, I/O and then back to the PBackground thread.
    169  NS_ProxyRelease("FileSystemTaskParentBase::mFileSystem",
    170                  mBackgroundEventTarget, mFileSystem.forget());
    171  NS_ProxyRelease("FileSystemTaskParentBase::mRequestParent",
    172                  mBackgroundEventTarget, mRequestParent.forget());
    173 }
    174 
    175 void FileSystemTaskParentBase::Start() {
    176  mozilla::ipc::AssertIsOnBackgroundThread();
    177  mFileSystem->AssertIsOnOwningThread();
    178 
    179  DebugOnly<nsresult> rv = DispatchToIOThread(this);
    180  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "DispatchToIOThread failed");
    181 }
    182 
    183 void FileSystemTaskParentBase::HandleResult() {
    184  mozilla::ipc::AssertIsOnBackgroundThread();
    185  mFileSystem->AssertIsOnOwningThread();
    186 
    187  if (mFileSystem->IsShutdown()) {
    188    return;
    189  }
    190 
    191  MOZ_ASSERT(mRequestParent);
    192  (void)mRequestParent->Send__delete__(mRequestParent, GetRequestResult());
    193 }
    194 
    195 FileSystemResponseValue FileSystemTaskParentBase::GetRequestResult() const {
    196  mozilla::ipc::AssertIsOnBackgroundThread();
    197  mFileSystem->AssertIsOnOwningThread();
    198 
    199  if (HasError()) {
    200    return FileSystemErrorResponse(mErrorValue);
    201  }
    202 
    203  ErrorResult rv;
    204  FileSystemResponseValue value = GetSuccessRequestResult(rv);
    205  if (NS_WARN_IF(rv.Failed())) {
    206    return FileSystemErrorResponse(rv.StealNSResult());
    207  }
    208 
    209  return value;
    210 }
    211 
    212 void FileSystemTaskParentBase::SetError(const nsresult& aErrorValue) {
    213  mErrorValue = FileSystemErrorFromNsError(aErrorValue);
    214 }
    215 
    216 NS_IMETHODIMP
    217 FileSystemTaskParentBase::Run() {
    218  // This method can run in 2 different threads. Here why:
    219  // 1. We are are on the I/O thread and we call IOWork().
    220  // 2. After step 1, if we need to run some code in the main-thread, we
    221  //    dispatch a runnable to this thread.
    222  // 3. The final step happens in the PBackground thread.
    223 
    224  // If we are here, it's because the I/O work has been done, but we have
    225  // something to do on the main-thread.
    226  if (NS_IsMainThread()) {
    227    MOZ_ASSERT(MainThreadNeeded());
    228 
    229    nsresult rv = MainThreadWork();
    230    if (NS_WARN_IF(NS_FAILED(rv))) {
    231      SetError(rv);
    232    }
    233 
    234    // Let's go back to PBackground thread to finish the work.
    235    rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    236    if (NS_WARN_IF(NS_FAILED(rv))) {
    237      return rv;
    238    }
    239 
    240    return NS_OK;
    241  }
    242 
    243  // Run I/O thread tasks
    244  if (!mozilla::ipc::IsOnBackgroundThread()) {
    245    nsresult rv = IOWork();
    246    if (NS_WARN_IF(NS_FAILED(rv))) {
    247      SetError(rv);
    248    }
    249 
    250    if (MainThreadNeeded()) {
    251      rv = GetMainThreadSerialEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL);
    252      if (NS_WARN_IF(NS_FAILED(rv))) {
    253        return rv;
    254      }
    255      return NS_OK;
    256    }
    257 
    258    // Let's go back to PBackground thread to finish the work.
    259    rv = mBackgroundEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
    260    if (NS_WARN_IF(NS_FAILED(rv))) {
    261      return rv;
    262    }
    263 
    264    return NS_OK;
    265  }
    266 
    267  // If we are here, it's because the I/O work has been done and we have to
    268  // handle the result back via IPC.
    269  mozilla::ipc::AssertIsOnBackgroundThread();
    270  HandleResult();
    271  return NS_OK;
    272 }
    273 
    274 }  // namespace mozilla::dom