Finish off the FormData implementation
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 10 Sep 2017 22:56:58 +0000 (22:56 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sun, 10 Sep 2017 22:56:58 +0000 (22:56 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176659

Reviewed by Darin Adler.

LayoutTests/imported/w3c:

* web-platform-tests/XMLHttpRequest/FormData-append-expected.txt:
* web-platform-tests/XMLHttpRequest/formdata-delete-expected.txt:
* web-platform-tests/XMLHttpRequest/formdata-foreach-expected.txt:
* web-platform-tests/XMLHttpRequest/formdata-get-expected.txt:
* web-platform-tests/XMLHttpRequest/formdata-has-expected.txt:
* web-platform-tests/XMLHttpRequest/formdata-set-expected.txt:
* web-platform-tests/XMLHttpRequest/interfaces-expected.txt:

    Update results.

Source/WebCore:

* fileapi/Blob.cpp:
* fileapi/Blob.h:
* fileapi/File.cpp:
* fileapi/File.h:

    Add constructors / create functions for making File objects
    from an existing Blob or File with an override name.

* html/DOMFormData.cpp:
* html/DOMFormData.h:

    Add missing operations and iterator implementation
    and bring append up to spec by no ignoring empty names.

* html/DOMFormData.idl:

    Bring IDL up to spec. Leave its exposure to just the window for
    now, as FormData currently depends on the Document/Page for replace
    file generation and therefore cannot operate in a worker.

* html/FormDataList.cpp:
* html/FormDataList.h:

    Changes FormDataList::Item to a String key and Variant<RefPtr<File>, String>
    data, matching spec concepts more cleanly. Normalization / encoding has also
    been made lazy, and now does not happen until creating a FormData from the
    FormDataList.

    Since we now store Files, rather than Blobs, we follow the spec's 'create an
    entry' algorithm to convert Blobs into Files with the same backing bytes. This
    was previously done as part of FormData::appendKeyValuePairItems.

* html/HTMLKeygenElement.cpp:
(WebCore::HTMLKeygenElement::appendFormData):

    Remove unnecessary conversion to utf8, the data is base64 encoded, allowing
    us to remove an overload of appendData that took a CString.

* inspector/InspectorNetworkAgent.cpp:
(WebCore::buildObjectForResourceRequest):

    Update for new signature of FormData::flatten() which now
    returns a Vector, rather than takes one in.

* platform/network/FormData.h:
* platform/network/FormData.cpp:
(WebCore::FormData::FormData):
(WebCore::FormData::create):
(WebCore::FormData::createMultiPart):

    Cleanup redundancy by using auto.

(WebCore::FormData::appendKeyValuePairItems):

    Updated to handle new FormDataList item format (e.g. pairs of key / data) allowing
    us to remove two-by-two iteration. Some complexity was removed around Blobs, as
    FormDataList now always creates File.

    Since FormDataList no longer eagerly encodes / normalizes the keys and string data
    values, we now perform those operations here.

(WebCore::FormData::expandDataStore):
(WebCore::appendBlobResolved):
(WebCore::FormData::resolveBlobReferences):
(WebCore::FormData::generateFiles):
(WebCore::FormData::hasGeneratedFiles const):
(WebCore::FormData::hasOwnedGeneratedFiles const):
(WebCore::FormData::removeGeneratedFilesIfNeeded):

    Adopt auto and modern for-in loops.

(WebCore::FormData::flatten const):
(WebCore::FormData::flattenToString const):

    Update flatten to return a Vector, rather than take it in.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221839 268f45cc-cd09-0410-ab3c-d52691b4dbfc

22 files changed:
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/FormData-append-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/formdata-delete-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/formdata-foreach-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/formdata-get-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/formdata-has-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/formdata-set-expected.txt
LayoutTests/imported/w3c/web-platform-tests/XMLHttpRequest/interfaces-expected.txt
Source/WebCore/ChangeLog
Source/WebCore/fileapi/Blob.cpp
Source/WebCore/fileapi/Blob.h
Source/WebCore/fileapi/File.cpp
Source/WebCore/fileapi/File.h
Source/WebCore/html/DOMFormData.cpp
Source/WebCore/html/DOMFormData.h
Source/WebCore/html/DOMFormData.idl
Source/WebCore/html/FormDataList.cpp
Source/WebCore/html/FormDataList.h
Source/WebCore/html/HTMLKeygenElement.cpp
Source/WebCore/inspector/InspectorNetworkAgent.cpp
Source/WebCore/platform/network/FormData.cpp
Source/WebCore/platform/network/FormData.h

index 4b22d65..2861763 100644 (file)
@@ -1,3 +1,20 @@
+2017-09-09  Sam Weinig  <sam@webkit.org>
+
+        Finish off the FormData implementation
+        https://bugs.webkit.org/show_bug.cgi?id=176659
+
+        Reviewed by Darin Adler.
+
+        * web-platform-tests/XMLHttpRequest/FormData-append-expected.txt:
+        * web-platform-tests/XMLHttpRequest/formdata-delete-expected.txt:
+        * web-platform-tests/XMLHttpRequest/formdata-foreach-expected.txt:
+        * web-platform-tests/XMLHttpRequest/formdata-get-expected.txt:
+        * web-platform-tests/XMLHttpRequest/formdata-has-expected.txt:
+        * web-platform-tests/XMLHttpRequest/formdata-set-expected.txt:
+        * web-platform-tests/XMLHttpRequest/interfaces-expected.txt:
+        
+            Update results.
+
 2017-09-08  Antti Koivisto  <antti@apple.com>
 
         Remove support for >> descendant combinator syntax
index f684317..ca2ce71 100644 (file)
@@ -1,16 +1,16 @@
 
 PASS Passing a String object to FormData.append should work. 
-FAIL testFormDataAppend1 create_formdata(['key', 'value1']).get is not a function. (In 'create_formdata(['key', 'value1']).get('key')', 'create_formdata(['key', 'value1']).get' is undefined)
-FAIL testFormDataAppend2 create_formdata(['key', 'value2'], ['key', 'value1']).get is not a function. (In 'create_formdata(['key', 'value2'], ['key', 'value1']).get('key')', 'create_formdata(['key', 'value2'], ['key', 'value1']).get' is undefined)
-FAIL testFormDataAppendUndefined1 create_formdata(['key', undefined]).get is not a function. (In 'create_formdata(['key', undefined]).get('key')', 'create_formdata(['key', undefined]).get' is undefined)
-FAIL testFormDataAppendUndefined2 create_formdata(['key', undefined], ['key', 'value1']).get is not a function. (In 'create_formdata(['key', undefined], ['key', 'value1']).get('key')', 'create_formdata(['key', undefined], ['key', 'value1']).get' is undefined)
-FAIL testFormDataAppendNull1 create_formdata(['key', null]).get is not a function. (In 'create_formdata(['key', null]).get('key')', 'create_formdata(['key', null]).get' is undefined)
-FAIL testFormDataAppendNull2 create_formdata(['key', null], ['key', 'value1']).get is not a function. (In 'create_formdata(['key', null], ['key', 'value1']).get('key')', 'create_formdata(['key', null], ['key', 'value1']).get' is undefined)
-FAIL testFormDataAppendToForm1 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendToForm2 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendToFormUndefined1 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendToFormUndefined2 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendToFormNull1 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendToFormNull2 fd.get is not a function. (In 'fd.get('key')', 'fd.get' is undefined)
-FAIL testFormDataAppendEmptyBlob create_formdata(['key', new Blob(), 'blank.txt']).get is not a function. (In 'create_formdata(['key', new Blob(), 'blank.txt']).get('key')', 'create_formdata(['key', new Blob(), 'blank.txt']).get' is undefined)
+PASS testFormDataAppend1 
+PASS testFormDataAppend2 
+PASS testFormDataAppendUndefined1 
+PASS testFormDataAppendUndefined2 
+PASS testFormDataAppendNull1 
+PASS testFormDataAppendNull2 
+PASS testFormDataAppendToForm1 
+PASS testFormDataAppendToForm2 
+PASS testFormDataAppendToFormUndefined1 
+PASS testFormDataAppendToFormUndefined2 
+PASS testFormDataAppendToFormNull1 
+PASS testFormDataAppendToFormNull2 
+PASS testFormDataAppendEmptyBlob 
 
index 0833557..66b9e3a 100644 (file)
@@ -1,9 +1,9 @@
 
-FAIL testFormDataDelete fd.delete is not a function. (In 'fd.delete('key')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteFromForm fd.delete is not a function. (In 'fd.delete('key')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteFromFormNonExistentKey fd.delete is not a function. (In 'fd.delete('nil')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteFromFormOtherKey fd.delete is not a function. (In 'fd.delete('key1')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteFromEmptyForm fd.delete is not a function. (In 'fd.delete('key')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteNonExistentKey fd.delete is not a function. (In 'fd.delete('nil')', 'fd.delete' is undefined)
-FAIL testFormDataDeleteOtherKey fd.delete is not a function. (In 'fd.delete('key1')', 'fd.delete' is undefined)
+PASS testFormDataDelete 
+PASS testFormDataDeleteFromForm 
+PASS testFormDataDeleteFromFormNonExistentKey 
+PASS testFormDataDeleteFromFormOtherKey 
+PASS testFormDataDeleteFromEmptyForm 
+PASS testFormDataDeleteNonExistentKey 
+PASS testFormDataDeleteOtherKey 
 
index 2d6ead3..aaf6f64 100644 (file)
@@ -1,4 +1,6 @@
-CONSOLE MESSAGE: line 16: TypeError: fd.delete is not a function. (In 'fd.delete('n2')', 'fd.delete' is undefined)
 
-FAIL FormData: foreach TypeError: fd.delete is not a function. (In 'fd.delete('n2')', 'fd.delete' is undefined)
+PASS Iterator should return duplicate keys and non-deleted values 
+PASS Entries iterator should return duplicate keys and non-deleted values 
+PASS Keys iterator should return duplicates 
+PASS Values iterator should return non-deleted values 
 
index f9a6ba9..ab512c0 100644 (file)
@@ -1,14 +1,14 @@
 
-FAIL testFormDataGet create_formdata(['key', 'value1'], ['key', 'value2']).get is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).get('key')', 'create_formdata(['key', 'value1'], ['key', 'value2']).get' is undefined)
-FAIL testFormDataGetFromForm new FormData(document.getElementById('form')).get is not a function. (In 'new FormData(document.getElementById('form')).get('key')', 'new FormData(document.getElementById('form')).get' is undefined)
-FAIL testFormDataGetFromFormNull new FormData(document.getElementById('form')).get is not a function. (In 'new FormData(document.getElementById('form')).get('nil')', 'new FormData(document.getElementById('form')).get' is undefined)
-FAIL testFormDataGetFromEmptyForm new FormData(document.getElementById('empty-form')).get is not a function. (In 'new FormData(document.getElementById('empty-form')).get('key')', 'new FormData(document.getElementById('empty-form')).get' is undefined)
-FAIL testFormDataGetNull1 create_formdata(['key', 'value1'], ['key', 'value2']).get is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).get('nil')', 'create_formdata(['key', 'value1'], ['key', 'value2']).get' is undefined)
-FAIL testFormDataGetNull2 create_formdata().get is not a function. (In 'create_formdata().get('key')', 'create_formdata().get' is undefined)
-FAIL testFormDataGetAll create_formdata(['key', 'value1'], ['key', 'value2']).getAll is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).getAll('key')', 'create_formdata(['key', 'value1'], ['key', 'value2']).getAll' is undefined)
-FAIL testFormDataGetAllEmpty1 create_formdata(['key', 'value1'], ['key', 'value2']).getAll is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).getAll('nil')', 'create_formdata(['key', 'value1'], ['key', 'value2']).getAll' is undefined)
-FAIL testFormDataGetAllEmpty2 create_formdata().getAll is not a function. (In 'create_formdata().getAll('key')', 'create_formdata().getAll' is undefined)
-FAIL testFormDataGetAllFromForm new FormData(document.getElementById('form')).getAll is not a function. (In 'new FormData(document.getElementById('form')).getAll('key')', 'new FormData(document.getElementById('form')).getAll' is undefined)
-FAIL testFormDataGetAllFromFormNull new FormData(document.getElementById('form')).getAll is not a function. (In 'new FormData(document.getElementById('form')).getAll('nil')', 'new FormData(document.getElementById('form')).getAll' is undefined)
-FAIL testFormDataGetAllFromEmptyForm new FormData(document.getElementById('empty-form')).getAll is not a function. (In 'new FormData(document.getElementById('empty-form')).getAll('key')', 'new FormData(document.getElementById('empty-form')).getAll' is undefined)
+PASS testFormDataGet 
+PASS testFormDataGetFromForm 
+PASS testFormDataGetFromFormNull 
+PASS testFormDataGetFromEmptyForm 
+PASS testFormDataGetNull1 
+PASS testFormDataGetNull2 
+PASS testFormDataGetAll 
+PASS testFormDataGetAllEmpty1 
+PASS testFormDataGetAllEmpty2 
+PASS testFormDataGetAllFromForm 
+PASS testFormDataGetAllFromFormNull 
+PASS testFormDataGetAllFromEmptyForm 
 
index ec06042..fafb511 100644 (file)
@@ -1,8 +1,8 @@
 
-FAIL testFormDataHas create_formdata(['key', 'value1'], ['key', 'value2']).has is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).has('key')', 'create_formdata(['key', 'value1'], ['key', 'value2']).has' is undefined)
-FAIL testFormDataHasFromForm new FormData(document.getElementById('form')).has is not a function. (In 'new FormData(document.getElementById('form')).has('key')', 'new FormData(document.getElementById('form')).has' is undefined)
-FAIL testFormDataHasFromFormNull new FormData(document.getElementById('form')).has is not a function. (In 'new FormData(document.getElementById('form')).has('nil')', 'new FormData(document.getElementById('form')).has' is undefined)
-FAIL testFormDataHasFromEmptyForm new FormData(document.getElementById('empty-form')).has is not a function. (In 'new FormData(document.getElementById('empty-form')).has('key')', 'new FormData(document.getElementById('empty-form')).has' is undefined)
-FAIL testFormDataHasEmpty1 create_formdata(['key', 'value1'], ['key', 'value2']).has is not a function. (In 'create_formdata(['key', 'value1'], ['key', 'value2']).has('nil')', 'create_formdata(['key', 'value1'], ['key', 'value2']).has' is undefined)
-FAIL testFormDataHasEmpty2 create_formdata().has is not a function. (In 'create_formdata().has('key')', 'create_formdata().has' is undefined)
+PASS testFormDataHas 
+PASS testFormDataHasFromForm 
+PASS testFormDataHasFromFormNull 
+PASS testFormDataHasFromEmptyForm 
+PASS testFormDataHasEmpty1 
+PASS testFormDataHasEmpty2 
 
