Bug 1700051: part 35) Reduce accessibility of `mSoftText.mDOMMapping` to `private...
[gecko.git] / dom / base / FormData.cpp
blobf95b95c9020c0c042a250d6b98edef593c2549a1
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/. */
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"
14 #include "MultipartBlobImpl.h"
16 using namespace mozilla;
17 using namespace mozilla::dom;
19 FormData::FormData(nsISupports* aOwner, NotNull<const Encoding*> aEncoding,
20 Element* aSubmitter)
21 : HTMLFormSubmission(nullptr, u""_ns, aEncoding, aSubmitter),
22 mOwner(aOwner) {}
24 FormData::FormData(const FormData& aFormData)
25 : HTMLFormSubmission(aFormData.mActionURL, aFormData.mTarget,
26 aFormData.mEncoding, aFormData.mSubmitter) {
27 mOwner = aFormData.mOwner;
28 mFormData = aFormData.mFormData.Clone();
31 namespace {
33 already_AddRefed<File> GetOrCreateFileCalledBlob(Blob& aBlob,
34 ErrorResult& aRv) {
35 // If this is file, we can just use it
36 RefPtr<File> file = aBlob.ToFile();
37 if (file) {
38 return file.forget();
41 // Forcing 'blob' as filename
42 file = aBlob.ToFile(u"blob"_ns, aRv);
43 if (NS_WARN_IF(aRv.Failed())) {
44 return nullptr;
47 return file.forget();
50 already_AddRefed<File> GetBlobForFormDataStorage(
51 Blob& aBlob, const Optional<nsAString>& aFilename, ErrorResult& aRv) {
52 // Forcing a filename
53 if (aFilename.WasPassed()) {
54 RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
55 if (NS_WARN_IF(aRv.Failed())) {
56 return nullptr;
59 return file.forget();
62 return GetOrCreateFileCalledBlob(aBlob, aRv);
65 } // namespace
67 // -------------------------------------------------------------------------
68 // nsISupports
70 NS_IMPL_CYCLE_COLLECTION_CLASS(FormData)
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FormData)
73 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
75 for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
76 ImplCycleCollectionUnlink(tmp->mFormData[i].value);
79 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
80 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FormData)
83 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
85 for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
86 ImplCycleCollectionTraverse(cb, tmp->mFormData[i].value,
87 "mFormData[i].GetAsBlob()", 0);
90 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
92 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FormData)
94 NS_IMPL_CYCLE_COLLECTING_ADDREF(FormData)
95 NS_IMPL_CYCLE_COLLECTING_RELEASE(FormData)
97 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FormData)
98 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
99 NS_INTERFACE_MAP_ENTRY(nsISupports)
100 NS_INTERFACE_MAP_END
102 // -------------------------------------------------------------------------
103 // HTMLFormSubmission
104 nsresult FormData::GetEncodedSubmission(nsIURI* aURI,
105 nsIInputStream** aPostDataStream,
106 nsCOMPtr<nsIURI>& aOutURI) {
107 MOZ_ASSERT_UNREACHABLE("Shouldn't call FormData::GetEncodedSubmission");
108 return NS_OK;
111 void FormData::Append(const nsAString& aName, const nsAString& aValue,
112 ErrorResult& aRv) {
113 AddNameValuePair(aName, aValue);
116 void FormData::Append(const nsAString& aName, Blob& aBlob,
117 const Optional<nsAString>& aFilename, ErrorResult& aRv) {
118 RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
119 if (NS_WARN_IF(aRv.Failed())) {
120 return;
123 AddNameBlobPair(aName, file);
126 void FormData::Append(const nsAString& aName, Directory* aDirectory) {
127 AddNameDirectoryPair(aName, aDirectory);
130 void FormData::Delete(const nsAString& aName) {
131 mFormData.RemoveElementsBy([&aName](const auto& formDataItem) {
132 return aName.Equals(formDataItem.name);
136 void FormData::Get(const nsAString& aName,
137 Nullable<OwningBlobOrDirectoryOrUSVString>& aOutValue) {
138 for (uint32_t i = 0; i < mFormData.Length(); ++i) {
139 if (aName.Equals(mFormData[i].name)) {
140 aOutValue.SetValue() = mFormData[i].value;
141 return;
145 aOutValue.SetNull();
148 void FormData::GetAll(const nsAString& aName,
149 nsTArray<OwningBlobOrDirectoryOrUSVString>& aValues) {
150 for (uint32_t i = 0; i < mFormData.Length(); ++i) {
151 if (aName.Equals(mFormData[i].name)) {
152 OwningBlobOrDirectoryOrUSVString* element = aValues.AppendElement();
153 *element = mFormData[i].value;
158 bool FormData::Has(const nsAString& aName) {
159 for (uint32_t i = 0; i < mFormData.Length(); ++i) {
160 if (aName.Equals(mFormData[i].name)) {
161 return true;
165 return false;
168 nsresult FormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob) {
169 MOZ_ASSERT(aBlob);
171 RefPtr<File> file;
172 ErrorResult rv;
173 file = GetOrCreateFileCalledBlob(*aBlob, rv);
174 if (NS_WARN_IF(rv.Failed())) {
175 return rv.StealNSResult();
178 FormDataTuple* data = mFormData.AppendElement();
179 SetNameFilePair(data, aName, file);
180 return NS_OK;
183 nsresult FormData::AddNameDirectoryPair(const nsAString& aName,
184 Directory* aDirectory) {
185 MOZ_ASSERT(aDirectory);
187 FormDataTuple* data = mFormData.AppendElement();
188 SetNameDirectoryPair(data, aName, aDirectory);
189 return NS_OK;
192 FormData::FormDataTuple* FormData::RemoveAllOthersAndGetFirstFormDataTuple(
193 const nsAString& aName) {
194 FormDataTuple* lastFoundTuple = nullptr;
195 uint32_t lastFoundIndex = mFormData.Length();
196 // We have to use this slightly awkward for loop since uint32_t >= 0 is an
197 // error for being always true.
198 for (uint32_t i = mFormData.Length(); i-- > 0;) {
199 if (aName.Equals(mFormData[i].name)) {
200 if (lastFoundTuple) {
201 // The one we found earlier was not the first one, we can remove it.
202 mFormData.RemoveElementAt(lastFoundIndex);
205 lastFoundTuple = &mFormData[i];
206 lastFoundIndex = i;
210 return lastFoundTuple;
213 void FormData::Set(const nsAString& aName, Blob& aBlob,
214 const Optional<nsAString>& aFilename, ErrorResult& aRv) {
215 FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
216 if (tuple) {
217 RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
218 if (NS_WARN_IF(aRv.Failed())) {
219 return;
222 SetNameFilePair(tuple, aName, file);
223 } else {
224 Append(aName, aBlob, aFilename, aRv);
228 void FormData::Set(const nsAString& aName, const nsAString& aValue,
229 ErrorResult& aRv) {
230 FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
231 if (tuple) {
232 SetNameValuePair(tuple, aName, aValue);
233 } else {
234 Append(aName, aValue, aRv);
238 uint32_t FormData::GetIterableLength() const { return mFormData.Length(); }
240 const nsAString& FormData::GetKeyAtIndex(uint32_t aIndex) const {
241 MOZ_ASSERT(aIndex < mFormData.Length());
242 return mFormData[aIndex].name;
245 const OwningBlobOrDirectoryOrUSVString& FormData::GetValueAtIndex(
246 uint32_t aIndex) const {
247 MOZ_ASSERT(aIndex < mFormData.Length());
248 return mFormData[aIndex].value;
251 void FormData::SetNameValuePair(FormDataTuple* aData, const nsAString& aName,
252 const nsAString& aValue) {
253 MOZ_ASSERT(aData);
254 aData->name = aName;
255 aData->value.SetAsUSVString() = aValue;
258 void FormData::SetNameFilePair(FormDataTuple* aData, const nsAString& aName,
259 File* aFile) {
260 MOZ_ASSERT(aData);
261 MOZ_ASSERT(aFile);
263 aData->name = aName;
264 aData->value.SetAsBlob() = aFile;
267 void FormData::SetNameDirectoryPair(FormDataTuple* aData,
268 const nsAString& aName,
269 Directory* aDirectory) {
270 MOZ_ASSERT(aData);
271 MOZ_ASSERT(aDirectory);
273 aData->name = aName;
274 aData->value.SetAsDirectory() = aDirectory;
277 /* virtual */
278 JSObject* FormData::WrapObject(JSContext* aCx,
279 JS::Handle<JSObject*> aGivenProto) {
280 return FormData_Binding::Wrap(aCx, this, aGivenProto);
283 /* static */
284 already_AddRefed<FormData> FormData::Constructor(
285 const GlobalObject& aGlobal,
286 const Optional<NonNull<HTMLFormElement> >& aFormElement, ErrorResult& aRv) {
287 RefPtr<FormData> formData = new FormData(aGlobal.GetAsSupports());
288 if (aFormElement.WasPassed()) {
289 aRv = aFormElement.Value().ConstructEntryList(formData);
290 if (NS_WARN_IF(aRv.Failed())) {
291 return nullptr;
294 // Step 9. Return a shallow clone of entry list.
295 // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-form-data-set
296 formData = formData->Clone();
299 return formData.forget();
302 // contentTypeWithCharset can be set to the contentType or
303 // contentType+charset based on what the spec says.
304 // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
305 nsresult FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
306 nsACString& aContentTypeWithCharset,
307 nsACString& aCharset) const {
308 FSMultipartFormData fs(nullptr, u""_ns, UTF_8_ENCODING, nullptr);
309 nsresult rv = CopySubmissionDataTo(&fs);
310 NS_ENSURE_SUCCESS(rv, rv);
312 fs.GetContentType(aContentTypeWithCharset);
313 aCharset.Truncate();
314 *aContentLength = 0;
315 NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
317 return NS_OK;
320 already_AddRefed<FormData> FormData::Clone() {
321 RefPtr<FormData> formData = new FormData(*this);
322 return formData.forget();
325 nsresult FormData::CopySubmissionDataTo(
326 HTMLFormSubmission* aFormSubmission) const {
327 MOZ_ASSERT(aFormSubmission, "Must have FormSubmission!");
328 for (size_t i = 0; i < mFormData.Length(); ++i) {
329 if (mFormData[i].value.IsUSVString()) {
330 aFormSubmission->AddNameValuePair(mFormData[i].name,
331 mFormData[i].value.GetAsUSVString());
332 } else if (mFormData[i].value.IsBlob()) {
333 aFormSubmission->AddNameBlobPair(mFormData[i].name,
334 mFormData[i].value.GetAsBlob());
335 } else {
336 MOZ_ASSERT(mFormData[i].value.IsDirectory());
337 aFormSubmission->AddNameDirectoryPair(
338 mFormData[i].name, mFormData[i].value.GetAsDirectory());
342 return NS_OK;