-
Notifications
You must be signed in to change notification settings - Fork 233
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement support for form owner #137
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -207,40 +207,8 @@ impl<Handle, Sink> TreeBuilderActions<Handle> | |
|
||
// Insert at the "appropriate place for inserting a node". | ||
fn insert_appropriately(&mut self, child: NodeOrText<Handle>, override_target: Option<Handle>) { | ||
declare_tag_set!(foster_target = table tbody tfoot thead tr); | ||
let target = override_target.unwrap_or_else(|| self.current_node()); | ||
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) { | ||
// No foster parenting (the common case). | ||
return self.sink.append(target, child); | ||
} | ||
|
||
// Foster parenting | ||
// FIXME: <template> | ||
let last_table = self.open_elems.iter() | ||
.enumerate() | ||
.rev() | ||
.filter(|&(_, e)| self.html_elem_named(e.clone(), atom!(table))) | ||
.next(); | ||
|
||
match last_table { | ||
None => { | ||
let html_elem = self.html_elem(); | ||
self.sink.append(html_elem, child); | ||
} | ||
Some((idx, last_table)) => { | ||
// Try inserting "inside last table's parent node, immediately before last table" | ||
match self.sink.append_before_sibling(last_table.clone(), child) { | ||
Ok(()) => (), | ||
|
||
// If last_table has no parent, we regain ownership of the child. | ||
// Insert "inside previous element, after its last child (if any)" | ||
Err(child) => { | ||
let previous_element = self.open_elems[idx-1].clone(); | ||
self.sink.append(previous_element, child); | ||
} | ||
} | ||
} | ||
} | ||
let insertion_point = self.appropriate_place_for_insertion(override_target); | ||
self.insert_at(insertion_point, child); | ||
} | ||
|
||
fn adoption_agency(&mut self, subject: Atom) { | ||
|
@@ -755,10 +723,39 @@ impl<Handle, Sink> TreeBuilderActions<Handle> | |
// FIXME: application cache selection algorithm | ||
} | ||
|
||
// https://html.spec.whatwg.org/multipage/syntax.html#create-an-element-for-the-token | ||
fn insert_element(&mut self, push: PushFlag, ns: Namespace, name: Atom, attrs: Vec<Attribute>) | ||
-> Handle { | ||
let elem = self.sink.create_element(QualName::new(ns, name), attrs); | ||
self.insert_appropriately(AppendNode(elem.clone()), None); | ||
declare_tag_set!(form_associatable = | ||
button fieldset input keygen label | ||
object output select textarea img); | ||
|
||
declare_tag_set!(reassociatable = form_associatable - img); | ||
|
||
let qname = QualName::new(ns, name); | ||
let elem = self.sink.create_element(qname.clone(), attrs.clone()); | ||
|
||
let insertion_point = self.appropriate_place_for_insertion(None); | ||
let tree_node = match insertion_point { | ||
LastChild(ref p) | | ||
BeforeSibling(ref p) => p.clone() | ||
}; | ||
|
||
// Step 4. | ||
// TODO: Handle template element case | ||
if form_associatable(qname.clone()) | ||
&& self.form_elem.is_some() | ||
&& !(reassociatable(qname.clone()) | ||
&& attrs.iter().any(|a| a.name == qualname!("","form"))) { | ||
|
||
let form = self.form_elem.as_ref().unwrap().clone(); | ||
if self.sink.same_home_subtree(tree_node, form.clone()) { | ||
self.sink.associate_with_form(elem.clone(), form) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
} | ||
|
||
self.insert_at(insertion_point, AppendNode(elem.clone())); | ||
|
||
match push { | ||
Push => self.push(&elem), | ||
NoPush => (), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,6 +61,10 @@ pub trait TreeSink { | |
/// Do two handles refer to the same node? | ||
fn same_node(&self, x: Self::Handle, y: Self::Handle) -> bool; | ||
|
||
/// Are two handles present in the same tree | ||
/// https://html.spec.whatwg.org/multipage/infrastructure.html#home-subtree | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don’t understand how this can ever be false. Could the doc-comment give an example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As far as I understand there are two cases where it will be false:
I must admit that I don't fully understand case 1. In particular, why does the fragment parser algorithm initialize the parser's form element pointer to the closest form element ancestor of the context element? (https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments:form-element-pointer step 11) I'm not sure if there are other cases that I might have missed. |
||
fn same_home_subtree(&self, x: Self::Handle, y: Self::Handle) -> bool; | ||
|
||
/// What is the name of this element? | ||
/// | ||
/// Should never be called on a non-element node; | ||
|
@@ -76,15 +80,18 @@ pub trait TreeSink { | |
/// Create a comment node. | ||
fn create_comment(&mut self, text: String) -> Self::Handle; | ||
|
||
/// Does the node have a parent? | ||
fn has_parent_node(&self, node: Self::Handle) -> bool; | ||
|
||
/// Append a node as the last child of the given node. If this would | ||
/// produce adjacent sibling text nodes, it should concatenate the text | ||
/// instead. | ||
/// | ||
/// The child node will not already have a parent. | ||
fn append(&mut self, parent: Self::Handle, child: NodeOrText<Self::Handle>); | ||
|
||
/// Append a node as the sibling immediately before the given node. If that node | ||
/// has no parent, do nothing and return Err(new_node). | ||
/// Append a node as the sibling immediately before the given node. | ||
/// This method will only be called if has_parent_node(sibling) is true | ||
/// | ||
/// The tree builder promises that `sibling` is not a text node. However its | ||
/// old previous sibling, which would become the new node's previous sibling, | ||
|
@@ -94,7 +101,7 @@ pub trait TreeSink { | |
/// NB: `new_node` may have an old parent, from which it should be removed. | ||
fn append_before_sibling(&mut self, | ||
sibling: Self::Handle, | ||
new_node: NodeOrText<Self::Handle>) -> Result<(), NodeOrText<Self::Handle>>; | ||
new_node: NodeOrText<Self::Handle>); | ||
|
||
/// Append a `DOCTYPE` element to the `Document` node. | ||
fn append_doctype_to_document(&mut self, name: String, public_id: String, system_id: String); | ||
|
@@ -103,6 +110,9 @@ pub trait TreeSink { | |
/// with that name already exists. | ||
fn add_attrs_if_missing(&mut self, target: Self::Handle, attrs: Vec<Attribute>); | ||
|
||
/// Associate the given form-associatable element with the form element | ||
fn associate_with_form(&mut self, target: Self::Handle, form: Self::Handle); | ||
|
||
/// Detach the given node from its parent. | ||
fn remove_from_parent(&mut self, target: Self::Handle); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -364,6 +364,48 @@ impl<Handle, Sink> TreeBuilder<Handle, Sink> | |
pub fn is_fragment(&self) -> bool { | ||
self.context_elem.is_some() | ||
} | ||
|
||
fn appropriate_place_for_insertion(&self, override_target: Option<Handle>) -> InsertionPoint<Handle> { | ||
use self::tag_sets::*; | ||
|
||
declare_tag_set!(foster_target = table tbody tfoot thead tr); | ||
let target = override_target.unwrap_or_else(|| self.current_node()); | ||
if !(self.foster_parenting && self.elem_in(target.clone(), foster_target)) { | ||
// No foster parenting (the common case). | ||
return LastChild(target) | ||
} | ||
|
||
// Foster parenting | ||
// FIXME: <template> | ||
let last_table = self.open_elems.iter() | ||
.enumerate() | ||
.rev() | ||
.filter(|&(_, e)| self.html_elem_named(e.clone(), atom!(table))) | ||
.next(); | ||
|
||
match last_table { | ||
None => { | ||
LastChild(self.html_elem()) | ||
} | ||
Some((idx, last_table)) => { | ||
// Try inserting "inside last table's parent node, immediately before last table" | ||
if self.sink.has_parent_node(last_table.clone()) { | ||
BeforeSibling(last_table.clone()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We've lost the fallback behaviour from https://github.com/servo/html5ever/pull/137/files#diff-3f339094e126866dfc7763b9a1d54beaL232, haven't we? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, I've read the other comments and I understand why this change is appropriate. |
||
} else { | ||
// Insert "inside previous element, after its last child (if any)" | ||
let previous_element = self.open_elems[idx-1].clone(); | ||
LastChild(previous_element) | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn insert_at(&mut self, insertion_point: InsertionPoint<Handle>, child: NodeOrText<Handle>) { | ||
match insertion_point { | ||
LastChild(parent) => self.sink.append(parent, child), | ||
BeforeSibling(sibling) => self.sink.append_before_sibling(sibling, child) | ||
} | ||
} | ||
} | ||
|
||
impl<Handle, Sink> TokenSink | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this (and
same_home_subtree
?) be a default implementation in the trait?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did this because I thought owned_dom and rc_dom were just examples and don't implement all features (form owner support in this case). For real clients wouldn't we want to enforce the implementation of form owners? Or is it optional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be useful to parse HTML in a "real" application that is not a browser doesn't support form submission.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That makes sense. I'll change them to be default implementations.