/src/serenity/Userland/Libraries/LibWeb/HTML/Parser/StackOfOpenElements.h
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <LibWeb/DOM/Element.h> |
10 | | #include <LibWeb/Forward.h> |
11 | | |
12 | | namespace Web::HTML { |
13 | | |
14 | | // https://html.spec.whatwg.org/multipage/parsing.html#stack-of-open-elements |
15 | | class StackOfOpenElements { |
16 | | public: |
17 | | // Initially, the stack of open elements is empty. |
18 | | // The stack grows downwards; the topmost node on the stack is the first one added to the stack, |
19 | | // and the bottommost node of the stack is the most recently added node in the stack |
20 | | // (notwithstanding when the stack is manipulated in a random access fashion as part of the handling for misnested tags). |
21 | | |
22 | 0 | StackOfOpenElements() = default; |
23 | | ~StackOfOpenElements(); |
24 | | |
25 | 0 | DOM::Element& first() { return *m_elements.first(); } |
26 | 0 | DOM::Element& last() { return *m_elements.last(); } |
27 | | |
28 | 0 | bool is_empty() const { return m_elements.is_empty(); } |
29 | 0 | void push(JS::NonnullGCPtr<DOM::Element> element) { m_elements.append(element); } |
30 | 0 | JS::NonnullGCPtr<DOM::Element> pop() { return *m_elements.take_last(); } |
31 | | void remove(DOM::Element const& element); |
32 | | void replace(DOM::Element const& to_remove, JS::NonnullGCPtr<DOM::Element> to_add); |
33 | | void insert_immediately_below(JS::NonnullGCPtr<DOM::Element> element_to_add, DOM::Element const& target); |
34 | | |
35 | 0 | const DOM::Element& current_node() const { return *m_elements.last(); } |
36 | 0 | DOM::Element& current_node() { return *m_elements.last(); } |
37 | | |
38 | | bool has_in_scope(FlyString const& tag_name) const; |
39 | | bool has_in_button_scope(FlyString const& tag_name) const; |
40 | | bool has_in_table_scope(FlyString const& tag_name) const; |
41 | | bool has_in_list_item_scope(FlyString const& tag_name) const; |
42 | | bool has_in_select_scope(FlyString const& tag_name) const; |
43 | | |
44 | | bool has_in_scope(const DOM::Element&) const; |
45 | | |
46 | | bool contains(const DOM::Element&) const; |
47 | | bool contains(FlyString const& tag_name) const; |
48 | | |
49 | 0 | auto const& elements() const { return m_elements; } |
50 | 0 | auto& elements() { return m_elements; } |
51 | | |
52 | | void pop_until_an_element_with_tag_name_has_been_popped(FlyString const&); |
53 | | |
54 | | JS::GCPtr<DOM::Element> topmost_special_node_below(DOM::Element const&); |
55 | | |
56 | | struct LastElementResult { |
57 | | JS::GCPtr<DOM::Element> element; |
58 | | ssize_t index; |
59 | | }; |
60 | | LastElementResult last_element_with_tag_name(FlyString const&); |
61 | | JS::GCPtr<DOM::Element> element_immediately_above(DOM::Element const&); |
62 | | |
63 | | void visit_edges(JS::Cell::Visitor&); |
64 | | |
65 | | private: |
66 | | bool has_in_scope_impl(FlyString const& tag_name, Vector<FlyString> const&) const; |
67 | | bool has_in_scope_impl(const DOM::Element& target_node, Vector<FlyString> const&) const; |
68 | | |
69 | | Vector<JS::NonnullGCPtr<DOM::Element>> m_elements; |
70 | | }; |
71 | | |
72 | | } |