index f1a7005..f04616c 100644 (file)
@@ -1,16 +1,16 @@
 
-FAIL Passing a String object to FormData.set should work fd.set is not a function. (In 'fd.set("name", new String("value"))', 'fd.set' is undefined)
-FAIL testFormDataSet1 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSet2 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSetUndefined1 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSetUndefined2 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSetNull1 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSetNull2 undefined is not an object (evaluating 'fd.set.apply')
-FAIL testFormDataSetToForm1 fd.set is not a function. (In 'fd.set('key', 'value1')', 'fd.set' is undefined)
-FAIL testFormDataSetToForm2 fd.set is not a function. (In 'fd.set('key', 'value2')', 'fd.set' is undefined)
-FAIL testFormDataSetToFormUndefined1 fd.set is not a function. (In 'fd.set('key', undefined)', 'fd.set' is undefined)
-FAIL testFormDataSetToFormUndefined2 fd.set is not a function. (In 'fd.set('key', undefined)', 'fd.set' is undefined)
-FAIL testFormDataSetToFormNull1 fd.set is not a function. (In 'fd.set('key', null)', 'fd.set' is undefined)
-FAIL testFormDataSetToFormNull2 fd.set is not a function. (In 'fd.set('key', null)', 'fd.set' is undefined)
-FAIL testFormDataSetEmptyBlob fd.set is not a function. (In 'fd.set('key', new Blob([]), 'blank.txt')', 'fd.set' is undefined)
+PASS Passing a String object to FormData.set should work 
+PASS testFormDataSet1 
+PASS testFormDataSet2 
+PASS testFormDataSetUndefined1 
+PASS testFormDataSetUndefined2 
+PASS testFormDataSetNull1 
+PASS testFormDataSetNull2 
+PASS testFormDataSetToForm1 
+PASS testFormDataSetToForm2 
+PASS testFormDataSetToFormUndefined1 
+PASS testFormDataSetToFormUndefined2 
+PASS testFormDataSetToFormNull1 
+PASS testFormDataSetToFormNull2 
+PASS testFormDataSetEmptyBlob 
 
