diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 8e4f0360f5..d0aa9a33d5 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -139,7 +139,7 @@ CppType idl_type_name_to_cpp_type(Type const& type, Interface const& interface) return { .name = "JS::Handle", .sequence_storage_type = SequenceStorageType::MarkedVector }; if (type.name() == "File") - return { .name = "JS::NonnullGCPtr", .sequence_storage_type = SequenceStorageType::MarkedVector }; + return { .name = "JS::Handle", .sequence_storage_type = SequenceStorageType::MarkedVector }; if (type.name() == "sequence") { auto& parameterized_type = verify_cast(type); diff --git a/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.cpp b/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.cpp index dd60727a4c..73cefdfaeb 100644 --- a/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.cpp +++ b/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.cpp @@ -15,7 +15,7 @@ namespace Web::HTML { // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry -WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Variant, String> const& value, Optional const& filename) +WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Variant, String> const& value, Optional const& filename) { auto& vm = realm.vm(); @@ -24,11 +24,11 @@ WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Va auto entry_value = TRY(value.visit( // 2. If value is a string, then set value to the result of converting value into a scalar value string. - [&](String const& string) -> WebIDL::ExceptionOr, String>> { + [&](String const& string) -> WebIDL::ExceptionOr, String>> { return TRY_OR_THROW_OOM(vm, Infra::convert_to_scalar_value_string(string)); }, // 3. Otherwise: - [&](JS::NonnullGCPtr const& blob) -> WebIDL::ExceptionOr, String>> { + [&](JS::NonnullGCPtr const& blob) -> WebIDL::ExceptionOr, String>> { // 1. If value is not a File object, then set value to a new File object, representing the same bytes, whose name attribute value is "blob". // 2. If filename is given, then set value to a new File object, representing the same bytes, whose name attribute is filename. String name_attribute; @@ -36,11 +36,11 @@ WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Va name_attribute = filename.value(); else name_attribute = TRY_OR_THROW_OOM(vm, String::from_utf8("blob"sv)); - return TRY(FileAPI::File::create(realm, { JS::make_handle(*blob) }, name_attribute.to_deprecated_string(), {})); + return JS::make_handle(TRY(FileAPI::File::create(realm, { JS::make_handle(*blob) }, name_attribute.to_deprecated_string(), {}))); })); // 4. Return an entry whose name is name and whose value is value. - return Entry { + return XHR::FormDataEntry { .name = move(entry_name), .value = move(entry_value), }; @@ -48,13 +48,13 @@ WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Va // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#constructing-the-form-data-set // FIXME: Add missing parameters optional submitter, and optional encoding -WebIDL::ExceptionOr> construct_entry_list(JS::Realm& realm, HTMLFormElement& form) +WebIDL::ExceptionOr>> construct_entry_list(JS::Realm& realm, HTMLFormElement& form) { auto& vm = realm.vm(); // 1. If form's constructing entry list is true, then return null. if (form.constructing_entry_list()) - return Optional {}; + return Optional> {}; // 2. Set form's constructing entry list to true. form.set_constructing_entry_list(true); @@ -63,7 +63,7 @@ WebIDL::ExceptionOr> construct_e auto controls = TRY_OR_THROW_OOM(vm, form.get_submittable_elements()); // 4. Let entry list be a new empty entry list. - HashMapWithVectorOfFormDataEntryValue entry_list; + Vector entry_list; // 5. For each element field in controls, in tree order: for (auto const& control : controls) { @@ -101,19 +101,17 @@ WebIDL::ExceptionOr> construct_e continue; // 5. Let name be the value of the field element's name attribute. - auto name = control->name(); - - auto form_data_entries = entry_list.contains(name) && entry_list.get(name).has_value() - ? entry_list.get(name).value() - : Vector {}; + auto name = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(control->name())); // 6. If the field element is a select element, then for each option element in the select element's list of options whose selectedness is true and that is not disabled, create an entry with name and the value of the option element, and append it to entry list. if (auto* select_element = dynamic_cast(control.ptr())) { for (auto const& option_element : select_element->list_of_options()) { - if (option_element->selected() && !option_element->disabled()) - TRY_OR_THROW_OOM(vm, form_data_entries.try_append(option_element->value())); + if (option_element->selected() && !option_element->disabled()) { + auto option_name = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(option_element->name())); + auto option_value = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(option_element->value())); + TRY_OR_THROW_OOM(vm, entry_list.try_append(XHR::FormDataEntry { .name = move(option_name), .value = move(option_value) })); + } } - TRY_OR_THROW_OOM(vm, entry_list.try_set(name, form_data_entries)); } // 7. Otherwise, if the field element is an input element whose type attribute is in the Checkbox state or the Radio Button state, then: else if (auto* checkbox_or_radio_element = dynamic_cast(control.ptr()); checkbox_or_radio_element && (checkbox_or_radio_element->type() == "checkbox" || checkbox_or_radio_element->type() == "radio") && checkbox_or_radio_element->checked()) { @@ -123,8 +121,9 @@ WebIDL::ExceptionOr> construct_e value = "on"; // 2. Create an entry with name and value, and append it to entry list. - TRY_OR_THROW_OOM(vm, form_data_entries.try_append(value)); - TRY_OR_THROW_OOM(vm, entry_list.try_set(name, form_data_entries)); + auto checkbox_or_radio_element_name = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(checkbox_or_radio_element->name())); + auto checkbox_or_radio_element_value = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(value)); + TRY_OR_THROW_OOM(vm, entry_list.try_append(XHR::FormDataEntry { .name = move(checkbox_or_radio_element_name), .value = move(checkbox_or_radio_element_value) })); } // 8. Otherwise, if the field element is an input element whose type attribute is in the File Upload state, then: else if (auto* file_element = dynamic_cast(control.ptr()); file_element && file_element->type() == "file") { @@ -133,16 +132,14 @@ WebIDL::ExceptionOr> construct_e FileAPI::FilePropertyBag options {}; options.type = "application/octet-stream"; auto file = TRY(FileAPI::File::create(realm, {}, "", options)); - TRY_OR_THROW_OOM(vm, form_data_entries.try_append(file)); - TRY_OR_THROW_OOM(vm, entry_list.try_set(name, form_data_entries)); + TRY_OR_THROW_OOM(vm, entry_list.try_append(XHR::FormDataEntry { .name = move(name), .value = JS::make_handle(file) })); } // 2. Otherwise, for each file in selected files, create an entry with name and a File object representing the file, and append it to entry list. else { for (size_t i = 0; i < file_element->files()->length(); i++) { auto file = JS::NonnullGCPtr { *file_element->files()->item(i) }; - TRY_OR_THROW_OOM(vm, form_data_entries.try_append(file)); + TRY_OR_THROW_OOM(vm, entry_list.try_append(XHR::FormDataEntry { .name = move(name), .value = JS::make_handle(file) })); } - TRY_OR_THROW_OOM(vm, entry_list.try_set(name, form_data_entries)); } } // FIXME: 9. Otherwise, if the field element is an input element whose type attribute is in the Hidden state and name is an ASCII case-insensitive match for "_charset_": @@ -152,8 +149,8 @@ WebIDL::ExceptionOr> construct_e else { auto* element = dynamic_cast(control.ptr()); VERIFY(element); - TRY_OR_THROW_OOM(vm, form_data_entries.try_append(element->attribute("value"sv))); - TRY_OR_THROW_OOM(vm, entry_list.try_set(name, form_data_entries)); + auto value_attribute = TRY_OR_THROW_OOM(vm, String::from_deprecated_string(element->attribute("value"sv))); + TRY_OR_THROW_OOM(vm, entry_list.try_append(XHR::FormDataEntry { .name = move(name), .value = move(value_attribute) })); } // FIXME: 11. If the element has a dirname attribute, and that attribute's value is not the empty string, then: @@ -175,7 +172,7 @@ WebIDL::ExceptionOr> construct_e form.set_constructing_entry_list(false); // 9. Return a clone of entry list. - return TRY_OR_THROW_OOM(vm, entry_list.clone()); + return entry_list; } } diff --git a/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.h b/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.h index 3ea46634e5..493b568198 100644 --- a/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.h +++ b/Userland/Libraries/LibWeb/HTML/FormControlInfrastructure.h @@ -10,14 +10,7 @@ namespace Web::HTML { -using HashMapWithVectorOfFormDataEntryValue = HashMap>; - -struct Entry { - String name; - Variant, String> value; -}; - -WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Variant, String> const& value, Optional const& filename = {}); -WebIDL::ExceptionOr> construct_entry_list(JS::Realm&, HTMLFormElement&); +WebIDL::ExceptionOr create_entry(JS::Realm& realm, String const& name, Variant, String> const& value, Optional const& filename = {}); +WebIDL::ExceptionOr>> construct_entry_list(JS::Realm&, HTMLFormElement&); } diff --git a/Userland/Libraries/LibWeb/XHR/FormData.cpp b/Userland/Libraries/LibWeb/XHR/FormData.cpp index 19722689bc..d2fca57323 100644 --- a/Userland/Libraries/LibWeb/XHR/FormData.cpp +++ b/Userland/Libraries/LibWeb/XHR/FormData.cpp @@ -18,7 +18,7 @@ namespace Web::XHR { // https://xhr.spec.whatwg.org/#dom-formdata WebIDL::ExceptionOr> FormData::construct_impl(JS::Realm& realm, Optional> form) { - HashMap> list; + Vector list; // 1. If form is given, then: if (form.has_value()) { // 1. Let list be the result of constructing the entry list for form. @@ -33,12 +33,12 @@ WebIDL::ExceptionOr> FormData::construct_impl(JS::Rea return construct_impl(realm, move(list)); } -WebIDL::ExceptionOr> FormData::construct_impl(JS::Realm& realm, HashMap> entry_list) +WebIDL::ExceptionOr> FormData::construct_impl(JS::Realm& realm, Vector entry_list) { return MUST_OR_THROW_OOM(realm.heap().allocate(realm, realm, move(entry_list))); } -FormData::FormData(JS::Realm& realm, HashMap> entry_list) +FormData::FormData(JS::Realm& realm, Vector entry_list) : PlatformObject(realm) , m_entry_list(move(entry_list)) { @@ -54,30 +54,17 @@ JS::ThrowCompletionOr FormData::initialize(JS::Realm& realm) return {}; } -void FormData::visit_edges(Cell::Visitor& visitor) -{ - Base::visit_edges(visitor); - for (auto const& entry : m_entry_list) { - for (auto const& value : entry.value) { - if (auto* file = value.get_pointer>()) - visitor.visit(*file); - } - } -} - // https://xhr.spec.whatwg.org/#dom-formdata-append -WebIDL::ExceptionOr FormData::append(DeprecatedString const& name, DeprecatedString const& value) +WebIDL::ExceptionOr FormData::append(String const& name, String const& value) { - auto& vm = realm().vm(); - return append_impl(TRY_OR_THROW_OOM(vm, String::from_deprecated_string(name)), TRY_OR_THROW_OOM(vm, String::from_deprecated_string(value))); + return append_impl(name, value); } // https://xhr.spec.whatwg.org/#dom-formdata-append-blob -WebIDL::ExceptionOr FormData::append(DeprecatedString const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename) +WebIDL::ExceptionOr FormData::append(String const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename) { - auto& vm = realm().vm(); - auto inner_filename = filename.has_value() ? TRY_OR_THROW_OOM(vm, String::from_deprecated_string(filename.value())) : Optional {}; - return append_impl(TRY_OR_THROW_OOM(vm, String::from_deprecated_string(name)), blob_value, inner_filename); + auto inner_filename = filename.has_value() ? filename.value() : Optional {}; + return append_impl(name, blob_value, inner_filename); } // https://xhr.spec.whatwg.org/#dom-formdata-append @@ -91,67 +78,67 @@ WebIDL::ExceptionOr FormData::append_impl(String const& name, Variant() - ? FormDataEntryValue { entry.value.get().to_deprecated_string() } - : FormDataEntryValue { entry.value.get>() }; - // 3. Append entry to this’s entry list. - if (auto entries = m_entry_list.get(entry.name.to_deprecated_string()); entries.has_value() && !entries->is_empty()) - TRY_OR_THROW_OOM(vm, entries->try_append(form_data_entry_value)); - else - TRY_OR_THROW_OOM(vm, m_entry_list.try_set(entry.name.to_deprecated_string(), { form_data_entry_value })); - + TRY_OR_THROW_OOM(vm, m_entry_list.try_append(move(entry))); return {}; } // https://xhr.spec.whatwg.org/#dom-formdata-delete -void FormData::delete_(DeprecatedString const& name) +void FormData::delete_(String const& name) { // The delete(name) method steps are to remove all entries whose name is name from this’s entry list. - m_entry_list.remove(name); + m_entry_list.remove_all_matching([&name](FormDataEntry const& entry) { + return entry.name == name; + }); } // https://xhr.spec.whatwg.org/#dom-formdata-get -Variant, DeprecatedString, Empty> FormData::get(DeprecatedString const& name) +Variant, String, Empty> FormData::get(String const& name) { // 1. If there is no entry whose name is name in this’s entry list, then return null. - if (!m_entry_list.contains(name)) + auto entry_iterator = m_entry_list.find_if([&name](FormDataEntry const& entry) { + return entry.name == name; + }); + if (entry_iterator.is_end()) return Empty {}; // 2. Return the value of the first entry whose name is name from this’s entry list. - return m_entry_list.get(name)->at(0); + return entry_iterator->value; } // https://xhr.spec.whatwg.org/#dom-formdata-getall -Vector FormData::get_all(DeprecatedString const& name) +WebIDL::ExceptionOr> FormData::get_all(String const& name) { // 1. If there is no entry whose name is name in this’s entry list, then return the empty list. - if (!m_entry_list.contains(name)) - return {}; // 2. Return the values of all entries whose name is name, in order, from this’s entry list. - return *m_entry_list.get(name); + Vector values; + for (auto const& entry : m_entry_list) { + if (entry.name == name) + TRY_OR_THROW_OOM(vm(), values.try_append(entry.value)); + } + return values; } // https://xhr.spec.whatwg.org/#dom-formdata-has -bool FormData::has(DeprecatedString const& name) +bool FormData::has(String const& name) { // The has(name) method steps are to return true if there is an entry whose name is name in this’s entry list; otherwise false. - return m_entry_list.contains(name); + return !m_entry_list.find_if([&name](auto& entry) { + return entry.name == name; + }) + .is_end(); } // https://xhr.spec.whatwg.org/#dom-formdata-set -WebIDL::ExceptionOr FormData::set(DeprecatedString const& name, DeprecatedString const& value) +WebIDL::ExceptionOr FormData::set(String const& name, String const& value) { - auto& vm = realm().vm(); - return set_impl(TRY_OR_THROW_OOM(vm, String::from_deprecated_string(name)), TRY_OR_THROW_OOM(vm, String::from_deprecated_string(value))); + return set_impl(name, value); } // https://xhr.spec.whatwg.org/#dom-formdata-set-blob -WebIDL::ExceptionOr FormData::set(DeprecatedString const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename) +WebIDL::ExceptionOr FormData::set(String const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename) { - auto& vm = realm().vm(); - auto inner_filename = filename.has_value() ? TRY_OR_THROW_OOM(vm, String::from_deprecated_string(filename.value())) : Optional {}; - return set_impl(TRY_OR_THROW_OOM(vm, String::from_deprecated_string(name)), blob_value, inner_filename); + auto inner_filename = filename.has_value() ? filename.value() : Optional {}; + return set_impl(name, blob_value, inner_filename); } // https://xhr.spec.whatwg.org/#dom-formdata-set @@ -165,19 +152,20 @@ WebIDL::ExceptionOr FormData::set_impl(String const& name, Variant() - ? FormDataEntryValue { entry.value.get().to_deprecated_string() } - : FormDataEntryValue { entry.value.get>() }; + auto existing = m_entry_list.find_if([&name](auto& entry) { + return entry.name == name; + }); // 3. If there are entries in this’s entry list whose name is name, then replace the first such entry with entry and remove the others. - if (auto entries = m_entry_list.get(entry.name.to_deprecated_string()); entries.has_value() && !entries->is_empty()) { - entries->remove(0, entries->size()); - TRY_OR_THROW_OOM(vm, entries->try_append(form_data_entry_value)); + if (!existing.is_end()) { + existing->value = entry.value; + m_entry_list.remove_all_matching([&name, &existing](auto& entry) { + return &entry != &*existing && entry.name == name; + }); } // 4. Otherwise, append entry to this’s entry list. else { - TRY_OR_THROW_OOM(vm, m_entry_list.try_set(entry.name.to_deprecated_string(), { form_data_entry_value })); + TRY_OR_THROW_OOM(vm, m_entry_list.try_append(move(entry))); } return {}; diff --git a/Userland/Libraries/LibWeb/XHR/FormData.h b/Userland/Libraries/LibWeb/XHR/FormData.h index 99171309a6..b8a3f300ef 100644 --- a/Userland/Libraries/LibWeb/XHR/FormData.h +++ b/Userland/Libraries/LibWeb/XHR/FormData.h @@ -15,7 +15,12 @@ namespace Web::XHR { // https://xhr.spec.whatwg.org/#formdataentryvalue -using FormDataEntryValue = Variant, DeprecatedString>; +using FormDataEntryValue = Variant, String>; + +struct FormDataEntry { + String name; + FormDataEntryValue value; +}; // https://xhr.spec.whatwg.org/#interface-formdata class FormData : public Bindings::PlatformObject { @@ -25,27 +30,26 @@ public: virtual ~FormData() override; static WebIDL::ExceptionOr> construct_impl(JS::Realm&, Optional> form = {}); - static WebIDL::ExceptionOr> construct_impl(JS::Realm&, HashMap> entry_list); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, Vector entry_list); - WebIDL::ExceptionOr append(DeprecatedString const& name, DeprecatedString const& value); - WebIDL::ExceptionOr append(DeprecatedString const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename = {}); - void delete_(DeprecatedString const& name); - Variant, DeprecatedString, Empty> get(DeprecatedString const& name); - Vector get_all(DeprecatedString const& name); - bool has(DeprecatedString const& name); - WebIDL::ExceptionOr set(DeprecatedString const& name, DeprecatedString const& value); - WebIDL::ExceptionOr set(DeprecatedString const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename = {}); + WebIDL::ExceptionOr append(String const& name, String const& value); + WebIDL::ExceptionOr append(String const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename = {}); + void delete_(String const& name); + Variant, String, Empty> get(String const& name); + WebIDL::ExceptionOr> get_all(String const& name); + bool has(String const& name); + WebIDL::ExceptionOr set(String const& name, String const& value); + WebIDL::ExceptionOr set(String const& name, JS::NonnullGCPtr const& blob_value, Optional const& filename = {}); private: - explicit FormData(JS::Realm&, HashMap> entry_list = {}); + explicit FormData(JS::Realm&, Vector entry_list = {}); virtual JS::ThrowCompletionOr initialize(JS::Realm&) override; - virtual void visit_edges(Cell::Visitor&) override; WebIDL::ExceptionOr append_impl(String const& name, Variant, String> const& value, Optional const& filename = {}); WebIDL::ExceptionOr set_impl(String const& name, Variant, String> const& value, Optional const& filename = {}); - HashMap> m_entry_list; + Vector m_entry_list; }; } diff --git a/Userland/Libraries/LibWeb/XHR/FormData.idl b/Userland/Libraries/LibWeb/XHR/FormData.idl index ca5b170026..38af410dfc 100644 --- a/Userland/Libraries/LibWeb/XHR/FormData.idl +++ b/Userland/Libraries/LibWeb/XHR/FormData.idl @@ -5,7 +5,7 @@ typedef (File or USVString) FormDataEntryValue; // https://xhr.spec.whatwg.org/#interface-formdata -[Exposed=Window] +[Exposed=Window, UseNewAKString] interface FormData { constructor(optional HTMLFormElement form);