Auto merge of #5338 - ChrisParis:outerhtml, r=jdm

The first commit refactors the fragment parsing and innerHTML setter. This makes the code mirror the structure of the spec more closely, and also prepares for reusing code with the outerHTML setter.
This commit is contained in:
bors-servo 2015-04-07 01:15:45 -05:00
commit 45b40d49a5
5 changed files with 102 additions and 62 deletions

View file

@ -10,7 +10,6 @@ use dom::attr::AttrValue;
use dom::namednodemap::NamedNodeMap;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::ElementBinding;
use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
@ -24,10 +23,10 @@ use dom::bindings::codegen::InheritTypes::{HTMLTableElementDerived, HTMLTableCel
use dom::bindings::codegen::InheritTypes::{HTMLTableRowElementDerived, HTMLTextAreaElementDerived};
use dom::bindings::codegen::InheritTypes::{HTMLTableSectionElementDerived, NodeCast};
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLFormElementDerived;
use dom::bindings::error::{ErrorResult, Fallible};
use dom::bindings::error::Error;
use dom::bindings::error::Error::{InvalidCharacter, Syntax};
use dom::bindings::error::Error::NoModificationAllowed;
use dom::bindings::js::{MutNullableJS, JS, JSRef, LayoutJS, Temporary, TemporaryPushable};
use dom::bindings::js::OptionalRootable;
use dom::bindings::trace::RootedVec;
@ -37,7 +36,6 @@ use dom::create::create_element;
use dom::domrect::DOMRect;
use dom::domrectlist::DOMRectList;
use dom::document::{Document, DocumentHelpers, LayoutDocumentHelpers};
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::domtokenlist::DOMTokenList;
use dom::event::{Event, EventHelpers};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
@ -55,10 +53,8 @@ use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node, NodeHelpers, NodeTyp
use dom::node::{document_from_node, NodeDamage};
use dom::node::{window_from_node};
use dom::nodelist::NodeList;
use dom::servohtmlparser::FragmentContext;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use devtools_traits::AttrInfo;
use parse::html::{HTMLInput, parse_html};
use style::legacy::{SimpleColorAttribute, UnsignedIntegerAttribute, IntegerAttribute, LengthAttribute};
use selectors::matching::matches;
use style::properties::{PropertyDeclarationBlock, PropertyDeclaration, parse_style_attribute};
@ -1169,60 +1165,18 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
rect.origin.x + rect.size.width)
}
// https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#extensions-to-the-element-interface
fn GetInnerHTML(self) -> Fallible<DOMString> {
//XXX TODO: XML case
self.serialize(ChildrenOnly)
}
fn SetInnerHTML(self, value: DOMString) -> Fallible<()> {
let window = window_from_node(self).root();
let context_document = document_from_node(self).root();
let context_node: JSRef<Node> = NodeCast::from_ref(self);
let url = context_document.r().url();
// Follows https://html.spec.whatwg.org/multipage/syntax.html#parsing-html-fragments
// 1. Create a new Document node, and mark it as being an HTML document.
let document = Document::new(window.r(), Some(url.clone()),
IsHTMLDocument::HTMLDocument,
None, None,
DocumentSource::FromParser).root();
// 2. If the node document of the context element is in quirks mode,
// then let the Document be in quirks mode. Otherwise,
// the node document of the context element is in limited-quirks mode,
// then let the Document be in limited-quirks mode. Otherwise,
// leave the Document in no-quirks mode.
document.r().set_quirks_mode(context_document.r().quirks_mode());
// 11. Set the parser's form element pointer to the nearest node to
// the context element that is a form element (going straight up
// the ancestor chain, and including the element itself, if it
// is a form element), if any. (If there is no such form element,
// the form element pointer keeps its initial value, null.)
let form = context_node.inclusive_ancestors()
.find(|element| element.is_htmlformelement());
let fragment_context = FragmentContext {
context_elem: context_node,
form_elem: form,
};
parse_html(document.r(), HTMLInput::InputString(value), &url, Some(fragment_context));
// "14. Return the child nodes of root, in tree order."
// We do this by deleting all nodes of the context node,
// and then moving all nodes parsed into the new root_node
// into the context node.
while let Some(child) = context_node.GetFirstChild() {
try!(context_node.RemoveChild(child.root().r()));
}
let root_element = document.r().GetDocumentElement().expect("no document element").root();
let root_node: JSRef<Node> = NodeCast::from_ref(root_element.r());
while let Some(child) = root_node.GetFirstChild() {
let child = child.root();
try!(root_node.RemoveChild(child.r()));
try!(context_node.AppendChild(child.r()));
}
// Step 1.
let frag = try!(context_node.parse_fragment(value));
// Step 2.
Node::replace_all(Some(NodeCast::from_ref(frag.root().r())), context_node);
Ok(())
}
@ -1230,6 +1184,39 @@ impl<'a> ElementMethods for JSRef<'a, Element> {
self.serialize(IncludeNode)
}
fn SetOuterHTML(self, value: DOMString) -> Fallible<()> {
let context_document = document_from_node(self).root();
let context_node: JSRef<Node> = NodeCast::from_ref(self);
// Step 1.
let context_parent = match context_node.parent_node() {
// Step 2.
None => return Ok(()),
Some(parent) => parent.root()
};
let parent = match context_parent.r().type_id() {
// Step 3.
NodeTypeId::Document => return Err(NoModificationAllowed),
// Step 4.
NodeTypeId::DocumentFragment => {
let body_elem = Element::create(QualName::new(ns!(HTML), atom!(body)),
None, context_document.r(),
ElementCreator::ScriptCreated);
let body_node: Temporary<Node> = NodeCast::from_temporary(body_elem);
body_node.root()
},
_ => context_node.parent_node().unwrap().root()
};
// Step 5.
let frag = try!(parent.r().parse_fragment(value));
// Step 6.
try!(context_parent.r().ReplaceChild(NodeCast::from_ref(frag.root().r()),
context_node));
Ok(())
}
// http://dom.spec.whatwg.org/#dom-parentnode-children
fn Children(self) -> Temporary<HTMLCollection> {
let window = window_from_node(self).root();

View file

@ -46,6 +46,7 @@ use dom::window::{Window, WindowHelpers};
use geom::rect::Rect;
use layout_interface::{LayoutChan, Msg};
use devtools_traits::NodeInfo;
use parse::html::parse_html_fragment;
use script_traits::UntrustedNodeAddress;
use util::geometry::Au;
use util::str::{DOMString, null_str_as_empty};
@ -502,6 +503,8 @@ pub trait NodeHelpers<'a> {
fn summarize(self) -> NodeInfo;
fn teardown(self);
fn parse_fragment(self, markup: DOMString) -> Fallible<Temporary<DocumentFragment>>;
}
impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
@ -929,6 +932,24 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
}
}
// https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-parse-fragment
fn parse_fragment(self, markup: DOMString) -> Fallible<Temporary<DocumentFragment>> {
let context_node: JSRef<Node> = NodeCast::from_ref(self);
let context_document = document_from_node(self).root();
let mut new_children: RootedVec<JS<Node>> = RootedVec::new();
if context_document.r().is_html_document() {
parse_html_fragment(context_node, markup, &mut new_children);
} else {
// FIXME: XML case
unimplemented!();
}
let fragment = DocumentFragment::new(context_document.r()).root();
let fragment_node: JSRef<Node> = NodeCast::from_ref(fragment.r());
for node in new_children.iter() {
fragment_node.AppendChild(node.root().r()).unwrap();
}
Ok(Temporary::from_rooted(fragment.r()))
}
}
/// If the given untrusted node address represents a valid DOM node in the given runtime,
@ -1456,7 +1477,7 @@ impl Node {
}
// http://dom.spec.whatwg.org/#concept-node-replace-all
fn replace_all(node: Option<JSRef<Node>>, parent: JSRef<Node>) {
pub fn replace_all(node: Option<JSRef<Node>>, parent: JSRef<Node>) {
// Step 1.
match node {
Some(node) => {

View file

@ -66,7 +66,7 @@ partial interface Element {
[Throws,TreatNullAs=EmptyString]
attribute DOMString innerHTML;
[Throws,TreatNullAs=EmptyString]
readonly attribute DOMString outerHTML;
attribute DOMString outerHTML;
};
Element implements ChildNode;