1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  *
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 "nsFilePickerProxy.h"
8 #include "nsComponentManagerUtils.h"
9 #include "nsIFile.h"
10 #include "nsSimpleEnumerator.h"
11 #include "mozilla/dom/BlobImpl.h"
12 #include "mozilla/dom/Directory.h"
13 #include "mozilla/dom/File.h"
14 #include "mozilla/dom/BrowserChild.h"
15 #include "mozilla/dom/IPCBlobUtils.h"
16 
17 using namespace mozilla::dom;
18 
NS_IMPL_ISUPPORTS(nsFilePickerProxy,nsIFilePicker)19 NS_IMPL_ISUPPORTS(nsFilePickerProxy, nsIFilePicker)
20 
21 nsFilePickerProxy::nsFilePickerProxy()
22     : mSelectedType(0), mCapture(captureNone), mIPCActive(false) {}
23 
24 nsFilePickerProxy::~nsFilePickerProxy() = default;
25 
26 NS_IMETHODIMP
Init(mozIDOMWindowProxy * aParent,const nsAString & aTitle,int16_t aMode)27 nsFilePickerProxy::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
28                         int16_t aMode) {
29   BrowserChild* browserChild = BrowserChild::GetFrom(aParent);
30   if (!browserChild) {
31     return NS_ERROR_FAILURE;
32   }
33 
34   mParent = nsPIDOMWindowOuter::From(aParent);
35 
36   mMode = aMode;
37 
38   NS_ADDREF_THIS();
39   browserChild->SendPFilePickerConstructor(this, nsString(aTitle), aMode);
40 
41   mIPCActive = true;
42   return NS_OK;
43 }
44 
InitNative(nsIWidget * aParent,const nsAString & aTitle)45 void nsFilePickerProxy::InitNative(nsIWidget* aParent,
46                                    const nsAString& aTitle) {}
47 
48 NS_IMETHODIMP
AppendFilter(const nsAString & aTitle,const nsAString & aFilter)49 nsFilePickerProxy::AppendFilter(const nsAString& aTitle,
50                                 const nsAString& aFilter) {
51   mFilterNames.AppendElement(aTitle);
52   mFilters.AppendElement(aFilter);
53   return NS_OK;
54 }
55 
56 NS_IMETHODIMP
GetCapture(int16_t * aCapture)57 nsFilePickerProxy::GetCapture(int16_t* aCapture) {
58   *aCapture = mCapture;
59   return NS_OK;
60 }
61 
62 NS_IMETHODIMP
SetCapture(int16_t aCapture)63 nsFilePickerProxy::SetCapture(int16_t aCapture) {
64   mCapture = aCapture;
65   return NS_OK;
66 }
67 
68 NS_IMETHODIMP
GetDefaultString(nsAString & aDefaultString)69 nsFilePickerProxy::GetDefaultString(nsAString& aDefaultString) {
70   aDefaultString = mDefault;
71   return NS_OK;
72 }
73 
74 NS_IMETHODIMP
SetDefaultString(const nsAString & aDefaultString)75 nsFilePickerProxy::SetDefaultString(const nsAString& aDefaultString) {
76   mDefault = aDefaultString;
77   return NS_OK;
78 }
79 
80 NS_IMETHODIMP
GetDefaultExtension(nsAString & aDefaultExtension)81 nsFilePickerProxy::GetDefaultExtension(nsAString& aDefaultExtension) {
82   aDefaultExtension = mDefaultExtension;
83   return NS_OK;
84 }
85 
86 NS_IMETHODIMP
SetDefaultExtension(const nsAString & aDefaultExtension)87 nsFilePickerProxy::SetDefaultExtension(const nsAString& aDefaultExtension) {
88   mDefaultExtension = aDefaultExtension;
89   return NS_OK;
90 }
91 
92 NS_IMETHODIMP
GetFilterIndex(int32_t * aFilterIndex)93 nsFilePickerProxy::GetFilterIndex(int32_t* aFilterIndex) {
94   *aFilterIndex = mSelectedType;
95   return NS_OK;
96 }
97 
98 NS_IMETHODIMP
SetFilterIndex(int32_t aFilterIndex)99 nsFilePickerProxy::SetFilterIndex(int32_t aFilterIndex) {
100   mSelectedType = aFilterIndex;
101   return NS_OK;
102 }
103 
104 NS_IMETHODIMP
GetFile(nsIFile ** aFile)105 nsFilePickerProxy::GetFile(nsIFile** aFile) {
106   MOZ_ASSERT(false, "GetFile is unimplemented; use GetDomFileOrDirectory");
107   return NS_ERROR_FAILURE;
108 }
109 
110 NS_IMETHODIMP
GetFileURL(nsIURI ** aFileURL)111 nsFilePickerProxy::GetFileURL(nsIURI** aFileURL) {
112   MOZ_ASSERT(false, "GetFileURL is unimplemented; use GetDomFileOrDirectory");
113   return NS_ERROR_FAILURE;
114 }
115 
116 NS_IMETHODIMP
GetFiles(nsISimpleEnumerator ** aFiles)117 nsFilePickerProxy::GetFiles(nsISimpleEnumerator** aFiles) {
118   MOZ_ASSERT(false,
119              "GetFiles is unimplemented; use GetDomFileOrDirectoryEnumerator");
120   return NS_ERROR_FAILURE;
121 }
122 
Show(int16_t * aReturn)123 nsresult nsFilePickerProxy::Show(int16_t* aReturn) {
124   MOZ_ASSERT(false, "Show is unimplemented; use Open");
125   return NS_ERROR_NOT_IMPLEMENTED;
126 }
127 
128 NS_IMETHODIMP
Open(nsIFilePickerShownCallback * aCallback)129 nsFilePickerProxy::Open(nsIFilePickerShownCallback* aCallback) {
130   mCallback = aCallback;
131 
132   nsString displayDirectory;
133   if (mDisplayDirectory) {
134     mDisplayDirectory->GetPath(displayDirectory);
135   }
136 
137   if (!mIPCActive) {
138     return NS_ERROR_FAILURE;
139   }
140 
141   SendOpen(mSelectedType, mAddToRecentDocs, mDefault, mDefaultExtension,
142            mFilters, mFilterNames, mRawFilters, displayDirectory,
143            mDisplaySpecialDirectory, mOkButtonLabel, mCapture);
144 
145   return NS_OK;
146 }
147 
Recv__delete__(const MaybeInputData & aData,const int16_t & aResult)148 mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__(
149     const MaybeInputData& aData, const int16_t& aResult) {
150   nsPIDOMWindowInner* inner =
151       mParent ? mParent->GetCurrentInnerWindow() : nullptr;
152 
153   if (NS_WARN_IF(!inner)) {
154     return IPC_OK();
155   }
156 
157   if (aData.type() == MaybeInputData::TInputBlobs) {
158     const nsTArray<IPCBlob>& blobs = aData.get_InputBlobs().blobs();
159     for (uint32_t i = 0; i < blobs.Length(); ++i) {
160       RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]);
161       NS_ENSURE_TRUE(blobImpl, IPC_OK());
162 
163       if (!blobImpl->IsFile()) {
164         return IPC_OK();
165       }
166 
167       RefPtr<File> file = File::Create(inner->AsGlobal(), blobImpl);
168       if (NS_WARN_IF(!file)) {
169         return IPC_OK();
170       }
171 
172       OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
173       element->SetAsFile() = file;
174     }
175   } else if (aData.type() == MaybeInputData::TInputDirectory) {
176     nsCOMPtr<nsIFile> file;
177     const nsAString& path(aData.get_InputDirectory().directoryPath());
178     nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file));
179     if (NS_WARN_IF(NS_FAILED(rv))) {
180       return IPC_OK();
181     }
182 
183     RefPtr<Directory> directory = Directory::Create(inner->AsGlobal(), file);
184     MOZ_ASSERT(directory);
185 
186     OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
187     element->SetAsDirectory() = directory;
188   }
189 
190   if (mCallback) {
191     mCallback->Done(aResult);
192     mCallback = nullptr;
193   }
194 
195   return IPC_OK();
196 }
197 
198 NS_IMETHODIMP
GetDomFileOrDirectory(nsISupports ** aValue)199 nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue) {
200   *aValue = nullptr;
201   if (mFilesOrDirectories.IsEmpty()) {
202     return NS_OK;
203   }
204 
205   MOZ_ASSERT(mFilesOrDirectories.Length() == 1);
206 
207   if (mFilesOrDirectories[0].IsFile()) {
208     nsCOMPtr<nsISupports> blob = ToSupports(mFilesOrDirectories[0].GetAsFile());
209     blob.forget(aValue);
210     return NS_OK;
211   }
212 
213   MOZ_ASSERT(mFilesOrDirectories[0].IsDirectory());
214   RefPtr<Directory> directory = mFilesOrDirectories[0].GetAsDirectory();
215   directory.forget(aValue);
216   return NS_OK;
217 }
218 
219 namespace {
220 
221 class SimpleEnumerator final : public nsSimpleEnumerator {
222  public:
SimpleEnumerator(const nsTArray<OwningFileOrDirectory> & aFilesOrDirectories)223   explicit SimpleEnumerator(
224       const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
225       : mFilesOrDirectories(aFilesOrDirectories.Clone()), mIndex(0) {}
226 
227   NS_IMETHOD
HasMoreElements(bool * aRetvalue)228   HasMoreElements(bool* aRetvalue) override {
229     MOZ_ASSERT(aRetvalue);
230     *aRetvalue = mIndex < mFilesOrDirectories.Length();
231     return NS_OK;
232   }
233 
234   NS_IMETHOD
GetNext(nsISupports ** aValue)235   GetNext(nsISupports** aValue) override {
236     NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE);
237 
238     uint32_t index = mIndex++;
239 
240     if (mFilesOrDirectories[index].IsFile()) {
241       nsCOMPtr<nsISupports> blob =
242           ToSupports(mFilesOrDirectories[index].GetAsFile());
243       blob.forget(aValue);
244       return NS_OK;
245     }
246 
247     MOZ_ASSERT(mFilesOrDirectories[index].IsDirectory());
248     RefPtr<Directory> directory = mFilesOrDirectories[index].GetAsDirectory();
249     directory.forget(aValue);
250     return NS_OK;
251   }
252 
253  private:
254   nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
255   uint32_t mIndex;
256 };
257 
258 }  // namespace
259 
260 NS_IMETHODIMP
GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator ** aDomfiles)261 nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(
262     nsISimpleEnumerator** aDomfiles) {
263   RefPtr<SimpleEnumerator> enumerator =
264       new SimpleEnumerator(mFilesOrDirectories);
265   enumerator.forget(aDomfiles);
266   return NS_OK;
267 }
268 
ActorDestroy(ActorDestroyReason aWhy)269 void nsFilePickerProxy::ActorDestroy(ActorDestroyReason aWhy) {
270   mIPCActive = false;
271 
272   if (mCallback) {
273     mCallback->Done(nsIFilePicker::returnCancel);
274     mCallback = nullptr;
275   }
276 }
277