index 8496013..bc86927 100644 (file)
@@ -108,44 +108,44 @@ PASS FormData interface: existence and properties of interface prototype object
 PASS FormData interface: existence and properties of interface prototype object's "constructor" property 
 PASS FormData interface: operation append(USVString,Blob,USVString) 
 PASS FormData interface: operation append(USVString,USVString) 
-FAIL FormData interface: operation delete(USVString) assert_own_property: interface prototype object missing non-static operation expected property "delete" missing
-FAIL FormData interface: operation get(USVString) assert_own_property: interface prototype object missing non-static operation expected property "get" missing
-FAIL FormData interface: operation getAll(USVString) assert_own_property: interface prototype object missing non-static operation expected property "getAll" missing
-FAIL FormData interface: operation has(USVString) assert_own_property: interface prototype object missing non-static operation expected property "has" missing
-FAIL FormData interface: operation set(USVString,Blob,USVString) assert_own_property: interface prototype object missing non-static operation expected property "set" missing
-FAIL FormData interface: operation set(USVString,USVString) assert_own_property: interface prototype object missing non-static operation expected property "set" missing
+PASS FormData interface: operation delete(USVString) 
+PASS FormData interface: operation get(USVString) 
+PASS FormData interface: operation getAll(USVString) 
+PASS FormData interface: operation has(USVString) 
+PASS FormData interface: operation set(USVString,Blob,USVString) 
+PASS FormData interface: operation set(USVString,USVString) 
 PASS FormData interface: new FormData() must inherit property "append" with the proper type (0) 
 PASS FormData interface: calling append(USVString,Blob,USVString) on new FormData() with too few arguments must throw TypeError 
 PASS FormData interface: new FormData() must inherit property "append" with the proper type (1) 
 PASS FormData interface: calling append(USVString,USVString) on new FormData() with too few arguments must throw TypeError 
