Move innerHTML and outerHTML to Element

Per the DOM Parsing specification, innerHTML and outerHTML should be
available on Element, not HTMLElement. Since HTMLElement and SVGElement
are the only interfaces that inherit from Element, the only Web visible
difference should be that innerHTML and outerHTML are now available for
SVG elements.

For <svg>.innerHTML = "<!-- svg elements here -->" to insert elements
with correct namespace URI, one adjustment has to be made to the tree
builder step of the html parsing algorithm. The changes are described in
the spec change <http://html5.org/r/7768>. The new concept of
'adjustedCurrentNode' is used when processing and when deciding to
process foreign content and makes sure that parsed elements go into the
correct namespace.

Some layout tests that (perhaps accidentally) use innerHTML and
outerHTML on SVG elements are adjusted slightly.

This is a re-land of 160869 https://codereview.chromium.org/44333002/

BUG=311080

Review URL: https://codereview.chromium.org/52443003

git-svn-id: svn://svn.chromium.org/blink/trunk@160981 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/fast/innerHTML/innerHTML-svg-read-expected.txt b/LayoutTests/fast/innerHTML/innerHTML-svg-read-expected.txt
new file mode 100644
index 0000000..9495ada
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-svg-read-expected.txt
@@ -0,0 +1,15 @@
+Test that accessing innerHTML and outerHTML properties works on SVG elements
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS innerHTML("emptysvg") is ""
+PASS innerHTML("rectsvg") is "<rect></rect>"
+PASS innerHTML("foreign") is "<foreignObject width=\"100\" height=\"30\"><p>html</p></foreignObject>"
+PASS outerHTML("emptysvg") is "<svg id=\"emptysvg\"></svg>"
+PASS outerHTML("rectsvg") is "<svg id=\"rectsvg\"><rect></rect></svg>"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+
diff --git a/LayoutTests/fast/innerHTML/innerHTML-svg-read.html b/LayoutTests/fast/innerHTML/innerHTML-svg-read.html
new file mode 100644
index 0000000..c42440b
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-svg-read.html
@@ -0,0 +1,36 @@
+<html>
+<head>
+<script src="../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<div style="visibility:hidden">
+  <svg id="emptysvg"></svg>
+  <svg id="rectsvg"><rect/></svg>
+  <svg id="foreign" width="100" height="30"><foreignObject width="100" height="30"><p>html</p></foreignObject></svg>
+</div>
+<script>
+description("Test that accessing innerHTML and outerHTML properties works on SVG elements");
+
+var tests = [
+    ['innerHTML("emptysvg")'  , ''],
+    ['innerHTML("rectsvg")'  , '<rect></rect>' ],
+    ['innerHTML("foreign")'  , '<foreignObject width="100" height="30"><p>html</p></foreignObject>' ],
+    ['outerHTML("emptysvg")'  , '<svg id="emptysvg"></svg>'],
+    ['outerHTML("rectsvg")'  , '<svg id="rectsvg"><rect></rect></svg>' ],
+  ];
+
+function innerHTML(id) {
+    return document.getElementById(id).innerHTML;
+}
+
+function outerHTML(id) {
+    return document.getElementById(id).outerHTML;
+}
+
+for (var i in tests) {
+    shouldBeEqualToString(tests[i][0], tests[i][1]);
+}
+</script>
+<script src="../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/innerHTML/innerHTML-svg-write-expected.txt b/LayoutTests/fast/innerHTML/innerHTML-svg-write-expected.txt
new file mode 100644
index 0000000..39cb5b2
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-svg-write-expected.txt
@@ -0,0 +1,19 @@
+Test that setting innerHTML and outerHTML properties works on SVG elements
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS svg.innerHTML is "<rect></rect>"
+PASS svg.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+PASS svg.innerHTML is "<foreignObject><svg></svg></foreignObject>"
+PASS svg.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+PASS svg.firstChild.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+PASS svg.innerHTML is "<foreignObject><p>x</p></foreignObject>"
+PASS svg.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+PASS svg.firstChild.firstChild.namespaceURI is "http://www.w3.org/1999/xhtml"
+PASS svg.innerHTML is "<circle></circle>"
+PASS svg.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/innerHTML/innerHTML-svg-write.html b/LayoutTests/fast/innerHTML/innerHTML-svg-write.html
new file mode 100644
index 0000000..a9666a3
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-svg-write.html
@@ -0,0 +1,34 @@
+<html>
+<head>
+<script src="../js/resources/js-test-pre.js"></script>
+</head>
+<body>
+<div style="visibility:hidden">
+  <svg></svg>
+</div>
+<script>
+  description("Test that setting innerHTML and outerHTML properties works on SVG elements");
+  var svg = document.getElementsByTagName('svg')[0];
+
+  svg.innerHTML = "<rect/>";
+  shouldBeEqualToString("svg.innerHTML", '<rect></rect>');
+  shouldBeEqualToString("svg.firstChild.namespaceURI", 'http://www.w3.org/2000/svg');
+
+  svg.innerHTML = "<foreignobject><svg/></foreignobject>";
+  shouldBeEqualToString("svg.innerHTML", '<foreignObject><svg></svg></foreignObject>');
+  shouldBeEqualToString("svg.firstChild.namespaceURI", 'http://www.w3.org/2000/svg');
+  shouldBeEqualToString("svg.firstChild.firstChild.namespaceURI", 'http://www.w3.org/2000/svg');
+
+  svg.innerHTML = "<foreignobject><p>x</foreignobject>";
+  shouldBeEqualToString("svg.innerHTML", '<foreignObject><p>x</p></foreignObject>');
+  shouldBeEqualToString("svg.firstChild.namespaceURI", 'http://www.w3.org/2000/svg');
+  shouldBeEqualToString("svg.firstChild.firstChild.namespaceURI", 'http://www.w3.org/1999/xhtml');
+
+  svg.outerHTML = "<svg><circle/></svg>";
+  var svg = document.getElementsByTagName('svg')[0];
+  shouldBeEqualToString("svg.innerHTML", '<circle></circle>');
+  shouldBeEqualToString("svg.firstChild.namespaceURI", 'http://www.w3.org/2000/svg');
+</script>
+<script src="../js/resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/innerHTML/innerHTML-template-crash-expected.xhtml b/LayoutTests/fast/innerHTML/innerHTML-template-crash-expected.xhtml
new file mode 100644
index 0000000..64c4e65
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-template-crash-expected.xhtml
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <body>
+    <p>This test passes if it does not CRASH.</p>
+  </body>
+</html>
diff --git a/LayoutTests/fast/innerHTML/innerHTML-template-crash.xhtml b/LayoutTests/fast/innerHTML/innerHTML-template-crash.xhtml
new file mode 100644
index 0000000..2f0d5a6
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-template-crash.xhtml
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <body>
+    <template xmlns="http://test/foo"></template>
+    <script>
+      document.querySelector('template').innerHTML = "<p>Lorem Ipsum</p>";
+    </script>
+    <p>This test passes if it does not CRASH.</p>
+  </body>
+</html>
diff --git a/LayoutTests/fast/innerHTML/innerHTML-xml-expected.txt b/LayoutTests/fast/innerHTML/innerHTML-xml-expected.txt
new file mode 100644
index 0000000..a8be8ab
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-xml-expected.txt
@@ -0,0 +1,25 @@
+Test innerHTML in XHTML/XML documents
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+FAIL element.firstChild.namespaceURI should be http://www.w3.org/2000/svg. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+FAIL element.firstChild.namespaceURI should be http://www.w3.org/2000/svg. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+FAIL element.firstChild.namespaceURI should be http://www.w3.org/2000/svg. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://test/foo"
+FAIL element.firstChild.namespaceURI should be http://www.w3.org/2000/svg. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://test/foo"
+FAIL element.firstChild.namespaceURI should be http://test/foo. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+FAIL element.firstChild.namespaceURI should be http://test/foo. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://www.w3.org/2000/svg"
+FAIL element.firstChild.namespaceURI should be http://test/foo. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://test/foo"
+FAIL element.firstChild.namespaceURI should be http://test/foo. Was http://www.w3.org/1999/xhtml.
+PASS element.firstChild.namespaceURI is "http://test/foo"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/innerHTML/innerHTML-xml.xhtml b/LayoutTests/fast/innerHTML/innerHTML-xml.xhtml
new file mode 100644
index 0000000..56c027b
--- /dev/null
+++ b/LayoutTests/fast/innerHTML/innerHTML-xml.xhtml
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <script src="../js/resources/js-test-pre.js"></script>
+    <style>
+    </style>
+  </head>
+  <body>
+    <svg xmlns="http://www.w3.org/2000/svg"></svg>
+    <bar xmlns="http://test/foo"></bar>
+    <script>
+    description("Test innerHTML in XHTML/XML documents");
+
+    var namespaces = [
+        "http://www.w3.org/2000/svg",
+        "http://test/foo"
+    ];
+    var elementnames = [
+        "svg",
+        "bar"
+    ];
+    var texts = [
+        "&lt;g/>",
+        "&lt;body>&lt;/body>"
+
+    ];
+
+    var element, childns;
+    function g(ns, elementName, text, appendOrSelect) {
+        if (appendOrSelect == "append") {
+            element = document.createElementNS(ns, elementName);
+            document.body.appendChild(element);
+            childns = ns;
+        } else {
+            element = document.querySelector(elementName);
+            childns = element.namespaceURI;
+        }
+        element.innerHTML = text;
+        shouldBeEqualToString("element.firstChild.namespaceURI", childns);
+    }
+
+    namespaces.forEach(function(ns) {
+        elementnames.forEach(function(elementname) {
+            texts.forEach(function(text) {
+                g(ns, elementname, text, "append");
+                g(ns, elementname, text, "select");
+            });
+        });
+    });
+    </script>
+    <script src="../js/resources/js-test-post.js"></script>
+  </body>
+</html>
diff --git a/LayoutTests/fast/loader/delete-inside-cancelTimer-expected.txt b/LayoutTests/fast/loader/delete-inside-cancelTimer-expected.txt
index 9d572e6..2ec41cc 100644
--- a/LayoutTests/fast/loader/delete-inside-cancelTimer-expected.txt
+++ b/LayoutTests/fast/loader/delete-inside-cancelTimer-expected.txt
@@ -1,5 +1,3 @@
-CONSOLE ERROR: line 62: Uncaught NoModificationAllowedError: An attempt was made to modify an object where modifications are not allowed.
-CONSOLE ERROR: line 62: Uncaught NoModificationAllowedError: An attempt was made to modify an object where modifications are not allowed.
 %%%%%
  
 
