servo: Merge #14423 - Properly mark application/xhtml+xml documents as XML (from nox:xml-document); r=Ms2ger
authorAnthony Ramine <n.oxyde@gmail.com>
Thu, 01 Dec 2016 03:57:14 -0800
changeset 340262 2ca07971434160711fa690dfe90db46f52032d94
parent 340261 fc18c37273eda7200f883473b7409526bb03a592
child 340263 6e75c58b3876e218c27529a3148aa6e7c908b8b8
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMs2ger
servo: Merge #14423 - Properly mark application/xhtml+xml documents as XML (from nox:xml-document); r=Ms2ger Source-Repo: https://github.com/servo/servo Source-Revision: 5abbc9f69668f23469a9c01937c68e305fa36cd8
servo/Cargo.lock
servo/components/script/Cargo.toml
servo/components/script/dom/attr.rs
servo/components/script/dom/csskeyframesrule.rs
servo/components/script/dom/document.rs
servo/components/script/dom/element.rs
servo/components/script/dom/htmlcollection.rs
servo/components/script/dom/node.rs
servo/components/script/dom/range.rs
servo/components/script/dom/servoparser/mod.rs
servo/components/script/dom/servoparser/xml.rs
servo/components/script/lib.rs
servo/components/script/script_thread.rs
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -2106,16 +2106,21 @@ source = "registry+https://github.com/ru
 dependencies = [
  "deque 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "ref_filter_map"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "ref_slice"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "regex"
 version = "0.1.76"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2195,16 +2200,17 @@ dependencies = [
  "open 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_macros 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "range 0.0.1",
+ "ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "ref_slice 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.1.76 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 0.8.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_atoms 0.0.1",
@@ -3351,16 +3357,17 @@ dependencies = [
 "checksum pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8cee804ecc7eaf201a4a207241472cc870e825206f6c031e3ee2a72fa425f2fa"
 "checksum pnacl-build-helper 1.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "61c9231d31aea845007443d62fcbb58bb6949ab9c18081ee1e09920e0cf1118b"
 "checksum png 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "06208e2ee243e3118a55dda9318f821f206d8563fb8d4df258767f8e62bb0997"
 "checksum quickersort 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e952ea7699262481636004bc4ab8afaccf2bc13f91b79d1aee6617bd8fc39651"
 "checksum quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
 "checksum quote 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1e0c9bc6bfb0a60d539aab6e338207c1a5456e62f5bd5375132cee119aa4b3"
 "checksum rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2791d88c6defac799c3f20d74f094ca33b9332612d9aef9078519c82e4fe04a5"
 "checksum rayon 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b6a6e05e0e6b703e9f2ad266eb63f3712e693a17a2702b95a23de14ce8defa9"
+"checksum ref_filter_map 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b5ceb840e4009da4841ed22a15eb49f64fdd00a2138945c5beacf506b2fb5ed"
 "checksum ref_slice 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "546bb4aa91c85f232732cc5b3c8097ea97ae9a77304f9ab4df8b203ff7672dad"
 "checksum regex 0.1.76 (registry+https://github.com/rust-lang/crates.io-index)" = "63b49f873f36ddc838d773972511e5fed2ef7350885af07d58e2f48ce8073dcd"
 "checksum regex-syntax 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279401017ae31cf4e15344aa3f085d0e2e5c1e70067289ef906906fdbe92c8fd"
 "checksum rustc-demangle 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c4c2d35b2ed94cec4fad26a36eee4d6eff394ce70a8ceea064b0b6ca42ea4cf0"
 "checksum rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6159e4e6e559c81bd706afe9c8fd68f547d3e851ce12e76b1de7914bab61691b"
 "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084"
 "checksum scoped_threadpool 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef399c8893e8cb7aa9696e895427fab3a6bf265977bb96e126f24ddd2cda85a"
 "checksum selectors 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b127ac14249f6ce720277f6a163b3837706e9dc1ad4e2948db55f262f11a97"
--- a/servo/components/script/Cargo.toml
+++ b/servo/components/script/Cargo.toml
@@ -56,16 +56,17 @@ offscreen_gl_context = "0.5.0"
 open = "1.1.1"
 parking_lot = "0.3"
 phf = "0.7.18"
 phf_macros = "0.7.18"
 plugins = {path = "../plugins"}
 profile_traits = {path = "../profile_traits"}
 rand = "0.3"
 range = {path = "../range"}
+ref_filter_map = "1.0.1"
 ref_slice = "1.0"
 regex = "0.1.43"
 rustc-serialize = "0.3"
 script_layout_interface = {path = "../script_layout_interface"}
 script_traits = {path = "../script_traits"}
 selectors = "0.15"
 serde = "0.8"
 servo_atoms = {path = "../atoms"}
--- a/servo/components/script/dom/attr.rs
+++ b/servo/components/script/dom/attr.rs
@@ -76,18 +76,18 @@ impl Attr {
     }
 
     #[inline]
     pub fn namespace(&self) -> &Namespace {
         &self.identifier.namespace
     }
 
     #[inline]
-    pub fn prefix(&self) -> &Option<Prefix> {
-        &self.identifier.prefix
+    pub fn prefix(&self) -> Option<&Prefix> {
+        self.identifier.prefix.as_ref()
     }
 }
 
 impl AttrMethods for Attr {
     // https://dom.spec.whatwg.org/#dom-attr-localname
     fn LocalName(&self) -> DOMString {
         // FIXME(ajeffrey): convert directly from LocalName to DOMString
         DOMString::from(&**self.local_name())
@@ -148,17 +148,17 @@ impl AttrMethods for Attr {
             ns!() => None,
             ref url => Some(DOMString::from(&**url)),
         }
     }
 
     // https://dom.spec.whatwg.org/#dom-attr-prefix
     fn GetPrefix(&self) -> Option<DOMString> {
         // FIXME(ajeffrey): convert directly from LocalName to DOMString
-        self.prefix().as_ref().map(|p| DOMString::from(&**p))
+        self.prefix().map(|p| DOMString::from(&**p))
     }
 
     // https://dom.spec.whatwg.org/#dom-attr-ownerelement
     fn GetOwnerElement(&self) -> Option<Root<Element>> {
         self.owner()
     }
 
     // https://dom.spec.whatwg.org/#dom-attr-specified
--- a/servo/components/script/dom/csskeyframesrule.rs
+++ b/servo/components/script/dom/csskeyframesrule.rs
@@ -75,17 +75,16 @@ impl CSSKeyframesRule {
 impl CSSKeyframesRuleMethods for CSSKeyframesRule {
     // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-cssrules
     fn CssRules(&self) -> Root<CSSRuleList> {
         self.rulelist()
     }
 
     // https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-appendrule
     fn AppendRule(&self, rule: DOMString) {
-        let global = self.global();
         let rule = Keyframe::parse(&rule, self.cssrule.parent_stylesheet().style_stylesheet(),
                                    ParserContextExtraData::default());
         if let Ok(rule) = rule {
             self.keyframesrule.write().keyframes.push(rule);
             self.rulelist().append_lazy_dom_rule();
         }
     }
 
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -131,17 +131,17 @@ use time;
 use url::percent_encoding::percent_decode;
 use util::prefs::PREFS;
 
 pub enum TouchEventResult {
     Processed(bool),
     Forwarded,
 }
 
-#[derive(JSTraceable, PartialEq, HeapSizeOf)]
+#[derive(Clone, Copy, Debug, HeapSizeOf, JSTraceable, PartialEq)]
 pub enum IsHTMLDocument {
     HTMLDocument,
     NonHTMLDocument,
 }
 
 #[derive(JSTraceable, HeapSizeOf)]
 #[must_root]
 pub struct StylesheetInDocument {
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -25,16 +25,17 @@ use dom::bindings::inheritance::{Castabl
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap};
 use dom::bindings::js::{Root, RootedReference};
 use dom::bindings::str::DOMString;
 use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type};
 use dom::bindings::xmlname::XMLName::InvalidXMLName;
 use dom::characterdata::CharacterData;
 use dom::create::create_element;
 use dom::document::{Document, LayoutDocumentHelpers};
+use dom::documentfragment::DocumentFragment;
 use dom::domrect::DOMRect;
 use dom::domrectlist::DOMRectList;
 use dom::domtokenlist::DOMTokenList;
 use dom::event::Event;
 use dom::htmlanchorelement::HTMLAnchorElement;
 use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
 use dom::htmlbuttonelement::HTMLButtonElement;
 use dom::htmlcollection::HTMLCollection;
@@ -56,26 +57,28 @@ use dom::htmltablerowelement::{HTMLTable
 use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers};
 use dom::htmltemplateelement::HTMLTemplateElement;
 use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
 use dom::namednodemap::NamedNodeMap;
 use dom::node::{CLICK_IN_PROGRESS, ChildrenMutation, LayoutNodeHelpers, Node};
 use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext};
 use dom::node::{document_from_node, window_from_node};
 use dom::nodelist::NodeList;
+use dom::servoparser::ServoParser;
 use dom::text::Text;
 use dom::validation::Validatable;
 use dom::virtualmethods::{VirtualMethods, vtable_for};
 use html5ever::serialize;
 use html5ever::serialize::SerializeOpts;
 use html5ever::serialize::TraversalScope;
 use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
 use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks};
 use html5ever_atoms::{Prefix, LocalName, Namespace, QualName};
 use parking_lot::RwLock;
+use ref_filter_map::ref_filter_map;
 use selectors::matching::{ElementFlags, MatchingReason, matches};
 use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use servo_atoms::Atom;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::{Cell, Ref};
 use std::convert::TryFrom;
@@ -707,22 +710,62 @@ impl Element {
         }
         LocalName::from(name)
     }
 
     pub fn namespace(&self) -> &Namespace {
         &self.namespace
     }
 
-    pub fn prefix(&self) -> &Option<DOMString> {
-        &self.prefix
+    pub fn prefix(&self) -> Option<&DOMString> {
+        self.prefix.as_ref()
+    }
+
+    pub fn attrs(&self) -> Ref<[JS<Attr>]> {
+        Ref::map(self.attrs.borrow(), |attrs| &**attrs)
     }
 
-    pub fn attrs(&self) -> Ref<Vec<JS<Attr>>> {
-        self.attrs.borrow()
+    // Element branch of https://dom.spec.whatwg.org/#locate-a-namespace
+    pub fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
+        let prefix = prefix.map(String::from).map(LocalName::from);
+
+        let inclusive_ancestor_elements =
+            self.upcast::<Node>()
+                .inclusive_ancestors()
+                .filter_map(Root::downcast::<Self>);
+
+        // Steps 3-4.
+        for element in inclusive_ancestor_elements {
+            // Step 1.
+            if element.namespace() != &ns!() && element.prefix().map(|p| &**p) == prefix.as_ref().map(|p| &**p) {
+                return element.namespace().clone();
+            }
+
+            // Step 2.
+            let attr = ref_filter_map(self.attrs(), |attrs| {
+                attrs.iter().find(|attr| {
+                    if attr.namespace() != &ns!(xmlns) {
+                        return false;
+                    }
+                    match (attr.prefix(), prefix.as_ref()) {
+                        (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
+                            attr.local_name() == prefix
+                        },
+                        (None, None) => attr.local_name() == &local_name!("xmlns"),
+                        _ => false,
+                    }
+                })
+            });
+
+            if let Some(attr) = attr {
+                return (**attr.value()).into();
+            }
+        }
+
+        ns!()
     }
 
     pub fn style_attribute(&self) -> &DOMRefCell<Option<Arc<RwLock<PropertyDeclarationBlock>>>> {
         &self.style_attribute
     }
 
     pub fn summarize(&self) -> Vec<AttrInfo> {
         self.attrs.borrow().iter()
@@ -813,17 +856,17 @@ impl Element {
                     if *element.namespace() == namespace {
                         if let Some(prefix) = element.GetPrefix() {
                             return Some(prefix);
                         }
                     }
 
                     // Step 2.
                     for attr in element.attrs.borrow().iter() {
-                        if *attr.prefix() == Some(namespace_prefix!("xmlns")) &&
+                        if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
                            **attr.value() == *namespace {
                             return Some(attr.LocalName());
                         }
                     }
                 },
                 None => return None,
             }
         }
@@ -1225,16 +1268,43 @@ impl Element {
                return;
         }
 
         // Step 10 (TODO)
 
         // Step 11
         win.scroll_node(node.to_trusted_node_address(), x, y, behavior);
     }
+
+    // https://w3c.github.io/DOM-Parsing/#parsing
+    pub fn parse_fragment(&self, markup: DOMString) -> Fallible<Root<DocumentFragment>> {
+        // Steps 1-2.
+        let context_document = document_from_node(self);
+        // TODO(#11995): XML case.
+        let new_children = ServoParser::parse_html_fragment(self, markup);
+        // Step 3.
+        let fragment = DocumentFragment::new(&context_document);
+        // Step 4.
+        for child in new_children {
+            fragment.upcast::<Node>().AppendChild(&child).unwrap();
+        }
+        // Step 5.
+        Ok(fragment)
+    }
+
+    pub fn fragment_parsing_context(owner_doc: &Document, element: Option<&Self>) -> Root<Self> {
+        match element {
+            Some(elem) if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => {
+                Root::from_ref(elem)
+            },
+            _ => {
+                Root::upcast(HTMLBodyElement::new(local_name!("body"), None, owner_doc))
+            }
+        }
+    }
 }
 
 impl ElementMethods for Element {
     // https://dom.spec.whatwg.org/#dom-element-namespaceuri
     fn GetNamespaceURI(&self) -> Option<DOMString> {
         Node::namespace_to_string(self.namespace.clone())
     }
 
@@ -1752,36 +1822,35 @@ impl ElementMethods for Element {
     /// https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML
     fn GetInnerHTML(&self) -> Fallible<DOMString> {
         // XXX TODO: XML case
         self.serialize(ChildrenOnly)
     }
 
     /// https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML
     fn SetInnerHTML(&self, value: DOMString) -> ErrorResult {
-        let context_node = self.upcast::<Node>();
         // Step 1.
-        let frag = try!(context_node.parse_fragment(value));
+        let frag = try!(self.parse_fragment(value));
         // Step 2.
         // https://github.com/w3c/DOM-Parsing/issues/1
         let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
             Root::upcast(template.Content())
         } else {
-            Root::from_ref(context_node)
+            Root::from_ref(self.upcast())
         };
         Node::replace_all(Some(frag.upcast()), &target);
         Ok(())
     }
 
     // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
     fn GetOuterHTML(&self) -> Fallible<DOMString> {
         self.serialize(IncludeNode)
     }
 
-    // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
+    // https://w3c.github.io/DOM-Parsing/#dom-element-outerhtml
     fn SetOuterHTML(&self, value: DOMString) -> ErrorResult {
         let context_document = document_from_node(self);
         let context_node = self.upcast::<Node>();
         // Step 1.
         let context_parent = match context_node.GetParentNode() {
             None => {
                 // Step 2.
                 return Ok(());
@@ -1795,17 +1864,17 @@ impl ElementMethods for Element {
 
             // Step 4.
             NodeTypeId::DocumentFragment => {
                 let body_elem = Element::create(QualName::new(ns!(html), local_name!("body")),
                                                 None, &context_document,
                                                 ElementCreator::ScriptCreated);
                 Root::upcast(body_elem)
             },
-            _ => context_node.GetParentNode().unwrap()
+            _ => context_node.GetParentElement().unwrap()
         };
 
         // Step 5.
         let frag = try!(parent.parse_fragment(value));
         // Step 6.
         try!(context_parent.ReplaceChild(frag.upcast(), context_node));
         Ok(())
     }
@@ -1952,24 +2021,21 @@ impl ElementMethods for Element {
                 }
             }
             AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
                 Root::from_ref(self.upcast::<Node>())
             }
         };
 
         // Step 2.
-        let context = match context.downcast::<Element>() {
-            Some(elem) if elem.local_name() != &local_name!("html") ||
-                          !elem.html_element_in_html_document() => Root::from_ref(elem),
-            _ => Root::upcast(HTMLBodyElement::new(local_name!("body"), None, &*context.owner_doc())),
-        };
+        let context = Element::fragment_parsing_context(
+            &context.owner_doc(), context.downcast::<Element>());
 
         // Step 3.
-        let fragment = try!(context.upcast::<Node>().parse_fragment(text));
+        let fragment = try!(context.parse_fragment(text));
 
         // Step 4.
         self.insert_adjacent(position, fragment.upcast()).map(|_| ())
     }
 
     // check-tidy: no specs after this line
     fn EnterFormalActivationState(&self) -> ErrorResult {
         match self.as_maybe_activatable() {
--- a/servo/components/script/dom/htmlcollection.rs
+++ b/servo/components/script/dom/htmlcollection.rs
@@ -146,21 +146,21 @@ impl HTMLCollection {
         let filter = HtmlDocumentFilter {
             ascii_lower_qualified_name: qualified_name.to_ascii_lowercase(),
             qualified_name: qualified_name,
         };
         HTMLCollection::create(window, root, box filter)
     }
 
     fn match_element(elem: &Element, qualified_name: &LocalName) -> bool {
-        match *elem.prefix() {
+        match elem.prefix() {
             None => elem.local_name() == qualified_name,
-            Some(ref prefix) => qualified_name.starts_with(prefix as &str) &&
-                qualified_name.find(":") == Some((prefix as &str).len()) &&
-                qualified_name.ends_with(elem.local_name() as &str),
+            Some(prefix) => qualified_name.starts_with(&**prefix) &&
+                qualified_name.find(":") == Some(prefix.len()) &&
+                qualified_name.ends_with(&**elem.local_name()),
         }
     }
 
     pub fn by_tag_name_ns(window: &Window, root: &Node, tag: DOMString,
                           maybe_ns: Option<DOMString>) -> Root<HTMLCollection> {
         let local = LocalName::from(tag);
         let ns = namespace_from_domstring(maybe_ns);
         let qname = QualName::new(ns, local);
--- a/servo/components/script/dom/node.rs
+++ b/servo/components/script/dom/node.rs
@@ -2,18 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
 
 use app_units::Au;
 use devtools_traits::NodeInfo;
 use document_loader::DocumentLoader;
-use dom::attr::Attr;
-use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
 use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
 use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
 use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
 use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
 use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
 use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
@@ -46,27 +44,26 @@ use dom::htmlimageelement::{HTMLImageEle
 use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
 use dom::htmllinkelement::HTMLLinkElement;
 use dom::htmlmetaelement::HTMLMetaElement;
 use dom::htmlstyleelement::HTMLStyleElement;
 use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
 use dom::nodelist::NodeList;
 use dom::processinginstruction::ProcessingInstruction;
 use dom::range::WeakRangeVec;
-use dom::servoparser::ServoParser;
 use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers};
 use dom::text::Text;
 use dom::virtualmethods::{VirtualMethods, vtable_for};
 use dom::window::Window;
 use euclid::point::Point2D;
 use euclid::rect::Rect;
 use euclid::size::Size2D;
 use heapsize::{HeapSizeOf, heap_size_of};
 use html5ever::tree_builder::QuirksMode;
-use html5ever_atoms::{Prefix, LocalName, Namespace, QualName};
+use html5ever_atoms::{Prefix, Namespace, QualName};
 use js::jsapi::{JSContext, JSObject, JSRuntime};
 use libc::{self, c_void, uintptr_t};
 use msg::constellation_msg::PipelineId;
 use ref_slice::ref_slice;
 use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
 use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
 use script_layout_interface::message::Msg;
 use script_traits::UntrustedNodeAddress;
@@ -794,29 +791,16 @@ impl Node {
                     .GetDocumentElement()
                     .map_or(false, |elem| elem.upcast::<Node>() == self),
 
             shortValue: self.GetNodeValue().map(String::from).unwrap_or_default(), //FIXME: truncate
             incompleteValue: false, //FIXME: reflect truncation
         }
     }
 
-    // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#dfn-concept-parse-fragment
-    pub fn parse_fragment(&self, markup: DOMString) -> Fallible<Root<DocumentFragment>> {
-        let context_document = document_from_node(self);
-        let fragment = DocumentFragment::new(&context_document);
-        if context_document.is_html_document() {
-            ServoParser::parse_html_fragment(self.upcast(), markup, fragment.upcast());
-        } else {
-            // FIXME: XML case
-            unimplemented!();
-        }
-        Ok(fragment)
-    }
-
     /// Used by `HTMLTableSectionElement::InsertRow` and `HTMLTableRowElement::InsertCell`
     pub fn insert_cell_or_row<F, G, I>(&self, index: i32, get_items: F, new_child: G) -> Fallible<Root<HTMLElement>>
         where F: Fn() -> Root<HTMLCollection>,
               G: Fn() -> Root<I>,
               I: DerivedFrom<Node> + DerivedFrom<HTMLElement> + Reflectable,
     {
         if index < -1 {
             return Err(Error::IndexSize);
@@ -1747,17 +1731,17 @@ impl Node {
             },
             NodeTypeId::Element(..) => {
                 let element = node.downcast::<Element>().unwrap();
                 let name = QualName {
                     ns: element.namespace().clone(),
                     local: element.local_name().clone()
                 };
                 let element = Element::create(name,
-                    element.prefix().as_ref().map(|p| Prefix::from(&**p)),
+                    element.prefix().map(|p| Prefix::from(&**p)),
                     &document, ElementCreator::ScriptCreated);
                 Root::upcast::<Node>(element)
             },
         };
 
         // Step 3.
         let document = match copy.downcast::<Document>() {
             Some(doc) => Root::from_ref(doc),
@@ -1777,17 +1761,17 @@ impl Node {
                 let node_elem = node.downcast::<Element>().unwrap();
                 let copy_elem = copy.downcast::<Element>().unwrap();
 
                 for attr in node_elem.attrs().iter() {
                     copy_elem.push_new_attribute(attr.local_name().clone(),
                                                  attr.value().clone(),
                                                  attr.name().clone(),
                                                  attr.namespace().clone(),
-                                                 attr.prefix().clone());
+                                                 attr.prefix().cloned());
                 }
             },
             _ => ()
         }
 
         // Step 5: cloning steps.
         vtable_for(&node).cloning_steps(&copy, maybe_doc, clone_children);
 
@@ -1820,78 +1804,30 @@ impl Node {
             ns!() => None,
             // FIXME(ajeffrey): convert directly from Namespace to DOMString
             _ => Some(DOMString::from(&*namespace))
         }
     }
 
     // https://dom.spec.whatwg.org/#locate-a-namespace
     pub fn locate_namespace(node: &Node, prefix: Option<DOMString>) -> Namespace {
-        fn attr_defines_namespace(attr: &Attr,
-                                  defined_prefix: &Option<LocalName>) -> bool {
-            *attr.namespace() == ns!(xmlns) &&
-                match (attr.prefix(), defined_prefix) {
-                    (&Some(ref attr_prefix), &Some(ref defined_prefix)) =>
-                        attr_prefix == &namespace_prefix!("xmlns") &&
-                            attr.local_name() == defined_prefix,
-                    (&None, &None) => *attr.local_name() == local_name!("xmlns"),
-                    _ => false
-                }
-        }
-
         match node.type_id() {
             NodeTypeId::Element(_) => {
-                let element = node.downcast::<Element>().unwrap();
-                // Step 1.
-                if *element.namespace() != ns!() && *element.prefix() == prefix {
-                    return element.namespace().clone()
-                }
-
-                // Even though this is conceptually a namespace prefix,
-                // in the `xmlns:foo="https://example.net/namespace" declaration
-                // it is a local name.
-                // FIXME(ajeffrey): directly convert DOMString to LocalName
-                let prefix_atom = prefix.as_ref().map(|s| LocalName::from(&**s));
-
-                // Step 2.
-                let attrs = element.attrs();
-                let namespace_attr = attrs.iter().find(|attr| {
-                    attr_defines_namespace(attr, &prefix_atom)
-                });
-
-                // Steps 2.1-2.
-                if let Some(attr) = namespace_attr {
-                    return namespace_from_domstring(Some(attr.Value()));
-                }
-
-                match node.GetParentElement() {
-                    // Step 3.
-                    None => ns!(),
-                    // Step 4.
-                    Some(parent) => Node::locate_namespace(parent.upcast(), prefix)
-                }
+                node.downcast::<Element>().unwrap().locate_namespace(prefix)
             },
             NodeTypeId::Document(_) => {
-                match node.downcast::<Document>().unwrap().GetDocumentElement().r() {
-                    // Step 1.
-                    None => ns!(),
-                    // Step 2.
-                    Some(document_element) => {
-                        Node::locate_namespace(document_element.upcast(), prefix)
-                    }
-                }
+                node.downcast::<Document>().unwrap()
+                    .GetDocumentElement().as_ref()
+                    .map_or(ns!(), |elem| elem.locate_namespace(prefix))
             },
-            NodeTypeId::DocumentType => ns!(),
-            NodeTypeId::DocumentFragment => ns!(),
-            _ => match node.GetParentElement() {
-                     // Step 1.
-                     None => ns!(),
-                     // Step 2.
-                     Some(parent) => Node::locate_namespace(parent.upcast(), prefix)
-                 }
+            NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => ns!(),
+            _ => {
+                node.GetParentElement().as_ref()
+                    .map_or(ns!(), |elem| elem.locate_namespace(prefix))
+            }
         }
     }
 }
 
 impl NodeMethods for Node {
     // https://dom.spec.whatwg.org/#dom-node-nodetype
     fn NodeType(&self) -> u16 {
         match self.type_id() {
@@ -2245,17 +2181,17 @@ impl NodeMethods for Node {
             (*doctype.name() == *other_doctype.name()) &&
             (*doctype.public_id() == *other_doctype.public_id()) &&
             (*doctype.system_id() == *other_doctype.system_id())
         }
         fn is_equal_element(node: &Node, other: &Node) -> bool {
             let element = node.downcast::<Element>().unwrap();
             let other_element = other.downcast::<Element>().unwrap();
             (*element.namespace() == *other_element.namespace()) &&
-            (*element.prefix() == *other_element.prefix()) &&
+            (element.prefix() == other_element.prefix()) &&
             (*element.local_name() == *other_element.local_name()) &&
             (element.attrs().len() == other_element.attrs().len())
         }
         fn is_equal_processinginstruction(node: &Node, other: &Node) -> bool {
             let pi = node.downcast::<ProcessingInstruction>().unwrap();
             let other_pi = other.downcast::<ProcessingInstruction>().unwrap();
             (*pi.target() == *other_pi.target()) &&
             (*pi.upcast::<CharacterData>().data() == *other_pi.upcast::<CharacterData>().data())
--- a/servo/components/script/dom/range.rs
+++ b/servo/components/script/dom/range.rs
@@ -17,17 +17,16 @@ use dom::bindings::js::{JS, MutHeap, Roo
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::bindings::trace::JSTraceable;
 use dom::bindings::weakref::{WeakRef, WeakRefVec};
 use dom::characterdata::CharacterData;
 use dom::document::Document;
 use dom::documentfragment::DocumentFragment;
 use dom::element::Element;
-use dom::htmlbodyelement::HTMLBodyElement;
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::node::{Node, UnbindContext};
 use dom::text::Text;
 use dom::window::Window;
 use heapsize::HeapSizeOf;
 use js::jsapi::JSTracer;
 use std::cell::{Cell, UnsafeCell};
 use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
@@ -896,35 +895,28 @@ impl RangeMethods for Range {
         // Step 6.
         s
     }
 
     // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#extensions-to-the-range-interface
     fn CreateContextualFragment(&self, fragment: DOMString) -> Fallible<Root<DocumentFragment>> {
         // Step 1.
         let node = self.StartContainer();
+        let owner_doc = node.owner_doc();
         let element = match node.type_id() {
             NodeTypeId::Document(_) | NodeTypeId::DocumentFragment => None,
             NodeTypeId::Element(_) => Some(Root::downcast::<Element>(node).unwrap()),
             NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
             NodeTypeId::CharacterData(CharacterDataTypeId::Text) => node.GetParentElement(),
             NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
             NodeTypeId::DocumentType => unreachable!(),
         };
 
         // Step 2.
-        let should_create_body = element.as_ref().map_or(true, |elem| {
-            let elem = elem.downcast::<Element>().unwrap();
-            elem.local_name() == &local_name!("html") && elem.html_element_in_html_document()
-        });
-        let element: Root<Node> = if should_create_body {
-            Root::upcast(HTMLBodyElement::new(local_name!("body"), None, &self.StartContainer().owner_doc()))
-        } else {
-            Root::upcast(element.unwrap())
-        };
+        let element = Element::fragment_parsing_context(&owner_doc, element.r());
 
         // Step 3.
         let fragment_node = try!(element.parse_fragment(fragment));
 
         // Step 4.
         for node in fragment_node.upcast::<Node>().traverse_preorder() {
             if let Some(script) = node.downcast::<HTMLScriptElement>() {
                 script.set_already_started(false);
--- a/servo/components/script/dom/servoparser/mod.rs
+++ b/servo/components/script/dom/servoparser/mod.rs
@@ -9,21 +9,22 @@ use dom::bindings::codegen::Bindings::HT
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
 use dom::bindings::codegen::Bindings::ServoParserBinding;
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, Root, RootedReference};
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::{Reflector, reflect_dom_object};
 use dom::bindings::str::DOMString;
 use dom::document::{Document, DocumentSource, IsHTMLDocument};
+use dom::element::Element;
 use dom::globalscope::GlobalScope;
 use dom::htmlformelement::HTMLFormElement;
 use dom::htmlimageelement::HTMLImageElement;
 use dom::htmlscriptelement::HTMLScriptElement;
-use dom::node::{Node, document_from_node, window_from_node};
+use dom::node::{Node, NodeSiblingIterator};
 use encoding::all::UTF_8;
 use encoding::types::{DecoderTrap, Encoding};
 use html5ever::tokenizer::buffer_queue::BufferQueue;
 use hyper::header::ContentType;
 use hyper::mime::{Mime, SubLevel, TopLevel};
 use hyper_serde::Serde;
 use msg::constellation_msg::PipelineId;
 use net_traits::{FetchMetadata, FetchResponseListener, Metadata, NetworkError};
@@ -91,27 +92,25 @@ impl ServoParser {
             document,
             owner,
             Tokenizer::Html(self::html::Tokenizer::new(document, url, None)),
             LastChunkState::NotReceived);
         parser.parse_chunk(String::from(input));
     }
 
     // https://html.spec.whatwg.org/multipage/#parsing-html-fragments
-    pub fn parse_html_fragment(
-            context_node: &Node,
-            input: DOMString,
-            output: &Node) {
-        let window = window_from_node(context_node);
-        let context_document = document_from_node(context_node);
+    pub fn parse_html_fragment(context: &Element, input: DOMString) -> FragmentParsingResult {
+        let context_node = context.upcast::<Node>();
+        let context_document = context_node.owner_doc();
+        let window = context_document.window();
         let url = context_document.url();
 
         // Step 1.
         let loader = DocumentLoader::new(&*context_document.loader());
-        let document = Document::new(&window, None, Some(url.clone()),
+        let document = Document::new(window, None, Some(url.clone()),
                                      IsHTMLDocument::HTMLDocument,
                                      None, None,
                                      DocumentSource::FromParser,
                                      loader,
                                      None, None);
 
         // Step 2.
         document.set_quirks_mode(context_document.quirks_mode());
@@ -129,19 +128,17 @@ impl ServoParser {
             None,
             Tokenizer::Html(
                 self::html::Tokenizer::new(&document, url.clone(), Some(fragment_context))),
             LastChunkState::Received);
         parser.parse_chunk(String::from(input));
 
         // Step 14.
         let root_element = document.GetDocumentElement().expect("no document element");
-        for child in root_element.upcast::<Node>().children() {
-            output.AppendChild(&child).unwrap();
-        }
+        FragmentParsingResult { inner: root_element.upcast::<Node>().children() }
     }
 
     pub fn parse_xml_document(
             document: &Document,
             input: DOMString,
             url: ServoUrl,
             owner: Option<PipelineId>) {
         let parser = ServoParser::new(
@@ -344,16 +341,33 @@ impl ServoParser {
         self.document.set_current_parser(None);
 
         if let Some(pipeline) = self.pipeline {
             ScriptThread::parsing_complete(pipeline);
         }
     }
 }
 
+pub struct FragmentParsingResult {
+    inner: NodeSiblingIterator,
+}
+
+impl Iterator for FragmentParsingResult {
+    type Item = Root<Node>;
+
+    fn next(&mut self) -> Option<Root<Node>> {
+        let next = match self.inner.next() {
+            Some(next) => next,
+            None => return None,
+        };
+        next.remove_self();
+        Some(next)
+    }
+}
+
 #[derive(HeapSizeOf, JSTraceable)]
 #[must_root]
 enum Tokenizer {
     Html(self::html::Tokenizer),
     Xml(self::xml::Tokenizer),
 }
 
 impl Tokenizer {
--- a/servo/components/script/dom/servoparser/xml.rs
+++ b/servo/components/script/dom/servoparser/xml.rs
@@ -109,17 +109,17 @@ impl<'a> TreeSink for Sink {
     fn get_document(&mut self) -> JS<Node> {
         JS::from_ref(self.document.upcast())
     }
 
     fn elem_name(&self, target: &JS<Node>) -> QName {
         let elem = target.downcast::<Element>()
             .expect("tried to get name of non-Element in XML parsing");
         QName {
-            prefix: elem.prefix().as_ref().map_or(namespace_prefix!(""), |p| Prefix::from(&**p)),
+            prefix: elem.prefix().map_or(namespace_prefix!(""), |p| Prefix::from(&**p)),
             namespace_url: elem.namespace().clone(),
             local: elem.local_name().clone(),
         }
     }
 
     fn create_element(&mut self, name: QName, attrs: Vec<Attribute>)
             -> JS<Node> {
         let prefix = if name.prefix == namespace_prefix!("") { None } else { Some(name.prefix) };
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -69,16 +69,17 @@ extern crate num_traits;
 extern crate offscreen_gl_context;
 extern crate open;
 extern crate parking_lot;
 extern crate phf;
 #[macro_use]
 extern crate profile_traits;
 extern crate rand;
 extern crate range;
+extern crate ref_filter_map;
 extern crate ref_slice;
 extern crate regex;
 extern crate rustc_serialize;
 extern crate script_layout_interface;
 extern crate script_traits;
 extern crate selectors;
 extern crate serde;
 #[macro_use] extern crate servo_atoms;
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -1746,19 +1746,22 @@ impl ScriptThread {
                 _ => None,
             }
         });
 
         let loader = DocumentLoader::new_with_threads(self.resource_threads.clone(),
                                                       Some(incomplete.url.clone()));
 
         let is_html_document = match metadata.content_type {
+            Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Ext(ref sub_level), _))))
+                if sub_level.ends_with("+xml") => IsHTMLDocument::NonHTMLDocument,
+
             Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _)))) |
-            Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) =>
-                IsHTMLDocument::NonHTMLDocument,
+            Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => IsHTMLDocument::NonHTMLDocument,
+
             _ => IsHTMLDocument::HTMLDocument,
         };
 
         let referrer = match metadata.referrer {
             Some(ref referrer) => Some(referrer.clone().into_string()),
             None => None,
         };
 
@@ -1839,27 +1842,17 @@ impl ScriptThread {
                 }
             }
         } else {
             DOMString::new()
         };
 
         document.set_https_state(metadata.https_state);
 
-        let is_xml = match metadata.content_type {
-            Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Ext(ref sub_level), _))))
-                if sub_level.ends_with("+xml") => true,
-
-            Some(Serde(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _)))) |
-            Some(Serde(ContentType(Mime(TopLevel::Text, SubLevel::Xml, _)))) => true,
-
-            _ => false,
-        };
-
-        if is_xml {
+        if is_html_document == IsHTMLDocument::NonHTMLDocument {
             ServoParser::parse_xml_document(
                 &document,
                 parse_input,
                 final_url,
                 Some(incomplete.pipeline_id));
         } else {
             ServoParser::parse_html_document(
                 &document,