/src/serenity/Userland/Libraries/LibWeb/HTML/HTMLIFrameElement.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2023, Sam Atkins <atkinssj@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <LibURL/Origin.h> |
9 | | #include <LibWeb/Bindings/HTMLIFrameElementPrototype.h> |
10 | | #include <LibWeb/DOM/Document.h> |
11 | | #include <LibWeb/DOM/Event.h> |
12 | | #include <LibWeb/HTML/BrowsingContext.h> |
13 | | #include <LibWeb/HTML/HTMLIFrameElement.h> |
14 | | #include <LibWeb/HTML/Navigable.h> |
15 | | #include <LibWeb/HTML/Parser/HTMLParser.h> |
16 | | #include <LibWeb/Layout/FrameBox.h> |
17 | | |
18 | | namespace Web::HTML { |
19 | | |
20 | | JS_DEFINE_ALLOCATOR(HTMLIFrameElement); |
21 | | |
22 | | HTMLIFrameElement::HTMLIFrameElement(DOM::Document& document, DOM::QualifiedName qualified_name) |
23 | 0 | : NavigableContainer(document, move(qualified_name)) |
24 | 0 | { |
25 | 0 | } |
26 | | |
27 | 0 | HTMLIFrameElement::~HTMLIFrameElement() = default; |
28 | | |
29 | | void HTMLIFrameElement::initialize(JS::Realm& realm) |
30 | 0 | { |
31 | 0 | Base::initialize(realm); |
32 | 0 | WEB_SET_PROTOTYPE_FOR_INTERFACE(HTMLIFrameElement); |
33 | 0 | } |
34 | | |
35 | | JS::GCPtr<Layout::Node> HTMLIFrameElement::create_layout_node(NonnullRefPtr<CSS::StyleProperties> style) |
36 | 0 | { |
37 | 0 | return heap().allocate_without_realm<Layout::FrameBox>(document(), *this, move(style)); |
38 | 0 | } |
39 | | |
40 | | void HTMLIFrameElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value) |
41 | 0 | { |
42 | 0 | HTMLElement::attribute_changed(name, old_value, value); |
43 | | |
44 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-2 |
45 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:process-the-iframe-attributes-3 |
46 | | // Whenever an iframe element with a non-null content navigable has its srcdoc attribute set, changed, or removed, |
47 | | // the user agent must process the iframe attributes. |
48 | | // Similarly, whenever an iframe element with a non-null content navigable but with no srcdoc attribute specified |
49 | | // has its src attribute set, changed, or removed, the user agent must process the iframe attributes. |
50 | 0 | if (m_content_navigable) { |
51 | 0 | if (name == AttributeNames::srcdoc || (name == AttributeNames::src && !has_attribute(AttributeNames::srcdoc))) |
52 | 0 | process_the_iframe_attributes(); |
53 | 0 | } |
54 | 0 | } |
55 | | |
56 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:the-iframe-element-6 |
57 | | void HTMLIFrameElement::inserted() |
58 | 0 | { |
59 | 0 | HTMLElement::inserted(); |
60 | | |
61 | | // The iframe HTML element insertion steps, given insertedNode, are: |
62 | | // 1. If insertedNode's shadow-including root's browsing context is null, then return. |
63 | 0 | if (!is<DOM::Document>(shadow_including_root())) |
64 | 0 | return; |
65 | | |
66 | 0 | DOM::Document& document = verify_cast<DOM::Document>(shadow_including_root()); |
67 | | |
68 | | // NOTE: The check for "not fully active" is to prevent a crash on the dom/nodes/node-appendchild-crash.html WPT test. |
69 | 0 | if (!document.browsing_context() || !document.is_fully_active()) |
70 | 0 | return; |
71 | | |
72 | | // 2. Create a new child navigable for insertedNode. |
73 | 0 | MUST(create_new_child_navigable(JS::create_heap_function(realm().heap(), [this] { |
74 | | // FIXME: 3. If insertedNode has a sandbox attribute, then parse the sandboxing directive given the attribute's |
75 | | // value and insertedNode's iframe sandboxing flag set. |
76 | | |
77 | | // 4. Process the iframe attributes for insertedNode, with initialInsertion set to true. |
78 | 0 | process_the_iframe_attributes(true); |
79 | 0 | set_content_navigable_initialized(); |
80 | 0 | }))); |
81 | 0 | } |
82 | | |
83 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#process-the-iframe-attributes |
84 | | void HTMLIFrameElement::process_the_iframe_attributes(bool initial_insertion) |
85 | 0 | { |
86 | 0 | if (!content_navigable()) |
87 | 0 | return; |
88 | | |
89 | | // 1. If element's srcdoc attribute is specified, then: |
90 | 0 | if (has_attribute(HTML::AttributeNames::srcdoc)) { |
91 | | // 1. Set element's current navigation was lazy loaded boolean to false. |
92 | 0 | set_current_navigation_was_lazy_loaded(false); |
93 | | |
94 | | // 2. If the will lazy load element steps given element return true, then: |
95 | 0 | if (will_lazy_load_element()) { |
96 | | // 1. Set element's lazy load resumption steps to the rest of this algorithm starting with the step labeled navigate to the srcdoc resource. |
97 | 0 | set_lazy_load_resumption_steps([this]() { |
98 | | // 3. Navigate to the srcdoc resource: navigate an iframe or frame given element, about:srcdoc, the empty string, and the value of element's srcdoc attribute. |
99 | 0 | navigate_an_iframe_or_frame(URL::URL("about:srcdoc"sv), ReferrerPolicy::ReferrerPolicy::EmptyString, get_attribute(HTML::AttributeNames::srcdoc)); |
100 | | |
101 | | // FIXME: The resulting Document must be considered an iframe srcdoc document. |
102 | 0 | }); |
103 | | |
104 | | // 2. Set element's current navigation was lazy loaded boolean to true. |
105 | 0 | set_current_navigation_was_lazy_loaded(true); |
106 | | |
107 | | // 3. Start intersection-observing a lazy loading element for element. |
108 | 0 | document().start_intersection_observing_a_lazy_loading_element(*this); |
109 | | |
110 | | // 4. Return. |
111 | 0 | return; |
112 | 0 | } |
113 | | |
114 | | // 3. Navigate to the srcdoc resource: navigate an iframe or frame given element, about:srcdoc, the empty string, and the value of element's srcdoc attribute. |
115 | 0 | navigate_an_iframe_or_frame(URL::URL("about:srcdoc"sv), ReferrerPolicy::ReferrerPolicy::EmptyString, get_attribute(HTML::AttributeNames::srcdoc)); |
116 | | |
117 | | // FIXME: The resulting Document must be considered an iframe srcdoc document. |
118 | |
|
119 | 0 | return; |
120 | 0 | } |
121 | | |
122 | | // 1. Let url be the result of running the shared attribute processing steps for iframe and frame elements given element and initialInsertion. |
123 | 0 | auto url = shared_attribute_processing_steps_for_iframe_and_frame(initial_insertion); |
124 | | |
125 | | // 2. If url is null, then return. |
126 | 0 | if (!url.has_value()) { |
127 | 0 | return; |
128 | 0 | } |
129 | | |
130 | | // 3. If url matches about:blank and initialInsertion is true, then: |
131 | 0 | if (url_matches_about_blank(*url) && initial_insertion) { |
132 | | // 1. Run the iframe load event steps given element. |
133 | 0 | run_iframe_load_event_steps(*this); |
134 | | |
135 | | // 2. Return. |
136 | 0 | return; |
137 | 0 | } |
138 | | |
139 | | // 4. Let referrerPolicy be the current state of element's referrerpolicy content attribute. |
140 | 0 | auto referrer_policy = ReferrerPolicy::from_string(get_attribute_value(HTML::AttributeNames::referrerpolicy)).value_or(ReferrerPolicy::ReferrerPolicy::EmptyString); |
141 | | |
142 | | // 5. Set element's current navigation was lazy loaded boolean to false. |
143 | 0 | set_current_navigation_was_lazy_loaded(false); |
144 | | |
145 | | // 6. If the will lazy load element steps given element return true, then: |
146 | 0 | if (will_lazy_load_element()) { |
147 | | // 1. Set element's lazy load resumption steps to the rest of this algorithm starting with the step labeled navigate. |
148 | 0 | set_lazy_load_resumption_steps([this, url, referrer_policy]() { |
149 | | // 7. Navigate: navigate an iframe or frame given element, url, and referrerPolicy. |
150 | 0 | navigate_an_iframe_or_frame(*url, referrer_policy); |
151 | 0 | }); |
152 | | |
153 | | // 2. Set element's current navigation was lazy loaded boolean to true. |
154 | 0 | set_current_navigation_was_lazy_loaded(true); |
155 | | |
156 | | // 3. Start intersection-observing a lazy loading element for element. |
157 | 0 | document().start_intersection_observing_a_lazy_loading_element(*this); |
158 | | |
159 | | // 4. Return. |
160 | 0 | return; |
161 | 0 | } |
162 | | |
163 | | // 7. Navigate: navigate an iframe or frame given element, url, and referrerPolicy. |
164 | 0 | navigate_an_iframe_or_frame(*url, referrer_policy); |
165 | 0 | } |
166 | | |
167 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:the-iframe-element-7 |
168 | | void HTMLIFrameElement::removed_from(DOM::Node* node) |
169 | 0 | { |
170 | 0 | HTMLElement::removed_from(node); |
171 | | |
172 | | // When an iframe element is removed from a document, the user agent must destroy the nested navigable of the element. |
173 | 0 | destroy_the_child_navigable(); |
174 | 0 | } |
175 | | |
176 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#iframe-load-event-steps |
177 | | void run_iframe_load_event_steps(HTML::HTMLIFrameElement& element) |
178 | 0 | { |
179 | | // FIXME: 1. Assert: element's content navigable is not null. |
180 | 0 | if (!element.content_navigable()) { |
181 | | // FIXME: For some reason, we sometimes end up here in the middle of SunSpider. |
182 | 0 | dbgln("FIXME: run_iframe_load_event_steps called with null nested browsing context"); |
183 | 0 | return; |
184 | 0 | } |
185 | | |
186 | | // 2. Let childDocument be element's content navigable's active document. |
187 | 0 | [[maybe_unused]] auto child_document = element.content_navigable()->active_document(); |
188 | | |
189 | | // FIXME: 3. If childDocument has its mute iframe load flag set, then return. |
190 | | |
191 | | // FIXME: 4. Set childDocument's iframe load in progress flag. |
192 | | |
193 | | // 5. Fire an event named load at element. |
194 | 0 | element.dispatch_event(DOM::Event::create(element.realm(), HTML::EventNames::load)); |
195 | | |
196 | | // FIXME: 6. Unset childDocument's iframe load in progress flag. |
197 | 0 | } |
198 | | |
199 | | // https://html.spec.whatwg.org/multipage/interaction.html#dom-tabindex |
200 | | i32 HTMLIFrameElement::default_tab_index_value() const |
201 | 0 | { |
202 | | // See the base function for the spec comments. |
203 | 0 | return 0; |
204 | 0 | } |
205 | | |
206 | | void HTMLIFrameElement::visit_edges(Cell::Visitor& visitor) |
207 | 0 | { |
208 | 0 | Base::visit_edges(visitor); |
209 | 0 | visit_lazy_loading_element(visitor); |
210 | 0 | } |
211 | | |
212 | | void HTMLIFrameElement::set_current_navigation_was_lazy_loaded(bool value) |
213 | 0 | { |
214 | 0 | m_current_navigation_was_lazy_loaded = value; |
215 | | |
216 | | // An iframe element whose current navigation was lazy loaded boolean is false potentially delays the load event. |
217 | | // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#the-iframe-element:potentially-delays-the-load-event |
218 | 0 | set_potentially_delays_the_load_event(!value); |
219 | 0 | } |
220 | | |
221 | | } |