diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp
index 7e5484506a..365f75ed3b 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.cpp
@@ -4,7 +4,11 @@
* SPDX-License-Identifier: BSD-2-Clause
*/
+#include
+#include
+#include
#include
+#include
namespace Web::HTML {
@@ -13,4 +17,43 @@ HTMLOptionsCollection::HTMLOptionsCollection(DOM::ParentNode& root, Function HTMLOptionsCollection::add(HTMLOptionOrOptGroupElement element, Optional before)
+{
+ auto resolved_element = element.visit([](auto const& e) -> NonnullRefPtr { return e; });
+
+ RefPtr before_element;
+ if (before.has_value() && before->has>())
+ before_element = before->get>();
+
+ // 1. If element is an ancestor of the select element on which the HTMLOptionsCollection is rooted, then throw a "HierarchyRequestError" DOMException.
+ if (resolved_element->is_ancestor_of(root()))
+ return DOM::HierarchyRequestError::create("The provided element is an ancestor of the root select element.");
+
+ // 2. If before is an element, but that element isn't a descendant of the select element on which the HTMLOptionsCollection is rooted, then throw a "NotFoundError" DOMException.
+ if (before_element && !before_element->is_descendant_of(root()))
+ return DOM::NotFoundError::create("The 'before' element is not a descendant of the root select element.");
+
+ // 3. If element and before are the same element, then return.
+ if (before_element && (resolved_element.ptr() == before_element.ptr()))
+ return {};
+
+ // 4. If before is a node, then let reference be that node. Otherwise, if before is an integer, and there is a beforeth node in the collection, let reference be that node. Otherwise, let reference be null.
+ RefPtr reference;
+
+ if (before_element)
+ reference = move(before_element);
+ else if (before.has_value() && before->has())
+ reference = item(before->get());
+
+ // 5. If reference is not null, let parent be the parent node of reference. Otherwise, let parent be the select element on which the HTMLOptionsCollection is rooted.
+ DOM::Node* parent = reference ? reference->parent() : root().ptr();
+
+ // 6. Pre-insert element into parent node before reference.
+ if (auto result = parent->pre_insert(resolved_element, reference); result.is_exception())
+ return result.exception();
+
+ return {};
+}
+
}
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h
index 1d2bfcd2c5..7444607cfd 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.h
@@ -6,10 +6,15 @@
#pragma once
+#include
+#include
#include
namespace Web::HTML {
+using HTMLOptionOrOptGroupElement = Variant, NonnullRefPtr>;
+using HTMLElementOrElementIndex = Variant, i32>;
+
class HTMLOptionsCollection final : public DOM::HTMLCollection {
public:
using WrapperType = Bindings::HTMLOptionsCollectionWrapper;
@@ -19,6 +24,8 @@ public:
return adopt_ref(*new HTMLOptionsCollection(root, move(filter)));
}
+ DOM::ExceptionOr add(HTMLOptionOrOptGroupElement element, Optional before = {});
+
protected:
HTMLOptionsCollection(DOM::ParentNode& root, Function filter);
};
diff --git a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl
index ae33079903..084317ab03 100644
--- a/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl
+++ b/Userland/Libraries/LibWeb/HTML/HTMLOptionsCollection.idl
@@ -6,7 +6,7 @@
interface HTMLOptionsCollection : HTMLCollection {
// [CEReactions] attribute unsigned long length; // shadows inherited length
// [CEReactions] setter undefined (unsigned long index, HTMLOptionElement? option);
- // [CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
+ [CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null);
// [CEReactions] undefined remove(long index);
// attribute long selectedIndex;
};