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!
+{}