Modernize how many platform/network classes do isolatedCopy().
[WebKit-https.git] / Source / WebCore / platform / network / FormData.cpp
1 /*
2  * Copyright (C) 2004, 2006, 2008, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2009 Google Inc. All rights reserved.
4  * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB. If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #include "config.h"
23
24 #include "FormData.h"
25
26 #include "BlobRegistryImpl.h"
27 #include "BlobURL.h"
28 #include "Chrome.h"
29 #include "ChromeClient.h"
30 #include "Document.h"
31 #include "File.h"
32 #include "FileSystem.h"
33 #include "FormDataBuilder.h"
34 #include "FormDataList.h"
35 #include "MIMETypeRegistry.h"
36 #include "Page.h"
37 #include "TextEncoding.h"
38
39 namespace WebCore {
40
41 inline FormData::FormData()
42 {
43 }
44
45 inline FormData::FormData(const FormData& data)
46     : RefCounted<FormData>()
47     , m_elements(data.m_elements)
48     , m_identifier(data.m_identifier)
49     , m_alwaysStream(false)
50     , m_containsPasswordData(data.m_containsPasswordData)
51 {
52     // We shouldn't be copying FormData that hasn't already removed its generated files
53     // but just in case, make sure the new FormData is ready to generate its own files.
54     for (FormDataElement& element : m_elements) {
55         if (element.m_type == FormDataElement::Type::EncodedFile) {
56             element.m_generatedFilename = String();
57             element.m_ownsGeneratedFile = false;
58         }
59     }
60 }
61
62 FormData::~FormData()
63 {
64     // This cleanup should've happened when the form submission finished.
65     // Just in case, let's assert, and do the cleanup anyway in release builds.
66     ASSERT(!hasOwnedGeneratedFiles());
67     removeGeneratedFilesIfNeeded();
68 }
69
70 Ref<FormData> FormData::create()
71 {
72     return adoptRef(*new FormData);
73 }
74
75 Ref<FormData> FormData::create(const void* data, size_t size)
76 {
77     Ref<FormData> result = create();
78     result->appendData(data, size);
79     return result;
80 }
81
82 Ref<FormData> FormData::create(const CString& string)
83 {
84     Ref<FormData> result = create();
85     result->appendData(string.data(), string.length());
86     return result;
87 }
88
89 Ref<FormData> FormData::create(const Vector<char>& vector)
90 {
91     Ref<FormData> result = create();
92     result->appendData(vector.data(), vector.size());
93     return result;
94 }
95
96 Ref<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding, EncodingType encodingType)
97 {
98     Ref<FormData> result = create();
99     result->appendKeyValuePairItems(list, encoding, false, 0, encodingType);
100     return result;
101 }
102
103 Ref<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document)
104 {
105     Ref<FormData> result = create();
106     result->appendKeyValuePairItems(list, encoding, true, document);
107     return result;
108 }
109
110 Ref<FormData> FormData::copy() const
111 {
112     return adoptRef(*new FormData(*this));
113 }
114
115 Ref<FormData> FormData::isolatedCopy() const
116 {
117     // FIXME: isolatedCopy() (historically deepCopy()) only copies certain values from `this`. Why is that?
118     auto formData = create();
119
120     formData->m_alwaysStream = m_alwaysStream;
121
122     formData->m_elements.reserveInitialCapacity(m_elements.size());
123     for (auto& element : m_elements)
124         formData->m_elements.uncheckedAppend(element.isolatedCopy());
125
126     return formData;
127 }
128
129 FormDataElement FormDataElement::isolatedCopy() const
130 {
131     switch (m_type) {
132     case Type::Data:
133         return FormDataElement(m_data);
134     case Type::EncodedFile:
135         return FormDataElement(m_filename.isolatedCopy(), m_fileStart, m_fileLength, m_expectedFileModificationTime, m_shouldGenerateFile);
136     case Type::EncodedBlob:
137         return FormDataElement(m_url.isolatedCopy());
138     }
139
140     RELEASE_ASSERT_NOT_REACHED();
141 }
142
143 void FormData::appendData(const void* data, size_t size)
144 {
145     memcpy(expandDataStore(size), data, size);
146 }
147
148 void FormData::appendFile(const String& filename, bool shouldGenerateFile)
149 {
150     m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, invalidFileTime(), shouldGenerateFile));
151 }
152
153 void FormData::appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile)
154 {
155     m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
156 }
157
158 void FormData::appendBlob(const URL& blobURL)
159 {
160     m_elements.append(FormDataElement(blobURL));
161 }
162
163 void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncoding& encoding, bool isMultiPartForm, Document* document, EncodingType encodingType)
164 {
165     if (isMultiPartForm)
166         m_boundary = FormDataBuilder::generateUniqueBoundaryString();
167
168     Vector<char> encodedData;
169
170     const Vector<FormDataList::Item>& items = list.items();
171     size_t formDataListSize = items.size();
172     ASSERT(!(formDataListSize % 2));
173     for (size_t i = 0; i < formDataListSize; i += 2) {
174         const FormDataList::Item& key = items[i];
175         const FormDataList::Item& value = items[i + 1];
176         if (isMultiPartForm) {
177             Vector<char> header;
178             FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), key.data());
179
180             bool shouldGenerateFile = false;
181
182             // If the current type is blob, then we also need to include the filename
183             if (value.blob()) {
184                 String name;
185                 if (is<File>(*value.blob())) {
186                     File& file = downcast<File>(*value.blob());
187                     name = file.name();
188                     // Let the application specify a filename if it's going to generate a replacement file for the upload.
189                     const String& path = file.path();
190                     if (!path.isEmpty()) {
191                         if (Page* page = document->page()) {
192                             String generatedFileName;
193                             shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
194                             if (shouldGenerateFile)
195                                 name = generatedFileName;
196                         }
197                     }
198
199                     // If a filename is passed in FormData.append(), use it instead of the file blob's name.
200                     if (!value.filename().isNull())
201                         name = value.filename();
202                 } else {
203                     // For non-file blob, use the filename if it is passed in FormData.append().
204                     if (!value.filename().isNull())
205                         name = value.filename();
206                     else
207                         name = "blob";
208                 }
209
210                 // We have to include the filename=".." part in the header, even if the filename is empty
211                 FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name);
212
213                 // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867).
214                 String contentType = value.blob()->type();
215                 if (contentType.isEmpty())
216                     contentType = "application/octet-stream";
217                 ASSERT(Blob::isNormalizedContentType(contentType));
218                 FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.ascii());
219             }
220
221             FormDataBuilder::finishMultiPartHeader(header);
222
223             // Append body
224             appendData(header.data(), header.size());
225             if (value.blob()) {
226                 if (is<File>(*value.blob())) {
227                     File& file = downcast<File>(*value.blob());
228                     // Do not add the file if the path is empty.
229                     if (!file.path().isEmpty())
230                         appendFile(file.path(), shouldGenerateFile);
231                 }
232                 else
233                     appendBlob(value.blob()->url());
234             } else
235                 appendData(value.data().data(), value.data().length());
236             appendData("\r\n", 2);
237         } else {
238             // Omit the name "isindex" if it's the first form data element.
239             // FIXME: Why is this a good rule? Is this obsolete now?
240             if (encodedData.isEmpty() && key.data() == "isindex")
241                 FormDataBuilder::encodeStringAsFormData(encodedData, value.data());
242             else
243                 FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data(), encodingType);
244         }
245     }
246
247     if (isMultiPartForm)
248         FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true);
249
250     appendData(encodedData.data(), encodedData.size());
251 }
252
253 char* FormData::expandDataStore(size_t size)
254 {
255     if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::Type::Data)
256         m_elements.append(FormDataElement());
257     FormDataElement& e = m_elements.last();
258     size_t oldSize = e.m_data.size();
259     e.m_data.grow(oldSize + size);
260     return e.m_data.data() + oldSize;
261 }
262
263 void FormData::flatten(Vector<char>& data) const
264 {
265     // Concatenate all the byte arrays, but omit any files.
266     data.clear();
267     size_t n = m_elements.size();
268     for (size_t i = 0; i < n; ++i) {
269         const FormDataElement& e = m_elements[i];
270         if (e.m_type == FormDataElement::Type::Data)
271             data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size()));
272     }
273 }
274
275 String FormData::flattenToString() const
276 {
277     Vector<char> bytes;
278     flatten(bytes);
279     return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
280 }
281
282 static void appendBlobResolved(FormData* formData, const URL& url)
283 {
284     if (!blobRegistry().isBlobRegistryImpl()) {
285         LOG_ERROR("Tried to resolve a blob without a usable registry");
286         return;
287     }
288     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url);
289     if (!blobData) {
290         LOG_ERROR("Could not get blob data from a registry");
291         return;
292     }
293
294     BlobDataItemList::const_iterator it = blobData->items().begin();
295     const BlobDataItemList::const_iterator itend = blobData->items().end();
296     for (; it != itend; ++it) {
297         const BlobDataItem& blobItem = *it;
298         if (blobItem.type() == BlobDataItem::Type::Data) {
299             ASSERT(blobItem.data().data());
300             formData->appendData(blobItem.data().data()->data() + static_cast<int>(blobItem.offset()), static_cast<int>(blobItem.length()));
301         } else if (blobItem.type() == BlobDataItem::Type::File)
302             formData->appendFileRange(blobItem.file()->path(), blobItem.offset(), blobItem.length(), blobItem.file()->expectedModificationTime());
303         else
304             ASSERT_NOT_REACHED();
305     }
306 }
307
308 Ref<FormData> FormData::resolveBlobReferences()
309 {
310     // First check if any blobs needs to be resolved, or we can take the fast path.
311     bool hasBlob = false;
312     Vector<FormDataElement>::const_iterator it = elements().begin();
313     const Vector<FormDataElement>::const_iterator itend = elements().end();
314     for (; it != itend; ++it) {
315         if (it->m_type == FormDataElement::Type::EncodedBlob) {
316             hasBlob = true;
317             break;
318         }
319     }
320
321     if (!hasBlob)
322         return *this;
323
324     // Create a copy to append the result into.
325     Ref<FormData> newFormData = FormData::create();
326     newFormData->setAlwaysStream(alwaysStream());
327     newFormData->setIdentifier(identifier());
328     it = elements().begin();
329     for (; it != itend; ++it) {
330         const FormDataElement& element = *it;
331         if (element.m_type == FormDataElement::Type::Data)
332             newFormData->appendData(element.m_data.data(), element.m_data.size());
333         else if (element.m_type == FormDataElement::Type::EncodedFile)
334             newFormData->appendFileRange(element.m_filename, element.m_fileStart, element.m_fileLength, element.m_expectedFileModificationTime, element.m_shouldGenerateFile);
335         else if (element.m_type == FormDataElement::Type::EncodedBlob)
336             appendBlobResolved(newFormData.ptr(), element.m_url);
337         else
338             ASSERT_NOT_REACHED();
339     }
340     return newFormData;
341 }
342
343 void FormData::generateFiles(Document* document)
344 {
345     Page* page = document->page();
346     if (!page)
347         return;
348
349     for (FormDataElement& element : m_elements) {
350         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_shouldGenerateFile) {
351             ASSERT(!element.m_ownsGeneratedFile);
352             ASSERT(element.m_generatedFilename.isEmpty());
353             if (!element.m_generatedFilename.isEmpty())
354                 continue;
355             element.m_generatedFilename = page->chrome().client().generateReplacementFile(element.m_filename);
356             if (!element.m_generatedFilename.isEmpty())
357                 element.m_ownsGeneratedFile = true;
358         }
359     }
360 }
361
362 bool FormData::hasGeneratedFiles() const
363 {
364     for (const FormDataElement& element : m_elements) {
365         if (element.m_type == FormDataElement::Type::EncodedFile && !element.m_generatedFilename.isEmpty())
366             return true;
367     }
368     return false;
369 }
370
371 bool FormData::hasOwnedGeneratedFiles() const
372 {
373     for (const FormDataElement& element : m_elements) {
374         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
375             ASSERT(!element.m_generatedFilename.isEmpty());
376             return true;
377         }
378     }
379     return false;
380 }
381
382 void FormData::removeGeneratedFilesIfNeeded()
383 {
384     for (FormDataElement& element : m_elements) {
385         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
386             ASSERT(!element.m_generatedFilename.isEmpty());
387             ASSERT(element.m_shouldGenerateFile);
388             String directory = directoryName(element.m_generatedFilename);
389             deleteFile(element.m_generatedFilename);
390             deleteEmptyDirectory(directory);
391             element.m_generatedFilename = String();
392             element.m_ownsGeneratedFile = false;
393         }
394     }
395 }
396
397 } // namespace WebCore