Skip to content

Commit 0e3a25e

Browse files
committed
Implement support for form owner
1 parent 15d10bf commit 0e3a25e

File tree

8 files changed

+159
-57
lines changed

8 files changed

+159
-57
lines changed

dom_sink/src/owned_dom.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ impl TreeSink for Sink {
197197
x == y
198198
}
199199

200+
fn same_home_subtree(&self, _x: Handle, _y: Handle) -> bool {
201+
true
202+
}
203+
204+
fn associate_with_form(&mut self, _target: Handle, _form: Handle) {
205+
}
206+
207+
fn has_parent_node(&self, node: Handle) -> bool {
208+
!node.parent.is_null()
209+
}
210+
200211
fn elem_name(&self, target: Handle) -> QualName {
201212
match target.node {
202213
Element(ref name, _) => name.clone(),
@@ -230,8 +241,9 @@ impl TreeSink for Sink {
230241

231242
fn append_before_sibling(&mut self,
232243
sibling: Handle,
233-
child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
234-
let (mut parent, i) = unwrap_or_return!(get_parent_and_index(sibling), Err(child));
244+
child: NodeOrText<Handle>) {
245+
let (mut parent, i) = get_parent_and_index(sibling)
246+
.expect("append_before_sibling called on node without parent");
235247

236248
let mut child = match (child, i) {
237249
// No previous node.
@@ -241,7 +253,7 @@ impl TreeSink for Sink {
241253
(AppendText(text), i) => {
242254
let prev = parent.children[i-1];
243255
if append_to_existing_text(prev, &text) {
244-
return Ok(());
256+
return;
245257
}
246258
self.new_node(Text(text))
247259
}
@@ -259,7 +271,6 @@ impl TreeSink for Sink {
259271

260272
child.parent = parent;
261273
parent.children.insert(i, child);
262-
Ok(())
263274
}
264275

265276
fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {

dom_sink/src/rcdom.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,18 @@ impl TreeSink for RcDom {
163163
new_node(Comment(text))
164164
}
165165

166+
fn same_home_subtree(&self, _x: Handle, _y: Handle) -> bool {
167+
true
168+
}
169+
170+
fn associate_with_form(&mut self, _target: Handle, _form: Handle) {
171+
}
172+
173+
fn has_parent_node(&self, node: Handle) -> bool {
174+
let node = node.borrow();
175+
node.parent.is_some()
176+
}
177+
166178
fn append(&mut self, parent: Handle, child: NodeOrText<Handle>) {
167179
// Append to an existing Text node if we have one.
168180
match child {
@@ -181,8 +193,9 @@ impl TreeSink for RcDom {
181193

182194
fn append_before_sibling(&mut self,
183195
sibling: Handle,
184-
child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
185-
let (parent, i) = unwrap_or_return!(get_parent_and_index(&sibling), Err(child));
196+
child: NodeOrText<Handle>) {
197+
let (parent, i) = get_parent_and_index(&sibling)
198+
.expect("append_before_sibling called on node without parent");
186199

187200
let child = match (child, i) {
188201
// No previous node.
@@ -193,7 +206,7 @@ impl TreeSink for RcDom {
193206
let parent = parent.borrow();
194207
let prev = &parent.children[i-1];
195208
if append_to_existing_text(prev, &text) {
196-
return Ok(());
209+
return;
197210
}
198211
new_node(Text(text))
199212
}
@@ -211,7 +224,6 @@ impl TreeSink for RcDom {
211224

212225
child.borrow_mut().parent = Some(parent.clone().downgrade());
213226
parent.borrow_mut().children.insert(i, child);
214-
Ok(())
215227
}
216228

217229
fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {

examples/noop-tree-builder.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ impl TreeSink for Sink {
4646
x == y
4747
}
4848

49+
fn same_home_subtree(&self, _x: usize, _y: usize) -> bool {
50+
true
51+
}
52+
4953
fn elem_name(&self, target: usize) -> QualName {
5054
self.names.get(&target).expect("not an element").clone()
5155
}
@@ -60,20 +64,23 @@ impl TreeSink for Sink {
6064
self.get_id()
6165
}
6266

67+
fn has_parent_node(&self, _node: usize) -> bool {
68+
// `node` will have a parent unless a script moved it, and we're
69+
// not running scripts. Therefore we can aways return true.
70+
true
71+
}
72+
6373
fn append_before_sibling(&mut self,
6474
_sibling: usize,
65-
_new_node: NodeOrText<usize>) -> Result<(), NodeOrText<usize>> {
66-
// `sibling` will have a parent unless a script moved it, and we're
67-
// not running scripts. Therefore we can aways return `Ok(())`.
68-
Ok(())
69-
}
75+
_new_node: NodeOrText<usize>) { }
7076

7177
fn parse_error(&mut self, _msg: Cow<'static, str>) { }
7278
fn set_quirks_mode(&mut self, _mode: QuirksMode) { }
7379
fn append(&mut self, _parent: usize, _child: NodeOrText<usize>) { }
7480

7581
fn append_doctype_to_document(&mut self, _name: String, _public_id: String, _system_id: String) { }
7682
fn add_attrs_if_missing(&mut self, _target: usize, _attrs: Vec<Attribute>) { }
83+
fn associate_with_form(&mut self, _target: usize, _form: usize) { }
7784
fn remove_from_parent(&mut self, _target: usize) { }
7885
fn reparent_children(&mut self, _node: usize, _new_parent: usize) { }
7986
fn mark_script_already_started(&mut self, _node: usize) { }

examples/print-tree-actions.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ impl TreeSink for Sink {
5656
x == y
5757
}
5858

59+
fn same_home_subtree(&self, _x: usize, _y: usize) -> bool {
60+
true
61+
}
62+
5963
fn elem_name(&self, target: usize) -> QualName {
6064
self.names.get(&target).expect("not an element").clone()
6165
}
@@ -73,6 +77,12 @@ impl TreeSink for Sink {
7377
id
7478
}
7579

80+
fn has_parent_node(&self, _node: usize) -> bool {
81+
// `node` will have a parent unless a script moved it, and we're
82+
// not running scripts. Therefore we can aways return true
83+
true
84+
}
85+
7686
fn append(&mut self, parent: usize, child: NodeOrText<usize>) {
7787
match child {
7888
AppendNode(n)
@@ -84,17 +94,13 @@ impl TreeSink for Sink {
8494

8595
fn append_before_sibling(&mut self,
8696
sibling: usize,
87-
new_node: NodeOrText<usize>) -> Result<(), NodeOrText<usize>> {
97+
new_node: NodeOrText<usize>) {
8898
match new_node {
8999
AppendNode(n)
90100
=> println!("Append node {} before {}", n, sibling),
91101
AppendText(t)
92102
=> println!("Append text before {}: \"{}\"", sibling, t.escape_default()),
93103
}
94-
95-
// `sibling` will have a parent unless a script moved it, and we're
96-
// not running scripts. Therefore we can aways return `Ok(())`.
97-
Ok(())
98104
}
99105

100106
fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String) {
@@ -108,6 +114,12 @@ impl TreeSink for Sink {
108114
}
109115
}
110116

117+
fn associate_with_form(&mut self, _target: usize, _form: usize) {
118+
// No form owner support. Since same_home_subtree always returns
119+
// true we cannot be sure that this associate_with_form call is
120+
// valid
121+
}
122+
111123
fn remove_from_parent(&mut self, target: usize) {
112124
println!("Remove {} from parent", target);
113125
}

src/tree_builder/actions.rs

+33-36
Original file line numberDiff line numberDiff line change
@@ -207,40 +207,8 @@ impl<Handle, Sink> TreeBuilderActions<Handle>
207207

208208
// Insert at the "appropriate place for inserting a node".
209209
fn insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>) {
210-
declare_tag_set!(foster_target = table tbody tfoot thead tr);
211-
let target = override_target.unwrap_or_else(|| self.current_node());
212-
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) {
213-
// No foster parenting (the common case).
214-
return self.sink.append(target, child);
215-
}
216-
217-
// Foster parenting
218-
// FIXME: <template>
219-
let last_table = self.open_elems.iter()
220-
.enumerate()
221-
.rev()
222-
.filter(|&(_, e)| self.html_elem_named(e.clone(), atom!(table)))
223-
.next();
224-
225-
match last_table {
226-
None => {
227-
let html_elem = self.html_elem();
228-
self.sink.append(html_elem, child);
229-
}
230-
Some((idx, last_table)) => {
231-
// Try inserting "inside last table's parent node, immediately before last table"
232-
match self.sink.append_before_sibling(last_table.clone(), child) {
233-
Ok(()) => (),
234-
235-
// If last_table has no parent, we regain ownership of the child.
236-
// Insert "inside previous element, after its last child (if any)"
237-
Err(child) => {
238-
let previous_element = self.open_elems[idx-1].clone();
239-
self.sink.append(previous_element, child);
240-
}
241-
}
242-
}
243-
}
210+
let insertion_point = self.appropriate_place_for_insertion(override_target);
211+
self.insert_at(insertion_point, child);
244212
}
245213

246214
fn adoption_agency(&mut self, subject: Atom) {
@@ -755,10 +723,39 @@ impl<Handle, Sink> TreeBuilderActions<Handle>
755723
// FIXME: application cache selection algorithm
756724
}
757725

726+
// https://html.spec.whatwg.org/multipage/syntax.html#create-an-element-for-the-token
758727
fn insert_element(&mut self, push: PushFlag, ns: Namespace, name: Atom, attrs: Vec<Attribute>)
759728
-> Handle {
760-
let elem = self.sink.create_element(QualName::new(ns, name), attrs);
761-
self.insert_appropriately(AppendNode(elem.clone()), None);
729+
declare_tag_set!(form_associatable =
730+
button fieldset input keygen label
731+
object output select textarea img);
732+
733+
declare_tag_set!(reassociatable = form_associatable - img);
734+
735+
let qname = QualName::new(ns, name);
736+
let elem = self.sink.create_element(qname.clone(), attrs.clone());
737+
738+
let insertion_point = self.appropriate_place_for_insertion(None);
739+
let tree_node = match insertion_point {
740+
LastChild(ref p) |
741+
BeforeSibling(ref p) => p.clone()
742+
};
743+
744+
// Step 4.
745+
// TODO: Handle template element case
746+
if form_associatable(qname.clone())
747+
&& self.form_elem.is_some()
748+
&& !(reassociatable(qname.clone())
749+
&& attrs.iter().any(|a| a.name == qualname!("","form"))) {
750+
751+
let form = self.form_elem.as_ref().unwrap().clone();
752+
if self.sink.same_home_subtree(tree_node, form.clone()) {
753+
self.sink.associate_with_form(elem.clone(), form)
754+
}
755+
}
756+
757+
self.insert_at(insertion_point, AppendNode(elem.clone()));
758+
762759
match push {
763760
Push => self.push(&elem),
764761
NoPush => (),

src/tree_builder/interface.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ pub trait TreeSink {
6161
/// Do two handles refer to the same node?
6262
fn same_node(&self, x: Self::Handle, y: Self::Handle) -> bool;
6363

64+
/// Are two handles present in the same tree
65+
/// https://html.spec.whatwg.org/multipage/infrastructure.html#home-subtree
66+
fn same_home_subtree(&self, x: Self::Handle, y: Self::Handle) -> bool;
67+
6468
/// What is the name of this element?
6569
///
6670
/// Should never be called on a non-element node;
@@ -76,15 +80,18 @@ pub trait TreeSink {
7680
/// Create a comment node.
7781
fn create_comment(&mut self, text: String) -> Self::Handle;
7882

83+
/// Does the node have a parent?
84+
fn has_parent_node(&self, node: Self::Handle) -> bool;
85+
7986
/// Append a node as the last child of the given node. If this would
8087
/// produce adjacent sibling text nodes, it should concatenate the text
8188
/// instead.
8289
///
8390
/// The child node will not already have a parent.
8491
fn append(&mut self, parent: Self::Handle, child: NodeOrText<Self::Handle>);
8592

86-
/// Append a node as the sibling immediately before the given node. If that node
87-
/// has no parent, do nothing and return Err(new_node).
93+
/// Append a node as the sibling immediately before the given node.
94+
/// This method will only be called if has_parent_node(sibling) is true
8895
///
8996
/// The tree builder promises that `sibling` is not a text node. However its
9097
/// old previous sibling, which would become the new node's previous sibling,
@@ -94,7 +101,7 @@ pub trait TreeSink {
94101
/// NB: `new_node` may have an old parent, from which it should be removed.
95102
fn append_before_sibling(&mut self,
96103
sibling: Self::Handle,
97-
new_node: NodeOrText<Self::Handle>) -> Result<(), NodeOrText<Self::Handle>>;
104+
new_node: NodeOrText<Self::Handle>);
98105

99106
/// Append a `DOCTYPE` element to the `Document` node.
100107
fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String);
@@ -103,6 +110,9 @@ pub trait TreeSink {
103110
/// with that name already exists.
104111
fn add_attrs_if_missing(&mut self, target: Self::Handle, attrs: Vec<Attribute>);
105112

113+
/// Associate the given form-associatable element with the form element
114+
fn associate_with_form(&mut self, target: Self::Handle, form: Self::Handle);
115+
106116
/// Detach the given node from its parent.
107117
fn remove_from_parent(&mut self, target: Self::Handle);
108118

src/tree_builder/mod.rs

+42
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,48 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
364364
pub fn is_fragment(&self) -> bool {
365365
self.context_elem.is_some()
366366
}
367+
368+
fn appropriate_place_for_insertion(&self, override_target: Option<Handle>) -> InsertionPoint<Handle> {
369+
use self::tag_sets::*;
370+
371+
declare_tag_set!(foster_target = table tbody tfoot thead tr);
372+
let target = override_target.unwrap_or_else(|| self.current_node());
373+
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) {
374+
// No foster parenting (the common case).
375+
return LastChild(target)
376+
}
377+
378+
// Foster parenting
379+
// FIXME: <template>
380+
let last_table = self.open_elems.iter()
381+
.enumerate()
382+
.rev()
383+
.filter(|&(_, e)| self.html_elem_named(e.clone(), atom!(table)))
384+
.next();
385+
386+
match last_table {
387+
None => {
388+
LastChild(self.html_elem())
389+
}
390+
Some((idx, last_table)) => {
391+
// Try inserting "inside last table's parent node, immediately before last table"
392+
if self.sink.has_parent_node(last_table.clone()) {
393+
BeforeSibling(last_table.clone())
394+
} else {
395+
// Insert "inside previous element, after its last child (if any)"
396+
let previous_element = self.open_elems[idx-1].clone();
397+
LastChild(previous_element)
398+
}
399+
}
400+
}
401+
}
402+
403+
fn insert_at(&mut self, insertion_point: InsertionPoint<Handle>, child: NodeOrText<Handle>) {
404+
match insertion_point {
405+
LastChild(parent) => self.sink.append(parent, child),
406+
BeforeSibling(sibling) => self.sink.append_before_sibling(sibling, child)
407+
}
408+
}
367409
}
368410

369411
impl<Handle, Sink> TokenSink

src/tree_builder/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub use self::SplitStatus::*;
1616
pub use self::Token::*;
1717
pub use self::ProcessResult::*;
1818
pub use self::FormatEntry::*;
19+
pub use self::InsertionPoint::*;
1920

2021
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
2122
pub enum InsertionMode {
@@ -74,3 +75,13 @@ pub enum FormatEntry<Handle> {
7475
Element(Handle, Tag),
7576
Marker,
7677
}
78+
79+
pub enum InsertionPoint<Handle> {
80+
/// Holds the parent
81+
LastChild(Handle),
82+
/// Holds the sibling before which the node will be inserted
83+
/// TODO: Is the parent node needed? Is there a problem with using
84+
/// the sibling to find if the form element is in the same home
85+
/// subtree?
86+
BeforeSibling(Handle)
87+
}

0 commit comments

Comments
 (0)