-FAIL FormData interface: new FormData() must inherit property "delete" with the proper type (2) assert_inherits: property "delete" not found in prototype chain
-FAIL FormData interface: calling delete(USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "delete" not found in prototype chain
-FAIL FormData interface: new FormData() must inherit property "get" with the proper type (3) assert_inherits: property "get" not found in prototype chain
-FAIL FormData interface: calling get(USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "get" not found in prototype chain
-FAIL FormData interface: new FormData() must inherit property "getAll" with the proper type (4) assert_inherits: property "getAll" not found in prototype chain
-FAIL FormData interface: calling getAll(USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "getAll" not found in prototype chain
-FAIL FormData interface: new FormData() must inherit property "has" with the proper type (5) assert_inherits: property "has" not found in prototype chain
-FAIL FormData interface: calling has(USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "has" not found in prototype chain
-FAIL FormData interface: new FormData() must inherit property "set" with the proper type (6) assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: calling set(USVString,Blob,USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: new FormData() must inherit property "set" with the proper type (7) assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: calling set(USVString,USVString) on new FormData() with too few arguments must throw TypeError assert_inherits: property "set" not found in prototype chain
+PASS FormData interface: new FormData() must inherit property "delete" with the proper type (2) 
+PASS FormData interface: calling delete(USVString) on new FormData() with too few arguments must throw TypeError 
+PASS FormData interface: new FormData() must inherit property "get" with the proper type (3) 
+PASS FormData interface: calling get(USVString) on new FormData() with too few arguments must throw TypeError 
+PASS FormData interface: new FormData() must inherit property "getAll" with the proper type (4) 
+PASS FormData interface: calling getAll(USVString) on new FormData() with too few arguments must throw TypeError 
+PASS FormData interface: new FormData() must inherit property "has" with the proper type (5) 
+PASS FormData interface: calling has(USVString) on new FormData() with too few arguments must throw TypeError 
+PASS FormData interface: new FormData() must inherit property "set" with the proper type (6) 
+PASS FormData interface: calling set(USVString,Blob,USVString) on new FormData() with too few arguments must throw TypeError 
+PASS FormData interface: new FormData() must inherit property "set" with the proper type (7) 
+PASS FormData interface: calling set(USVString,USVString) on new FormData() with too few arguments must throw TypeError 
 PASS FormData interface: new FormData(form) must inherit property "append" with the proper type (0) 
 PASS FormData interface: calling append(USVString,Blob,USVString) on new FormData(form) with too few arguments must throw TypeError 
 PASS FormData interface: new FormData(form) must inherit property "append" with the proper type (1) 
 PASS FormData interface: calling append(USVString,USVString) on new FormData(form) with too few arguments must throw TypeError 
-FAIL FormData interface: new FormData(form) must inherit property "delete" with the proper type (2) assert_inherits: property "delete" not found in prototype chain
-FAIL FormData interface: calling delete(USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "delete" not found in prototype chain
-FAIL FormData interface: new FormData(form) must inherit property "get" with the proper type (3) assert_inherits: property "get" not found in prototype chain
-FAIL FormData interface: calling get(USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "get" not found in prototype chain
-FAIL FormData interface: new FormData(form) must inherit property "getAll" with the proper type (4) assert_inherits: property "getAll" not found in prototype chain
-FAIL FormData interface: calling getAll(USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "getAll" not found in prototype chain
-FAIL FormData interface: new FormData(form) must inherit property "has" with the proper type (5) assert_inherits: property "has" not found in prototype chain
-FAIL FormData interface: calling has(USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "has" not found in prototype chain
-FAIL FormData interface: new FormData(form) must inherit property "set" with the proper type (6) assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: calling set(USVString,Blob,USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: new FormData(form) must inherit property "set" with the proper type (7) assert_inherits: property "set" not found in prototype chain
-FAIL FormData interface: calling set(USVString,USVString) on new FormData(form) with too few arguments must throw TypeError assert_inherits: property "set" not found in prototype chain
+PASS FormData interface: new FormData(form) must inherit property "delete" with the proper type (2) 
+PASS FormData interface: calling delete(USVString) on new FormData(form) with too few arguments must throw TypeError 
+PASS FormData interface: new FormData(form) must inherit property "get" with the proper type (3) 
+PASS FormData interface: calling get(USVString) on new FormData(form) with too few arguments must throw TypeError 
+PASS FormData interface: new FormData(form) must inherit property "getAll" with the proper type (4) 
+PASS FormData interface: calling getAll(USVString) on new FormData(form) with too few arguments must throw TypeError 
+PASS FormData interface: new FormData(form) must inherit property "has" with the proper type (5) 
+PASS FormData interface: calling has(USVString) on new FormData(form) with too few arguments must throw TypeError 
+PASS FormData interface: new FormData(form) must inherit property "set" with the proper type (6) 
+PASS FormData interface: calling set(USVString,Blob,USVString) on new FormData(form) with too few arguments must throw TypeError 
+PASS FormData interface: new FormData(form) must inherit property "set" with the proper type (7) 
+PASS FormData interface: calling set(USVString,USVString) on new FormData(form) with too few arguments must throw TypeError 
 PASS ProgressEvent interface: existence and properties of interface object 
 PASS ProgressEvent interface object length 
 PASS ProgressEvent interface object name 
index 928f85b..7ce5762 100644 (file)
@@ -1,3 +1,86 @@
+2017-09-09  Sam Weinig  <sam@webkit.org>
+
+        Finish off the FormData implementation
+        https://bugs.webkit.org/show_bug.cgi?id=176659
+
+        Reviewed by Darin Adler.
+
+        * fileapi/Blob.cpp:
+        * fileapi/Blob.h:
+        * fileapi/File.cpp:
+        * fileapi/File.h:
+
+            Add constructors / create functions for making File objects
+            from an existing Blob or File with an override name.
+
+        * html/DOMFormData.cpp:
+        * html/DOMFormData.h:
+
+            Add missing operations and iterator implementation
+            and bring append up to spec by no ignoring empty names.
+
+        * html/DOMFormData.idl:
+
+            Bring IDL up to spec. Leave its exposure to just the window for
+            now, as FormData currently depends on the Document/Page for replace
+            file generation and therefore cannot operate in a worker.
+
+        * html/FormDataList.cpp:
+        * html/FormDataList.h:
+
+            Changes FormDataList::Item to a String key and Variant<RefPtr<File>, String>
+            data, matching spec concepts more cleanly. Normalization / encoding has also
+            been made lazy, and now does not happen until creating a FormData from the
+            FormDataList.
+
+            Since we now store Files, rather than Blobs, we follow the spec's 'create an 
+            entry' algorithm to convert Blobs into Files with the same backing bytes. This
+            was previously done as part of FormData::appendKeyValuePairItems.
+
+        * html/HTMLKeygenElement.cpp:
+        (WebCore::HTMLKeygenElement::appendFormData):
+
+            Remove unnecessary conversion to utf8, the data is base64 encoded, allowing
+            us to remove an overload of appendData that took a CString.
+
+        * inspector/InspectorNetworkAgent.cpp:
+        (WebCore::buildObjectForResourceRequest):
+
+            Update for new signature of FormData::flatten() which now
+            returns a Vector, rather than takes one in.
+
+        * platform/network/FormData.h:
+        * platform/network/FormData.cpp:
+        (WebCore::FormData::FormData):
+        (WebCore::FormData::create):
+        (WebCore::FormData::createMultiPart):
+        
+            Cleanup redundancy by using auto.
+        
+        (WebCore::FormData::appendKeyValuePairItems):
+        
+            Updated to handle new FormDataList item format (e.g. pairs of key / data) allowing
+            us to remove two-by-two iteration. Some complexity was removed around Blobs, as
+            FormDataList now always creates File. 
+
+            Since FormDataList no longer eagerly encodes / normalizes the keys and string data
+            values, we now perform those operations here.
+        
+        (WebCore::FormData::expandDataStore):
+        (WebCore::appendBlobResolved):
+        (WebCore::FormData::resolveBlobReferences):
+        (WebCore::FormData::generateFiles):
+        (WebCore::FormData::hasGeneratedFiles const):
+        (WebCore::FormData::hasOwnedGeneratedFiles const):
+        (WebCore::FormData::removeGeneratedFilesIfNeeded):
+
+            Adopt auto and modern for-in loops.
+
+        (WebCore::FormData::flatten const):
+        (WebCore::FormData::flattenToString const):
+
+            Update flatten to return a Vector, rather than take it in.
+
 2017-09-10  Darin Adler  <darin@apple.com>
 
         Refactor Document::updateTitleElement to use traits instead of function pointers
index f595b1d..8cc2e80 100644 (file)
@@ -79,6 +79,14 @@ Blob::Blob()
     ThreadableBlobRegistry::registerBlobURL(m_internalURL, { },  { });
 }
 
+Blob::Blob(const Blob& blob)
+    : m_internalURL(BlobURL::createInternalURL())
+    , m_type(blob.type())
+    , m_size(blob.size())
+{
+    ThreadableBlobRegistry::registerBlobURL(m_internalURL, { BlobPart(blob.url()) } , m_type);
+}
+
 Blob::Blob(Vector<BlobPartVariant>&& blobPartVariants, const BlobPropertyBag& propertyBag)
     : m_internalURL(BlobURL::createInternalURL())
     , m_type(normalizedContentType(propertyBag.type))
index e7b6f8e..023b9d3 100644 (file)
@@ -98,6 +98,7 @@ public:
 
 protected:
     Blob();
+    Blob(const Blob&);
     Blob(Vector<BlobPartVariant>&&, const BlobPropertyBag&);
     Blob(Vector<uint8_t>&&, const String& contentType);
 
index 138d4b2..dbc74d3 100644 (file)
@@ -85,6 +85,23 @@ File::File(Vector<BlobPartVariant>&& blobPartVariants, const String& filename, c
 {
 }
 
+File::File(const Blob& blob, const String& name)
+    : Blob(blob)
+    , m_name(name)
+{
+    ASSERT(!blob.isFile());
+}
+
+File::File(const File& file, const String& name)
+    : Blob(file)
+    , m_path(file.path())
+    , m_relativePath(file.relativePath())
+    , m_name(!name.isNull() ? name : file.name())
+    , m_overrideLastModifiedDate(file.m_overrideLastModifiedDate)
+    , m_isDirectory(file.isDirectory())
+{
+}
+
 double File::lastModified() const
 {
     if (m_overrideLastModifiedDate)
index a9601f5..f797c4a 100644 (file)
@@ -65,6 +65,16 @@ public:
         return adoptRef(*new File(path, nameOverride));
     }
 
+    static Ref<File> create(const Blob& blob, const String& name)
+    {
+        return adoptRef(*new File(blob, name));
+    }
+
+    static Ref<File> create(const File& file, const String& name)
+    {
+        return adoptRef(*new File(file, name));
+    }
+
     static Ref<File> createWithRelativePath(const String& path, const String& relativePath);
 
     bool isFile() const override { return true; }
@@ -87,6 +97,8 @@ private:
     WEBCORE_EXPORT explicit File(const String& path);
     File(const String& path, const String& nameOverride);
     File(Vector<BlobPartVariant>&& blobPartVariants, const String& filename, const PropertyBag&);
+    File(const Blob&, const String& name);
+    File(const File&, const String& name);
 
     File(DeserializationContructor, const String& path, const URL& srcURL, const String& type, const String& name);
 
index 6442d57..9ffe98d 100644 (file)
@@ -55,14 +55,76 @@ DOMFormData::DOMFormData(HTMLFormElement* form)
 
 void DOMFormData::append(const String& name, const String& value)
 {
-    if (!name.isEmpty())
-        appendData(name, value);
+    appendData(name, value);
 }
 
 void DOMFormData::append(const String& name, Blob& blob, const String& filename)
 {
-    if (!name.isEmpty())
-        appendBlob(name, blob, filename);
+    appendBlob(name, blob, filename);
+}
+
+void DOMFormData::remove(const String& name)
+{
+    m_items.removeAllMatching([&name] (const auto& item) {
+        return item.name == name;
+    });
+}
+
+auto DOMFormData::get(const String& name) -> std::optional<FormDataEntryValue>
+{
+    for (auto& item : m_items) {
+        if (item.name == name)
+            return item.data;
+    }
+
+    return std::nullopt;
+}
+
+auto DOMFormData::getAll(const String& name) -> Vector<FormDataEntryValue>
+{
+    Vector<FormDataEntryValue> result;
+
+    for (auto& item : m_items) {
+        if (item.name == name)
+            result.append(item.data);
+    }
+
+    return result;
+}
+
+bool DOMFormData::has(const String& name)
+{
+    for (auto& item : m_items) {
+        if (item.name == name)
+            return true;
+    }
+    
+    return false;
+}
+
+void DOMFormData::set(const String& name, const String& value)
+{
+    setData(name, value);
+}
+
+void DOMFormData::set(const String& name, Blob& blob, const String& filename)
+{
+    setBlob(name, blob, filename);
+}
+
+DOMFormData::Iterator::Iterator(DOMFormData& target)
+    : m_target(target)
+{
+}
+
+std::optional<WTF::KeyValuePair<String, DOMFormData::FormDataEntryValue>> DOMFormData::Iterator::next()
+{
+    auto& items = m_target->items();
+    if (m_index >= items.size())
+        return std::nullopt;
+
+    auto& item = items[m_index++];
+    return WTF::KeyValuePair<String, FormDataEntryValue> { item.name, item.data };
 }
 
 } // namespace WebCore
index adbcbbf..c78f246 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "FormDataList.h"
 #include <wtf/Forward.h>
+#include <wtf/Optional.h>
 #include <wtf/RefCounted.h>
 
 namespace WebCore {
@@ -40,13 +41,33 @@ class Blob;
 class HTMLFormElement;
 class TextEncoding;
 
+// FIXME: Do we need to have separate DOMFormData and FormDataList classes?
 class DOMFormData : public FormDataList, public RefCounted<DOMFormData> {
 public:
     static Ref<DOMFormData> create(HTMLFormElement* form) { return adoptRef(*new DOMFormData(form)); }
     static Ref<DOMFormData> create(const TextEncoding& encoding) { return adoptRef(*new DOMFormData(encoding)); }
 
+    using FormDataEntryValue = Item::Data;
+
     void append(const String& name, const String& value);
-    void append(const String& name, Blob&, const String& filename = String());
+    void append(const String& name, Blob&, const String& filename = { });
+    void remove(const String& name);
+    std::optional<FormDataEntryValue> get(const String& name);
+    Vector<FormDataEntryValue> getAll(const String& name);
+    bool has(const String& name);
+    void set(const String& name, const String& value);
+    void set(const String& name, Blob&, const String& filename = { });
+
+    class Iterator {
+    public:
+        explicit Iterator(DOMFormData&);
+        std::optional<WTF::KeyValuePair<String, FormDataEntryValue>> next();
+
+    private:
+        Ref<DOMFormData> m_target;
+        size_t m_index { 0 };
+    };
+    Iterator createIterator() { return Iterator { *this }; }
 
 private:
     explicit DOMFormData(const TextEncoding&);
index c8a7f46..5f622fe 100644 (file)
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+typedef (File or USVString) FormDataEntryValue;
+
+// FIXME: Should be Exposed=(Window,Worker) It is not currently possible to do this due to
+// Fetch and XMLHttpRequest expecting to have Document when processing DOMFormData to perform
+// file replacement (https://webkit.org/b/176674).
+
 [
     Constructor(optional HTMLFormElement? form),
     JSGenerateToNativeObject,
     ImplementationLacksVTable,
 ] interface DOMFormData {
     void append(USVString name, USVString value);
-    void append(USVString name, Blob value, optional USVString filename);
-};
+    void append(USVString name, Blob blobValue, optional USVString filename);
+    [ImplementedAs=remove] void delete(USVString name);
+    FormDataEntryValue? get(USVString name);
+    sequence<FormDataEntryValue> getAll(USVString name);
+    boolean has(USVString name);
+    void set(USVString name, USVString value);
+    void set(USVString name, Blob blobValue, optional USVString filename);
 
+    iterable<USVString, FormDataEntryValue>;
+};
index 644faf7..6cce4da 100644 (file)
 #include "FormDataList.h"
 
 #include "LineEnding.h"
-#include <wtf/text/StringView.h>
+#include <wtf/text/CString.h>
 
 namespace WebCore {
 
-FormDataList::FormDataList(const TextEncoding& c)
-    : m_encoding(c)
+FormDataList::FormDataList(const TextEncoding& encoding)
+    : m_encoding(encoding)
 {
 }
 
-void FormDataList::appendString(const String& s)
+CString FormDataList::normalizeString(const String& value) const
 {
-    CString cstr = m_encoding.encode(s, EntitiesForUnencodables);
-    m_items.append(normalizeLineEndingsToCRLF(cstr));
+    return normalizeLineEndingsToCRLF(m_encoding.encode(value, EntitiesForUnencodables));
 }
 
-void FormDataList::appendString(const CString& s)
+// https://xhr.spec.whatwg.org/#create-an-entry
+auto FormDataList::createFileEntry(const String& name, Ref<Blob>&& blob, const String& filename) -> Item
 {
-    m_items.append(s);
+    if (!blob->isFile())
+        return { name, File::create(blob.get(), filename.isNull() ? ASCIILiteral("blob") : filename) };
+    
+    if (!filename.isNull())
+        return { name, File::create(downcast<File>(blob.get()), filename) };
+
+    return { name, static_reference_cast<File>(WTFMove(blob)) };
+}
+
+void FormDataList::appendData(const String& name, const String& value)
+{
+    m_items.append({ name, value });
 }
 
-void FormDataList::appendBlob(Ref<Blob>&& blob, const String& filename)
+void FormDataList::appendData(const String& name, int value)
 {
-    m_items.append(Item(WTFMove(blob), filename));
+    m_items.append({ name, String::number(value) });
 }
 
-} // namespace
+void FormDataList::appendBlob(const String& name, Ref<Blob>&& blob, const String& filename)
+{
+    m_items.append(createFileEntry(name, WTFMove(blob), filename));
+}
+
+void FormDataList::set(const String& name, Item&& item)
+{
+    std::optional<size_t> initialMatchLocation;
+
+    // Find location of the first item with a matching name.
+    for (size_t i = 0; i < m_items.size(); ++i) {
+        if (name == m_items[i].name) {
+            initialMatchLocation = i;
+            break;
+        }
+    }
+
+    if (initialMatchLocation) {
+        m_items[*initialMatchLocation] = WTFMove(item);
+
+        m_items.removeAllMatching([&name] (const auto& item) {
+            return item.name == name;
+        }, *initialMatchLocation + 1);
+        return;
+    }
+
+    m_items.append(WTFMove(item));
+}
+
+void FormDataList::setData(const String& name, const String& value)
+{
+    set(name, { name, value });
+}
+
+void FormDataList::setBlob(const String& name, Ref<Blob>&& blob, const String& filename)
+{
+    set(name, createFileEntry(name, WTFMove(blob), filename));
+}
+
+}
index aa2c297..0cb50ad 100644 (file)
 
 #pragma once
 
-#include "Blob.h"
+#include "File.h"
 #include "TextEncoding.h"
 #include <wtf/Forward.h>
-#include <wtf/text/CString.h>
+#include <wtf/Variant.h>
 
 namespace WebCore {
 
 class FormDataList {
 public:
-    class Item {
-    public:
-        Item() { }
-        Item(const WTF::CString& data) : m_data(data) { }
-        Item(Ref<Blob>&& blob, const String& filename)
-            : m_blob(WTFMove(blob))
-            , m_filename(filename)
-        { }
+    struct Item {
+        using Data = Variant<RefPtr<File>, String>;
 
-        const WTF::CString& data() const { return m_data; }
-        Blob* blob() const { return m_blob.get(); }
-        const String& filename() const { return m_filename; }
-
-    private:
-        WTF::CString m_data;
-        RefPtr<Blob> m_blob;
-        String m_filename;
+        String name;
+        Data data;
     };
 
-    FormDataList(const TextEncoding&);
-
-    void appendData(const String& key, const String& value)
-    {
-        appendString(key);
-        appendString(value);
-    }
-    void appendData(const String& key, const CString& value)
-    {
-        appendString(key);
-        appendString(value);
-    }
-    void appendData(const String& key, int value)
-    {
-        appendString(key);
-        appendString(String::number(value));
-    }
-    void appendBlob(const String& key, Ref<Blob>&& blob, const String& filename = String())
-    {
-        appendString(key);
-        appendBlob(WTFMove(blob), filename);
-    }
+    void appendData(const String& name, const String& value);
+    void appendData(const String& name, int value);
+    void appendBlob(const String& name, Ref<Blob>&&, const String& filename = { });
+    void setData(const String& name, const String& value);
+    void setBlob(const String& name, Ref<Blob>&&, const String& filename = { });
 
     const Vector<Item>& items() const { return m_items; }
     const TextEncoding& encoding() const { return m_encoding; }
 
-private:
-    void appendString(const CString&);
-    void appendString(const String&);
-    void appendBlob(Ref<Blob>&&, const String& filename);
+    CString normalizeString(const String&) const;
+
+protected:
+    FormDataList(const TextEncoding&);
+
+    Item createFileEntry(const String& name, Ref<Blob>&&, const String& filename);
+    void set(const String& name, Item&&);
 
     TextEncoding m_encoding;
     Vector<Item> m_items;
index 3758ae3..afc2847 100644 (file)
@@ -123,7 +123,7 @@ bool HTMLKeygenElement::appendFormData(FormDataList& encoded_values, bool)
     String value = signedPublicKeyAndChallengeString(shadowSelect()->selectedIndex(), attributeWithoutSynchronization(challengeAttr), document().baseURL());
     if (value.isNull())
         return false;
-    encoded_values.appendData(name(), value.utf8());
+    encoded_values.appendData(name(), value);
     return true;
 }
 
index 8a9f066..a383897 100644 (file)
@@ -256,8 +256,7 @@ static Ref<Inspector::Protocol::Network::Request> buildObjectForResourceRequest(
         .setHeaders(buildObjectForHeaders(request.httpHeaderFields()))
         .release();
     if (request.httpBody() && !request.httpBody()->isEmpty()) {
-        Vector<char> bytes;
-        request.httpBody()->flatten(bytes);
+        auto bytes = request.httpBody()->flatten();
         requestObject->setPostData(String::fromUTF8WithLatin1Fallback(bytes.data(), bytes.size()));
     }
     return requestObject;
index 4d1801f..c037940 100644 (file)
@@ -50,7 +50,7 @@ inline FormData::FormData(const FormData& data)
 {
     // We shouldn't be copying FormData that hasn't already removed its generated files
     // but just in case, make sure the new FormData is ready to generate its own files.
-    for (FormDataElement& element : m_elements) {
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::EncodedFile) {
             element.m_generatedFilename = String();
             element.m_ownsGeneratedFile = false;
@@ -73,35 +73,35 @@ Ref<FormData> FormData::create()
 
 Ref<FormData> FormData::create(const void* data, size_t size)
 {
-    Ref<FormData> result = create();
+    auto result = create();
     result->appendData(data, size);
     return result;
 }
 
 Ref<FormData> FormData::create(const CString& string)
 {
-    Ref<FormData> result = create();
+    auto result = create();
     result->appendData(string.data(), string.length());
     return result;
 }
 
 Ref<FormData> FormData::create(const Vector<char>& vector)
 {
-    Ref<FormData> result = create();
+    auto result = create();
     result->appendData(vector.data(), vector.size());
     return result;
 }
 
 Ref<FormData> FormData::create(const FormDataList& list, const TextEncoding& encoding, EncodingType encodingType)
 {
-    Ref<FormData> result = create();
-    result->appendKeyValuePairItems(list, encoding, false, 0, encodingType);
+    auto result = create();
+    result->appendKeyValuePairItems(list, encoding, false, nullptr, encodingType);
     return result;
 }
 
 Ref<FormData> FormData::createMultiPart(const FormDataList& list, const TextEncoding& encoding, Document* document)
 {
-    Ref<FormData> result = create();
+    auto result = create();
     result->appendKeyValuePairItems(list, encoding, true, document);
     return result;
 }
@@ -188,52 +188,36 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod
         m_boundary = FormDataBuilder::generateUniqueBoundaryString();
 
     Vector<char> encodedData;
-
-    const Vector<FormDataList::Item>& items = list.items();
-    size_t formDataListSize = items.size();
-    ASSERT(!(formDataListSize % 2));
-    for (size_t i = 0; i < formDataListSize; i += 2) {
-        const FormDataList::Item& key = items[i];
-        const FormDataList::Item& value = items[i + 1];
+    for (const auto& item : list.items()) {
+        auto normalizedName = list.normalizeString(item.name);
+    
         if (isMultiPartForm) {
             Vector<char> header;
-            FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), key.data());
+            FormDataBuilder::beginMultiPartHeader(header, m_boundary.data(), normalizedName);
 
             bool shouldGenerateFile = false;
 
-            // If the current type is blob, then we also need to include the filename
-            if (value.blob()) {
-                String name;
-                if (is<File>(*value.blob())) {
-                    File& file = downcast<File>(*value.blob());
-                    name = file.name();
-                    // Let the application specify a filename if it's going to generate a replacement file for the upload.
-                    const String& path = file.path();
-                    if (!path.isEmpty()) {
-                        if (Page* page = document->page()) {
-                            String generatedFileName;
-                            shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
-                            if (shouldGenerateFile)
-                                name = generatedFileName;
-                        }
+            if (WTF::holds_alternative<RefPtr<File>>(item.data)) {
+                // If the current type is a file, then we also need to include the filename
+                auto& file = *WTF::get<RefPtr<File>>(item.data);
+                auto name = file.name();
+
+                // Let the application specify a filename if it's going to generate a replacement file for the upload.
+                const auto& path = file.path();
+                if (!path.isEmpty()) {
+                    if (Page* page = document->page()) {
+                        String generatedFileName;
+                        shouldGenerateFile = page->chrome().client().shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
+                        if (shouldGenerateFile)
+                            name = generatedFileName;
                     }
-
-                    // If a filename is passed in FormData.append(), use it instead of the file blob's name.
-                    if (!value.filename().isNull())
-                        name = value.filename();
-                } else {
-                    // For non-file blob, use the filename if it is passed in FormData.append().
-                    if (!value.filename().isNull())
-                        name = value.filename();
-                    else
-                        name = "blob";
                 }
 
                 // We have to include the filename=".." part in the header, even if the filename is empty
                 FormDataBuilder::addFilenameToMultiPartHeader(header, encoding, name);
 
                 // Add the content type if available, or "application/octet-stream" otherwise (RFC 1867).
-                String contentType = value.blob()->type();
+                auto contentType = file.type();
                 if (contentType.isEmpty())
                     contentType = "application/octet-stream";
                 ASSERT(Blob::isNormalizedContentType(contentType));
@@ -242,22 +226,26 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod
 
             FormDataBuilder::finishMultiPartHeader(header);
 
-            // Append body
             appendData(header.data(), header.size());
-            if (value.blob()) {
-                if (is<File>(*value.blob())) {
-                    File& file = downcast<File>(*value.blob());
-                    // Do not add the file if the path is empty.
-                    if (!file.path().isEmpty())
-                        appendFile(file.path(), shouldGenerateFile);
-                }
+
+            if (WTF::holds_alternative<RefPtr<File>>(item.data)) {
+                auto& file = *WTF::get<RefPtr<File>>(item.data);
+                if (!file.path().isEmpty())
+                    appendFile(file.path(), shouldGenerateFile);
                 else
-                    appendBlob(value.blob()->url());
-            } else
-                appendData(value.data().data(), value.data().length());
+                    appendBlob(file.url());
+            } else {
+                auto normalizedStringData = list.normalizeString(WTF::get<String>(item.data));
+                appendData(normalizedStringData.data(), normalizedStringData.length());
+            }
+
             appendData("\r\n", 2);
-        } else
-            FormDataBuilder::addKeyValuePairAsFormData(encodedData, key.data(), value.data(), encodingType);
+        } else {
+            ASSERT(WTF::holds_alternative<String>(item.data));
+
+            auto normalizedStringData = list.normalizeString(WTF::get<String>(item.data));
+            FormDataBuilder::addKeyValuePairAsFormData(encodedData, normalizedName, normalizedStringData, encodingType);
+        }
     }
 
     if (isMultiPartForm)
@@ -270,29 +258,31 @@ char* FormData::expandDataStore(size_t size)
 {
     m_lengthInBytes = std::nullopt;
     if (m_elements.isEmpty() || m_elements.last().m_type != FormDataElement::Type::Data)
-        m_elements.append(FormDataElement());
-    FormDataElement& e = m_elements.last();
-    size_t oldSize = e.m_data.size();
-    e.m_data.grow(oldSize + size);
-    return e.m_data.data() + oldSize;
+        m_elements.append({ });
+
+    auto& lastElement = m_elements.last();
+    size_t oldSize = lastElement.m_data.size();
+
+    auto newSize = Checked<size_t>(oldSize) + size;
+
+    lastElement.m_data.grow(newSize.unsafeGet());
+    return lastElement.m_data.data() + oldSize;
 }
 
-void FormData::flatten(Vector<char>& data) const
+Vector<char> FormData::flatten() const
 {
     // Concatenate all the byte arrays, but omit any files.
-    data.clear();
-    size_t n = m_elements.size();
-    for (size_t i = 0; i < n; ++i) {
-        const FormDataElement& e = m_elements[i];
-        if (e.m_type == FormDataElement::Type::Data)
-            data.append(e.m_data.data(), static_cast<size_t>(e.m_data.size()));
+    Vector<char> data;
+    for (auto& element : m_elements) {
+        if (element.m_type == FormDataElement::Type::Data)
+            data.append(element.m_data.data(), static_cast<size_t>(element.m_data.size()));
     }
+    return data;
 }
 
 String FormData::flattenToString() const
 {
-    Vector<char> bytes;
-    flatten(bytes);
+    auto bytes = flatten();
     return Latin1Encoding().decode(reinterpret_cast<const char*>(bytes.data()), bytes.size());
 }
 
@@ -302,16 +292,14 @@ static void appendBlobResolved(FormData* formData, const URL& url)
         LOG_ERROR("Tried to resolve a blob without a usable registry");
         return;
     }
+
     BlobData* blobData = static_cast<BlobRegistryImpl&>(blobRegistry()).getBlobDataFromURL(url);
     if (!blobData) {
         LOG_ERROR("Could not get blob data from a registry");
         return;
     }
 
-    BlobDataItemList::const_iterator it = blobData->items().begin();
-    const BlobDataItemList::const_iterator itend = blobData->items().end();
-    for (; it != itend; ++it) {
-        const BlobDataItem& blobItem = *it;
+    for (const auto& blobItem : blobData->items()) {
         if (blobItem.type() == BlobDataItem::Type::Data) {
             ASSERT(blobItem.data().data());
             formData->appendData(blobItem.data().data()->data() + static_cast<int>(blobItem.offset()), static_cast<int>(blobItem.length()));
@@ -326,10 +314,8 @@ Ref<FormData> FormData::resolveBlobReferences()
 {
     // First check if any blobs needs to be resolved, or we can take the fast path.
     bool hasBlob = false;
-    Vector<FormDataElement>::const_iterator it = elements().begin();
-    const Vector<FormDataElement>::const_iterator itend = elements().end();
-    for (; it != itend; ++it) {
-        if (it->m_type == FormDataElement::Type::EncodedBlob) {
+    for (auto& element : m_elements) {
+        if (element.m_type == FormDataElement::Type::EncodedBlob) {
             hasBlob = true;
             break;
         }
@@ -339,12 +325,11 @@ Ref<FormData> FormData::resolveBlobReferences()
         return *this;
 
     // Create a copy to append the result into.
-    Ref<FormData> newFormData = FormData::create();
+    auto newFormData = FormData::create();
     newFormData->setAlwaysStream(alwaysStream());
     newFormData->setIdentifier(identifier());
-    it = elements().begin();
-    for (; it != itend; ++it) {
-        const FormDataElement& element = *it;
+
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::Data)
             newFormData->appendData(element.m_data.data(), element.m_data.size());
         else if (element.m_type == FormDataElement::Type::EncodedFile)
@@ -363,7 +348,7 @@ void FormData::generateFiles(Document* document)
     if (!page)
         return;
 
-    for (FormDataElement& element : m_elements) {
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_shouldGenerateFile) {
             ASSERT(!element.m_ownsGeneratedFile);
             ASSERT(element.m_generatedFilename.isEmpty());
@@ -378,7 +363,7 @@ void FormData::generateFiles(Document* document)
 
 bool FormData::hasGeneratedFiles() const
 {
-    for (const FormDataElement& element : m_elements) {
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::EncodedFile && !element.m_generatedFilename.isEmpty())
             return true;
     }
@@ -387,7 +372,7 @@ bool FormData::hasGeneratedFiles() const
 
 bool FormData::hasOwnedGeneratedFiles() const
 {
-    for (const FormDataElement& element : m_elements) {
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
             ASSERT(!element.m_generatedFilename.isEmpty());
             return true;
@@ -398,7 +383,7 @@ bool FormData::hasOwnedGeneratedFiles() const
 
 void FormData::removeGeneratedFilesIfNeeded()
 {
-    for (FormDataElement& element : m_elements) {
+    for (auto& element : m_elements) {
         if (element.m_type == FormDataElement::Type::EncodedFile && element.m_ownsGeneratedFile) {
             ASSERT(!element.m_generatedFilename.isEmpty());
             ASSERT(element.m_shouldGenerateFile);
index bcb8813..4612fbf 100644 (file)
@@ -222,7 +222,7 @@ public:
     WEBCORE_EXPORT void appendBlob(const URL& blobURL);
     char* expandDataStore(size_t);
 
-    void flatten(Vector<char>&) const; // omits files
+    Vector<char> flatten() const; // omits files
     String flattenToString() const; // omits files
 
     // Resolve all blob references so we only have file and data.