@@ -9,5 +7,3 @@
 %%
 
 
-
-%%
diff --git a/LayoutTests/svg/css/script-tests/svg-attribute-length-parsing.js b/LayoutTests/svg/css/script-tests/svg-attribute-length-parsing.js
index 7b99d74..bdd14fa 100644
--- a/LayoutTests/svg/css/script-tests/svg-attribute-length-parsing.js
+++ b/LayoutTests/svg/css/script-tests/svg-attribute-length-parsing.js
@@ -8,6 +8,7 @@
 text.setAttribute("id", "text");
 text.setAttribute("x", "20px");
 text.setAttribute("y", "100px");
+text.setAttribute("visibility", "hidden");
 text.innerHTML = "Test";
 rootSVGElement.appendChild(text);
 
diff --git a/Source/core/dom/Element.cpp b/Source/core/dom/Element.cpp
index 41c734a..1602593 100644
--- a/Source/core/dom/Element.cpp
+++ b/Source/core/dom/Element.cpp
@@ -72,6 +72,7 @@
 #include "core/editing/FrameSelection.h"
 #include "core/editing/TextIterator.h"
 #include "core/editing/htmlediting.h"
+#include "core/editing/markup.h"
 #include "core/events/EventDispatcher.h"
 #include "core/events/FocusEvent.h"
 #include "core/frame/ContentSecurityPolicy.h"
