Browse Source

LibWeb: Implement formData() method steps for x-www-form-urlencoded

The Response interface of the Fetch API can now parse form urlencoded
bodies when Content-Type is set to 'application/x-www-form-urlencoded'.
Kenneth Myhra 1 year ago
parent
commit
b8fa572c67

+ 3 - 0
Tests/LibWeb/Text/expected/Fetch/fetch-response-url-encoded.txt

@@ -0,0 +1,3 @@
+value-a
+value-b
+value-c1,value-c2

+ 17 - 0
Tests/LibWeb/Text/input/Fetch/fetch-response-url-encoded.html

@@ -0,0 +1,17 @@
+<script src="../include.js"></script>
+<script>
+    test(() => {
+        const data = "param-a=value-a&param-b=value-b&param-c=value-c1&param-c=value-c2";
+        const response = new Response(data, {
+            headers: {
+                "Content-Type": "application/x-www-form-urlencoded",
+            },
+        });
+
+        response.formData().then((formData) => {
+            println(formData.get("param-a"));
+            println(formData.get("param-b"));
+            println(formData.getAll("param-c"));
+        });
+    });
+</script>

+ 11 - 4
Userland/Libraries/LibWeb/Fetch/Body.cpp

@@ -15,6 +15,7 @@
 #include <LibWeb/Bindings/ExceptionOrUtils.h>
 #include <LibWeb/Bindings/HostDefined.h>
 #include <LibWeb/Bindings/MainThreadVM.h>
+#include <LibWeb/DOMURL/URLSearchParams.h>
 #include <LibWeb/Fetch/Body.h>
 #include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
 #include <LibWeb/FileAPI/Blob.h>
@@ -23,6 +24,7 @@
 #include <LibWeb/MimeSniff/MimeType.h>
 #include <LibWeb/Streams/ReadableStream.h>
 #include <LibWeb/WebIDL/Promise.h>
+#include <LibWeb/XHR/FormData.h>
 
 namespace Web::Fetch {
 
@@ -143,10 +145,15 @@ WebIDL::ExceptionOr<JS::Value> package_data(JS::Realm& realm, ByteBuffer bytes,
         }
         // Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then:
         else if (mime_type.has_value() && mime_type->essence() == "application/x-www-form-urlencoded"sv) {
-            // FIXME: 1. Let entries be the result of parsing bytes.
-            // FIXME: 2. If entries is failure, then throw a TypeError.
-            // FIXME: 3. Return a new FormData object whose entry list is entries.
-            return JS::js_null();
+            // 1. Let entries be the result of parsing bytes.
+            auto entries = DOMURL::url_decode(StringView { bytes });
+
+            // 2. If entries is failure, then throw a TypeError.
+            if (entries.is_error())
+                return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, entries.error().string_literal() };
+
+            // 3. Return a new FormData object whose entry list is entries.
+            return TRY(XHR::FormData::create(realm, entries.release_value()));
         }
         // Otherwise, throw a TypeError.
         else {

+ 11 - 1
Userland/Libraries/LibWeb/XHR/FormData.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
+ * Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -40,6 +40,16 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> FormData::construct_impl(JS::Rea
     return realm.heap().allocate<FormData>(realm, realm, move(entry_list));
 }
 
+WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> FormData::create(JS::Realm& realm, Vector<DOMURL::QueryParam> entry_list)
+{
+    Vector<FormDataEntry> list;
+    list.ensure_capacity(entry_list.size());
+    for (auto& entry : entry_list)
+        list.unchecked_append({ .name = move(entry.name), .value = move(entry.value) });
+
+    return construct_impl(realm, move(list));
+}
+
 FormData::FormData(JS::Realm& realm, Vector<FormDataEntry> entry_list)
     : PlatformObject(realm)
     , m_entry_list(move(entry_list))

+ 4 - 1
Userland/Libraries/LibWeb/XHR/FormData.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, Kenneth Myhra <kennethmyhra@serenityos.org>
+ * Copyright (c) 2023-2024, Kenneth Myhra <kennethmyhra@serenityos.org>
  *
  * SPDX-License-Identifier: BSD-2-Clause
  */
@@ -8,6 +8,7 @@
 
 #include <LibWeb/Bindings/FormDataPrototype.h>
 #include <LibWeb/Bindings/PlatformObject.h>
+#include <LibWeb/DOMURL/URLSearchParams.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/HTML/HTMLFormElement.h>
 #include <LibWeb/WebIDL/ExceptionOr.h>
@@ -26,6 +27,8 @@ public:
     static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> construct_impl(JS::Realm&, JS::GCPtr<HTML::HTMLFormElement> form = {});
     static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> construct_impl(JS::Realm&, Vector<FormDataEntry> entry_list);
 
+    static WebIDL::ExceptionOr<JS::NonnullGCPtr<FormData>> create(JS::Realm&, Vector<DOMURL::QueryParam> entry_list);
+
     WebIDL::ExceptionOr<void> append(String const& name, String const& value);
     WebIDL::ExceptionOr<void> append(String const& name, JS::NonnullGCPtr<FileAPI::Blob> const& blob_value, Optional<String> const& filename = {});
     void delete_(String const& name);