Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion moli-benchmark/wpt-cross-current/failed-cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2611,7 +2611,6 @@ domparsing/DOMParser-parseFromString-xml-internal-subset.html
domparsing/DOMParser-parseFromString-xml-parsererror.html
domparsing/DOMParser-parseFromString-xml.html
domparsing/XMLSerializer-serializeToString.html
domparsing/insert_adjacent_html.html
domparsing/tentative/all-stream-methods-with-trusted-types-no-policy.html
domparsing/tentative/positional-methods-with-trusted-types.html
domparsing/tentative/positional-methods.html
Expand Down
1 change: 1 addition & 0 deletions moli-benchmark/wpt-cross-current/passed-cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3747,6 +3747,7 @@ domparsing/innerhtml-06.html
domparsing/innerhtml-07.html
domparsing/innerhtml-li-autoclosing.html
domparsing/insert-adjacent.html
domparsing/insert_adjacent_html.html
domparsing/outerhtml-01.html
domparsing/outerhtml-02.html
domparsing/style_attribute_html.html
Expand Down
20 changes: 16 additions & 4 deletions moli-renderer-v8/src/document_runtime/document_write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum DocumentWriteParserPumpInput<'a> {
enum HtmlFragmentParserContextMode {
Standard,
RangeCreateContextualFragment,
SiblingInsertion,
}

struct DocumentWriteParserMutationOwner<'a, 'scope, 'pin> {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -946,19 +950,27 @@ 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,
context_handle,
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -296,15 +296,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)
Expand Down
76 changes: 76 additions & 0 deletions moli-renderer-v8/src/script_vm/tests/dom_xhr/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,82 @@ fn element_insert_adjacent_methods_parse_webidl_arguments() {
"true|elementtextundefinedbold|bold|throw:TypeError|throw:TypeError|throw:RangeError|throw:TypeError|throw:SyntaxError|throw:TypeError|throw:TypeError|throw:TypeError|throw:TypeError"
);
}

#[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/");
Expand Down
Loading