///|
fn apply_selector_transform(
root : @dom.Node,
transform : TransformSpec,
limits : @sel.SelectorLimits,
) -> Unit raise @core.HtmlError {
match root.kind {
Document | Fragment | Element => transform_children(root, transform, limits)
_ => ()
}
}
///|
fn transform_children(
parent : @dom.Node,
transform : TransformSpec,
limits : @sel.SelectorLimits,
) -> Unit raise @core.HtmlError {
let mut index = 0
while index < parent.children.length() {
let child = parent.children[index]
if child.kind == Element &&
transform_matches_element(child, transform, limits) {
match transform.kind {
DropElements => {
transform_report_node_action(child, transform, "Dropped")
@san.remove_child_at(parent, index)
}
UnwrapElements => {
transform_report_node_action(child, transform, "Unwrapped")
let inserted = @san.unwrap_child_at(parent, index)
if inserted > 0 {
transform_inserted_children_after_unwrap(
parent, index, inserted, transform, limits,
)
index += inserted
}
}
EscapeElements => {
transform_report_node_action(child, transform, "Escaped")
ignore(escape_transform_child_at(parent, index))
}
EmptyElements => {
if empty_transform_node(child) {
transform_report_node_action(child, transform, "Emptied")
}
index += 1
}
SetAttributes => {
if apply_set_attrs_transform(child, transform) {
transform_report_node_action(child, transform, "Set attributes on")
}
transform_children(child, transform, limits)
index += 1
}
DropAttributes => {
ignore(apply_drop_attrs_transform(child, transform))
transform_children(child, transform, limits)
index += 1
}
AllowlistAttributes => {
ignore(apply_allowlist_attrs_transform(child, transform))
transform_children(child, transform, limits)
index += 1
}
MergeAttributeTokens => {
if apply_merge_attrs_transform(child, transform) {
transform_call_hook(child, transform)
transform_report(
transform,
"Merged tokens into attribute '" +
transform.merge_attr +
"' on <" +
transform.merge_tag +
">",
Some(child),
)
}
transform_children(child, transform, limits)
index += 1
}
EditElement => {
apply_edit_transform(child, transform)
if index < parent.children.length() &&
physical_equal(parent.children[index], child) {
transform_children(child, transform, limits)
index += 1
}
}
EditAttributes => {
if apply_edit_attrs_transform(child, transform) {
transform_report_node_action(
child, transform, "Edited attributes on",
)
}
if index < parent.children.length() &&
physical_equal(parent.children[index], child) {
transform_children(child, transform, limits)
index += 1
}
}
DropUrlAttributes => {
ignore(apply_drop_url_attrs_transform(child, transform))
transform_children(child, transform, limits)
index += 1
}
AllowStyleAttributes => {
ignore(apply_allow_style_attrs_transform(child, transform))
transform_children(child, transform, limits)
index += 1
}
_ => index += 1
}
} else {
match child.kind {
Document | Fragment | Element =>
transform_children(child, transform, limits)
_ => ()
}
index += 1
}
}
}
///|
fn transform_inserted_children_after_unwrap(
parent : @dom.Node,
start : Int,
count : Int,
transform : TransformSpec,
limits : @sel.SelectorLimits,
) -> Unit raise @core.HtmlError {
let mut offset = 0
while offset < count && start + offset < parent.children.length() {
let child = parent.children[start + offset]
match child.kind {
Document | Fragment | Element =>
transform_children(child, transform, limits)
_ => ()
}
offset += 1
}
}
///|
fn transform_matches_element(
node : @dom.Node,
transform : TransformSpec,
limits : @sel.SelectorLimits,
) -> Bool raise @core.HtmlError {
match transform.kind {
MergeAttributeTokens => @syn.lower_ascii(node.name) == transform.merge_tag
_ => @sel.matches_with_limits(node, transform.selector, limits)
}
}