@@ -86,6 +87,7 @@
 #include "core/html/HTMLLabelElement.h"
 #include "core/html/HTMLOptionsCollection.h"
 #include "core/html/HTMLTableRowsCollection.h"
+#include "core/html/HTMLTemplateElement.h"
 #include "core/html/parser/HTMLParserIdioms.h"
 #include "core/page/FocusController.h"
 #include "core/page/Page.h"
@@ -2202,6 +2204,50 @@
     dispatchScopedEventDispatchMediator(FocusOutEventDispatchMediator::create(FocusEvent::create(eventType, true, false, document().domWindow(), 0, newFocusedElement)));
 }
 
+String Element::innerHTML() const
+{
+    return createMarkup(this, ChildrenOnly);
+}
+
+String Element::outerHTML() const
+{
+    return createMarkup(this);
+}
+
+void Element::setInnerHTML(const String& html, ExceptionState& es)
+{
+    if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, this, AllowScriptingContent, "innerHTML", es)) {
+        ContainerNode* container = this;
+        if (hasTagName(templateTag))
+            container = toHTMLTemplateElement(this)->content();
+        replaceChildrenWithFragment(container, fragment.release(), es);
+    }
+}
+
+void Element::setOuterHTML(const String& html, ExceptionState& es)
+{
+    Node* p = parentNode();
+    if (!p || !p->isElementNode()) {
+        es.throwUninformativeAndGenericDOMException(NoModificationAllowedError);
+        return;
+    }
+    RefPtr<Element> parent = toElement(p);
+    RefPtr<Node> prev = previousSibling();
+    RefPtr<Node> next = nextSibling();
+
+    RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, parent.get(), AllowScriptingContent, "outerHTML", es);
+    if (es.hadException())
+        return;
+
+    parent->replaceChild(fragment.release(), this, es);
+    RefPtr<Node> node = next ? next->previousSibling() : 0;
+    if (!es.hadException() && node && node->isTextNode())
+        mergeWithNextTextNode(node.release(), es);
+
+    if (!es.hadException() && prev && prev->isTextNode())
+        mergeWithNextTextNode(prev.release(), es);
+}
+
 String Element::innerText()
 {
     // We need to update layout, since plainText uses line boxes in the render tree.
diff --git a/Source/core/dom/Element.h b/Source/core/dom/Element.h
index c35fe66..88a674b 100644
--- a/Source/core/dom/Element.h
+++ b/Source/core/dom/Element.h
@@ -82,6 +82,11 @@
     static PassRefPtr<Element> create(const QualifiedName&, Document*);
     virtual ~Element();
 
+    String innerHTML() const;
+    String outerHTML() const;
+    void setInnerHTML(const String&, ExceptionState&);
+    void setOuterHTML(const String&, ExceptionState&);
+
     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecopy);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforecut);
     DEFINE_ATTRIBUTE_EVENT_LISTENER(beforepaste);
