From a94e2e738af9274a654f0ddd2bece4e0ac78014c Mon Sep 17 00:00:00 2001 From: Patrick Steele-Idem Date: Tue, 28 Mar 2017 14:58:32 -0600 Subject: [PATCH] Fixes #629 - [vdom] Rendering unescaped HTML produces non-functioning HTML input controls --- morphdom/index.js | 7 ++++++ morphdom/specialElHandlers.js | 2 +- runtime/vdom/VNode.js | 9 ++++++++ runtime/vdom/vdom.js | 4 +++- .../component-unescaped-html/index.marko | 10 ++++++++ .../component-unescaped-html/test.js | 23 +++++++++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/autotests/components-browser/component-unescaped-html/index.marko create mode 100644 test/autotests/components-browser/component-unescaped-html/test.js diff --git a/morphdom/index.js b/morphdom/index.js index 828145ef8..224f3b2ba 100644 --- a/morphdom/index.js +++ b/morphdom/index.js @@ -74,6 +74,13 @@ function morphdom( vCurChild = vCurChild.nextSibling; } + if (vEl.$__nodeType === 1) { + var elHandler = specialElHandlers[vEl.nodeName]; + if (elHandler !== undefined) { + elHandler(realEl, vEl); + } + } + return realEl; } diff --git a/morphdom/specialElHandlers.js b/morphdom/specialElHandlers.js index d10a1485b..79608a331 100644 --- a/morphdom/specialElHandlers.js +++ b/morphdom/specialElHandlers.js @@ -61,7 +61,7 @@ module.exports = { var i = 0; var curChild = toEl.firstChild; while(curChild) { - if (curChild.nodeName == 'OPTION') { + if (curChild.$__nodeName == 'OPTION') { if (curChild.$__hasAttribute('selected')) { selectedIndex = i; break; diff --git a/runtime/vdom/VNode.js b/runtime/vdom/VNode.js index 298c0904a..17da75112 100644 --- a/runtime/vdom/VNode.js +++ b/runtime/vdom/VNode.js @@ -1,4 +1,6 @@ /* jshint newcap:false */ +var specialElHandlers = require('../../morphdom/specialElHandlers'); + function VNode() {} VNode.prototype = { @@ -89,6 +91,13 @@ VNode.prototype = { curChild = curChild.nextSibling; } + if (this.$__nodeType === 1) { + var elHandler = specialElHandlers[this.$__nodeName]; + if (elHandler !== undefined) { + elHandler(actualNode, this); + } + } + return actualNode; } diff --git a/runtime/vdom/vdom.js b/runtime/vdom/vdom.js index 1a97cd762..135109ff4 100644 --- a/runtime/vdom/vdom.js +++ b/runtime/vdom/vdom.js @@ -44,7 +44,9 @@ function virtualize(node) { } var vdomEl = new VElement(tagName, attrs, null, flags); - vdomEl.$__namespaceURI = node.namespaceURI; + if (node.namespaceURI !== 'http://www.w3.org/1999/xhtml') { + vdomEl.$__namespaceURI = node.namespaceURI; + } if (vdomEl.$__isTextArea) { vdomEl.$__value = node.value; diff --git a/test/autotests/components-browser/component-unescaped-html/index.marko b/test/autotests/components-browser/component-unescaped-html/index.marko new file mode 100644 index 000000000..3e7b334ab --- /dev/null +++ b/test/autotests/components-browser/component-unescaped-html/index.marko @@ -0,0 +1,10 @@ +class { + + onCreate() { + this.state = { + html: "" + } + } +} + +
$!{state.html}
diff --git a/test/autotests/components-browser/component-unescaped-html/test.js b/test/autotests/components-browser/component-unescaped-html/test.js new file mode 100644 index 000000000..7804fc0e2 --- /dev/null +++ b/test/autotests/components-browser/component-unescaped-html/test.js @@ -0,0 +1,23 @@ +var expect = require('chai').expect; + +module.exports = function(helpers) { + var component = helpers.mount(require('./index'), { }); + + expect(component.el.childNodes.length).to.equal(1); + expect(component.el.firstChild.nodeName).to.equal('INPUT'); + expect(component.el.firstChild.value).to.equal('x'); + + component.state.html = ''; + component.update(); + + expect(component.el.childNodes.length).to.equal(1); + expect(component.el.firstChild.nodeName).to.equal('TEXTAREA'); + expect(component.el.firstChild.value).to.equal('HELLO WORLD!'); + + component.state.html = ''; + component.update(); + + expect(component.el.childNodes.length).to.equal(1); + expect(component.el.firstChild.nodeName).to.equal('SELECT'); + expect(component.el.firstChild.selectedIndex).to.equal(1); +};