diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp index f5bce1a5ef..b633088055 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.cpp +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -151,6 +152,11 @@ bool HTMLLinkElement::has_loaded_icon() const return m_relationship & Relationship::Icon && m_loaded_icon.has_value(); } +bool HTMLLinkElement::has_icon_keyword() const +{ + return m_relationship & Relationship::Icon; +} + void HTMLLinkElement::attribute_changed(FlyString const& name, Optional const& old_value, Optional const& value, Optional const& namespace_) { Base::attribute_changed(name, old_value, value, namespace_); @@ -993,6 +999,14 @@ void HTMLLinkElement::load_fallback_favicon_if_needed(GC::Ref doc return; if (!document->url().scheme().is_one_of("http"sv, "https"sv)) return; + auto icon_link_elements = DOM::HTMLCollection::create(*document, DOM::HTMLCollection::Scope::Descendants, [](DOM::Element const& element) { + if (!is(element)) + return false; + + return static_cast(element).has_icon_keyword(); + }); + if (icon_link_elements->length() != 0) + return; // AD-HOC: Don't load fallback favicon for auxiliary browsing contexts (popup windows). // This matches the behavior observed in Chrome and Firefox, and avoids unnecessary network requests diff --git a/Libraries/LibWeb/HTML/HTMLLinkElement.h b/Libraries/LibWeb/HTML/HTMLLinkElement.h index 35c41ad4d8..edfec0f05d 100644 --- a/Libraries/LibWeb/HTML/HTMLLinkElement.h +++ b/Libraries/LibWeb/HTML/HTMLLinkElement.h @@ -38,6 +38,7 @@ public: GC::Ref sizes(); bool has_loaded_icon() const; + bool has_icon_keyword() const; bool load_favicon_and_use_if_window_is_active(); static void load_fallback_favicon_if_needed(GC::Ref); diff --git a/Libraries/LibWeb/HTML/Navigable.cpp b/Libraries/LibWeb/HTML/Navigable.cpp index b0fdf39aa7..6b7defa8fc 100644 --- a/Libraries/LibWeb/HTML/Navigable.cpp +++ b/Libraries/LibWeb/HTML/Navigable.cpp @@ -2165,8 +2165,81 @@ void Navigable::begin_navigation(NavigateParams params) // 7. Let navigationParams be null. NavigationParamsVariant navigation_params = Navigable::NullOrError {}; - // FIXME: 8. If response is non-null: + // 8. If response is non-null: if (response) { + auto response_url = response->url(); + VERIFY(response_url.has_value()); + + // 1. Let sourcePolicyContainer be a clone of the sourceDocument's policy container, if + // sourceDocument is not null; otherwise null. + auto source_policy_container = source_snapshot_params->source_policy_container; + + // 2. Let policyContainer be the result of determining navigation params policy container given + // response's URL, null, sourcePolicyContainer, navigable's container document's policy container, + // and null. + GC::Ptr parent_policy_container; + if (auto container_document = this->container_document()) + parent_policy_container = container_document->policy_container(); + else if (*response_url == URL::about_srcdoc()) { + // NOTE: Specification assumes that only navigables corresponding to iframes can be navigated to about:srcdoc. + // We also use srcdoc to implement load_html() for top level navigables so we need a policy container + // because the navigable might not have a container. + parent_policy_container = heap().allocate(heap()); + } + auto policy_container = determine_navigation_params_policy_container(*response_url, heap(), {}, source_policy_container, parent_policy_container, {}); + + // 3. Let finalSandboxFlags be the union of targetSnapshotParams's sandboxing flags and + // policyContainer's CSP list's CSP-derived sandboxing flags. + auto final_sandbox_flags = target_snapshot_params.sandboxing_flags | policy_container->csp_list->csp_derived_sandboxing_flags(); + + // 4. Let responseOrigin be the result of determining the origin given response's URL, + // finalSandboxFlags, and documentState's initiator origin. + auto response_origin = determine_the_origin(response_url, final_sandbox_flags, document_state->initiator_origin()); + + // 5. Let coop be a new opener policy. + OpenerPolicy response_coop = {}; + + // 6. Let coopEnforcementResult be a new opener policy enforcement result with + // url: response's URL + // origin: responseOrigin + // opener policy: coop + OpenerPolicyEnforcementResult coop_enforcement_result { + .url = *response_url, + .origin = response_origin, + .opener_policy = response_coop, + }; + + // 7. Set navigationParams to a new navigation params, with + // id: navigationId + // navigable: navigable + // request: null + // response: response + // fetch controller: null + // commit early hints: null + // COOP enforcement result: coopEnforcementResult + // reserved environment: null + // origin: responseOrigin + // policy container: policyContainer + // final sandboxing flag set: finalSandboxFlags + // opener policy: coop + // FIXME: navigation timing type: "navigate" + // about base URL: documentState's about base URL + // user involvement: userInvolvement + navigation_params = heap().allocate( + navigation_id, + this, + nullptr, + response, + nullptr, + nullptr, + move(coop_enforcement_result), + nullptr, + move(response_origin), + policy_container, + final_sandbox_flags, + response_coop, + document_state->about_base_url(), + user_involvement); } // 9. Attempt to populate the history entry's document for historyEntry, given navigable, "navigate", diff --git a/Libraries/LibWeb/Page/Page.cpp b/Libraries/LibWeb/Page/Page.cpp index 157b5315a6..9126d0d2db 100644 --- a/Libraries/LibWeb/Page/Page.cpp +++ b/Libraries/LibWeb/Page/Page.cpp @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -128,6 +130,31 @@ void Page::load_html(StringView html) .user_involvement = HTML::UserNavigationInvolvement::BrowserUI }); } +void Page::load_html(StringView html, URL::URL const& url) +{ + // FIXME: #23909 Figure out why GC threshold does not stay low when repeatedly loading html from the WebView + heap().collect_garbage(); + + auto document = top_level_traversable()->active_document(); + auto& realm = document->realm(); + auto html_string = String::from_utf8(html).release_value_but_fixme_should_propagate_errors(); + + auto response = Fetch::Infrastructure::Response::create(realm.vm()); + response->url_list().append(url); + response->header_list()->append({ "Content-Type"sv, "text/html"sv }); + response->set_body(Fetch::Infrastructure::byte_sequence_as_body(realm, html_string.bytes())); + + HTML::Navigable::NavigateParams params { .url = url, + .source_document = *document, + .response = response, + .user_involvement = HTML::UserNavigationInvolvement::BrowserUI }; + + if (url == URL::about_srcdoc()) + params.document_resource = move(html_string); + + (void)top_level_traversable()->navigate(move(params)); +} + void Page::reload() { top_level_traversable()->reload(); diff --git a/Libraries/LibWeb/Page/Page.h b/Libraries/LibWeb/Page/Page.h index cdc0865ae2..087fcc301c 100644 --- a/Libraries/LibWeb/Page/Page.h +++ b/Libraries/LibWeb/Page/Page.h @@ -97,6 +97,7 @@ public: void load(URL::URL const&); void load_html(StringView); + void load_html(StringView, URL::URL const&); void reload(); diff --git a/Libraries/LibWebView/ErrorHTML.h b/Libraries/LibWebView/ErrorHTML.h index 5a74b547b4..7a8d6689c8 100644 --- a/Libraries/LibWebView/ErrorHTML.h +++ b/Libraries/LibWebView/ErrorHTML.h @@ -18,6 +18,7 @@ constexpr inline auto ERROR_HTML_HEADER = R"~~~( Error! +{}