Line data Source code
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 "FormData.h"
8 : #include "nsIVariant.h"
9 : #include "nsIInputStream.h"
10 : #include "mozilla/dom/File.h"
11 : #include "mozilla/dom/Directory.h"
12 : #include "mozilla/dom/HTMLFormElement.h"
13 : #include "mozilla/Encoding.h"
14 :
15 : #include "MultipartBlobImpl.h"
16 :
17 : using namespace mozilla;
18 : using namespace mozilla::dom;
19 :
20 0 : FormData::FormData(nsISupports* aOwner)
21 : : HTMLFormSubmission(UTF_8_ENCODING, nullptr)
22 0 : , mOwner(aOwner)
23 : {
24 0 : }
25 :
26 : namespace {
27 :
28 : already_AddRefed<File>
29 0 : GetOrCreateFileCalledBlob(Blob& aBlob, ErrorResult& aRv)
30 : {
31 : // If this is file, we can just use it
32 0 : RefPtr<File> file = aBlob.ToFile();
33 0 : if (file) {
34 0 : return file.forget();
35 : }
36 :
37 : // Forcing 'blob' as filename
38 0 : file = aBlob.ToFile(NS_LITERAL_STRING("blob"), aRv);
39 0 : if (NS_WARN_IF(aRv.Failed())) {
40 0 : return nullptr;
41 : }
42 :
43 0 : return file.forget();
44 : }
45 :
46 : already_AddRefed<File>
47 0 : GetBlobForFormDataStorage(Blob& aBlob, const Optional<nsAString>& aFilename,
48 : ErrorResult& aRv)
49 : {
50 : // Forcing a filename
51 0 : if (aFilename.WasPassed()) {
52 0 : RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
53 0 : if (NS_WARN_IF(aRv.Failed())) {
54 0 : return nullptr;
55 : }
56 :
57 0 : return file.forget();
58 : }
59 :
60 0 : return GetOrCreateFileCalledBlob(aBlob, aRv);
61 : }
62 :
63 : } // namespace
64 :
65 : // -------------------------------------------------------------------------
66 : // nsISupports
67 :
68 : NS_IMPL_CYCLE_COLLECTION_CLASS(FormData)
69 :
70 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FormData)
71 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
72 :
73 0 : for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
74 0 : ImplCycleCollectionUnlink(tmp->mFormData[i].value);
75 : }
76 :
77 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
78 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
79 :
80 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FormData)
81 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
82 :
83 0 : for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
84 0 : ImplCycleCollectionTraverse(cb, tmp->mFormData[i].value,
85 0 : "mFormData[i].GetAsBlob()", 0);
86 : }
87 :
88 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
89 :
90 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FormData)
91 :
92 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(FormData)
93 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(FormData)
94 :
95 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FormData)
96 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
97 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMFormData)
98 0 : NS_INTERFACE_MAP_ENTRY(nsIXHRSendable)
99 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFormData)
100 0 : NS_INTERFACE_MAP_END
101 :
102 : // -------------------------------------------------------------------------
103 : // HTMLFormSubmission
104 : nsresult
105 0 : FormData::GetEncodedSubmission(nsIURI* aURI,
106 : nsIInputStream** aPostDataStream)
107 : {
108 0 : NS_NOTREACHED("Shouldn't call FormData::GetEncodedSubmission");
109 0 : return NS_OK;
110 : }
111 :
112 : void
113 0 : FormData::Append(const nsAString& aName, const nsAString& aValue,
114 : ErrorResult& aRv)
115 : {
116 0 : AddNameValuePair(aName, aValue);
117 0 : }
118 :
119 : void
120 0 : FormData::Append(const nsAString& aName, Blob& aBlob,
121 : const Optional<nsAString>& aFilename,
122 : ErrorResult& aRv)
123 : {
124 0 : RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
125 0 : if (NS_WARN_IF(aRv.Failed())) {
126 0 : return;
127 : }
128 :
129 0 : AddNameBlobOrNullPair(aName, file);
130 : }
131 :
132 : void
133 0 : FormData::Append(const nsAString& aName, Directory* aDirectory)
134 : {
135 0 : AddNameDirectoryPair(aName, aDirectory);
136 0 : }
137 :
138 : void
139 0 : FormData::Delete(const nsAString& aName)
140 : {
141 : // We have to use this slightly awkward for loop since uint32_t >= 0 is an
142 : // error for being always true.
143 0 : for (uint32_t i = mFormData.Length(); i-- > 0; ) {
144 0 : if (aName.Equals(mFormData[i].name)) {
145 0 : mFormData.RemoveElementAt(i);
146 : }
147 : }
148 0 : }
149 :
150 : void
151 0 : FormData::Get(const nsAString& aName,
152 : Nullable<OwningBlobOrDirectoryOrUSVString>& aOutValue)
153 : {
154 0 : for (uint32_t i = 0; i < mFormData.Length(); ++i) {
155 0 : if (aName.Equals(mFormData[i].name)) {
156 0 : aOutValue.SetValue() = mFormData[i].value;
157 0 : return;
158 : }
159 : }
160 :
161 0 : aOutValue.SetNull();
162 : }
163 :
164 : void
165 0 : FormData::GetAll(const nsAString& aName,
166 : nsTArray<OwningBlobOrDirectoryOrUSVString>& aValues)
167 : {
168 0 : for (uint32_t i = 0; i < mFormData.Length(); ++i) {
169 0 : if (aName.Equals(mFormData[i].name)) {
170 0 : OwningBlobOrDirectoryOrUSVString* element = aValues.AppendElement();
171 0 : *element = mFormData[i].value;
172 : }
173 : }
174 0 : }
175 :
176 : bool
177 0 : FormData::Has(const nsAString& aName)
178 : {
179 0 : for (uint32_t i = 0; i < mFormData.Length(); ++i) {
180 0 : if (aName.Equals(mFormData[i].name)) {
181 0 : return true;
182 : }
183 : }
184 :
185 0 : return false;
186 : }
187 :
188 : nsresult
189 0 : FormData::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob)
190 : {
191 0 : RefPtr<File> file;
192 :
193 0 : if (!aBlob) {
194 0 : FormDataTuple* data = mFormData.AppendElement();
195 0 : SetNameValuePair(data, aName, EmptyString(), true /* aWasNullBlob */);
196 0 : return NS_OK;
197 : }
198 :
199 0 : ErrorResult rv;
200 0 : file = GetOrCreateFileCalledBlob(*aBlob, rv);
201 0 : if (NS_WARN_IF(rv.Failed())) {
202 0 : return rv.StealNSResult();
203 : }
204 :
205 0 : FormDataTuple* data = mFormData.AppendElement();
206 0 : SetNameFilePair(data, aName, file);
207 0 : return NS_OK;
208 : }
209 :
210 : nsresult
211 0 : FormData::AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory)
212 : {
213 0 : MOZ_ASSERT(aDirectory);
214 :
215 0 : FormDataTuple* data = mFormData.AppendElement();
216 0 : SetNameDirectoryPair(data, aName, aDirectory);
217 0 : return NS_OK;
218 : }
219 :
220 : FormData::FormDataTuple*
221 0 : FormData::RemoveAllOthersAndGetFirstFormDataTuple(const nsAString& aName)
222 : {
223 0 : FormDataTuple* lastFoundTuple = nullptr;
224 0 : uint32_t lastFoundIndex = mFormData.Length();
225 : // We have to use this slightly awkward for loop since uint32_t >= 0 is an
226 : // error for being always true.
227 0 : for (uint32_t i = mFormData.Length(); i-- > 0; ) {
228 0 : if (aName.Equals(mFormData[i].name)) {
229 0 : if (lastFoundTuple) {
230 : // The one we found earlier was not the first one, we can remove it.
231 0 : mFormData.RemoveElementAt(lastFoundIndex);
232 : }
233 :
234 0 : lastFoundTuple = &mFormData[i];
235 0 : lastFoundIndex = i;
236 : }
237 : }
238 :
239 0 : return lastFoundTuple;
240 : }
241 :
242 : void
243 0 : FormData::Set(const nsAString& aName, Blob& aBlob,
244 : const Optional<nsAString>& aFilename,
245 : ErrorResult& aRv)
246 : {
247 0 : FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
248 0 : if (tuple) {
249 0 : RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
250 0 : if (NS_WARN_IF(aRv.Failed())) {
251 0 : return;
252 : }
253 :
254 0 : SetNameFilePair(tuple, aName, file);
255 : } else {
256 0 : Append(aName, aBlob, aFilename, aRv);
257 : }
258 : }
259 :
260 : void
261 0 : FormData::Set(const nsAString& aName, const nsAString& aValue,
262 : ErrorResult& aRv)
263 : {
264 0 : FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
265 0 : if (tuple) {
266 0 : SetNameValuePair(tuple, aName, aValue);
267 : } else {
268 0 : Append(aName, aValue, aRv);
269 : }
270 0 : }
271 :
272 : uint32_t
273 0 : FormData::GetIterableLength() const
274 : {
275 0 : return mFormData.Length();
276 : }
277 :
278 : const nsAString&
279 0 : FormData::GetKeyAtIndex(uint32_t aIndex) const
280 : {
281 0 : MOZ_ASSERT(aIndex < mFormData.Length());
282 0 : return mFormData[aIndex].name;
283 : }
284 :
285 : const OwningBlobOrDirectoryOrUSVString&
286 0 : FormData::GetValueAtIndex(uint32_t aIndex) const
287 : {
288 0 : MOZ_ASSERT(aIndex < mFormData.Length());
289 0 : return mFormData[aIndex].value;
290 : }
291 :
292 : void
293 0 : FormData::SetNameValuePair(FormDataTuple* aData,
294 : const nsAString& aName,
295 : const nsAString& aValue,
296 : bool aWasNullBlob)
297 : {
298 0 : MOZ_ASSERT(aData);
299 0 : aData->name = aName;
300 0 : aData->wasNullBlob = aWasNullBlob;
301 0 : aData->value.SetAsUSVString() = aValue;
302 0 : }
303 :
304 : void
305 0 : FormData::SetNameFilePair(FormDataTuple* aData,
306 : const nsAString& aName,
307 : File* aFile)
308 : {
309 0 : MOZ_ASSERT(aData);
310 0 : MOZ_ASSERT(aFile);
311 :
312 0 : aData->name = aName;
313 0 : aData->wasNullBlob = false;
314 0 : aData->value.SetAsBlob() = aFile;
315 0 : }
316 :
317 : void
318 0 : FormData::SetNameDirectoryPair(FormDataTuple* aData,
319 : const nsAString& aName,
320 : Directory* aDirectory)
321 : {
322 0 : MOZ_ASSERT(aData);
323 0 : MOZ_ASSERT(aDirectory);
324 :
325 0 : aData->name = aName;
326 0 : aData->wasNullBlob = false;
327 0 : aData->value.SetAsDirectory() = aDirectory;
328 0 : }
329 :
330 : // -------------------------------------------------------------------------
331 : // nsIDOMFormData
332 :
333 : NS_IMETHODIMP
334 0 : FormData::Append(const nsAString& aName, nsIVariant* aValue)
335 : {
336 : uint16_t dataType;
337 0 : nsresult rv = aValue->GetDataType(&dataType);
338 0 : NS_ENSURE_SUCCESS(rv, rv);
339 :
340 0 : if (dataType == nsIDataType::VTYPE_INTERFACE ||
341 0 : dataType == nsIDataType::VTYPE_INTERFACE_IS) {
342 0 : nsCOMPtr<nsISupports> supports;
343 : nsID *iid;
344 0 : rv = aValue->GetAsInterface(&iid, getter_AddRefs(supports));
345 0 : NS_ENSURE_SUCCESS(rv, rv);
346 :
347 0 : free(iid);
348 :
349 0 : nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(supports);
350 0 : RefPtr<Blob> blob = static_cast<Blob*>(domBlob.get());
351 0 : if (domBlob) {
352 0 : Optional<nsAString> temp;
353 0 : ErrorResult rv;
354 0 : Append(aName, *blob, temp, rv);
355 0 : if (NS_WARN_IF(rv.Failed())) {
356 0 : return rv.StealNSResult();
357 : }
358 :
359 0 : return NS_OK;
360 : }
361 : }
362 :
363 0 : char16_t* stringData = nullptr;
364 0 : uint32_t stringLen = 0;
365 0 : rv = aValue->GetAsWStringWithSize(&stringLen, &stringData);
366 0 : NS_ENSURE_SUCCESS(rv, rv);
367 :
368 0 : nsString valAsString;
369 0 : valAsString.Adopt(stringData, stringLen);
370 :
371 0 : ErrorResult error;
372 0 : Append(aName, valAsString, error);
373 0 : if (NS_WARN_IF(error.Failed())) {
374 0 : return error.StealNSResult();
375 : }
376 :
377 0 : return NS_OK;
378 : }
379 :
380 : /* virtual */ JSObject*
381 0 : FormData::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
382 : {
383 0 : return FormDataBinding::Wrap(aCx, this, aGivenProto);
384 : }
385 :
386 : /* static */ already_AddRefed<FormData>
387 0 : FormData::Constructor(const GlobalObject& aGlobal,
388 : const Optional<NonNull<HTMLFormElement> >& aFormElement,
389 : ErrorResult& aRv)
390 : {
391 0 : RefPtr<FormData> formData = new FormData(aGlobal.GetAsSupports());
392 0 : if (aFormElement.WasPassed()) {
393 0 : aRv = aFormElement.Value().WalkFormElements(formData);
394 : }
395 0 : return formData.forget();
396 : }
397 :
398 : // -------------------------------------------------------------------------
399 : // nsIXHRSendable
400 :
401 : NS_IMETHODIMP
402 0 : FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
403 : nsACString& aContentTypeWithCharset, nsACString& aCharset)
404 : {
405 0 : FSMultipartFormData fs(UTF_8_ENCODING, nullptr);
406 :
407 0 : for (uint32_t i = 0; i < mFormData.Length(); ++i) {
408 0 : if (mFormData[i].wasNullBlob) {
409 0 : MOZ_ASSERT(mFormData[i].value.IsUSVString());
410 0 : fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
411 0 : } else if (mFormData[i].value.IsUSVString()) {
412 0 : fs.AddNameValuePair(mFormData[i].name,
413 0 : mFormData[i].value.GetAsUSVString());
414 0 : } else if (mFormData[i].value.IsBlob()) {
415 0 : fs.AddNameBlobOrNullPair(mFormData[i].name,
416 0 : mFormData[i].value.GetAsBlob());
417 : } else {
418 0 : MOZ_ASSERT(mFormData[i].value.IsDirectory());
419 0 : fs.AddNameDirectoryPair(mFormData[i].name,
420 0 : mFormData[i].value.GetAsDirectory());
421 : }
422 : }
423 :
424 0 : fs.GetContentType(aContentTypeWithCharset);
425 0 : aCharset.Truncate();
426 0 : *aContentLength = 0;
427 0 : NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
428 :
429 0 : return NS_OK;
430 : }
|