FormDataElement::lengthInBytes should use ThreadableBlobRegistry
[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 #include "FormData.h"
24
25 #include "BlobRegistryImpl.h"
26 #include "BlobURL.h"
27 #include "Chrome.h"
28 #include "ChromeClient.h"
29 #include "DOMFormData.h"
30 #include "Document.h"
31 #include "File.h"
32 #include "FileSystem.h"
33 #include "FormDataBuilder.h"
34 #include "Page.h"
35 #include "TextEncoding.h"
36 #include "ThreadableBlobRegistry.h"
37 #include <wtf/text/LineEnding.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 (auto& 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     auto result = create();
78     result->appendData(data, size);
79     return result;
80 }
81
82 Ref<FormData> FormData::create(const CString& string)
83 {
84     return create(string.data(), string.length());
85 }
86
87 Ref<FormData> FormData::create(const Vector<char>& vector)
88 {
89     return create(vector.data(), vector.size());
90 }
91
92 Ref<FormData> FormData::create(const Vector<uint8_t>& vector)
93 {
94     return create(vector.data(), vector.size());
95 }
96
97 Ref<FormData> FormData::create(const DOMFormData& formData, EncodingType encodingType)
98 {
99     auto result = create();
100     result->appendNonMultiPartKeyValuePairItems(formData, encodingType);
101     return result;
102 }
103
104 Ref<FormData> FormData::createMultiPart(const DOMFormData& formData, Document* document)
105 {
106     auto result = create();
107     result->appendMultiPartKeyValuePairItems(formData, document);
108     return result;
109 }
110
111 Ref<FormData> FormData::copy() const
112 {
113     return adoptRef(*new FormData(*this));
114 }
115
116 Ref<FormData> FormData::isolatedCopy() const
117 {
118     // FIXME: isolatedCopy() does not copy m_identifier, m_boundary, or m_containsPasswordData.
119     // Is all of that correct and intentional?
120
121     auto formData = create();
122
123     formData->m_alwaysStream = m_alwaysStream;
124
125     formData->m_elements.reserveInitialCapacity(m_elements.size());
126     for (auto& element : m_elements)
127         formData->m_elements.uncheckedAppend(element.isolatedCopy());
128
129     return formData;
130 }
131
132 uint64_t FormDataElement::lengthInBytes() const
133 {
134     switch (m_type) {
135     case Type::Data:
136         return m_data.size();
137     case Type::EncodedFile: {
138         if (m_fileLength != BlobDataItem::toEndOfFile)
139             return m_fileLength;
140         long long fileSize;
141         if (FileSystem::getFileSize(m_shouldGenerateFile ? m_generatedFilename : m_filename, fileSize))
142             return fileSize;
143         return 0;
144     }
145     case Type::EncodedBlob:
146         return ThreadableBlobRegistry::blobSize(m_url);
147     }
148     ASSERT_NOT_REACHED();
149     return 0;
150 }
151
152 FormDataElement FormDataElement::isolatedCopy() const
153 {
154     switch (m_type) {
155     case Type::Data:
156         return FormDataElement(m_data);
157     case Type::EncodedFile:
158         return FormDataElement(m_filename.isolatedCopy(), m_fileStart, m_fileLength, m_expectedFileModificationTime, m_shouldGenerateFile);
159     case Type::EncodedBlob:
160         return FormDataElement(m_url.isolatedCopy());
161     }
162
163     RELEASE_ASSERT_NOT_REACHED();
164 }
165
166 void FormData::appendData(const void* data, size_t size)
167 {
168     memcpy(expandDataStore(size), data, size);
169 }
170
171 void FormData::appendFile(const String& filename, bool shouldGenerateFile)
172 {
173     m_elements.append(FormDataElement(filename, 0, BlobDataItem::toEndOfFile, FileSystem::invalidFileTime(), shouldGenerateFile));
174     m_lengthInBytes = std::nullopt;
175 }
176
177 void FormData::appendFileRange(const String& filename, long long start, long long length, double expectedModificationTime, bool shouldGenerateFile)
178 {
179     m_elements.append(FormDataElement(filename, start, length, expectedModificationTime, shouldGenerateFile));
180     m_lengthInBytes = std::nullopt;
181 }
182
183 void FormData::appendBlob(const URL& blobURL)
184 {
185     m_elements.append(FormDataElement(blobURL));
186     m_lengthInBytes = std::nullopt;
187 }
188
189 static Vector<uint8_t> normalizeStringData(TextEncoding& encoding, const String& value)
190 {
191     return normalizeLineEndingsToCRLF(encoding.encode(value, UnencodableHandling::Entities));
192 }
193
194 void FormData::appendMultiPartFileValue(const File& file, Vector<char>& header, TextEncoding& encoding, Document* document)
195 {
196     auto name = file.name();
197
198     // Let the application specify a filename if it's going to generate a replacement file for the upload.
199     bool shouldGenerateFile = false;
200     auto& path = file.path();
201     if (!path.isEmpty()) {
202         if (Page* page = document->page()) {
203             String generatedFileName;
204             shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
205             if (shouldGenerateFile)
206                 name = generatedFileName;
207         }
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     auto contentType = file.type();
215     if (contentType.isEmpty())
216         contentType = ASCIILiteral("application/octet-stream");
217     ASSERT(Blob::isNormalizedContentType(contentType));
218
219     FormDataBuilder::addContentTypeToMultiPartHeader(header, contentType.ascii());
220
221     FormDataBuilder::finishMultiPartHeader(header);
222     appendData(header.data(), header.size());
223
224     if (!file.path().isEmpty())
225         appendFile(file.path(), shouldGenerateFile);
226     else
227         appendBlob(file.url());
228 }
229
230 void FormData::appendMultiPartStringValue(const String& string, Vector<char>& header, TextEncoding& encoding)
231 {
232     FormDataBuilder::finishMultiPartHeader(header);
233     appendData(header.data(), header.size());
234
235     auto normalizedStringData = normalizeStringData(encoding, string);
236     appendData(normalizedStringData.data(), normalizedStringData.size());
237 }
238
239 void FormData::appendMultiPartKeyValuePairItems(const DOMFormData& formData, Document* document)
240 {
241     m_boundary = FormDataBuilder::generateUniqueBoundaryString();
242
243     auto encoding = formData.encoding();
244
245     Vector<char> encodedData;
246     for (auto& item : formData.items()) {
247         auto normalizedName = normalizeStringData(encoding, item.name);
248     
249         Vector<char> header;
250         FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), normalizedName);
251
252         if (WTF::holds_alternative<RefPtr<File>>(item.data))
253             appendMultiPartFileValue(*WTF::get<RefPtr<File>>(item.data), header, encoding, document);
254         else
255             appendMultiPartStringValue(WTF::get<String>(item.data), header, encoding);
256
257         appendData("\r\n", 2);
258     }
259     
260     FormDataBuilder::addBoundaryToMultiPartHeader(encodedData, m_boundary.data(), true);
261
262     appendData(encodedData.data(), encodedData.size());
263 }
264
265 void FormData::appendNonMultiPartKeyValuePairItems(const DOMFormData& formData, EncodingType encodingType)
266 {
267     auto encoding = formData.encoding();
268
269     Vector<char> encodedData;
270     for (auto& item : formData.items()) {
271         ASSERT(WTF::holds_alternative<String>(item.data));
272
273         auto normalizedName = normalizeStringData(encoding, item.name);
274         auto normalizedStringData = normalizeStringData(encoding, WTF::get<String>(item.data));
275         FormDataBuilder::addKeyValuePairAsFormData(encodedData, normalizedName, normalizedStringData, encodingType);
276     }
277
278     appendData(encodedData.data(), encodedData.size());
279 }
280
281 char* FormData::expandDataStore(size_t size)
282 {
283     m_lengthInBytes = std::nullopt;
284     if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::Type::Data)
285         m_elements.append({ });
286
287     auto& lastElement = m_elements.last();
288     size_t oldSize = lastElement.m_data.size();
289
290     auto newSize = Checked<size_t>(oldSize) + size;
291
292     lastElement.m_data.grow(newSize.unsafeGet());
293     return lastElement.m_data.data() + oldSize;
294 }
295
296 Vector<char> FormData::flatten() const
297 {
298     // Concatenate all the byte arrays, but omit any files.
299     Vector<char> data;
300     for (auto& element : m_elements) {
301         if (element.m_type == FormDataElement::Type::Data)
302             data.append(element.m_data.data(), static_cast<size_t>(element.m_data.size()));
303     }
304     return data;
305 }
306
307 String FormData::flattenToString() const
308 {
309     auto bytes = flatten();
310     return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
311 }
312
313 static void appendBlobResolved(FormData* formData, const URL& url)
314 {
315     if (!blobRegistry().isBlobRegistryImpl()) {
316         LOG_ERROR("Tried to resolve a blob without a usable registry");
317         return;
318     }
319
320     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url);
321     if (!blobData) {
322         LOG_ERROR("Could not get blob data from a registry");
323         return;
324     }
325
326     for (const auto& blobItem : blobData->items()) {
327         if (blobItem.type() == BlobDataItem::Type::Data) {
328             ASSERT(blobItem.data().data());
329             formData->appendData(blobItem.data().data()->data() + static_cast<int>(blobItem.offset()), static_cast<int>(blobItem.length()));
330         } else if (blobItem.type() == BlobDataItem::Type::File)
331             formData->appendFileRange(blobItem.file()->path(), blobItem.offset(), blobItem.length(), blobItem.file()->expectedModificationTime());
332         else
333             ASSERT_NOT_REACHED();
334     }
335 }
336
337 Ref<FormData> FormData::resolveBlobReferences()
338 {
339     // First check if any blobs needs to be resolved, or we can take the fast path.
340     bool hasBlob = false;
341     for (auto& element : m_elements) {
342         if (element.m_type == FormDataElement::Type::EncodedBlob) {
343             hasBlob = true;
344             break;
345         }
346     }
347
348     if (!hasBlob)
349         return *this;
350
351     // Create a copy to append the result into.
352     auto newFormData = FormData::create();
353     newFormData->setAlwaysStream(alwaysStream());
354     newFormData->setIdentifier(identifier());
355
356     for (auto& element : m_elements) {
357         if (element.m_type == FormDataElement::Type::Data)
358             newFormData->appendData(element.m_data.data(), element.m_data.size());
359         else if (element.m_type == FormDataElement::Type::EncodedFile)
360             newFormData->appendFileRange(element.m_filename, element.m_fileStart, element.m_fileLength, element.m_expectedFileModificationTime, element.m_shouldGenerateFile);
361         else if (element.m_type == FormDataElement::Type::EncodedBlob)
362             appendBlobResolved(newFormData.ptr(), element.m_url);
363         else
364             ASSERT_NOT_REACHED();
365     }
366     return newFormData;
367 }
368
369 void FormData::generateFiles(Document* document)
370 {
371     Page* page = document->page();
372     if (!page)
373         return;
374
375     for (auto& element : m_elements) {
376         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_shouldGenerateFile) {
377             ASSERT(!element.m_ownsGeneratedFile);
378             ASSERT(element.m_generatedFilename.isEmpty());
379             if (!element.m_generatedFilename.isEmpty())
380                 continue;
381             element.m_generatedFilename = page->chrome().client().generateReplacementFile(element.m_filename);
382             if (!element.m_generatedFilename.isEmpty())
383                 element.m_ownsGeneratedFile = true;
384         }
385     }
386 }
387
388 bool FormData::hasGeneratedFiles() const
389 {
390     for (auto& element : m_elements) {
391         if (element.m_type == FormDataElement::Type::EncodedFile && !element.m_generatedFilename.isEmpty())
392             return true;
393     }
394     return false;
395 }
396
397 bool FormData::hasOwnedGeneratedFiles() const
398 {
399     for (auto& element : m_elements) {
400         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
401             ASSERT(!element.m_generatedFilename.isEmpty());
402             return true;
403         }
404     }
405     return false;
406 }
407
408 void FormData::removeGeneratedFilesIfNeeded()
409 {
410     for (auto& element : m_elements) {
411         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
412             ASSERT(!element.m_generatedFilename.isEmpty());
413             ASSERT(element.m_shouldGenerateFile);
414             String directory = FileSystem::directoryName(element.m_generatedFilename);
415             FileSystem::deleteFile(element.m_generatedFilename);
416             FileSystem::deleteEmptyDirectory(directory);
417             element.m_generatedFilename = String();
418             element.m_ownsGeneratedFile = false;
419         }
420     }
421 }
422
423 uint64_t FormData::lengthInBytes() const
424 {
425     if (!m_lengthInBytes) {
426         uint64_t length = 0;
427         for (auto& element : m_elements)
428             length += element.lengthInBytes();
429         m_lengthInBytes = length;
430     }
431     return *m_lengthInBytes;
432 }
433
434 RefPtr<SharedBuffer> FormData::asSharedBuffer() const
435 {
436     for (auto& element : m_elements) {
437         if (element.m_type != FormDataElement::Type::Data)
438             return nullptr;
439     }
440     return SharedBuffer::create(flatten());
441 }
442
443 URL FormData::asBlobURL() const
444 {
445     if (m_elements.size() != 1)
446         return { };
447
448     ASSERT(m_elements.first().m_type == FormDataElement::Type::EncodedBlob || m_elements.first().m_url.isNull());
449     return m_elements.first().m_url;
450 }
451
452 } // namespace WebCore