Skip to content

rcdom: selectedcontent lookup reads self.data, option clone never runs #776

Description

@hey-jj

Observed

Parsing HTML with a customizable select leaves the <selectedcontent> element empty. Node::get_a_selects_enabled_selectedcontent in rcdom/lib.rs walks the descendants of the select, but the pattern match inside the loop reads &self.data instead of &node.data (line 205 on main, line 213 in the published markup5ever_rcdom 0.39.0+unofficial). self is the select element itself, so the local_name!("selectedcontent") comparison fails on every iteration and the function always returns None. As a result the maybe_clone_an_option_into_selectedcontent callback from #719 never clones anything, whether it is invoked by the html5ever tree builder or called directly on a correctly shaped tree.

Expected

Per https://html.spec.whatwg.org/#clone-an-option-into-a-selectedcontent, closing a selected option inside a select that holds an enabled selectedcontent should replace the children of that selectedcontent with clones of the option's children. Changing the match to &node.data produces exactly that tree.

Reproducer

cargo new sc-repro && cd sc-repro
cargo add html5ever@0.39 markup5ever_rcdom@0.39

src/main.rs:

use html5ever::tendril::TendrilSink;
use html5ever::{parse_document, serialize};
use markup5ever_rcdom::{Handle, NodeData, RcDom, SerializableHandle};

fn find(h: &Handle, name: &str, out: &mut Vec<Handle>) {
    if let NodeData::Element { name: n, .. } = &h.data {
        if &*n.local == name {
            out.push(h.clone());
        }
    }
    for child in h.children.borrow().iter() {
        find(child, name, out);
    }
}

fn main() {
    let input = "<select><button><selectedcontent></selectedcontent></button>\
                 <option selected>Hello</option></select>";
    let dom = parse_document(RcDom::default(), Default::default()).one(input);
    let mut buf = Vec::new();
    let doc: SerializableHandle = dom.document.clone().into();
    serialize(&mut buf, &doc, Default::default()).unwrap();
    println!("serialized: {}", String::from_utf8(buf).unwrap());
    let mut sc = Vec::new();
    find(&dom.document, "selectedcontent", &mut sc);
    println!("selectedcontent children: {}", sc[0].children.borrow().len());
}

Output with 0.39.0:

serialized: <html><head></head><body><select><button><selectedcontent></selectedcontent></button><option selected="">Hello</option></select></body></html>
selectedcontent children: 0

With the one word fix (&node.data on the line noted above) the same program prints <selectedcontent>Hello</selectedcontent> and a child count of 1.

Scope

Every code path through maybe_clone_an_option_into_selectedcontent returns without touching the tree, so the callback introduced in #719 never runs its clone step. Any DOM built with markup5ever_rcdom 0.39.0, by parsing or by direct TreeSink calls, leaves each <selectedcontent> element with whatever children it had before the option closed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions