tor-browser

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

FileSystemHandle.cpp (10668B)


      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/. */
      6 
      7 #include "FileSystemHandle.h"
      8 
      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"
     28 
     29 namespace mozilla::dom {
     30 
     31 namespace {
     32 
     33 bool ConstructHandleMetadata(JSContext* aCx, nsIGlobalObject* aGlobal,
     34                             JSStructuredCloneReader* aReader,
     35                             const bool aDirectory,
     36                             fs::FileSystemEntryMetadata& aMetadata) {
     37  using namespace mozilla::dom::fs;
     38 
     39  EntryId entryId;
     40  if (!entryId.SetLength(32u, fallible)) {
     41    return false;
     42  }
     43 
     44  if (!JS_ReadBytes(aReader, static_cast<void*>(entryId.BeginWriting()), 32u)) {
     45    return false;
     46  }
     47 
     48  Name name;
     49  if (!StructuredCloneHolder::ReadString(aReader, name)) {
     50    return false;
     51  }
     52 
     53  mozilla::ipc::PrincipalInfo storageKey;
     54  if (!nsJSPrincipals::ReadPrincipalInfo(aReader, storageKey)) {
     55    return false;
     56  }
     57 
     58  QM_TRY_UNWRAP(auto hasEqualStorageKey,
     59                aGlobal->HasEqualStorageKey(storageKey), false);
     60 
     61  if (!hasEqualStorageKey) {
     62    LOG(("Blocking deserialization of %s due to cross-origin",
     63         NS_ConvertUTF16toUTF8(name).get()));
     64    return false;
     65  }
     66 
     67  LOG_VERBOSE(("Deserializing %s", NS_ConvertUTF16toUTF8(name).get()));
     68 
     69  aMetadata = fs::FileSystemEntryMetadata(entryId, name, aDirectory);
     70  return true;
     71 }
     72 
     73 }  // namespace
     74 
     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
     79 
     80 NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle)
     81 NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle)
     82 
     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
     93 
     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());
    103 }
    104 
    105 // WebIDL Boilerplate
    106 
    107 nsIGlobalObject* FileSystemHandle::GetParentObject() const { return mGlobal; }
    108 
    109 JSObject* FileSystemHandle::WrapObject(JSContext* aCx,
    110                                       JS::Handle<JSObject*> aGivenProto) {
    111  return FileSystemHandle_Binding::Wrap(aCx, this, aGivenProto);
    112 }
    113 
    114 // WebIDL Interface
    115 
    116 void FileSystemHandle::GetName(nsAString& aResult) {
    117  aResult = mMetadata.entryName();
    118 }
    119 
    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;
    125  }
    126 
    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);
    132 
    133  return promise.forget();
    134 }
    135 
    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()));
    140 
    141  fs::EntryId parent;  // empty means same directory
    142  return Move(parent, aName, aError);
    143 }
    144 
    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);
    151 }
    152 
    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);
    160 }
    161 
    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;
    168  }
    169 
    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);
    179  }
    180  if (aError.Failed()) {
    181    return nullptr;
    182  }
    183 
    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();
    194      },
    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()));
    199      },
    200      RefPtr(this));
    201 
    202  return promise.forget();
    203 }
    204 
    205 // [Serializable] implementation
    206 
    207 // static
    208 already_AddRefed<FileSystemHandle> FileSystemHandle::ReadStructuredClone(
    209    JSContext* aCx, nsIGlobalObject* aGlobal,
    210    JSStructuredCloneReader* aReader) {
    211  LOG_VERBOSE(("Reading File/DirectoryHandle"));
    212 
    213  uint32_t kind = UINT32_MAX;
    214 
    215  if (!JS_ReadBytes(aReader, reinterpret_cast<void*>(&kind),
    216                    sizeof(uint32_t))) {
    217    return nullptr;
    218  }
    219 
    220  if (kind == static_cast<uint32_t>(FileSystemHandleKind::Directory)) {
    221    RefPtr<FileSystemHandle> result =
    222        FileSystemHandle::ConstructDirectoryHandle(aCx, aGlobal, aReader);
    223    return result.forget();
    224  }
    225 
    226  if (kind == static_cast<uint32_t>(FileSystemHandleKind::File)) {
    227    RefPtr<FileSystemHandle> result =
    228        FileSystemHandle::ConstructFileHandle(aCx, aGlobal, aReader);
    229    return result.forget();
    230  }
    231 
    232  return nullptr;
    233 }
    234 
    235 bool FileSystemHandle::WriteStructuredClone(
    236    JSContext* aCx, JSStructuredCloneWriter* aWriter) const {
    237  LOG_VERBOSE(("Writing File/DirectoryHandle"));
    238  MOZ_ASSERT(mMetadata.entryId().Length() == 32);
    239 
    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;
    244  }
    245 
    246  if (NS_WARN_IF(!JS_WriteBytes(
    247          aWriter, static_cast<const void*>(mMetadata.entryId().get()),
    248          mMetadata.entryId().Length()))) {
    249    return false;
    250  }
    251 
    252  if (!StructuredCloneHolder::WriteString(aWriter, mMetadata.entryName())) {
    253    return false;
    254  }
    255 
    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);
    259 
    260  return nsJSPrincipals::WritePrincipalInfo(aWriter, storageKey);
    261 }
    262 
    263 // static
    264 already_AddRefed<FileSystemFileHandle> FileSystemHandle::ConstructFileHandle(
    265    JSContext* aCx, nsIGlobalObject* aGlobal,
    266    JSStructuredCloneReader* aReader) {
    267  LOG(("Reading FileHandle"));
    268 
    269  fs::FileSystemEntryMetadata metadata;
    270  if (!ConstructHandleMetadata(aCx, aGlobal, aReader, /* aDirectory */ false,
    271                               metadata)) {
    272    return nullptr;
    273  }
    274 
    275  RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
    276  if (!storageManager) {
    277    return nullptr;
    278  }
    279 
    280  // Note that the actor may not exist or may not be connected yet.
    281  RefPtr<FileSystemManager> fileSystemManager =
    282      storageManager->GetFileSystemManager();
    283 
    284  RefPtr<FileSystemFileHandle> fsHandle =
    285      new FileSystemFileHandle(aGlobal, fileSystemManager, metadata);
    286 
    287  return fsHandle.forget();
    288 }
    289 
    290 // static
    291 already_AddRefed<FileSystemDirectoryHandle>
    292 FileSystemHandle::ConstructDirectoryHandle(JSContext* aCx,
    293                                           nsIGlobalObject* aGlobal,
    294                                           JSStructuredCloneReader* aReader) {
    295  LOG(("Reading DirectoryHandle"));
    296 
    297  fs::FileSystemEntryMetadata metadata;
    298  if (!ConstructHandleMetadata(aCx, aGlobal, aReader, /* aDirectory */ true,
    299                               metadata)) {
    300    return nullptr;
    301  }
    302 
    303  RefPtr<StorageManager> storageManager = aGlobal->GetStorageManager();
    304  if (!storageManager) {
    305    return nullptr;
    306  }
    307 
    308  // Note that the actor may not exist or may not be connected yet.
    309  RefPtr<FileSystemManager> fileSystemManager =
    310      storageManager->GetFileSystemManager();
    311 
    312  RefPtr<FileSystemDirectoryHandle> fsHandle =
    313      new FileSystemDirectoryHandle(aGlobal, fileSystemManager, metadata);
    314 
    315  return fsHandle.forget();
    316 }
    317 
    318 }  // namespace mozilla::dom