Skip to content

Commit dfeb635

Browse files
author
bors-servo
authored
Auto merge of #249 - canaltinova:form-owner, r=jdm
Implement support for form owner This is rebased version of #137. Fixed merge conflicts, updated to current codebase and addressed some comments. But there are still some todo's needs to be addressed. Servo side of this changes is currently WIP. Opening this for early feedbacks. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/html5ever/249) <!-- Reviewable:end -->
2 parents b1dad50 + 69f7069 commit dfeb635

File tree

7 files changed

+145
-54
lines changed

7 files changed

+145
-54
lines changed

examples/noop-tree-builder.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ impl TreeSink for Sink {
5757
x == y
5858
}
5959

60+
fn same_tree(&self, _x: usize, _y: usize) -> bool {
61+
true
62+
}
63+
6064
fn elem_name(&self, target: usize) -> QualName {
6165
self.names.get(&target).expect("not an element").clone()
6266
}
@@ -71,13 +75,15 @@ impl TreeSink for Sink {
7175
self.get_id()
7276
}
7377

78+
fn has_parent_node(&self, _node: usize) -> bool {
79+
// `node` will have a parent unless a script moved it, and we're
80+
// not running scripts. Therefore we can aways return true.
81+
true
82+
}
83+
7484
fn append_before_sibling(&mut self,
7585
_sibling: usize,
76-
_new_node: NodeOrText<usize>) -> Result<(), NodeOrText<usize>> {
77-
// `sibling` will have a parent unless a script moved it, and we're
78-
// not running scripts. Therefore we can aways return `Ok(())`.
79-
Ok(())
80-
}
86+
_new_node: NodeOrText<usize>) { }
8187

