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 |