Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / FormData.cpp
blobad6b574de6d04b83d857ec278e5db3be33cc8c08
1 /*
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
6 * met:
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
13 * distribution.
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.
31 #include "config.h"
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"
42 namespace blink {
44 namespace {
46 class FormDataIterationSource final : public PairIterable<String, FormDataEntryValue>::IterationSource {
47 public:
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())
53 return false;
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()));
59 } else {
60 ASSERT(entry.isFile());
61 value.setFile(entry.file());
63 return true;
66 DEFINE_INLINE_VIRTUAL_TRACE()
68 visitor->trace(m_formData);
69 PairIterable<String, FormDataEntryValue>::IterationSource::trace(visitor);
72 private:
73 const Member<FormData> m_formData;
74 size_t m_current;
77 } // namespace
79 FormData::FormData(const WTF::TextEncoding& encoding)
80 : m_encoding(encoding)
81 , m_opaque(false)
85 FormData::FormData(HTMLFormElement* form)
86 : m_encoding(UTF8Encoding())
87 , m_opaque(false)
89 if (!form)
90 return;
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)
111 if (blob) {
112 if (blob->isFile()) {
113 if (filename.isNull())
114 UseCounter::count(context, UseCounter::FormDataAppendFile);
115 else
116 UseCounter::count(context, UseCounter::FormDataAppendFileWithFilename);
117 } else {
118 if (filename.isNull())
119 UseCounter::count(context, UseCounter::FormDataAppendBlob);
120 else
121 UseCounter::count(context, UseCounter::FormDataAppendBlobWithFilename);
123 } else {
124 UseCounter::count(context, UseCounter::FormDataAppendNull);
126 append(name, blob, filename);
129 void FormData::deleteEntry(const String& name)
131 const CString encodedName = encodeAndNormalize(name);
132 size_t i = 0;
133 while (i < m_entries.size()) {
134 if (m_entries[i]->name() == encodedName) {
135 m_entries.remove(i);
136 } else {
137 ++i;
142 void FormData::get(const String& name, FormDataEntryValue& result)
144 if (m_opaque)
145 return;
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()));
151 } else {
152 ASSERT(entry->isFile());
153 result.setFile(entry->file());
155 return;
160 HeapVector<FormDataEntryValue> FormData::getAll(const String& name)
162 HeapVector<FormDataEntryValue> results;
164 if (m_opaque)
165 return results;
167 const CString encodedName = encodeAndNormalize(name);
168 for (const auto& entry : entries()) {
169 if (entry->name() != encodedName)
170 continue;
171 FormDataEntryValue value;
172 if (entry->isString()) {
173 value.setUSVString(decode(entry->value()));
174 } else {
175 ASSERT(entry->isFile());
176 value.setFile(entry->file());
178 results.append(value);
180 return results;
183 bool FormData::has(const String& name)
185 if (m_opaque)
186 return false;
187 const CString encodedName = encodeAndNormalize(name);
188 for (const auto& entry : entries()) {
189 if (entry->name() == encodedName)
190 return true;
192 return false;
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)
207 ASSERT(entry);
208 const CString encodedName = entry->name();
209 bool found = false;
210 size_t i = 0;
211 while (i < m_entries.size()) {
212 if (m_entries[i]->name() != encodedName) {
213 ++i;
214 } else if (found) {
215 m_entries.remove(i);
216 } else {
217 found = true;
218 m_entries[i] = entry;
219 ++i;
222 if (!found)
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()) {
263 Vector<char> header;
264 FormDataEncoder::beginMultiPartHeader(header, formData->boundary().data(), entry->name());
266 // If the current type is blob, then we also need to include the
267 // filename.
268 if (entry->blob()) {
269 String name;
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();
280 } else {
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();
285 else
286 name = "blob";
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).
295 String contentType;
296 if (entry->blob()->type().isEmpty())
297 contentType = "application/octet-stream";
298 else
299 contentType = entry->blob()->type();
300 FormDataEncoder::addContentTypeToMultiPartHeader(header, contentType.latin1());
303 FormDataEncoder::finishMultiPartHeader(header);
305 // Append body
306 formData->appendData(header.data(), header.size());
307 if (entry->blob()) {
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());
315 } else {
316 formData->appendBlob(entry->blob()->uuid(), entry->blob()->blobDataHandle());
318 } else {
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&)
330 if (m_opaque)
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
345 ASSERT(blob());
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
348 // entries.
349 // FIXME: Consider applying the name during insertion.
351 if (blob()->isFile()) {
352 File* file = toFile(blob());
353 if (filename().isNull())
354 return file;
355 return file->clone(filename());
358 String filename = m_filename;
359 if (filename.isNull())
360 filename = "blob";
361 return File::create(filename, currentTimeMS(), blob()->blobDataHandle());
364 } // namespace blink