mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-06 08:10:02 +00:00
LibWeb/HTML: Correctly set base elements frozen base url
Some checks are pending
CI / macOS, arm64, Sanitizer_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, Clang (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
Some checks are pending
CI / macOS, arm64, Sanitizer_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Fuzzers_CI, Clang (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, GNU (push) Waiting to run
CI / Linux, x86_64, Sanitizer_CI, Clang (push) Waiting to run
Package the js repl as a binary artifact / macOS, arm64 (push) Waiting to run
Package the js repl as a binary artifact / Linux, x86_64 (push) Waiting to run
Run test262 and test-wasm / run_and_update_results (push) Waiting to run
Lint Code / lint (push) Waiting to run
Label PRs with merge conflicts / auto-labeler (push) Waiting to run
Push notes / build (push) Waiting to run
This commit implements the fallback to the documents fallback base url if the href of the first base element is a data or javascript url. Additionally the frozen base url is set, if a base element becomes the first base element with an href content attribute because the previous one got removed.
This commit is contained in:
parent
4ed9f6fcc0
commit
6b84cd8d11
Notes:
github-actions[bot]
2025-06-23 06:57:47 +00:00
Author: https://github.com/skyz1
Commit: 6b84cd8d11
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4558
Reviewed-by: https://github.com/shannonbooth ✅
7 changed files with 103 additions and 12 deletions
|
@ -1135,10 +1135,10 @@ Vector<CSS::BackgroundLayerData> const* Document::background_layers() const
|
||||||
|
|
||||||
void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
|
void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
|
||||||
{
|
{
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> base_element_with_href = nullptr;
|
GC::Ptr<HTML::HTMLBaseElement> base_element_with_href = nullptr;
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> base_element_with_target = nullptr;
|
GC::Ptr<HTML::HTMLBaseElement> base_element_with_target = nullptr;
|
||||||
|
|
||||||
for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element_with_href, &base_element_with_target](HTML::HTMLBaseElement const& base_element_in_tree) {
|
for_each_in_subtree_of_type<HTML::HTMLBaseElement>([&base_element_with_href, &base_element_with_target](HTML::HTMLBaseElement& base_element_in_tree) {
|
||||||
if (!base_element_with_href && base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
|
if (!base_element_with_href && base_element_in_tree.has_attribute(HTML::AttributeNames::href)) {
|
||||||
base_element_with_href = &base_element_in_tree;
|
base_element_with_href = &base_element_in_tree;
|
||||||
if (base_element_with_target)
|
if (base_element_with_target)
|
||||||
|
@ -1157,12 +1157,12 @@ void Document::update_base_element(Badge<HTML::HTMLBaseElement>)
|
||||||
m_first_base_element_with_target_in_tree_order = base_element_with_target;
|
m_first_base_element_with_target_in_tree_order = base_element_with_target;
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> Document::first_base_element_with_href_in_tree_order() const
|
GC::Ptr<HTML::HTMLBaseElement> Document::first_base_element_with_href_in_tree_order() const
|
||||||
{
|
{
|
||||||
return m_first_base_element_with_href_in_tree_order;
|
return m_first_base_element_with_href_in_tree_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> Document::first_base_element_with_target_in_tree_order() const
|
GC::Ptr<HTML::HTMLBaseElement> Document::first_base_element_with_target_in_tree_order() const
|
||||||
{
|
{
|
||||||
return m_first_base_element_with_target_in_tree_order;
|
return m_first_base_element_with_target_in_tree_order;
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,8 +223,8 @@ public:
|
||||||
URL::URL base_url() const;
|
URL::URL base_url() const;
|
||||||
|
|
||||||
void update_base_element(Badge<HTML::HTMLBaseElement>);
|
void update_base_element(Badge<HTML::HTMLBaseElement>);
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> first_base_element_with_href_in_tree_order() const;
|
GC::Ptr<HTML::HTMLBaseElement> first_base_element_with_href_in_tree_order() const;
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> first_base_element_with_target_in_tree_order() const;
|
GC::Ptr<HTML::HTMLBaseElement> first_base_element_with_target_in_tree_order() const;
|
||||||
|
|
||||||
String url_string() const { return m_url.to_string(); }
|
String url_string() const { return m_url.to_string(); }
|
||||||
String document_uri() const { return url_string(); }
|
String document_uri() const { return url_string(); }
|
||||||
|
@ -1135,8 +1135,8 @@ private:
|
||||||
GC::Ptr<Selection::Selection> m_selection;
|
GC::Ptr<Selection::Selection> m_selection;
|
||||||
|
|
||||||
// NOTE: This is a cache to make finding the first <base href> or <base target> element O(1).
|
// NOTE: This is a cache to make finding the first <base href> or <base target> element O(1).
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> m_first_base_element_with_href_in_tree_order;
|
GC::Ptr<HTML::HTMLBaseElement> m_first_base_element_with_href_in_tree_order;
|
||||||
GC::Ptr<HTML::HTMLBaseElement const> m_first_base_element_with_target_in_tree_order;
|
GC::Ptr<HTML::HTMLBaseElement> m_first_base_element_with_target_in_tree_order;
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/images.html#list-of-available-images
|
// https://html.spec.whatwg.org/multipage/images.html#list-of-available-images
|
||||||
GC::Ptr<HTML::ListOfAvailableImages> m_list_of_available_images;
|
GC::Ptr<HTML::ListOfAvailableImages> m_list_of_available_images;
|
||||||
|
|
|
@ -43,7 +43,14 @@ void HTMLBaseElement::inserted()
|
||||||
void HTMLBaseElement::removed_from(Node* old_parent, Node& old_root)
|
void HTMLBaseElement::removed_from(Node* old_parent, Node& old_root)
|
||||||
{
|
{
|
||||||
HTMLElement::removed_from(old_parent, old_root);
|
HTMLElement::removed_from(old_parent, old_root);
|
||||||
|
auto old_first_base_element_with_href_in_tree_order = document().first_base_element_with_href_in_tree_order();
|
||||||
document().update_base_element({});
|
document().update_base_element({});
|
||||||
|
|
||||||
|
// The frozen base URL must be immediately set for an element whenever any of the following situations occur:
|
||||||
|
// - The base element becomes the first base element in tree order with an href content attribute in its Document.
|
||||||
|
auto first_base_element_with_href_in_document = document().first_base_element_with_href_in_tree_order();
|
||||||
|
if (first_base_element_with_href_in_document != old_first_base_element_with_href_in_tree_order)
|
||||||
|
first_base_element_with_href_in_document->set_the_frozen_base_url();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTMLBaseElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
void HTMLBaseElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value, Optional<FlyString> const& namespace_)
|
||||||
|
@ -72,13 +79,17 @@ void HTMLBaseElement::set_the_frozen_base_url()
|
||||||
auto href = get_attribute_value(AttributeNames::href);
|
auto href = get_attribute_value(AttributeNames::href);
|
||||||
auto url_record = document.fallback_base_url().complete_url(href);
|
auto url_record = document.fallback_base_url().complete_url(href);
|
||||||
|
|
||||||
// 3. Set element's frozen base URL to document's fallback base URL, if urlRecord is failure or running Is base allowed for Document? on the resulting URL record and document returns "Blocked", and to urlRecord otherwise.
|
// 3. If any of the following are true:
|
||||||
// FIXME: Apply "Is base allowed for Document?" CSP
|
// - urlRecord is failure;
|
||||||
if (!url_record.has_value()) {
|
// - urlRecord's scheme is "data" or "javascript"; or
|
||||||
|
// FIXME: - running Is base allowed for Document? on urlRecord and document returns "Blocked",
|
||||||
|
// then set element's frozen base URL to document's fallback base URL and return.
|
||||||
|
if (!url_record.has_value() || url_record->scheme() == "data" || url_record->scheme() == "javascript") {
|
||||||
m_frozen_base_url = document.fallback_base_url();
|
m_frozen_base_url = document.fallback_base_url();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. Set element's frozen base URL to urlRecord.
|
||||||
m_frozen_base_url = url_record.release_value();
|
m_frozen_base_url = url_record.release_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 3 tests
|
||||||
|
|
||||||
|
3 Pass
|
||||||
|
Pass First <base> has a data: URL so fallback is used
|
||||||
|
Pass First <base> is removed so second is used
|
||||||
|
Pass Dynamically inserted first <base> has a data: URL so fallback is used
|
|
@ -0,0 +1,8 @@
|
||||||
|
Harness status: OK
|
||||||
|
|
||||||
|
Found 3 tests
|
||||||
|
|
||||||
|
3 Pass
|
||||||
|
Pass First <base> has a javascript: URL so fallback is used
|
||||||
|
Pass First <base> is removed so second is used
|
||||||
|
Pass Dynamically inserted first <base> has a javascript: URL so fallback is used
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!-- Please update base-javascript.html together with this -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title><base> and data: URLs</title>
|
||||||
|
<script src=../../../../resources/testharness.js></script>
|
||||||
|
<script src=../../../../resources/testharnessreport.js></script>
|
||||||
|
<base href="data:/,test">
|
||||||
|
<base href="https://example.com/">
|
||||||
|
<div id=log></div>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", document.URL).href);
|
||||||
|
}, "First <base> has a data: URL so fallback is used");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
document.querySelector("base").remove();
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", "https://example.com/").href);
|
||||||
|
}, "First <base> is removed so second is used");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const base = document.createElement("base");
|
||||||
|
base.href = "data:/,more-test";
|
||||||
|
document.head.prepend(base);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", document.URL).href);
|
||||||
|
}, "Dynamically inserted first <base> has a data: URL so fallback is used");
|
||||||
|
</script>
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!-- Please update base-data.html together with this -->
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title><base> and javascript: URLs</title>
|
||||||
|
<script src=../../../../resources/testharness.js></script>
|
||||||
|
<script src=../../../../resources/testharnessreport.js></script>
|
||||||
|
<base href="javascript:/,test">
|
||||||
|
<base href="https://example.com/">
|
||||||
|
<div id=log></div>
|
||||||
|
<script>
|
||||||
|
test(() => {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", document.URL).href);
|
||||||
|
}, "First <base> has a javascript: URL so fallback is used");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
document.querySelector("base").remove();
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", "https://example.com/").href);
|
||||||
|
}, "First <base> is removed so second is used");
|
||||||
|
|
||||||
|
test(() => {
|
||||||
|
const base = document.createElement("base");
|
||||||
|
base.href = "javascript:/,more-test";
|
||||||
|
document.head.prepend(base);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = "blah";
|
||||||
|
assert_equals(link.href, new URL("blah", document.URL).href);
|
||||||
|
}, "Dynamically inserted first <base> has a javascript: URL so fallback is used");
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue