| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2009 Google Inc. All rights reserved. | 3 * Copyright (C) 2009 Google Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * This library is free software; you can redistribute it and/or | 5 * This library is free software; you can redistribute it and/or |
| 6 * modify it under the terms of the GNU Library General Public | 6 * modify it under the terms of the GNU Library General Public |
| 7 * License as published by the Free Software Foundation; either | 7 * License as published by the Free Software Foundation; either |
| 8 * version 2 of the License, or (at your option) any later version. | 8 * version 2 of the License, or (at your option) any later version. |
| 9 * | 9 * |
| 10 * This library is distributed in the hope that it will be useful, | 10 * This library is distributed in the hope that it will be useful, |
| 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 * Library General Public License for more details. | 13 * Library General Public License for more details. |
| 14 * | 14 * |
| 15 * You should have received a copy of the GNU Library General Public License | 15 * You should have received a copy of the GNU Library General Public License |
| 16 * along with this library; see the file COPYING.LIB. If not, write to | 16 * along with this library; see the file COPYING.LIB. If not, write to |
| 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| 18 * Boston, MA 02110-1301, USA. | 18 * Boston, MA 02110-1301, USA. |
| 19 */ | 19 */ |
| 20 | 20 |
| 21 #include "config.h" | 21 #include "config.h" |
| 22 |
| 22 #include "FormData.h" | 23 #include "FormData.h" |
| 23 | 24 |
| 24 #include "Blob.h" | 25 #include "BlobItem.h" |
| 25 #include "Chrome.h" | 26 #include "Chrome.h" |
| 26 #include "ChromeClient.h" | 27 #include "ChromeClient.h" |
| 27 #include "DOMFormData.h" | |
| 28 #include "Document.h" | 28 #include "Document.h" |
| 29 #include "File.h" | |
| 30 #include "FileSystem.h" | 29 #include "FileSystem.h" |
| 31 #include "FormDataBuilder.h" | 30 #include "FormDataBuilder.h" |
| 32 #include "MIMETypeRegistry.h" | 31 #include "MIMETypeRegistry.h" |
| 33 #include "Page.h" | 32 #include "Page.h" |
| 34 #include "TextEncoding.h" | 33 #include "TextEncoding.h" |
| 35 #include "UUID.h" | |
| 36 | 34 |
| 37 namespace WebCore { | 35 namespace WebCore { |
| 38 | 36 |
| 37 #if ENABLE(BLOB_SLICE) |
| 38 const long long FormDataElement::toEndOfFile = -1; |
| 39 const double FormDataElement::doNotCheckFileChange = 0; |
| 40 #endif |
| 41 |
| 39 inline FormData::FormData() | 42 inline FormData::FormData() |
| 40 : m_identifier(0) | 43 : m_identifier(0) |
| 41 , m_hasGeneratedFiles(false) | 44 , m_hasGeneratedFiles(false) |
| 42 , m_alwaysStream(false) | 45 , m_alwaysStream(false) |
| 43 { | 46 { |
| 44 } | 47 } |
| 45 | 48 |
| 46 inline FormData::FormData(const FormData& data) | 49 inline FormData::FormData(const FormData& data) |
| 47 : RefCounted<FormData>() | 50 : RefCounted<FormData>() |
| 48 , m_elements(data.m_elements) | 51 , m_elements(data.m_elements) |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 return result.release(); | 92 return result.release(); |
| 90 } | 93 } |
| 91 | 94 |
| 92 PassRefPtr<FormData> FormData::create(const Vector<char>& vector) | 95 PassRefPtr<FormData> FormData::create(const Vector<char>& vector) |
| 93 { | 96 { |
| 94 RefPtr<FormData> result = create(); | 97 RefPtr<FormData> result = create(); |
| 95 result->appendData(vector.data(), vector.size()); | 98 result->appendData(vector.data(), vector.size()); |
| 96 return result.release(); | 99 return result.release(); |
| 97 } | 100 } |
| 98 | 101 |
| 99 PassRefPtr<FormData> FormData::create(const DOMFormData& domFormData) | 102 PassRefPtr<FormData> FormData::create(const BlobItemList& items, const TextEncod
ing& encoding) |
| 100 { | 103 { |
| 101 RefPtr<FormData> result = create(); | 104 RefPtr<FormData> result = create(); |
| 102 result->appendDOMFormData(domFormData, false, 0); | 105 result->appendKeyValuePairItems(items, encoding, false, 0); |
| 103 return result.release(); | 106 return result.release(); |
| 104 } | 107 } |
| 105 | 108 |
| 106 PassRefPtr<FormData> FormData::createMultiPart(const DOMFormData& domFormData, D
ocument* document) | 109 PassRefPtr<FormData> FormData::createMultiPart(const BlobItemList& items, const
TextEncoding& encoding, Document* document) |
| 107 { | 110 { |
| 108 RefPtr<FormData> result = create(); | 111 RefPtr<FormData> result = create(); |
| 109 result->appendDOMFormData(domFormData, true, document); | 112 result->appendKeyValuePairItems(items, encoding, true, document); |
| 110 return result.release(); | 113 return result.release(); |
| 111 } | 114 } |
| 112 | 115 |
| 113 PassRefPtr<FormData> FormData::copy() const | 116 PassRefPtr<FormData> FormData::copy() const |
| 114 { | 117 { |
| 115 return adoptRef(new FormData(*this)); | 118 return adoptRef(new FormData(*this)); |
| 116 } | 119 } |
| 117 | 120 |
| 118 PassRefPtr<FormData> FormData::deepCopy() const | 121 PassRefPtr<FormData> FormData::deepCopy() const |
| 119 { | 122 { |
| (...skipping 27 matching lines...) Expand all Loading... |
| 147 m_elements.append(FormDataElement()); | 150 m_elements.append(FormDataElement()); |
| 148 FormDataElement& e = m_elements.last(); | 151 FormDataElement& e = m_elements.last(); |
| 149 size_t oldSize = e.m_data.size(); | 152 size_t oldSize = e.m_data.size(); |
| 150 e.m_data.grow(oldSize + size); | 153 e.m_data.grow(oldSize + size); |
| 151 memcpy(e.m_data.data() + oldSize, data, size); | 154 memcpy(e.m_data.data() + oldSize, data, size); |
| 152 } | 155 } |
| 153 | 156 |
| 154 void FormData::appendFile(const String& filename, bool shouldGenerateFile) | 157 void FormData::appendFile(const String& filename, bool shouldGenerateFile) |
| 155 { | 158 { |
| 156 #if ENABLE(BLOB_SLICE) | 159 #if ENABLE(BLOB_SLICE) |
| 157 m_elements.append(FormDataElement(filename, 0, Blob::toEndOfFile, Blob::doNo
tCheckFileChange, shouldGenerateFile)); | 160 m_elements.append(FormDataElement(filename, 0, FormDataElement::toEndOfFile,
FormDataElement::doNotCheckFileChange, shouldGenerateFile)); |
| 158 #else | 161 #else |
| 159 m_elements.append(FormDataElement(filename, shouldGenerateFile)); | 162 m_elements.append(FormDataElement(filename, shouldGenerateFile)); |
| 160 #endif | 163 #endif |
| 161 } | 164 } |
| 162 | 165 |
| 166 void FormData::appendItems(const BlobItemList& items) |
| 167 { |
| 168 for (BlobItemList::const_iterator iter(items.begin()); iter != items.end();
++iter) |
| 169 appendItem(iter->get(), false); |
| 170 } |
| 171 |
| 172 void FormData::appendItem(const BlobItem* item, bool shouldGenerateFile) |
| 173 { |
| 174 const DataBlobItem* dataItem = item->toDataBlobItem(); |
| 175 if (dataItem) { |
| 176 appendData(dataItem->data(), static_cast<size_t>(dataItem->size())); |
| 177 return; |
| 178 } |
| 179 |
| 180 const FileBlobItem* fileItem = item->toFileBlobItem(); |
| 181 ASSERT(fileItem); |
| 182 if (fileItem->path().isEmpty()) { |
| 183 // If the path is empty do not add the item. |
| 184 return; |
| 185 } |
| 186 |
| 187 #if ENABLE(BLOB_SLICE) |
| 188 const FileRangeBlobItem* fileRangeItem = item->toFileRangeBlobItem(); |
| 189 if (fileRangeItem) { |
| 190 appendFileRange(fileItem->path(), fileRangeItem->start(), fileRangeItem-
>size(), fileRangeItem->snapshotModificationTime(), shouldGenerateFile); |
| 191 return; |
| 192 } |
| 193 #endif |
| 194 |
| 195 appendFile(fileItem->path(), shouldGenerateFile); |
| 196 } |
| 197 |
| 163 #if ENABLE(BLOB_SLICE) | 198 #if ENABLE(BLOB_SLICE) |
| 164 void FormData::appendFileRange(const String& filename, long long start, long lon
g length, double expectedModificationTime, bool shouldGenerateFile) | 199 void FormData::appendFileRange(const String& filename, long long start, long lon
g length, double expectedModificationTime, bool shouldGenerateFile) |
| 165 { | 200 { |
| 166 m_elements.append(FormDataElement(filename, start, length, expectedModificat
ionTime, shouldGenerateFile)); | 201 m_elements.append(FormDataElement(filename, start, length, expectedModificat
ionTime, shouldGenerateFile)); |
| 167 } | 202 } |
| 168 #endif | 203 #endif |
| 169 | 204 |
| 170 void FormData::appendDOMFormData(const DOMFormData& domFormData, bool isMultiPar
tForm, Document* document) | 205 void FormData::appendKeyValuePairItems(const BlobItemList& items, const TextEnco
ding& encoding, bool isMultiPartForm, Document* document) |
| 171 { | 206 { |
| 172 FormDataBuilder formDataBuilder; | 207 FormDataBuilder formDataBuilder; |
| 173 if (isMultiPartForm) | 208 if (isMultiPartForm) |
| 174 m_boundary = formDataBuilder.generateUniqueBoundaryString(); | 209 m_boundary = formDataBuilder.generateUniqueBoundaryString(); |
| 175 | 210 |
| 176 Vector<char> encodedData; | 211 Vector<char> encodedData; |
| 177 TextEncoding encoding = domFormData.encoding(); | |
| 178 | 212 |
| 179 const Vector<FormDataList::Item>& list = domFormData.list(); | 213 size_t formDataListSize = items.size(); |
| 180 size_t formDataListSize = list.size(); | |
| 181 ASSERT(!(formDataListSize % 2)); | 214 ASSERT(!(formDataListSize % 2)); |
| 182 for (size_t i = 0; i < formDataListSize; i += 2) { | 215 for (size_t i = 0; i < formDataListSize; i += 2) { |
| 183 const FormDataList::Item& key = list[i]; | 216 const StringBlobItem* key = items[i]->toStringBlobItem(); |
| 184 const FormDataList::Item& value = list[i + 1]; | 217 const RefPtr<BlobItem> value = items[i + 1]; |
| 218 ASSERT(key); |
| 185 if (isMultiPartForm) { | 219 if (isMultiPartForm) { |
| 186 Vector<char> header; | 220 Vector<char> header; |
| 187 formDataBuilder.beginMultiPartHeader(header, m_boundary.data(), key.
data()); | 221 formDataBuilder.beginMultiPartHeader(header, m_boundary.data(), key-
>cstr()); |
| 188 | 222 |
| 189 bool shouldGenerateFile = false; | 223 bool shouldGenerateFile = false; |
| 190 // If the current type is FILE, then we also need to include the fil
ename | 224 // If the current type is FILE, then we also need to include the fil
ename |
| 191 if (value.blob()) { | 225 const FileBlobItem* fileItem = value->toFileBlobItem(); |
| 192 const String& path = value.blob()->path(); | 226 if (fileItem) { |
| 193 #if ENABLE(BLOB_SLICE) | 227 const String& path = fileItem->path(); |
| 194 String fileName; | 228 String fileName = fileItem->name(); |
| 195 if (value.blob()->isFile()) | |
| 196 fileName = static_cast<File*>(value.blob())->fileName(); | |
| 197 else { | |
| 198 // If a blob is sliced from a file, it does not have the fil
ename. In this case, let's produce a unique filename. | |
| 199 fileName = "Blob" + createCanonicalUUIDString(); | |
| 200 fileName.replace("-", ""); // For safty, remove '-' from the
filename snce some servers may not like it. | |
| 201 } | |
| 202 #else | |
| 203 ASSERT(value.blob()->isFile()); | |
| 204 String fileName = static_cast<File*>(value.blob())->fileName(); | |
| 205 #endif | |
| 206 | 229 |
| 207 // Let the application specify a filename if it's going to gener
ate a replacement file for the upload. | 230 // Let the application specify a filename if it's going to gener
ate a replacement file for the upload. |
| 208 if (!path.isEmpty()) { | 231 if (!path.isEmpty()) { |
| 209 if (Page* page = document->page()) { | 232 if (Page* page = document->page()) { |
| 210 String generatedFileName; | 233 String generatedFileName; |
| 211 shouldGenerateFile = page->chrome()->client()->shouldRep
laceWithGeneratedFileForUpload(path, generatedFileName); | 234 shouldGenerateFile = page->chrome()->client()->shouldRep
laceWithGeneratedFileForUpload(path, generatedFileName); |
| 212 if (shouldGenerateFile) | 235 if (shouldGenerateFile) |
| 213 fileName = generatedFileName; | 236 fileName = generatedFileName; |
| 214 } | 237 } |
| 215 } | 238 } |
| 216 | 239 |
| 217 // We have to include the filename=".." part in the header, even
if the filename is empty | 240 // We have to include the filename=".." part in the header, even
if the filename is empty |
| 218 formDataBuilder.addFilenameToMultiPartHeader(header, encoding, f
ileName); | 241 formDataBuilder.addFilenameToMultiPartHeader(header, encoding, f
ileName); |
| 219 | 242 |
| 220 // If a blob is sliced from a file, do not add the content type.
| 243 // If the item is sliced from a file, do not add the content typ
e. |
| 221 #if ENABLE(BLOB_SLICE) | 244 #if ENABLE(BLOB_SLICE) |
| 222 if (!fileName.isEmpty() && value.blob()->isFile()) { | 245 if (!fileName.isEmpty() && !value->toFileRangeBlobItem()) { |
| 223 #else | 246 #else |
| 224 if (!fileName.isEmpty()) { | 247 if (!fileName.isEmpty()) { |
| 225 #endif | 248 #endif |
| 226 // FIXME: The MIMETypeRegistry function's name makes it soun
d like it takes a path, | 249 // FIXME: The MIMETypeRegistry function's name makes it soun
d like it takes a path, |
| 227 // not just a basename. But filename is not the path. But no
te that it's not safe to | 250 // not just a basename. But filename is not the path. But no
te that it's not safe to |
| 228 // just use path instead since in the generated-file case it
will not reflect the | 251 // just use path instead since in the generated-file case it
will not reflect the |
| 229 // MIME type of the generated file. | 252 // MIME type of the generated file. |
| 230 String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileN
ame); | 253 String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileN
ame); |
| 231 if (!mimeType.isEmpty()) | 254 if (!mimeType.isEmpty()) |
| 232 formDataBuilder.addContentTypeToMultiPartHeader(header,
mimeType.latin1()); | 255 formDataBuilder.addContentTypeToMultiPartHeader(header,
mimeType.latin1()); |
| 233 } | 256 } |
| 234 } | 257 } |
| 235 | 258 |
| 236 formDataBuilder.finishMultiPartHeader(header); | 259 formDataBuilder.finishMultiPartHeader(header); |
| 237 | 260 |
| 238 // Append body | 261 // Append body |
| 239 appendData(header.data(), header.size()); | 262 appendData(header.data(), header.size()); |
| 240 if (size_t dataSize = value.data().length()) | 263 appendItem(value.get(), shouldGenerateFile); |
| 241 appendData(value.data().data(), dataSize); | |
| 242 else if (value.blob() && !value.blob()->path().isEmpty()) | |
| 243 #if ENABLE(BLOB_SLICE) | |
| 244 appendFileRange(value.blob()->path(), value.blob()->start(), val
ue.blob()->length(), value.blob()->modificationTime(), shouldGenerateFile); | |
| 245 #else | |
| 246 appendFile(value.blob()->path(), shouldGenerateFile); | |
| 247 #endif | |
| 248 | |
| 249 appendData("\r\n", 2); | 264 appendData("\r\n", 2); |
| 250 } else { | 265 } else { |
| 251 // Omit the name "isindex" if it's the first form data element. | 266 // Omit the name "isindex" if it's the first form data element. |
| 252 // FIXME: Why is this a good rule? Is this obsolete now? | 267 // FIXME: Why is this a good rule? Is this obsolete now? |
| 253 if (encodedData.isEmpty() && key.data() == "isindex") | 268 const StringBlobItem* stringValue = value->toStringBlobItem(); |
| 254 FormDataBuilder::encodeStringAsFormData(encodedData, value.data(
)); | 269 if (!stringValue) |
| 270 continue; |
| 271 if (encodedData.isEmpty() && key->cstr() == "isindex") |
| 272 FormDataBuilder::encodeStringAsFormData(encodedData, stringValue
->cstr()); |
| 255 else | 273 else |
| 256 formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(
), value.data()); | 274 formDataBuilder.addKeyValuePairAsFormData(encodedData, key->cstr
(), stringValue->cstr()); |
| 257 } | 275 } |
| 258 } | 276 } |
| 259 | 277 |
| 260 if (isMultiPartForm) | 278 if (isMultiPartForm) |
| 261 formDataBuilder.addBoundaryToMultiPartHeader(encodedData, m_boundary.dat
a(), true); | 279 formDataBuilder.addBoundaryToMultiPartHeader(encodedData, m_boundary.dat
a(), true); |
| 262 | 280 |
| 263 appendData(encodedData.data(), encodedData.size()); | 281 appendData(encodedData.data(), encodedData.size()); |
| 264 } | 282 } |
| 265 | 283 |
| 266 void FormData::flatten(Vector<char>& data) const | 284 void FormData::flatten(Vector<char>& data) const |
| 267 { | 285 { |
| 268 // Concatenate all the byte arrays, but omit any files. | 286 // Concatenate all the byte arrays, but omit any files. |
| 269 data.clear(); | 287 data.clear(); |
| 270 size_t n = m_elements.size(); | 288 size_t n = m_elements.size(); |
| 271 for (size_t i = 0; i < n; ++i) { | 289 for (size_t i = 0; i < n; ++i) { |
| 272 const FormDataElement& e = m_elements[i]; | 290 const FormDataElement& e = m_elements[i]; |
| 273 if (e.m_type == FormDataElement::data) { | 291 if (e.m_type == FormDataElement::data) |
| 274 size_t oldSize = data.size(); | 292 data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size())); |
| 275 size_t delta = e.m_data.size(); | |
| 276 data.grow(oldSize + delta); | |
| 277 memcpy(data.data() + oldSize, e.m_data.data(), delta); | |
| 278 } | |
| 279 } | 293 } |
| 280 } | 294 } |
| 281 | 295 |
| 282 String FormData::flattenToString() const | 296 String FormData::flattenToString() const |
| 283 { | 297 { |
| 284 Vector<char> bytes; | 298 Vector<char> bytes; |
| 285 flatten(bytes); | 299 flatten(bytes); |
| 286 return Latin1Encoding().decode(bytes.data(), bytes.size()); | 300 return Latin1Encoding().decode(bytes.data(), bytes.size()); |
| 287 } | 301 } |
| 288 | 302 |
| 289 void FormData::generateFiles(Document* document) | 303 void FormData::generateFiles(Document* document) |
| 290 { | 304 { |
| 291 ASSERT(!m_hasGeneratedFiles); | 305 ASSERT(!m_hasGeneratedFiles); |
| 292 | 306 |
| 293 if (m_hasGeneratedFiles) | 307 if (m_hasGeneratedFiles) |
| 294 return; | 308 return; |
| 295 | 309 |
| 296 Page* page = document->page(); | 310 Page* page = document->page(); |
| 297 if (!page) | 311 if (!page) |
| 298 return; | 312 return; |
| 299 ChromeClient* client = page->chrome()->client(); | 313 ChromeClient* client = page->chrome()->client(); |
| 300 | 314 |
| 301 size_t n = m_elements.size(); | 315 size_t n = m_elements.size(); |
| 302 for (size_t i = 0; i < n; ++i) { | 316 for (size_t i = 0; i < n; ++i) { |
| 303 FormDataElement& e = m_elements[i]; | 317 FormDataElement& e = m_elements[i]; |
| 304 if (e.m_type == FormDataElement::encodedFile && e.m_shouldGenerateFile)
{ | 318 if (e.m_type == FormDataElement::encodedFile && e.m_shouldGenerateFile)
{ |
| 305 e.m_generatedFilename = client->generateReplacementFile(e.m_filename
); | 319 e.m_generatedFilename = client->generateReplacementFile(e.m_filename
); |
| 306 m_hasGeneratedFiles = true; | 320 m_hasGeneratedFiles = true; |
| 307 } | 321 } |
| 308 } | 322 } |
| 309 } | 323 } |
| 310 | 324 |
| 311 void FormData::removeGeneratedFilesIfNeeded() | 325 void FormData::removeGeneratedFilesIfNeeded() |
| 312 { | 326 { |
| 313 if (!m_hasGeneratedFiles) | 327 if (!m_hasGeneratedFiles) |
| 314 return; | 328 return; |
| 315 | 329 |
| 316 size_t n = m_elements.size(); | 330 size_t n = m_elements.size(); |
| 317 for (size_t i = 0; i < n; ++i) { | 331 for (size_t i = 0; i < n; ++i) { |
| 318 FormDataElement& e = m_elements[i]; | 332 FormDataElement& e = m_elements[i]; |
| 319 if (e.m_type == FormDataElement::encodedFile && !e.m_generatedFilename.i
sEmpty()) { | 333 if (e.m_type == FormDataElement::encodedFile && !e.m_generatedFilename.i
sEmpty()) { |
| 320 ASSERT(e.m_shouldGenerateFile); | 334 ASSERT(e.m_shouldGenerateFile); |
| 321 String directory = directoryName(e.m_generatedFilename); | 335 String directory = directoryName(e.m_generatedFilename); |
| 322 deleteFile(e.m_generatedFilename); | 336 deleteFile(e.m_generatedFilename); |
| 323 deleteEmptyDirectory(directory); | 337 deleteEmptyDirectory(directory); |
| 324 e.m_generatedFilename = String(); | 338 e.m_generatedFilename = String(); |
| 325 } | 339 } |
| 326 } | 340 } |
| 327 m_hasGeneratedFiles = false; | 341 m_hasGeneratedFiles = false; |
| 328 } | 342 } |
| 329 | 343 |
| 330 } // namespace WebCore | 344 } // namespace WebCore |
| OLD | NEW |