summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp
blob: 8caf538fe78f8681e1405e03ee68944b4e7c0c6d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * Copyright (c) 2020-2022, Andreas Kling <kling@serenityos.org>
 * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <LibWeb/DOM/Document.h>
#include <LibWeb/DOM/Event.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Requests.h>
#include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/BrowsingContextContainer.h>
#include <LibWeb/HTML/BrowsingContextGroup.h>
#include <LibWeb/HTML/HTMLIFrameElement.h>
#include <LibWeb/HTML/NavigationParams.h>
#include <LibWeb/HTML/Origin.h>
#include <LibWeb/Page/Page.h>

namespace Web::HTML {

HashTable<BrowsingContextContainer*>& BrowsingContextContainer::all_instances()
{
    static HashTable<BrowsingContextContainer*> set;
    return set;
}

BrowsingContextContainer::BrowsingContextContainer(DOM::Document& document, DOM::QualifiedName qualified_name)
    : HTMLElement(document, move(qualified_name))
{
}

BrowsingContextContainer::~BrowsingContextContainer() = default;

void BrowsingContextContainer::visit_edges(Cell::Visitor& visitor)
{
    Base::visit_edges(visitor);
    visitor.visit(m_nested_browsing_context);
}

// https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-nested-browsing-context
void BrowsingContextContainer::create_new_nested_browsing_context()
{
    // 1. Let group be element's node document's browsing context's top-level browsing context's group.
    VERIFY(document().browsing_context());
    auto* group = document().browsing_context()->top_level_browsing_context().group();

    // NOTE: The spec assumes that `group` is non-null here.
    VERIFY(group);
    VERIFY(group->page());

    // 2. Let browsingContext be the result of creating a new browsing context with element's node document, element, and group.
    // 3. Set element's nested browsing context to browsingContext.
    m_nested_browsing_context = BrowsingContext::create_a_new_browsing_context(*group->page(), document(), *this, *group);

    document().browsing_context()->append_child(*m_nested_browsing_context);
    m_nested_browsing_context->set_frame_nesting_levels(document().browsing_context()->frame_nesting_levels());
    m_nested_browsing_context->register_frame_nesting(document().url());

    // 4. If element has a name attribute, then set browsingContext's name to the value of this attribute.
    if (auto name = attribute(HTML::AttributeNames::name); !name.is_empty())
        m_nested_browsing_context->set_name(String::from_deprecated_string(name).release_value_but_fixme_should_propagate_errors());
}

// https://html.spec.whatwg.org/multipage/browsers.html#concept-bcc-content-document
const DOM::Document* BrowsingContextContainer::content_document() const
{
    // 1. If container's nested browsing context is null, then return null.
    if (m_nested_browsing_context == nullptr)
        return nullptr;

    // 2. Let context be container's nested browsing context.
    auto const& context = *m_nested_browsing_context;

    // 3. Let document be context's active document.
    auto const* document = context.active_document();

    // FIXME: This should not be here, as we're expected to have a document at this point.
    if (!document)
        return nullptr;

    VERIFY(document);
    VERIFY(m_document);

    // 4. If document's origin and container's node document's origin are not same origin-domain, then return null.
    if (!document->origin().is_same_origin_domain(m_document->origin()))
        return nullptr;

    // 5. Return document.
    return document;
}

DOM::Document const* BrowsingContextContainer::content_document_without_origin_check() const
{
    if (!m_nested_browsing_context)
        return nullptr;
    return m_nested_browsing_context->active_document();
}

// https://html.spec.whatwg.org/multipage/embedded-content-other.html#dom-media-getsvgdocument
const DOM::Document* BrowsingContextContainer::get_svg_document() const
{
    // 1. Let document be this element's content document.
    auto const* document = content_document();

    // 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.
    if (document && document->content_type() == "image/svg+xml"sv)
        return document;
    // 3. Return null.
    return nullptr;
}

HTML::WindowProxy* BrowsingContextContainer::content_window()
{
    if (!m_nested_browsing_context)
        return nullptr;
    return m_nested_browsing_context->window_proxy();
}

// https://html.spec.whatwg.org/multipage/urls-and-fetching.html#matches-about:blank
static bool url_matches_about_blank(AK::URL const& url)
{
    // 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.
    return url.scheme() == "about"sv
        && url.serialize_path() == "blank"sv
        && url.username().is_empty()
        && url.password().is_empty()
        && url.host().is_null();
}

// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements
void BrowsingContextContainer::shared_attribute_processing_steps_for_iframe_and_frame(bool initial_insertion)
{
    // 1. Let url be the URL record about:blank.
    auto url = AK::URL("about:blank");

    // 2. If element has a src attribute specified, and its value is not the empty string,
    //    then parse the value of that attribute relative to element's node document.
    //    If this is successful, then set url to the resulting URL record.
    auto src_attribute_value = attribute(HTML::AttributeNames::src);
    if (!src_attribute_value.is_null() && !src_attribute_value.is_empty()) {
        auto parsed_src = document().parse_url(src_attribute_value);
        if (parsed_src.is_valid())
            url = parsed_src;
    }

    // 3. If there exists an ancestor browsing context of element's nested browsing context
    //    whose active document's URL, ignoring fragments, is equal to url, then return.
    if (m_nested_browsing_context) {
        for (auto ancestor = m_nested_browsing_context->parent(); ancestor; ancestor = ancestor->parent()) {
            VERIFY(ancestor->active_document());
            if (ancestor->active_document()->url().equals(url, AK::URL::ExcludeFragment::Yes))
                return;
        }
    }

    // 4. If url matches about:blank and initialInsertion is true, then:
    if (url_matches_about_blank(url) && initial_insertion) {
        // FIXME: 1. Perform the URL and history update steps given element's nested browsing context's active document and url.

        // 2. Run the iframe load event steps given element.
        // FIXME: The spec doesn't check frame vs iframe here. Bug: https://github.com/whatwg/html/issues/8295
        if (is<HTMLIFrameElement>(*this)) {
            run_iframe_load_event_steps(static_cast<HTMLIFrameElement&>(*this));
        }

        // 3. Return.
        return;
    }

    // 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.
    auto resource = Fetch::Infrastructure::Request::create(vm());
    resource->set_url(url);
    // FIXME: Set the referrer policy.

    // AD-HOC:
    if (url.scheme() == "file" && document().origin().scheme() != "file") {
        dbgln("iframe failed to load URL: Security violation: {} may not load {}", document().url(), url);
        return;
    }

    // 6. If element is an iframe element, then set element's current navigation was lazy loaded boolean to false.
    if (is<HTMLIFrameElement>(*this)) {
        static_cast<HTMLIFrameElement&>(*this).set_current_navigation_was_lazy_loaded(false);
    }

    // 7. If element is an iframe element, and the will lazy load element steps given element return true, then:
    if (is<HTMLIFrameElement>(*this) && static_cast<HTMLIFrameElement&>(*this).will_lazy_load_element()) {
        // FIXME: 1. Set element's lazy load resumption steps to the rest of this algorithm starting with the step labeled navigate to the resource.
        // FIXME: 2. Set element's current navigation was lazy loaded boolean to true.
        // FIXME: 3. Start intersection-observing a lazy loading element for element.
        // FIXME: 4. Return.
    }

    // 8. Navigate to the resource: navigate an iframe or frame given element and resource.
    navigate_an_iframe_or_frame(resource);
}

// https://html.spec.whatwg.org/multipage/iframe-embed-object.html#navigate-an-iframe-or-frame
void BrowsingContextContainer::navigate_an_iframe_or_frame(JS::NonnullGCPtr<Fetch::Infrastructure::Request> resource)
{
    // 1. Let historyHandling be "default".
    auto history_handling = HistoryHandlingBehavior::Default;

    // 2. If element's nested browsing context's active document is not completely loaded, then set historyHandling to "replace".
    VERIFY(m_nested_browsing_context);
    VERIFY(m_nested_browsing_context->active_document());
    if (!m_nested_browsing_context->active_document()->is_completely_loaded()) {
        history_handling = HistoryHandlingBehavior::Replace;
    }

    // FIXME: 3. Let reportFrameTiming be the following step given response response:
    //           queue an element task on the networking task source
    //           given element's node document's relevant global object
    //           to finalize and report timing given response, element's node document's relevant global object, and element's local name.

    // 4. Navigate element's nested browsing context to resource,
    //    with historyHandling set to historyHandling,
    //    the source browsing context set to element's node document's browsing context,
    //    FIXME: and processResponseEndOfBody set to reportFrameTiming.
    auto* source_browsing_context = document().browsing_context();
    VERIFY(source_browsing_context);
    MUST(m_nested_browsing_context->navigate(resource, *source_browsing_context, false, history_handling));
}

}