8288
fn parse_error(&mut self, _msg: Cow<'static, str>) { }
8389
fn set_quirks_mode(&mut self, _mode: QuirksMode) { }

examples/print-tree-actions.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ impl TreeSink for Sink {
8282
id
8383
}
8484

85+
fn has_parent_node(&self, _node: usize) -> bool {
86+
// `node` will have a parent unless a script moved it, and we're
87+
// not running scripts. Therefore we can aways return true
88+
true
89+
}
90+
8591
fn append(&mut self, parent: usize, child: NodeOrText<usize>) {
8692
match child {
8793
AppendNode(n)
@@ -93,17 +99,13 @@ impl TreeSink for Sink {
9399

94100
fn append_before_sibling(&mut self,
95101
sibling: usize,
96-
new_node: NodeOrText<usize>) -> Result<(), NodeOrText<usize>> {
102+
new_node: NodeOrText<usize>) {
97103
match new_node {
98104
AppendNode(n)
99105
=> println!("Append node {} before {}", n, sibling),
100106
AppendText(t)
101107
=> println!("Append text before {}: \"{}\"", sibling, escape_default(&t)),
102108
}
103-
104-
// `sibling` will have a parent unless a script moved it, and we're
105-
// not running scripts. Therefore we can aways return `Ok(())`.
106-
Ok(())
107109
}
108110

109111
fn append_doctype_to_document(&mut self,
@@ -121,6 +123,12 @@ impl TreeSink for Sink {
121123
}
122124
}
123125

126+
fn associate_with_form(&mut self, _target: usize, _form: usize) {
127+
// No form owner support. Since same_tree always returns
128+
// true we cannot be sure that this associate_with_form call is
129+
// valid
130+
}
131+
124132
fn remove_from_parent(&mut self, target: usize) {
125133
println!("Remove {} from parent", target);
126134
}

src/rcdom.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,11 @@ impl TreeSink for RcDom {
223223
new_node(Comment(text))
224224
}
225225

226+
fn has_parent_node(&self, node: Handle) -> bool {
227+
let node = node.borrow();
228+
node.parent.is_some()
229+
}
230+
226231
fn append(&mut self, parent: Handle, child: NodeOrText<Handle>) {
227232
// Append to an existing Text node if we have one.
228233
match child {
@@ -241,8 +246,9 @@ impl TreeSink for RcDom {
241246

242247
fn append_before_sibling(&mut self,
243248
sibling: Handle,
244-
child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
245-
let (parent, i) = unwrap_or_return!(get_parent_and_index(&sibling), Err(child));
249+
child: NodeOrText<Handle>) {
250+
let (parent, i) = get_parent_and_index(&sibling)
251+
.expect("append_before_sibling called on node without parent");
246252

247253
let child = match (child, i) {
248254
// No previous node.
@@ -253,7 +259,7 @@ impl TreeSink for RcDom {
253259
let parent = parent.borrow();
254260
let prev = &parent.children[i-1];
255261
if append_to_existing_text(prev, &text) {
256-
return Ok(());
262+
return;
257263
}
258264
new_node(Text(text))
259265
}
@@ -271,7 +277,6 @@ impl TreeSink for RcDom {
271277

272278
child.borrow_mut().parent = Some(Rc::downgrade(&parent));
273279
parent.borrow_mut().children.insert(i, child);
274-
Ok(())
275280
}
276281

277282
fn append_doctype_to_document(&mut self,

src/tree_builder/actions.rs

+34-36
Original file line numberDiff line numberDiff line change
@@ -210,40 +210,8 @@ impl<Handle, Sink> TreeBuilderActions<Handle>
210210

211211
// Insert at the "appropriate place for inserting a node".
212212
fn insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>) {
213-
declare_tag_set!(foster_target = "table" "tbody" "tfoot" "thead" "tr");
214-
let target = override_target.unwrap_or_else(|| self.current_node());
215-
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) {
216-
if self.html_elem_named(target.clone(), local_name!("template")) {
217-
// No foster parenting (inside template).
218-
let contents = self.sink.get_template_contents(target);
219-
self.sink.append(contents, child);
220-
} else {
221-
// No foster parenting (the common case).
222-
self.sink.append(target, child);
223-
}
224-
return;
225-
}
226-
227-
// Foster parenting
228-
let mut iter = self.open_elems.iter().rev().peekable();
229-
while let Some(elem) = iter.next() {
230-
if self.html_elem_named(elem.clone(), local_name!("template")) {
231-
let contents = self.sink.get_template_contents(elem.clone());
232-
self.sink.append(contents, child);
233-
return;
234-
} else if self.html_elem_named(elem.clone(), local_name!("table")) {
235-
// Try inserting "inside last table's parent node, immediately before last table"
236-
if let Err(child) = self.sink.append_before_sibling(elem.clone(), child) {
237-
// If last_table has no parent, we regain ownership of the child.
238-
// Insert "inside previous element, after its last child (if any)"
239-
let previous_element = (*iter.peek().unwrap()).clone();
240-
self.sink.append(previous_element, child);
241-
}
242-
return;
243-
}
244-
}
245-
let html_elem = self.html_elem();
246-
self.sink.append(html_elem, child);
213+
let insertion_point = self.appropriate_place_for_insertion(override_target);
214+
self.insert_at(insertion_point, child);
247215
}
248216

249217
fn adoption_agency(&mut self, subject: LocalName) {
@@ -775,10 +743,40 @@ impl<Handle, Sink> TreeBuilderActions<Handle>
775743
// FIXME: application cache selection algorithm
776744
}
777745

746+
// https://html.spec.whatwg.org/multipage/#create-an-element-for-the-token
778747
fn insert_element(&mut self, push: PushFlag, ns: Namespace, name: LocalName, attrs: Vec<Attribute>)
779748
-> Handle {
780-
let elem = self.sink.create_element(QualName::new(ns, name), attrs);
781-
self.insert_appropriately(AppendNode(elem.clone()), None);
749+
declare_tag_set!(form_associatable =
750+
"button" "fieldset" "input" "object"
751+
"output" "select" "textarea" "img");
752+
753+
declare_tag_set!(listed = [form_associatable] - "img");
754+
755+
// Step 7.
756+
let qname = QualName::new(ns, name);
757+
let elem = self.sink.create_element(qname.clone(), attrs.clone());
758+
759+
let insertion_point = self.appropriate_place_for_insertion(None);
760+
let tree_node = match insertion_point {
761+
LastChild(ref p) |
762+
BeforeSibling(ref p) => p.clone()
763+
};
764+
765+
// Step 12.
766+
if form_associatable(qname.clone()) &&
767+
self.form_elem.is_some() &&
768+
!self.in_html_elem_named(local_name!("template")) &&
769+
!(listed(qname.clone()) &&
770+
attrs.iter().any(|a| a.name == qualname!("","form"))) {
771+
772+
let form = self.form_elem.as_ref().unwrap().clone();
773+
if self.sink.same_tree(tree_node, form.clone()) {
774+
self.sink.associate_with_form(elem.clone(), form)
775+
}
776+
}
777+
778+
self.insert_at(insertion_point, AppendNode(elem.clone()));
779+
782780
match push {
783781
Push => self.push(&elem),
784782
NoPush => (),

src/tree_builder/interface.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ pub trait TreeSink {
7272
/// Do two handles refer to the same node?
7373
fn same_node(&self, x: Self::Handle, y: Self::Handle) -> bool;
7474

75+
/// Are two handles present in the same tree
76+
fn same_tree(&self, x: Self::Handle, y: Self::Handle) -> bool {
77+
true
78+
}
79+
7580
/// What is the name of this element?
7681
///
7782
/// Should never be called on a non-element node;
@@ -93,15 +98,18 @@ pub trait TreeSink {
9398
/// Create a comment node.
9499
fn create_comment(&mut self, text: StrTendril) -> Self::Handle;
95100

101+
/// Does the node have a parent?
102+
fn has_parent_node(&self, node: Self::Handle) -> bool;
103+
96104
/// Append a node as the last child of the given node. If this would
97105
/// produce adjacent sibling text nodes, it should concatenate the text
98106
/// instead.
99107
///
100108
/// The child node will not already have a parent.
101109
fn append(&mut self, parent: Self::Handle, child: NodeOrText<Self::Handle>);
102110

103-
/// Append a node as the sibling immediately before the given node. If that node
104-
/// has no parent, do nothing and return Err(new_node).
111+
/// Append a node as the sibling immediately before the given node.
112+
/// This method will only be called if has_parent_node(sibling) is true
105113
///
106114
/// The tree builder promises that `sibling` is not a text node. However its
107115
/// old previous sibling, which would become the new node's previous sibling,
@@ -111,7 +119,7 @@ pub trait TreeSink {
111119
/// NB: `new_node` may have an old parent, from which it should be removed.
112120
fn append_before_sibling(&mut self,
113121
sibling: Self::Handle,
114-
new_node: NodeOrText<Self::Handle>) -> Result<(), NodeOrText<Self::Handle>>;
122+
new_node: NodeOrText<Self::Handle>);
115123

116124
/// Append a `DOCTYPE` element to the `Document` node.
117125
fn append_doctype_to_document(&mut self,
@@ -124,6 +132,9 @@ pub trait TreeSink {
124132
/// with something else than an element.
125133
fn add_attrs_if_missing(&mut self, target: Self::Handle, attrs: Vec<Attribute>);
126134

135+
/// Associate the given form-associatable element with the form element
136+
fn associate_with_form(&mut self, target: Self::Handle, form: Self::Handle) {}
137+
127138
/// Detach the given node from its parent.
128139
fn remove_from_parent(&mut self, target: Self::Handle);
129140

src/tree_builder/mod.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,53 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink>
369369
pub fn is_fragment(&self) -> bool {
370370
self.context_elem.is_some()
371371
}
372+
373+
fn appropriate_place_for_insertion(&mut self,
374+
override_target: Option<Handle>)
375+
-> InsertionPoint<Handle> {
376+
use self::tag_sets::*;
377+
378+
declare_tag_set!(foster_target = "table" "tbody" "tfoot" "thead" "tr");
379+
let target = override_target.unwrap_or_else(|| self.current_node());
380+
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) {
381+
if self.html_elem_named(target.clone(), local_name!("template")) {
382+
// No foster parenting (inside template).
383+
let contents = self.sink.get_template_contents(target);
384+
return LastChild(contents);
385+
} else {
386+
// No foster parenting (the common case).
387+
return LastChild(target);
388+
}
389+
}
390+
391+
// Foster parenting
392+
let mut iter = self.open_elems.iter().rev().peekable();
393+
while let Some(elem) = iter.next() {
394+
if self.html_elem_named(elem.clone(), local_name!("template")) {
395+
let contents = self.sink.get_template_contents(elem.clone());
396+
return LastChild(contents);
397+
} else if self.html_elem_named(elem.clone(), local_name!("table")) {
398+
// Try inserting "inside last table's parent node, immediately before last table"
399+
if self.sink.has_parent_node(elem.clone()) {
400+
return BeforeSibling(elem.clone());
401+
} else {
402+
// If elem has no parent, we regain ownership of the child.
403+
// Insert "inside previous element, after its last child (if any)"
404+
let previous_element = (*iter.peek().unwrap()).clone();
405+
return LastChild(previous_element);
406+
}
407+
}
408+
}
409+
let html_elem = self.html_elem();
410+
LastChild(html_elem)
411+
}
412+
413+
fn insert_at(&mut self, insertion_point: InsertionPoint<Handle>, child: NodeOrText<Handle>) {
414+
match insertion_point {
415+
LastChild(parent) => self.sink.append(parent, child),
416+
BeforeSibling(sibling) => self.sink.append_before_sibling(sibling, child)
417+
}
418+
}
372419
}
373420

374421
impl<Handle, Sink> TokenSink
@@ -529,13 +576,18 @@ mod test {
529576
self.rcdom.create_comment(text)
530577
}
531578

579+
fn has_parent_node(&self, node: Handle) -> bool {
580+
let node = node.borrow();
581+
node.parent.is_some()
582+
}
583+
532584
fn append(&mut self, parent: Handle, child: NodeOrText<Handle>) {
533585
self.rcdom.append(parent, child)
534586
}
535587

536588
fn append_before_sibling(&mut self,
537589
sibling: Handle,
538-
child: NodeOrText<Handle>) -> Result<(), NodeOrText<Handle>> {
590+
child: NodeOrText<Handle>) {
539591
self.rcdom.append_before_sibling(sibling, child)
540592
}
541593

src/tree_builder/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub use self::SplitStatus::*;
1919
pub use self::Token::*;
2020
pub use self::ProcessResult::*;
2121
pub use self::FormatEntry::*;
22+
pub use self::InsertionPoint::*;
2223

2324
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
2425
pub enum InsertionMode {
@@ -80,3 +81,13 @@ pub enum FormatEntry<Handle> {
8081
Element(Handle, Tag),
8182
Marker,
8283
}
84+
85+
pub enum InsertionPoint<Handle> {
86+
/// Holds the parent
87+
LastChild(Handle),
88+
/// Holds the sibling before which the node will be inserted
89+
/// TODO: Is the parent node needed? Is there a problem with using
90+
/// the sibling to find if the form element is in the same home
91+
/// subtree?
92+
BeforeSibling(Handle)
93+
}

0 commit comments

Comments
 (0)