LibWeb: Don't attempt to set the frozen base url on a null base element

This commit is contained in:
Tim Ledbetter 2025-06-24 13:49:32 +01:00 committed by Shannon Booth
commit 1c2b6ae03e
Notes: github-actions[bot] 2025-06-24 13:34:55 +00:00
3 changed files with 162 additions and 1 deletions

View file

@ -49,7 +49,7 @@ void HTMLBaseElement::removed_from(Node* old_parent, Node& old_root)
// The frozen base URL must be immediately set for an element whenever any of the following situations occur: // 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. // - 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(); 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) if (first_base_element_with_href_in_document && 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(); first_base_element_with_href_in_document->set_the_frozen_base_url();
} }

View file

@ -0,0 +1,13 @@
Harness status: OK
Found 7 tests
6 Pass
1 Fail
Pass Style attribute should create CSS declaration block based on its content
Pass Changes to style attribute should reflect on CSS declaration block
Pass Changes to CSS declaration block should reflect on style attribute
Pass Changes to CSS declaration block should queue mutation record for style attribute
Pass Removing non-existing property or setting invalid value on CSS declaration block shouldn't queue mutation record
Fail Changes to CSS declaration block after a base URL change
Pass Expansion of all:unset and all:revert treated identically

View file

@ -0,0 +1,148 @@
<!DOCTYPE html>
<meta charset="UTF-8">
<link rel="author" title="Xidorn Quan" href="mailto:me@upsuper.org">
<link rel="help" href="https://drafts.csswg.org/cssom-1/#css-declaration-blocks">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<body>
<script>
function createTestElement(style) {
let wrapper = document.createElement("div");
wrapper.innerHTML = `<div id="test" style="${style}"></div>`;
return wrapper.querySelector("#test");
}
test(function() {
let elem = createTestElement("z-index: 10;");
assert_equals(elem.style.cssText, "z-index: 10;");
}, "Style attribute should create CSS declaration block based on its content");
test(function() {
let elem = createTestElement("z-index: 20;");
let style = elem.style;
assert_equals(style.cssText, "z-index: 20;");
function assert_css_text(value, action) {
assert_equals(style.cssText, value, "CSS declaration block after " + action);
}
elem.setAttribute("style", "z-index: 21;");
assert_css_text("z-index: 21;", "changing the style attribute");
elem.removeAttribute("style");
assert_css_text("", "removing the style attribute");
elem.setAttribute("style", "position: absolute;");
assert_css_text("position: absolute;", "adding style attribute again");
}, "Changes to style attribute should reflect on CSS declaration block");
test(function() {
let elem = createTestElement("z-index: 30;");
let style = elem.style;
assert_equals(style.cssText, "z-index: 30;");
function assert_attr(value, action) {
assert_equals(elem.getAttribute("style"), value, "style attribute after " + action);
}
style.setProperty("z-index", "31");
assert_attr("z-index: 31;", "changing property in CSS declaration block");
style.removeProperty("z-index");
assert_attr("", "removing property from CSS declaration block");
style.setProperty("position", "absolute");
assert_attr("position: absolute;", "adding property to CSS declaration block");
style.cssText = "z-index: 32;";
assert_attr("z-index: 32;", "changing cssText");
style.cssText = "z-index: 33; invalid";
assert_attr("z-index: 33;", "changing cssText to a partial invalid value");
}, "Changes to CSS declaration block should reflect on style attribute");
test(function() {
let elem = createTestElement("z-index: 40;");
let style = elem.style;
assert_equals(style.cssText, "z-index: 40;");
// Create an observer for the element.
let observer = new MutationObserver(function() {});
observer.observe(elem, {attributes: true, attributeOldValue: true});
function assert_record_with_old_value(oldValue, action) {
let records = observer.takeRecords();
assert_equals(records.length, 1, "number of mutation records after " + action);
let record = records[0];
assert_equals(record.type, "attributes", "mutation type after " + action);
assert_equals(record.attributeName, "style", "mutated attribute after " + action);
assert_equals(record.oldValue, oldValue, "old value after " + action);
}
style.setProperty("z-index", "41");
assert_record_with_old_value("z-index: 40;", "changing property in CSS declaration block");
style.cssText = "z-index: 42;";
assert_record_with_old_value("z-index: 41;", "changing cssText");
style.cssText = "z-index: 42;";
assert_record_with_old_value("z-index: 42;", "changing cssText with the same content");
style.removeProperty("z-index");
assert_record_with_old_value("z-index: 42;", "removing property from CSS declaration block");
// Mutation to shorthand properties should also trigger only one mutation record.
style.setProperty("margin", "1px");
assert_record_with_old_value("", "adding shorthand property to CSS declaration block");
style.removeProperty("margin");
assert_record_with_old_value("margin: 1px;", "removing shorthand property from CSS declaration block");
// Final sanity check.
assert_equals(elem.getAttribute("style"), "");
}, "Changes to CSS declaration block should queue mutation record for style attribute");
test(function() {
let elem = createTestElement("z-index: 50; invalid");
let style = elem.style;
assert_equals(style.cssText, "z-index: 50;");
// Create an observer for the element.
let observer = new MutationObserver(function() {});
observer.observe(elem, {attributes: true});
function assert_no_record(action) {
let records = observer.takeRecords();
assert_equals(records.length, 0, "expect no record after " + action);
}
style.setProperty("z-index", "invalid");
assert_no_record("setting invalid value to property");
// Longhand property.
style.removeProperty("position");
assert_no_record("removing non-existing longhand property");
style.setProperty("position", "");
assert_no_record("setting empty string to non-existing longhand property");
// Shorthand property.
style.removeProperty("margin");
assert_no_record("removing non-existing shorthand property");
style.setProperty("margin", "");
assert_no_record("setting empty string to non-existing shorthand property");
// Check that the value really isn't changed.
assert_equals(elem.getAttribute("style"), "z-index: 50; invalid",
"style attribute after removing non-existing properties");
}, "Removing non-existing property or setting invalid value on CSS declaration block shouldn't queue mutation record");
test(function() {
let elem = createTestElement("background-image: url(./);");
let style = elem.style;
let base = document.createElement("base");
base.href = "/";
document.body.appendChild(elem);
let originalComputedValue = getComputedStyle(elem).backgroundImage;
document.head.appendChild(base);
this.add_cleanup(() => {
document.head.removeChild(base);
document.body.removeChild(elem);
});
style.setProperty("background-color", "green");
assert_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
"getComputedStyle(elem).backgroundImage after setting background-color");
style.setProperty("background-image", "url(./)");
assert_not_equals(getComputedStyle(elem).backgroundImage, originalComputedValue,
"getComputedStyle(elem).backgroundImage after setting background-image");
}, "Changes to CSS declaration block after a base URL change");
test(function() {
let e1 = document.createElement('div');
let e2 = document.createElement('div');
document.body.append(e1, e2);
this.add_cleanup(() => {
e1.remove();
e2.remove();
});
e1.style.cssText = "all:revert;border-bottom-left-radius:1px;";
e2.style.cssText = "all:unset;border-bottom-left-radius:1px;";
let processed = e1.style.cssText.split(';')
.map(x => x.replace(/revert$/, 'unset')).join(';');
assert_equals(processed, e2.style.cssText);
}, "Expansion of all:unset and all:revert treated identically");
</script>