2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include "core/html/FormData.h"
34 #include "core/fileapi/Blob.h"
35 #include "core/fileapi/File.h"
36 #include "core/frame/UseCounter.h"
37 #include "core/html/HTMLFormElement.h"
38 #include "platform/network/FormDataEncoder.h"
39 #include "platform/text/LineEnding.h"
40 #include "wtf/text/WTFString.h"
46 class FormDataIterationSource final
: public PairIterable
<String
, FormDataEntryValue
>::IterationSource
{
48 FormDataIterationSource(FormData
* formData
) : m_formData(formData
), m_current(0) { }
50 bool next(ScriptState
* scriptState
, String
& name
, FormDataEntryValue
& value
, ExceptionState
& exceptionState
) override
52 if (m_current
>= m_formData
->size())
55 const FormData::Entry
& entry
= *m_formData
->entries()[m_current
++];
56 name
= m_formData
->decode(entry
.name());
57 if (entry
.isString()) {
58 value
.setUSVString(m_formData
->decode(entry
.value()));
60 ASSERT(entry
.isFile());
61 value
.setFile(entry
.file());
66 DEFINE_INLINE_VIRTUAL_TRACE()
68 visitor
->trace(m_formData
);
69 PairIterable
<String
, FormDataEntryValue
>::IterationSource::trace(visitor
);
73 const Member
<FormData
> m_formData
;
79 FormData::FormData(const WTF::TextEncoding
& encoding
)
80 : m_encoding(encoding
)
85 FormData::FormData(HTMLFormElement
* form
)
86 : m_encoding(UTF8Encoding())
92 for (unsigned i
= 0; i
< form
->associatedElements().size(); ++i
) {
93 FormAssociatedElement
* element
= form
->associatedElements()[i
];
94 if (!toHTMLElement(element
)->isDisabledFormControl())
95 element
->appendToFormData(*this);
99 DEFINE_TRACE(FormData
)
101 visitor
->trace(m_entries
);
104 void FormData::append(const String
& name
, const String
& value
)
106 m_entries
.append(new Entry(encodeAndNormalize(name
), encodeAndNormalize(value
)));
109 void FormData::append(ExecutionContext
* context
, const String
& name
, Blob
* blob
, const String
& filename
)
112 if (blob
->isFile()) {
113 if (filename
.isNull())
114 UseCounter::count(context
, UseCounter::FormDataAppendFile
);
116 UseCounter::count(context
, UseCounter::FormDataAppendFileWithFilename
);
118 if (filename
.isNull())
119 UseCounter::count(context
, UseCounter::FormDataAppendBlob
);
121 UseCounter::count(context
, UseCounter::FormDataAppendBlobWithFilename
);
124 UseCounter::count(context
, UseCounter::FormDataAppendNull
);
126 append(name
, blob
, filename
);
129 void FormData::deleteEntry(const String
& name
)
131 const CString encodedName
= encodeAndNormalize(name
);
133 while (i
< m_entries
.size()) {
134 if (m_entries
[i
]->name() == encodedName
) {
142 void FormData::get(const String
& name
, FormDataEntryValue
& result
)
146 const CString encodedName
= encodeAndNormalize(name
);
147 for (const auto& entry
: entries()) {
148 if (entry
->name() == encodedName
) {
149 if (entry
->isString()) {
150 result
.setUSVString(decode(entry
->value()));
152 ASSERT(entry
->isFile());
153 result
.setFile(entry
->file());
160 HeapVector
<FormDataEntryValue
> FormData::getAll(const String
& name
)
162 HeapVector
<FormDataEntryValue
> results
;
167 const CString encodedName
= encodeAndNormalize(name
);
168 for (const auto& entry
: entries()) {
169 if (entry
->name() != encodedName
)
171 FormDataEntryValue value
;
172 if (entry
->isString()) {
173 value
.setUSVString(decode(entry
->value()));
175 ASSERT(entry
->isFile());
176 value
.setFile(entry
->file());
178 results
.append(value
);
183 bool FormData::has(const String
& name
)
187 const CString encodedName
= encodeAndNormalize(name
);
188 for (const auto& entry
: entries()) {
189 if (entry
->name() == encodedName
)
195 void FormData::set(const String
& name
, const String
& value
)
197 setEntry(new Entry(encodeAndNormalize(name
), encodeAndNormalize(value
)));
200 void FormData::set(const String
& name
, Blob
* blob
, const String
& filename
)
202 setEntry(new Entry(encodeAndNormalize(name
), blob
, filename
));
205 void FormData::setEntry(const Entry
* entry
)
208 const CString encodedName
= entry
->name();
211 while (i
< m_entries
.size()) {
212 if (m_entries
[i
]->name() != encodedName
) {
218 m_entries
[i
] = entry
;
223 m_entries
.append(entry
);
226 void FormData::append(const String
& name
, int value
)
228 append(name
, String::number(value
));
231 void FormData::append(const String
& name
, Blob
* blob
, const String
& filename
)
233 m_entries
.append(new Entry(encodeAndNormalize(name
), blob
, filename
));
236 CString
FormData::encodeAndNormalize(const String
& string
) const
238 CString encodedString
= m_encoding
.encode(string
, WTF::EntitiesForUnencodables
);
239 return normalizeLineEndingsToCRLF(encodedString
);
242 String
FormData::decode(const CString
& data
) const
244 return encoding().decode(data
.data(), data
.length());
247 PassRefPtr
<EncodedFormData
> FormData::encodeFormData(EncodedFormData::EncodingType encodingType
)
249 RefPtr
<EncodedFormData
> formData
= EncodedFormData::create();
250 Vector
<char> encodedData
;
251 for (const auto& entry
: entries())
252 FormDataEncoder::addKeyValuePairAsFormData(encodedData
, entry
->name(), entry
->isFile() ? encodeAndNormalize(entry
->file()->name()) : entry
->value(), encodingType
);
253 formData
->appendData(encodedData
.data(), encodedData
.size());
254 return formData
.release();
257 PassRefPtr
<EncodedFormData
> FormData::encodeMultiPartFormData()
259 RefPtr
<EncodedFormData
> formData
= EncodedFormData::create();
260 formData
->setBoundary(FormDataEncoder::generateUniqueBoundaryString());
261 Vector
<char> encodedData
;
262 for (const auto& entry
: entries()) {
264 FormDataEncoder::beginMultiPartHeader(header
, formData
->boundary().data(), entry
->name());
266 // If the current type is blob, then we also need to include the
270 if (entry
->blob()->isFile()) {
271 File
* file
= toFile(entry
->blob());
272 // For file blob, use the filename (or relative path if it is
273 // present) as the name.
274 name
= file
->webkitRelativePath().isEmpty() ? file
->name() : file
->webkitRelativePath();
276 // If a filename is passed in FormData.append(), use it instead
277 // of the file blob's name.
278 if (!entry
->filename().isNull())
279 name
= entry
->filename();
281 // For non-file blob, use the filename if it is passed in
282 // FormData.append().
283 if (!entry
->filename().isNull())
284 name
= entry
->filename();
289 // We have to include the filename=".." part in the header, even if
290 // the filename is empty.
291 FormDataEncoder::addFilenameToMultiPartHeader(header
, encoding(), name
);
293 // Add the content type if available, or "application/octet-stream"
294 // otherwise (RFC 1867).
296 if (entry
->blob()->type().isEmpty())
297 contentType
= "application/octet-stream";
299 contentType
= entry
->blob()->type();
300 FormDataEncoder::addContentTypeToMultiPartHeader(header
, contentType
.latin1());
303 FormDataEncoder::finishMultiPartHeader(header
);
306 formData
->appendData(header
.data(), header
.size());
308 if (entry
->blob()->hasBackingFile()) {
309 File
* file
= toFile(entry
->blob());
310 // Do not add the file if the path is empty.
311 if (!file
->path().isEmpty())
312 formData
->appendFile(file
->path());
313 if (!file
->fileSystemURL().isEmpty())
314 formData
->appendFileSystemURL(file
->fileSystemURL());
316 formData
->appendBlob(entry
->blob()->uuid(), entry
->blob()->blobDataHandle());
319 formData
->appendData(entry
->value().data(), entry
->value().length());
321 formData
->appendData("\r\n", 2);
323 FormDataEncoder::addBoundaryToMultiPartHeader(encodedData
, formData
->boundary().data(), true);
324 formData
->appendData(encodedData
.data(), encodedData
.size());
325 return formData
.release();
328 PairIterable
<String
, FormDataEntryValue
>::IterationSource
* FormData::startIteration(ScriptState
*, ExceptionState
&)
331 return new FormDataIterationSource(new FormData(nullptr));
333 return new FormDataIterationSource(this);
336 // ----------------------------------------------------------------
338 DEFINE_TRACE(FormData::Entry
)
340 visitor
->trace(m_blob
);
343 File
* FormData::Entry::file() const
346 // The spec uses the passed filename when inserting entries into the list.
347 // Here, we apply the filename (if present) as an override when extracting
349 // FIXME: Consider applying the name during insertion.
351 if (blob()->isFile()) {
352 File
* file
= toFile(blob());
353 if (filename().isNull())
355 return file
->clone(filename());
358 String filename
= m_filename
;
359 if (filename
.isNull())
361 return File::create(filename
, currentTimeMS(), blob()->blobDataHandle());