LibWeb: Don't crash when upgrading custom element with a bad constructor

Previously, a crash would occur when attempting to throw an error in
this case because the method used to create the exception tried to get
the current realm from the execution context stack, which is empty. The
realm is now passed explicitly when constructing the error, avoiding
the crash.
This commit is contained in:
Tim Ledbetter 2025-01-12 17:17:21 +00:00 committed by Luke Wilde
parent 403cda5cd6
commit 619df0bc2c
Notes: github-actions[bot] 2025-01-13 10:56:42 +00:00
3 changed files with 101 additions and 3 deletions

View file

@ -568,8 +568,6 @@ WebIDL::ExceptionOr<GC::Ref<Element>> create_element(Document& document, FlyStri
// 1. If the synchronous custom elements flag is set, then run these steps while catching any exceptions:
if (synchronous_custom_elements_flag) {
auto synchronously_upgrade_custom_element = [&]() -> JS::ThrowCompletionOr<GC::Ref<HTML::HTMLElement>> {
auto& vm = document.vm();
// 1. Let C be definitions constructor.
auto& constructor = definition->constructor();
@ -578,7 +576,7 @@ WebIDL::ExceptionOr<GC::Ref<Element>> create_element(Document& document, FlyStri
// NOTE: IDL does not currently convert the object for us, so we will have to do it here.
if (!result.has_value() || !result->is_object() || !is<HTML::HTMLElement>(result->as_object()))
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "HTMLElement"sv);
return JS::throw_completion(JS::TypeError::create(realm, "Custom element constructor must return an object that implements HTMLElement"_string));
GC::Ref<HTML::HTMLElement> element = verify_cast<HTML::HTMLElement>(result->as_object());

View file

@ -0,0 +1,9 @@
Harness status: OK
Found 4 tests
4 Pass
Pass HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node
Pass HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object
Pass HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super()
Pass HTML parser must create a fallback HTMLUnknownElement when a custom element constructor throws an exception

View file

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html>
<head>
<title>Custom Elements: Changes to the HTML parser</title>
<meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
<meta name="assert" content="HTML parser must fallback to creating a HTMLUnknownElement when a custom element construction fails">
<link rel="help" href="https://html.spec.whatwg.org/#create-an-element-for-the-token">
<link rel="help" href="https://dom.spec.whatwg.org/#concept-create-element">
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>
<script>
setup({allow_uncaught_exception:true});
class ReturnsTextNode extends HTMLElement {
constructor() {
super();
return document.createTextNode('some text');
}
};
customElements.define('returns-text', ReturnsTextNode);
class ReturnsNonElementObject extends HTMLElement {
constructor() {
super();
return {};
}
};
customElements.define('returns-non-element-object', ReturnsNonElementObject);
class LacksSuperCall extends HTMLElement {
constructor() { }
};
customElements.define('lacks-super-call', LacksSuperCall);
class ThrowsException extends HTMLElement {
constructor() {
throw 'Bad';
}
};
customElements.define('throws-exception', ThrowsException);
</script>
<returns-text></returns-text>
<returns-non-element-object></returns-non-element-object>
<lacks-super-call></lacks-super-call>
<throws-exception></throws-exception>
<script>
test(function () {
var instance = document.querySelector('returns-text');
assert_false(instance instanceof ReturnsTextNode, 'HTML parser must NOT instantiate a custom element when the constructor returns a Text node');
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns a Text node');
test(function () {
var instance = document.querySelector('returns-non-element-object');
assert_false(instance instanceof ReturnsNonElementObject, 'HTML parser must NOT instantiate a custom element when the constructor returns a non-Element object');
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor returns non-Element object');
test(function () {
var instance = document.querySelector('lacks-super-call');
assert_false(instance instanceof LacksSuperCall, 'HTML parser must NOT instantiate a custom element when the constructor does not call super()');
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor does not call super()');
test(function () {
var instance = document.querySelector('throws-exception');
assert_false(instance instanceof ThrowsException, 'HTML parser must NOT instantiate a custom element when the constructor throws an exception');
assert_true(instance instanceof HTMLElement, 'The fallback element created by HTML parser must be an instance of HTMLElement');
assert_true(instance instanceof HTMLUnknownElement, 'The fallback element created by HTML parser must be an instance of HTMLUnknownElement');
}, 'HTML parser must create a fallback HTMLUnknownElement when a custom element constructor throws an exception');
</script>
</body>
</html>