Serenity Operating System
at master 225 lines 10 kB view raw
1/* 2 * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2022, Linus Groh <linusg@serenityos.org> 4 * 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8#include <LibWeb/DOM/Document.h> 9#include <LibWeb/DOM/Event.h> 10#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h> 11#include <LibWeb/HTML/BrowsingContext.h> 12#include <LibWeb/HTML/BrowsingContextContainer.h> 13#include <LibWeb/HTML/BrowsingContextGroup.h> 14#include <LibWeb/HTML/HTMLIFrameElement.h> 15#include <LibWeb/HTML/NavigationParams.h> 16#include <LibWeb/HTML/Origin.h> 17#include <LibWeb/Page/Page.h> 18 19namespace Web::HTML { 20 21HashTable<BrowsingContextContainer*>& BrowsingContextContainer::all_instances() 22{ 23 static HashTable<BrowsingContextContainer*> set; 24 return set; 25} 26 27BrowsingContextContainer::BrowsingContextContainer(DOM::Document& document, DOM::QualifiedName qualified_name) 28 : HTMLElement(document, move(qualified_name)) 29{ 30} 31 32BrowsingContextContainer::~BrowsingContextContainer() = default; 33 34void BrowsingContextContainer::visit_edges(Cell::Visitor& visitor) 35{ 36 Base::visit_edges(visitor); 37 visitor.visit(m_nested_browsing_context); 38} 39 40// https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-nested-browsing-context 41void BrowsingContextContainer::create_new_nested_browsing_context() 42{ 43 // 1. Let group be element's node document's browsing context's top-level browsing context's group. 44 VERIFY(document().browsing_context()); 45 auto* group = document().browsing_context()->top_level_browsing_context().group(); 46 47 // NOTE: The spec assumes that `group` is non-null here. 48 VERIFY(group); 49 VERIFY(group->page()); 50 51 // 2. Let browsingContext be the result of creating a new browsing context with element's node document, element, and group. 52 // 3. Set element's nested browsing context to browsingContext. 53 m_nested_browsing_context = BrowsingContext::create_a_new_browsing_context(*group->page(), document(), *this, *group); 54 55 document().browsing_context()->append_child(*m_nested_browsing_context); 56 m_nested_browsing_context->set_frame_nesting_levels(document().browsing_context()->frame_nesting_levels()); 57 m_nested_browsing_context->register_frame_nesting(document().url()); 58 59 // 4. If element has a name attribute, then set browsingContext's name to the value of this attribute. 60 if (auto name = attribute(HTML::AttributeNames::name); !name.is_empty()) 61 m_nested_browsing_context->set_name(name); 62} 63 64// https://html.spec.whatwg.org/multipage/browsers.html#concept-bcc-content-document 65const DOM::Document* BrowsingContextContainer::content_document() const 66{ 67 // 1. If container's nested browsing context is null, then return null. 68 if (m_nested_browsing_context == nullptr) 69 return nullptr; 70 71 // 2. Let context be container's nested browsing context. 72 auto const& context = *m_nested_browsing_context; 73 74 // 3. Let document be context's active document. 75 auto const* document = context.active_document(); 76 77 // FIXME: This should not be here, as we're expected to have a document at this point. 78 if (!document) 79 return nullptr; 80 81 VERIFY(document); 82 VERIFY(m_document); 83 84 // 4. If document's origin and container's node document's origin are not same origin-domain, then return null. 85 if (!document->origin().is_same_origin_domain(m_document->origin())) 86 return nullptr; 87 88 // 5. Return document. 89 return document; 90} 91 92DOM::Document const* BrowsingContextContainer::content_document_without_origin_check() const 93{ 94 if (!m_nested_browsing_context) 95 return nullptr; 96 return m_nested_browsing_context->active_document(); 97} 98 99// https://html.spec.whatwg.org/multipage/embedded-content-other.html#dom-media-getsvgdocument 100const DOM::Document* BrowsingContextContainer::get_svg_document() const 101{ 102 // 1. Let document be this element's content document. 103 auto const* document = content_document(); 104 105 // 2. If document is non-null and was created by the page load processing model for XML files section because the computed type of the resource in the navigate algorithm was image/svg+xml, then return document. 106 if (document && document->content_type() == "image/svg+xml"sv) 107 return document; 108 // 3. Return null. 109 return nullptr; 110} 111 112HTML::WindowProxy* BrowsingContextContainer::content_window() 113{ 114 if (!m_nested_browsing_context) 115 return nullptr; 116 return m_nested_browsing_context->window_proxy(); 117} 118 119// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#matches-about:blank 120static bool url_matches_about_blank(AK::URL const& url) 121{ 122 // A URL matches about:blank if its scheme is "about", its path contains a single string "blank", its username and password are the empty string, and its host is null. 123 return url.scheme() == "about"sv 124 && url.path() == "blank"sv 125 && url.username().is_empty() 126 && url.password().is_empty() 127 && url.host().is_null(); 128} 129 130// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements 131void BrowsingContextContainer::shared_attribute_processing_steps_for_iframe_and_frame(bool initial_insertion) 132{ 133 // 1. Let url be the URL record about:blank. 134 auto url = AK::URL("about:blank"); 135 136 // 2. If element has a src attribute specified, and its value is not the empty string, 137 // then parse the value of that attribute relative to element's node document. 138 // If this is successful, then set url to the resulting URL record. 139 auto src_attribute_value = attribute(HTML::AttributeNames::src); 140 if (!src_attribute_value.is_null() && !src_attribute_value.is_empty()) { 141 auto parsed_src = document().parse_url(src_attribute_value); 142 if (parsed_src.is_valid()) 143 url = parsed_src; 144 } 145 146 // 3. If there exists an ancestor browsing context of element's nested browsing context 147 // whose active document's URL, ignoring fragments, is equal to url, then return. 148 if (m_nested_browsing_context) { 149 for (auto ancestor = m_nested_browsing_context->parent(); ancestor; ancestor = ancestor->parent()) { 150 VERIFY(ancestor->active_document()); 151 if (ancestor->active_document()->url().equals(url, AK::URL::ExcludeFragment::Yes)) 152 return; 153 } 154 } 155 156 // 4. If url matches about:blank and initialInsertion is true, then: 157 if (url_matches_about_blank(url) && initial_insertion) { 158 // FIXME: 1. Perform the URL and history update steps given element's nested browsing context's active document and url. 159 160 // 2. Run the iframe load event steps given element. 161 // FIXME: The spec doesn't check frame vs iframe here. Bug: https://github.com/whatwg/html/issues/8295 162 if (is<HTMLIFrameElement>(*this)) { 163 run_iframe_load_event_steps(static_cast<HTMLIFrameElement&>(*this)); 164 } 165 166 // 3. Return. 167 return; 168 } 169 170 // 5. Let resource be a new request whose URL is url and whose referrer policy is the current state of element's referrerpolicy content attribute. 171 auto resource = Fetch::Infrastructure::Request::create(vm()); 172 resource->set_url(url); 173 // FIXME: Set the referrer policy. 174 175 // AD-HOC: 176 if (url.scheme() == "file" && document().origin().scheme() != "file") { 177 dbgln("iframe failed to load URL: Security violation: {} may not load {}", document().url(), url); 178 return; 179 } 180 181 // 6. If element is an iframe element, then set element's current navigation was lazy loaded boolean to false. 182 if (is<HTMLIFrameElement>(*this)) { 183 static_cast<HTMLIFrameElement&>(*this).set_current_navigation_was_lazy_loaded(false); 184 } 185 186 // 7. If element is an iframe element, and the will lazy load element steps given element return true, then: 187 if (is<HTMLIFrameElement>(*this) && static_cast<HTMLIFrameElement&>(*this).will_lazy_load_element()) { 188 // FIXME: 1. Set element's lazy load resumption steps to the rest of this algorithm starting with the step labeled navigate to the resource. 189 // FIXME: 2. Set element's current navigation was lazy loaded boolean to true. 190 // FIXME: 3. Start intersection-observing a lazy loading element for element. 191 // FIXME: 4. Return. 192 } 193 194 // 8. Navigate to the resource: navigate an iframe or frame given element and resource. 195 navigate_an_iframe_or_frame(resource); 196} 197 198// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#navigate-an-iframe-or-frame 199void BrowsingContextContainer::navigate_an_iframe_or_frame(JS::NonnullGCPtr<Fetch::Infrastructure::Request> resource) 200{ 201 // 1. Let historyHandling be "default". 202 auto history_handling = HistoryHandlingBehavior::Default; 203 204 // 2. If element's nested browsing context's active document is not completely loaded, then set historyHandling to "replace". 205 VERIFY(m_nested_browsing_context); 206 VERIFY(m_nested_browsing_context->active_document()); 207 if (!m_nested_browsing_context->active_document()->is_completely_loaded()) { 208 history_handling = HistoryHandlingBehavior::Replace; 209 } 210 211 // FIXME: 3. Let reportFrameTiming be the following step given response response: 212 // queue an element task on the networking task source 213 // given element's node document's relevant global object 214 // to finalize and report timing given response, element's node document's relevant global object, and element's local name. 215 216 // 4. Navigate element's nested browsing context to resource, 217 // with historyHandling set to historyHandling, 218 // the source browsing context set to element's node document's browsing context, 219 // FIXME: and processResponseEndOfBody set to reportFrameTiming. 220 auto* source_browsing_context = document().browsing_context(); 221 VERIFY(source_browsing_context); 222 MUST(m_nested_browsing_context->navigate(resource, *source_browsing_context, false, history_handling)); 223} 224 225}