diff --git a/Source/core/dom/Element.idl b/Source/core/dom/Element.idl
index 8d45e7d..3a8d2ba 100644
--- a/Source/core/dom/Element.idl
+++ b/Source/core/dom/Element.idl
@@ -92,6 +92,8 @@
 
     // HTML 5
     NodeList getElementsByClassName([Default=Undefined] optional DOMString name);
+    [TreatNullAs=NullString, CustomElementCallbacks, PerWorldBindings, ActivityLogging=SetterForIsolatedWorlds, SetterRaisesException] attribute DOMString innerHTML;
+    [TreatNullAs=NullString, CustomElementCallbacks, SetterRaisesException] attribute DOMString outerHTML;
 
     [Reflect=class, TreatNullAs=NullString, PerWorldBindings] attribute DOMString className;
     [PerWorldBindings] readonly attribute DOMTokenList classList;
diff --git a/Source/core/editing/markup.cpp b/Source/core/editing/markup.cpp
index 1d7b8b4..af001a8 100644
--- a/Source/core/editing/markup.cpp
+++ b/Source/core/editing/markup.cpp
@@ -1075,4 +1075,18 @@
     containerNode->appendChild(textNode.release(), es);
 }
 
+void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionState& es)
+{
+    ASSERT(node && node->isTextNode());
+    Node* next = node->nextSibling();
+    if (!next || !next->isTextNode())
+        return;
+
+    RefPtr<Text> textNode = toText(node.get());
+    RefPtr<Text> textNext = toText(next);
+    textNode->appendData(textNext->data());
+    if (textNext->parentNode()) // Might have been removed by mutation event.
+        textNext->remove(es);
+}
+
 }
diff --git a/Source/core/editing/markup.h b/Source/core/editing/markup.h
index 00e17b3..f122092 100644
--- a/Source/core/editing/markup.h
+++ b/Source/core/editing/markup.h
@@ -69,6 +69,7 @@
 String createFullMarkup(const Range*);
 
 String urlToMarkup(const KURL&, const String& title);
+void mergeWithNextTextNode(PassRefPtr<Node>, ExceptionState&);
 
 }
 
diff --git a/Source/core/html/HTMLElement.cpp b/Source/core/html/HTMLElement.cpp
index c19dcfb..2500b8a 100644
--- a/Source/core/html/HTMLElement.cpp
+++ b/Source/core/html/HTMLElement.cpp
@@ -325,64 +325,6 @@
     }
 }
 
