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