FilePickerParent.cpp (9735B)
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 "FilePickerParent.h" 8 9 #include "mozilla/dom/BrowserParent.h" 10 #include "mozilla/dom/CanonicalBrowsingContext.h" 11 #include "mozilla/dom/ContentParent.h" 12 #include "mozilla/dom/Document.h" 13 #include "mozilla/dom/Element.h" 14 #include "mozilla/dom/FileBlobImpl.h" 15 #include "mozilla/dom/FileSystemSecurity.h" 16 #include "mozilla/dom/IPCBlobUtils.h" 17 #include "nsComponentManagerUtils.h" 18 #include "nsIFile.h" 19 #include "nsISimpleEnumerator.h" 20 #include "nsNetCID.h" 21 22 using namespace mozilla::dom; 23 24 NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback, 25 nsIFilePickerShownCallback); 26 27 NS_IMETHODIMP 28 FilePickerParent::FilePickerShownCallback::Done( 29 nsIFilePicker::ResultCode aResult) { 30 if (mFilePickerParent) { 31 mFilePickerParent->Done(aResult); 32 } 33 return NS_OK; 34 } 35 36 void FilePickerParent::FilePickerShownCallback::Destroy() { 37 mFilePickerParent = nullptr; 38 } 39 40 FilePickerParent::~FilePickerParent() = default; 41 42 // We run code in three places: 43 // 1. The main thread calls Dispatch() to start the runnable. 44 // 2. The stream transport thread stat()s the file in Run() and then dispatches 45 // the same runnable on the main thread. 46 // 3. The main thread sends the results over IPC. 47 FilePickerParent::IORunnable::IORunnable( 48 FilePickerParent* aFPParent, nsTArray<nsCOMPtr<nsIFile>>&& aFiles, 49 nsTArray<RefPtr<BlobImpl>>&& aFilesInWebKitDirectory, bool aIsDirectory) 50 : mozilla::Runnable("dom::FilePickerParent::IORunnable"), 51 mFilePickerParent(aFPParent), 52 mFiles(std::move(aFiles)), 53 mFilesInWebKitDirectory(std::move(aFilesInWebKitDirectory)), 54 mIsDirectory(aIsDirectory) { 55 MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1); 56 } 57 58 bool FilePickerParent::IORunnable::Dispatch() { 59 MOZ_ASSERT(NS_IsMainThread()); 60 61 mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); 62 if (!mEventTarget) { 63 return false; 64 } 65 66 nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL); 67 return NS_SUCCEEDED(rv); 68 } 69 70 NS_IMETHODIMP 71 FilePickerParent::IORunnable::Run() { 72 // If we're on the main thread, then that means we're done. Just send the 73 // results. 74 if (NS_IsMainThread()) { 75 if (mFilePickerParent) { 76 mFilePickerParent->SendFilesOrDirectories(mResults, 77 mFilesInWebKitDirectory); 78 } 79 return NS_OK; 80 } 81 82 // We're not on the main thread, so do the IO. 83 84 for (uint32_t i = 0; i < mFiles.Length(); ++i) { 85 if (mIsDirectory) { 86 nsAutoString path; 87 nsresult rv = mFiles[i]->GetPath(path); 88 if (NS_WARN_IF(NS_FAILED(rv))) { 89 continue; 90 } 91 92 BlobImplOrString* data = mResults.AppendElement(); 93 data->mType = BlobImplOrString::eDirectoryPath; 94 data->mDirectoryPath = path; 95 continue; 96 } 97 98 RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mFiles[i]); 99 100 ErrorResult error; 101 blobImpl->GetSize(error); 102 if (NS_WARN_IF(error.Failed())) { 103 error.SuppressException(); 104 continue; 105 } 106 107 blobImpl->GetLastModified(error); 108 if (NS_WARN_IF(error.Failed())) { 109 error.SuppressException(); 110 continue; 111 } 112 113 BlobImplOrString* data = mResults.AppendElement(); 114 data->mType = BlobImplOrString::eBlobImpl; 115 data->mBlobImpl = blobImpl; 116 } 117 118 // Dispatch ourselves back on the main thread. 119 if (NS_FAILED(NS_DispatchToMainThread(this))) { 120 // It's hard to see how we can recover gracefully in this case. The child 121 // process is waiting for an IPC, but that can only happen on the main 122 // thread. 123 MOZ_CRASH(); 124 } 125 126 return NS_OK; 127 } 128 129 void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; } 130 131 void FilePickerParent::SendFilesOrDirectories( 132 const nsTArray<BlobImplOrString>& aData, 133 const nsTArray<RefPtr<BlobImpl>>& aFilesInWebKitDirectory) { 134 ContentParent* parent = BrowserParent::GetFrom(Manager())->Manager(); 135 136 if (mMode == nsIFilePicker::modeGetFolder) { 137 MOZ_ASSERT(aData.Length() <= 1); 138 if (aData.IsEmpty()) { 139 (void)Send__delete__(this, void_t(), mResult); 140 return; 141 } 142 143 MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath); 144 145 // Let's inform the security singleton about the given access of this tab on 146 // this directory path. 147 RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate(); 148 fss->GrantAccessToContentProcess(parent->ChildID(), 149 aData[0].mDirectoryPath); 150 151 nsTArray<IPCBlob> ipcBlobs; 152 for (const auto& blob : aFilesInWebKitDirectory) { 153 IPCBlob ipcBlob; 154 155 nsresult rv = IPCBlobUtils::Serialize(blob, ipcBlob); 156 if (NS_WARN_IF(NS_FAILED(rv))) { 157 break; 158 } 159 ipcBlobs.AppendElement(ipcBlob); 160 } 161 162 InputDirectory input; 163 input.directoryPath() = aData[0].mDirectoryPath; 164 input.blobsInWebKitDirectory() = std::move(ipcBlobs); 165 (void)Send__delete__(this, input, mResult); 166 return; 167 } 168 169 nsTArray<IPCBlob> ipcBlobs; 170 171 for (unsigned i = 0; i < aData.Length(); i++) { 172 IPCBlob ipcBlob; 173 174 MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl); 175 nsresult rv = IPCBlobUtils::Serialize(aData[i].mBlobImpl, ipcBlob); 176 if (NS_WARN_IF(NS_FAILED(rv))) { 177 break; 178 } 179 180 ipcBlobs.AppendElement(ipcBlob); 181 } 182 183 InputBlobs inblobs; 184 inblobs.blobs() = std::move(ipcBlobs); 185 186 (void)Send__delete__(this, inblobs, mResult); 187 } 188 189 void FilePickerParent::Done(nsIFilePicker::ResultCode aResult) { 190 mResult = aResult; 191 192 if (mResult != nsIFilePicker::returnOK) { 193 (void)Send__delete__(this, void_t(), mResult); 194 return; 195 } 196 197 nsTArray<nsCOMPtr<nsIFile>> files; 198 if (mMode == nsIFilePicker::modeOpenMultiple) { 199 nsCOMPtr<nsISimpleEnumerator> iter; 200 NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter))); 201 202 nsCOMPtr<nsISupports> supports; 203 bool loop = true; 204 while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { 205 iter->GetNext(getter_AddRefs(supports)); 206 if (supports) { 207 nsCOMPtr<nsIFile> file = do_QueryInterface(supports); 208 MOZ_ASSERT(file); 209 files.AppendElement(file); 210 } 211 } 212 } else { 213 nsCOMPtr<nsIFile> file; 214 mFilePicker->GetFile(getter_AddRefs(file)); 215 if (file) { 216 files.AppendElement(file); 217 } 218 } 219 220 if (files.IsEmpty()) { 221 (void)Send__delete__(this, void_t(), mResult); 222 return; 223 } 224 225 nsTArray<RefPtr<BlobImpl>> blobsInWebKitDirectory; 226 227 #ifdef MOZ_WIDGET_ANDROID 228 if (mMode == nsIFilePicker::modeGetFolder) { 229 nsCOMPtr<nsISimpleEnumerator> iter; 230 if (NS_SUCCEEDED( 231 mFilePicker->GetDomFilesInWebKitDirectory(getter_AddRefs(iter)))) { 232 nsCOMPtr<nsISupports> supports; 233 234 bool loop = true; 235 while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) { 236 iter->GetNext(getter_AddRefs(supports)); 237 if (supports) { 238 RefPtr<BlobImpl> file = static_cast<File*>(supports.get())->Impl(); 239 MOZ_ASSERT(file); 240 blobsInWebKitDirectory.AppendElement(file); 241 } 242 } 243 } 244 } 245 #endif 246 247 MOZ_ASSERT(!mRunnable); 248 mRunnable = 249 new IORunnable(this, std::move(files), std::move(blobsInWebKitDirectory), 250 mMode == nsIFilePicker::modeGetFolder); 251 252 // Dispatch to background thread to do I/O: 253 if (!mRunnable->Dispatch()) { 254 (void)Send__delete__(this, void_t(), nsIFilePicker::returnCancel); 255 } 256 } 257 258 bool FilePickerParent::CreateFilePicker() { 259 if (!mBrowsingContext) { 260 return false; 261 } 262 263 mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1"); 264 265 if (!mFilePicker) { 266 return false; 267 } 268 269 return NS_SUCCEEDED(mFilePicker->Init(mBrowsingContext, mTitle, mMode)); 270 } 271 272 mozilla::ipc::IPCResult FilePickerParent::RecvOpen( 273 const int16_t& aSelectedType, const bool& aAddToRecentDocs, 274 const nsString& aDefaultFile, const nsString& aDefaultExtension, 275 nsTArray<nsString>&& aFilters, nsTArray<nsString>&& aFilterNames, 276 nsTArray<nsString>&& aRawFilters, const nsString& aDisplayDirectory, 277 const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel, 278 const nsIFilePicker::CaptureTarget& aCapture) { 279 if (!CreateFilePicker()) { 280 (void)Send__delete__(this, void_t(), nsIFilePicker::returnCancel); 281 return IPC_OK(); 282 } 283 284 mFilePicker->SetAddToRecentDocs(aAddToRecentDocs); 285 286 for (uint32_t i = 0; i < aFilters.Length(); ++i) { 287 mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]); 288 } 289 290 for (uint32_t i = 0; i < aRawFilters.Length(); ++i) { 291 mFilePicker->AppendRawFilter(aRawFilters[i]); 292 } 293 294 mFilePicker->SetDefaultString(aDefaultFile); 295 mFilePicker->SetDefaultExtension(aDefaultExtension); 296 mFilePicker->SetFilterIndex(aSelectedType); 297 mFilePicker->SetOkButtonLabel(aOkButtonLabel); 298 mFilePicker->SetCapture(aCapture); 299 300 if (!aDisplayDirectory.IsEmpty()) { 301 nsCOMPtr<nsIFile> localFile; 302 if (NS_SUCCEEDED( 303 NS_NewLocalFile(aDisplayDirectory, getter_AddRefs(localFile)))) { 304 mFilePicker->SetDisplayDirectory(localFile); 305 } 306 } else if (!aDisplaySpecialDirectory.IsEmpty()) { 307 mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory); 308 } 309 310 MOZ_ASSERT(!mCallback); 311 mCallback = new FilePickerShownCallback(this); 312 313 mFilePicker->Open(mCallback); 314 return IPC_OK(); 315 } 316 317 void FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) { 318 if (mCallback) { 319 mCallback->Destroy(); 320 mCallback = nullptr; 321 } 322 if (mRunnable) { 323 mRunnable->Destroy(); 324 mRunnable = nullptr; 325 } 326 }