diff --git a/lib/mixins/template-stamp.js b/lib/mixins/template-stamp.js
index e7a576ec2a..e9e4a16da7 100644
--- a/lib/mixins/template-stamp.js
+++ b/lib/mixins/template-stamp.js
@@ -22,6 +22,53 @@ const templateExtensions = {
'dom-if': true,
'dom-repeat': true
};
+
+let placeholderBugDetect = false;
+let placeholderBug = false;
+
+function hasPlaceholderBug() {
+ if (!placeholderBugDetect) {
+ placeholderBugDetect = true;
+ const t = document.createElement('textarea');
+ t.placeholder = 'a';
+ placeholderBug = t.placeholder === t.textContent;
+ }
+ return placeholderBug;
+}
+
+/**
+ * Some browsers have a bug with textarea, where placeholder text is copied as
+ * a textnode child of the textarea.
+ *
+ * If the placeholder is a binding, this can break template stamping in two
+ * ways.
+ *
+ * One issue is that when the `placeholder` attribute is removed when the
+ * binding is processed, the textnode child of the textarea is deleted, and the
+ * template info tries to bind into that node.
+ *
+ * With `legacyOptimizations` in use, when the template is stamped and the
+ * `textarea.textContent` binding is processed, no corresponding node is found
+ * because it was removed during parsing. An exception is generated when this
+ * binding is updated.
+ *
+ * With `legacyOptimizations` not in use, the template is cloned before
+ * processing and this changes the above behavior. The cloned template also has
+ * a value property set to the placeholder and textContent. This prevents the
+ * removal of the textContent when the placeholder attribute is removed.
+ * Therefore the exception does not occur. However, there is an extra
+ * unnecessary binding.
+ *
+ * @param {!Node} node Check node for placeholder bug
+ * @return {void}
+ */
+function fixPlaceholder(node) {
+ if (hasPlaceholderBug() && node.localName === 'textarea' && node.placeholder
+ && node.placeholder === node.textContent) {
+ node.textContent = null;
+ }
+}
+
function wrapTemplateExtension(node) {
let is = node.getAttribute('is');
if (is && templateExtensions[is]) {
@@ -251,6 +298,7 @@ export const TemplateStamp = dedupingMixin(
// For ShadyDom optimization, indicating there is an insertion point
templateInfo.hasInsertionPoint = true;
}
+ fixPlaceholder(node);
if (element.firstChild) {
this._parseTemplateChildNodes(element, templateInfo, nodeInfo);
}
diff --git a/test/unit/property-effects-template.html b/test/unit/property-effects-template.html
index 2f761f672a..56327de127 100644
--- a/test/unit/property-effects-template.html
+++ b/test/unit/property-effects-template.html
@@ -13,7 +13,6 @@
-
@@ -282,9 +281,10 @@