/* * Copyright (c) 2022, the SerenityOS developers. * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include namespace Web::HTML { JS_DEFINE_ALLOCATOR(HTMLOptionsCollection); JS::NonnullGCPtr HTMLOptionsCollection::create(DOM::ParentNode& root, Function filter) { return root.heap().allocate(root.realm(), root, move(filter)); } HTMLOptionsCollection::HTMLOptionsCollection(DOM::ParentNode& root, Function filter) : DOM::HTMLCollection(root, Scope::Descendants, move(filter)) { } HTMLOptionsCollection::~HTMLOptionsCollection() = default; void HTMLOptionsCollection::initialize(JS::Realm& realm) { Base::initialize(realm); set_prototype(&Bindings::ensure_web_prototype(realm, "HTMLOptionsCollection"_fly_string)); } // https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#dom-htmloptionscollection-add WebIDL::ExceptionOr HTMLOptionsCollection::add(HTMLOptionOrOptGroupElement element, Optional before) { auto resolved_element = element.visit( [](auto& e) -> JS::Handle { return JS::make_handle(static_cast(*e)); }); JS::GCPtr before_element; if (before.has_value() && before->has>()) before_element = before->get>().ptr(); // 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 WebIDL::HierarchyRequestError::create(realm(), "The provided element is an ancestor of the root select element."_fly_string); // 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 WebIDL::NotFoundError::create(realm(), "The 'before' element is not a descendant of the root select element."_fly_string); // 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. JS::GCPtr 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. (void)TRY(parent->pre_insert(*resolved_element, reference)); return {}; } }