mirror of
https://github.com/lexmount/moli.git
synced 2026-10-07 16:01:01 +00:00
fix(dom): align insertAdjacentHTML sibling contexts
This commit is contained in:
@@ -36,6 +36,7 @@ enum DocumentWriteParserPumpInput<'a> {
|
||||
enum HtmlFragmentParserContextMode {
|
||||
Standard,
|
||||
RangeCreateContextualFragment,
|
||||
SiblingInsertion,
|
||||
}
|
||||
|
||||
struct DocumentWriteParserMutationOwner<'a, 'scope, 'pin> {
|
||||
@@ -553,8 +554,11 @@ impl DocumentRuntime {
|
||||
.and_then(Node::local_name)
|
||||
.unwrap_or("body")
|
||||
.to_owned();
|
||||
if context_mode == HtmlFragmentParserContextMode::RangeCreateContextualFragment
|
||||
&& context_namespace == "http://www.w3.org/1999/xhtml"
|
||||
if matches!(
|
||||
context_mode,
|
||||
HtmlFragmentParserContextMode::RangeCreateContextualFragment
|
||||
| HtmlFragmentParserContextMode::SiblingInsertion
|
||||
) && context_namespace == "http://www.w3.org/1999/xhtml"
|
||||
&& context_local_name.eq_ignore_ascii_case("html")
|
||||
{
|
||||
context_local_name = "body".to_owned();
|
||||
@@ -951,7 +955,13 @@ impl DocumentRuntime {
|
||||
html: &str,
|
||||
insert: impl FnOnce(&mut Self, &mut v8::PinScope<'_, '_>, *mut JsContextHost, DomHandle) -> bool,
|
||||
) -> bool {
|
||||
let Some(fragment) = self.build_fragment_from_html(
|
||||
let scripting_enabled = unsafe { &*host_ptr }.document_scripting_enabled(document_handle);
|
||||
let context_mode = if context_handle == target {
|
||||
HtmlFragmentParserContextMode::Standard
|
||||
} else {
|
||||
HtmlFragmentParserContextMode::SiblingInsertion
|
||||
};
|
||||
let Some(fragment) = self.build_fragment_from_html_with_context_mode(
|
||||
scope,
|
||||
host_ptr,
|
||||
document_handle,
|
||||
@@ -959,11 +969,13 @@ impl DocumentRuntime {
|
||||
html,
|
||||
true,
|
||||
HtmlFragmentCustomElementUpgradeTiming::AfterInsertion,
|
||||
context_mode,
|
||||
scripting_enabled,
|
||||
false,
|
||||
) else {
|
||||
return false;
|
||||
};
|
||||
let added_children = self.dom_host().child_handles(fragment).collect::<Vec<_>>();
|
||||
let _ = target;
|
||||
let changed = insert(self, scope, host_ptr, fragment);
|
||||
if changed
|
||||
&& !self.upgrade_inserted_html_fragment_custom_elements(
|
||||
|
||||
@@ -297,15 +297,26 @@ pub(in crate::native_bridge) fn node_insert_adjacent_html_callback<'s>(
|
||||
position,
|
||||
InsertAdjacentPosition::BeforeBegin | InsertAdjacentPosition::AfterEnd
|
||||
);
|
||||
if needs_parent
|
||||
&& unsafe { &*runtime_ptr }
|
||||
if needs_parent {
|
||||
let parent = unsafe { &*runtime_ptr }
|
||||
.dom_host()
|
||||
.node(target)
|
||||
.and_then(Node::parent_node)
|
||||
.is_none()
|
||||
{
|
||||
rv.set_undefined();
|
||||
return;
|
||||
.and_then(Node::parent_node);
|
||||
let has_no_modifiable_parent = parent.is_none_or(|parent| {
|
||||
unsafe { &*runtime_ptr }
|
||||
.dom_host()
|
||||
.node(parent)
|
||||
.is_none_or(Node::is_document)
|
||||
});
|
||||
if has_no_modifiable_parent {
|
||||
throw_dom_exception(
|
||||
scope,
|
||||
"NoModificationAllowedError",
|
||||
7,
|
||||
"The element has no modifiable parent for sibling insertion.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let Some(document_handle) =
|
||||
insert_adjacent_document_handle(unsafe { &*runtime_ptr }, target, position)
|
||||
|
||||
@@ -419,6 +419,82 @@ fn element_insert_adjacent_methods_parse_webidl_arguments() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn insert_adjacent_html_enforces_sibling_context_rules() {
|
||||
let mut vm = new_storage_test_vm("https://insert-adjacent-sibling-context.test/");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
const probe = callback => {
|
||||
try {
|
||||
callback();
|
||||
return 'missing';
|
||||
} catch (error) {
|
||||
return `${error.name}:${error.code}`;
|
||||
}
|
||||
};
|
||||
const sources = ['', 'text', '<!--comment-->', '<div></div>'];
|
||||
const positions = ['beforebegin', 'afterend'];
|
||||
const detached = document.createElement('div');
|
||||
const root = document.documentElement ||
|
||||
document.appendChild(document.createElement('html'));
|
||||
const detachedErrors = positions.flatMap(position =>
|
||||
sources.map(source => probe(() => detached.insertAdjacentHTML(position, source)))
|
||||
);
|
||||
const documentErrors = positions.flatMap(position =>
|
||||
sources.map(source => probe(() => root.insertAdjacentHTML(position, source)))
|
||||
);
|
||||
|
||||
while (root.firstChild) {
|
||||
root.removeChild(root.firstChild);
|
||||
}
|
||||
root.insertAdjacentHTML(
|
||||
'afterbegin',
|
||||
'<head id="inside-head"></head><body id="inside-body"></body>'
|
||||
);
|
||||
const preservedInnerHtmlContext =
|
||||
document.head?.id === 'inside-head' &&
|
||||
document.body?.id === 'inside-body' &&
|
||||
root.firstChild === document.head &&
|
||||
root.lastChild === document.body;
|
||||
const head = document.head ||
|
||||
root.insertBefore(document.createElement('head'), root.firstChild);
|
||||
const body = document.body || root.appendChild(document.createElement('body'));
|
||||
head.insertAdjacentHTML('beforebegin', '<p id="before-head"></p>');
|
||||
body.insertAdjacentHTML('afterend', '<p id="after-body"></p>');
|
||||
const beforeHead = document.getElementById('before-head');
|
||||
const afterBody = document.getElementById('after-body');
|
||||
|
||||
return JSON.stringify({
|
||||
detachedErrors,
|
||||
documentErrors,
|
||||
preservedInnerHtmlContext,
|
||||
counts: [
|
||||
document.getElementsByTagName('html').length,
|
||||
document.getElementsByTagName('head').length,
|
||||
document.getElementsByTagName('body').length
|
||||
],
|
||||
placement: [
|
||||
beforeHead.nextSibling === head,
|
||||
body.nextSibling === afterBody,
|
||||
beforeHead.parentNode === root,
|
||||
afterBody.parentNode === root
|
||||
]
|
||||
});
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("insertAdjacentHTML sibling context rules should evaluate");
|
||||
|
||||
assert_eq!(
|
||||
result,
|
||||
r#"{"detachedErrors":["NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7"],"documentErrors":["NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7","NoModificationAllowedError:7"],"preservedInnerHtmlContext":true,"counts":[1,1,1],"placement":[true,true,true,true]}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn document_fragment_and_shadow_root_get_element_by_id_match_browser_lookup_boundaries() {
|
||||
let mut vm = new_storage_test_vm("https://fragment-shadow-get-by-id.test/");
|
||||
|
||||
Reference in New Issue
Block a user