-String HTMLElement::innerHTML() const
-{
-    return createMarkup(this, ChildrenOnly);
-}
-
-String HTMLElement::outerHTML() const
-{
-    return createMarkup(this);
-}
-
-void HTMLElement::setInnerHTML(const String& html, ExceptionState& es)
-{
-    if (RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, this, AllowScriptingContent, "innerHTML", es)) {
-        ContainerNode* container = this;
-        if (hasLocalName(templateTag))
-            container = toHTMLTemplateElement(this)->content();
-        replaceChildrenWithFragment(container, fragment.release(), es);
-    }
-}
-
-static void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionState& es)
-{
-    ASSERT(node && node->isTextNode());
-    Node* next = node->nextSibling();
-    if (!next || !next->isTextNode())
-        return;
-
-    RefPtr<Text> textNode = toText(node.get());
-    RefPtr<Text> textNext = toText(next);
-    textNode->appendData(textNext->data());
-    if (textNext->parentNode()) // Might have been removed by mutation event.
-        textNext->remove(es);
-}
-
-void HTMLElement::setOuterHTML(const String& html, ExceptionState& es)
-{
-    Node* p = parentNode();
-    if (!p || !p->isHTMLElement()) {
-        es.throwUninformativeAndGenericDOMException(NoModificationAllowedError);
-        return;
-    }
-    RefPtr<HTMLElement> parent = toHTMLElement(p);
-    RefPtr<Node> prev = previousSibling();
-    RefPtr<Node> next = nextSibling();
-
-    RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(html, parent.get(), AllowScriptingContent, "outerHTML", es);
-    if (es.hadException())
-        return;
-
-    parent->replaceChild(fragment.release(), this, es);
-    RefPtr<Node> node = next ? next->previousSibling() : 0;
-    if (!es.hadException() && node && node->isTextNode())
-        mergeWithNextTextNode(node.release(), es);
-
-    if (!es.hadException() && prev && prev->isTextNode())
-        mergeWithNextTextNode(prev.release(), es);
-}
-
 PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionState& es)
 {
     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document());
diff --git a/Source/core/html/HTMLElement.h b/Source/core/html/HTMLElement.h
index e58a25e..3ce2ffe 100644
--- a/Source/core/html/HTMLElement.h
+++ b/Source/core/html/HTMLElement.h
@@ -47,10 +47,6 @@
     virtual short tabIndex() const;
     void setTabIndex(int);
 
-    String innerHTML() const;
-    String outerHTML() const;
-    void setInnerHTML(const String&, ExceptionState&);
-    void setOuterHTML(const String&, ExceptionState&);
     void setInnerText(const String&, ExceptionState&);
     void setOuterText(const String&, ExceptionState&);
 
diff --git a/Source/core/html/HTMLElement.idl b/Source/core/html/HTMLElement.idl
index efc9ceb..c5640d5 100644
--- a/Source/core/html/HTMLElement.idl
+++ b/Source/core/html/HTMLElement.idl
@@ -33,9 +33,7 @@
     [Reflect, TreatNullAs=NullString] attribute DOMString accessKey;
 
     // Extensions
-    [TreatNullAs=NullString, CustomElementCallbacks, PerWorldBindings, ActivityLogging=SetterForIsolatedWorlds, SetterRaisesException] attribute DOMString innerHTML;
     [TreatNullAs=NullString, CustomElementCallbacks, SetterRaisesException] attribute DOMString innerText;
-    [TreatNullAs=NullString, CustomElementCallbacks, SetterRaisesException] attribute DOMString outerHTML;
     [TreatNullAs=NullString, CustomElementCallbacks, SetterRaisesException] attribute DOMString outerText;
 
     [RaisesException, CustomElementCallbacks, MeasureAs=InsertAdjacentElement] Element insertAdjacentElement([Default=Undefined] optional DOMString where,
diff --git a/Source/core/html/parser/HTMLTreeBuilder.cpp b/Source/core/html/parser/HTMLTreeBuilder.cpp
index 4caa70c..f419b65 100644
--- a/Source/core/html/parser/HTMLTreeBuilder.cpp
+++ b/Source/core/html/parser/HTMLTreeBuilder.cpp
@@ -375,10 +375,13 @@
         processToken(token);
 
     if (m_parser->tokenizer()) {
-        bool inForeignContent = !m_tree.isEmpty()
-            && !m_tree.currentStackItem()->isInHTMLNamespace()
-            && !HTMLElementStack::isHTMLIntegrationPoint(m_tree.currentStackItem())
-            && !HTMLElementStack::isMathMLTextIntegrationPoint(m_tree.currentStackItem());
+        bool inForeignContent = false;
+        if (!m_tree.isEmpty()) {
+            RefPtr<HTMLStackItem> adjustedCurrentNode = adjustedCurrentStackItem();
+            inForeignContent = !adjustedCurrentNode->isInHTMLNamespace()
+                && !HTMLElementStack::isHTMLIntegrationPoint(adjustedCurrentNode.get())
+                && !HTMLElementStack::isMathMLTextIntegrationPoint(adjustedCurrentNode.get());
+        }
 
         m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || inForeignContent);
         m_parser->tokenizer()->setShouldAllowCDATA(inForeignContent);
@@ -995,6 +998,16 @@
     return true;
 }
 
