Coverage Report

Created: 2018-09-25 14:53

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