+// http://www.whatwg.org/specs/web-apps/current-work/#adjusted-current-node
+PassRefPtr<HTMLStackItem> HTMLTreeBuilder::adjustedCurrentStackItem() const
+{
+    ASSERT(!m_tree.isEmpty());
+    if (isParsingFragment() && m_tree.openElements()->hasOnlyOneElement())
+        return HTMLStackItem::create(m_fragmentContext.contextElement(), HTMLStackItem::ItemForContextElement);
+
+    return m_tree.currentStackItem();
+}
+
 // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell
 void HTMLTreeBuilder::closeTheCell()
 {
@@ -2675,10 +2688,11 @@
 {
     if (m_tree.isEmpty())
         return false;
-    HTMLStackItem* item = m_tree.currentStackItem();
-    if (item->isInHTMLNamespace())
+    RefPtr<HTMLStackItem> adjustedCurrentNode = adjustedCurrentStackItem();
+
+    if (adjustedCurrentNode->isInHTMLNamespace())
         return false;
-    if (HTMLElementStack::isMathMLTextIntegrationPoint(item)) {
+    if (HTMLElementStack::isMathMLTextIntegrationPoint(adjustedCurrentNode.get())) {
         if (token->type() == HTMLToken::StartTag
             && token->name() != MathMLNames::mglyphTag
             && token->name() != MathMLNames::malignmarkTag)
@@ -2686,11 +2700,11 @@
         if (token->type() == HTMLToken::Character)
             return false;
     }
-    if (item->hasTagName(MathMLNames::annotation_xmlTag)
+    if (adjustedCurrentNode->hasTagName(MathMLNames::annotation_xmlTag)
         && token->type() == HTMLToken::StartTag
         && token->name() == SVGNames::svgTag)
         return false;
-    if (HTMLElementStack::isHTMLIntegrationPoint(item)) {
+    if (HTMLElementStack::isHTMLIntegrationPoint(adjustedCurrentNode.get())) {
         if (token->type() == HTMLToken::StartTag)
             return false;
         if (token->type() == HTMLToken::Character)
@@ -2703,6 +2717,8 @@
 
 void HTMLTreeBuilder::processTokenInForeignContent(AtomicHTMLToken* token)
 {
+    RefPtr<HTMLStackItem> adjustedCurrentNode = adjustedCurrentStackItem();
+
     switch (token->type()) {
     case HTMLToken::Uninitialized:
         ASSERT_NOT_REACHED();
@@ -2756,7 +2772,7 @@
             processStartTag(token);
             return;
         }
-        const AtomicString& currentNamespace = m_tree.currentStackItem()->namespaceURI();
+        const AtomicString& currentNamespace = adjustedCurrentNode->namespaceURI();
         if (currentNamespace == MathMLNames::mathmlNamespaceURI)
             adjustMathMLAttributes(token);
         if (currentNamespace == SVGNames::svgNamespaceURI) {
@@ -2768,7 +2784,7 @@
         break;
     }
     case HTMLToken::EndTag: {
-        if (m_tree.currentStackItem()->namespaceURI() == SVGNames::svgNamespaceURI)
+        if (adjustedCurrentNode->namespaceURI() == SVGNames::svgNamespaceURI)
             adjustSVGTagNameCase(token);
 
         if (token->name() == SVGNames::scriptTag && m_tree.currentStackItem()->hasTagName(SVGNames::scriptTag)) {
diff --git a/Source/core/html/parser/HTMLTreeBuilder.h b/Source/core/html/parser/HTMLTreeBuilder.h
index c434d56..2c05af5 100644
--- a/Source/core/html/parser/HTMLTreeBuilder.h
+++ b/Source/core/html/parser/HTMLTreeBuilder.h
@@ -166,6 +166,7 @@
     void defaultForAfterHead();
     void defaultForInTableText();
 
+    inline PassRefPtr<HTMLStackItem> adjustedCurrentStackItem() const;
     inline bool shouldProcessTokenInForeignContent(AtomicHTMLToken*);
     void processTokenInForeignContent(AtomicHTMLToken*);