[BUGFIX] RTE: Correct behavior on empty textfield in Firefox 50/49150/8
authorAndreas Fernandez <a.fernandez@scripting-base.de>
Thu, 21 Jul 2016 12:14:31 +0000 (14:14 +0200)
committerJigal van Hemert <jigal.van.hemert@typo3.org>
Tue, 26 Jul 2016 14:35:56 +0000 (16:35 +0200)
With Firefox, the RTE produces several JavaScript errors if the textfield
is empty. The issue is that on Firefox it's tried to access the parent
node of the html tag which is nonsense.

Some regular expressions and an additional 'undefined' check are added
to fix this wrong behavior.

Resolves: #77191
Releases: master, 7.6
Change-Id: I617bdb6415c0468894aee08101c9d26cb790a077
Reviewed-on: https://review.typo3.org/49150
Reviewed-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Wouter Wolters <typo3@wouterwolters.nl>
Tested-by: Bamboo TYPO3com <info@typo3.com>
Tested-by: Riccardo De Contardi <erredeco@gmail.com>
Reviewed-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
Tested-by: Jigal van Hemert <jigal.van.hemert@typo3.org>
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/DOM.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/HTMLArea/DOM/Selection.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/BlockElements.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/DefinitionList.js
typo3/sysext/rtehtmlarea/Resources/Public/JavaScript/Plugins/MicrodataSchema.js

index 74a282a..733c617 100644 (file)
@@ -250,7 +250,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                getBlockAncestors: function (node, withinBlock) {
                        var ancestors = [];
                        var ancestor = node;
-                       while (ancestor && (ancestor.nodeType === Dom.ELEMENT_NODE) && !/^(body)$/i.test(ancestor.nodeName) && ancestor != withinBlock) {
+                       while (ancestor && (ancestor.nodeType === Dom.ELEMENT_NODE) && !/^(html|body)$/i.test(ancestor.nodeName) && ancestor != withinBlock) {
                                if (Dom.isBlockElement(ancestor)) {
                                        ancestors.unshift(ancestor);
                                }
@@ -277,7 +277,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                        // Is types a non-empty array?
                        if (types && Object.prototype.toString.call(types) === '[object Array]' && types.length > 0) {
                                types = new RegExp( '^(' + types.join('|') + ')$', 'i');
-                               while (parent && parent.nodeType === Dom.ELEMENT_NODE && !/^(body)$/i.test(parent.nodeName)) {
+                               while (parent && parent.nodeType === Dom.ELEMENT_NODE && !/^(html|body)$/i.test(parent.nodeName)) {
                                        if (types.test(parent.nodeName)) {
                                                ancestor = parent;
                                                break;
index 43d8953..b834f49 100644 (file)
@@ -250,7 +250,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                                this.selection.setBaseAndExtent(node, 0, node, 1);
                        } else {
                                var range = this.document.createRange();
-                               if (node.nodeType === Dom.ELEMENT_NODE && /^(body)$/i.test(node.nodeName)) {
+                               if (node.nodeType === Dom.ELEMENT_NODE && /^(html|body)$/i.test(node.nodeName)) {
                                        if (UserAgent.isWebKit) {
                                                range.setStart(node, 0);
                                                range.setEnd(node, node.childNodes.length);
@@ -367,7 +367,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
        Selection.prototype.getAllAncestors = function () {
                var parent = this.getParentElement(),
                        ancestors = [];
-               while (parent && parent.nodeType === Dom.ELEMENT_NODE && !/^(body)$/i.test(parent.nodeName)) {
+               while (parent && parent.nodeType === Dom.ELEMENT_NODE && !/^(html|body)$/i.test(parent.nodeName)) {
                        ancestors.push(parent);
                        parent = parent.parentNode;
                }
@@ -451,11 +451,11 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                        parentStart,
                        parentEnd;
                parentStart = range.startContainer;
-               if (/^(body)$/i.test(parentStart.nodeName)) {
+               if (/^(html|body)$/i.test(parentStart.nodeName)) {
                        parentStart = parentStart.firstChild;
                }
                parentEnd = range.endContainer;
-               if (/^(body)$/i.test(parentEnd.nodeName)) {
+               if (/^(html|body)$/i.test(parentEnd.nodeName)) {
                        parentEnd = parentEnd.lastChild;
                }
                while (parentStart && !Dom.isBlockElement(parentStart)) {
@@ -583,7 +583,6 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
         * @return      boolean         true to stop the event and cancel the default action
         */
        Selection.prototype.handleBackSpace = function () {
-               var range = this.createRange();
                var self = this;
                window.setTimeout(function() {
                        var range = self.createRange();
@@ -592,9 +591,9 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                        // If the selection is collapsed...
                        if (self.isEmpty()) {
                                // ... and the cursor lies in a direct child of body...
-                               if (/^(body)$/i.test(startContainer.nodeName)) {
+                               if (/^(html|body)$/i.test(startContainer.nodeName)) {
                                        var node = startContainer.childNodes[startOffset-1];
-                               } else if (/^(body)$/i.test(startContainer.parentNode.nodeName)) {
+                               } else if (/^(html|body)$/i.test(startContainer.parentNode.nodeName)) {
                                        var node = startContainer;
                                // ... or, in Google, a span tag may have been inserted inside a heading element
                                } else if (UserAgent.isWebKit && /^(#text)$/i.test(startContainer.nodeName)) {
@@ -608,39 +607,41 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/UserAgent/UserAgent',
                                } else {
                                        return false;
                                }
-                               // ... which is a br or text node containing no non-whitespace character...
-                               node.normalize();
-                               if (/^(br|#text)$/i.test(node.nodeName) && !/\S/.test(node.textContent)) {
-                                       // Get a meaningful previous sibling in which to reposition de cursor
-                                       var previousSibling = node.previousSibling;
-                                       while (previousSibling && /^(br|#text)$/i.test(previousSibling.nodeName) && !/\S/.test(previousSibling.textContent)) {
-                                               previousSibling = previousSibling.previousSibling;
-                                       }
-                                       // If there is no meaningful previous sibling, the cursor is at the start of body or the start of a direct child of body
-                                       if (previousSibling) {
-                                               // Remove the node
-                                               Dom.removeFromParent(node);
-                                               // Position the cursor
-                                               if (/^(ol|ul|dl)$/i.test(previousSibling.nodeName)) {
-                                                       self.selectNodeContents(previousSibling.lastChild, false);
-                                               } else if (/^(table)$/i.test(previousSibling.nodeName)) {
-                                                       self.selectNodeContents(previousSibling.rows[previousSibling.rows.length-1].cells[previousSibling.rows[previousSibling.rows.length-1].cells.length-1], false);
-                                               } else if (!/\S/.test(previousSibling.textContent) && previousSibling.firstChild) {
-                                                       self.selectNode(previousSibling.firstChild, true);
-                                               } else {
-                                                       self.selectNodeContents(previousSibling, false);
+                               if (typeof node !== 'undefined') {
+                                       // ... which is a br or text node containing no non-whitespace character...
+                                       node.normalize();
+                                       if (/^(br|#text)$/i.test(node.nodeName) && !/\S/.test(node.textContent)) {
+                                               // Get a meaningful previous sibling in which to reposition de cursor
+                                               var previousSibling = node.previousSibling;
+                                               while (previousSibling && /^(br|#text)$/i.test(previousSibling.nodeName) && !/\S/.test(previousSibling.textContent)) {
+                                                       previousSibling = previousSibling.previousSibling;
                                                }
-                                       }
-                               // ... or the only child of body and having no child (IE) or only a br child (FF)
-                               } else if (
-                                               /^(body)$/i.test(node.parentNode.nodeName)
+                                               // If there is no meaningful previous sibling, the cursor is at the start of body or the start of a direct child of body
+                                               if (previousSibling) {
+                                                       // Remove the node
+                                                       Dom.removeFromParent(node);
+                                                       // Position the cursor
+                                                       if (/^(ol|ul|dl)$/i.test(previousSibling.nodeName)) {
+                                                               self.selectNodeContents(previousSibling.lastChild, false);
+                                                       } else if (/^(table)$/i.test(previousSibling.nodeName)) {
+                                                               self.selectNodeContents(previousSibling.rows[previousSibling.rows.length - 1].cells[previousSibling.rows[previousSibling.rows.length - 1].cells.length - 1], false);
+                                                       } else if (!/\S/.test(previousSibling.textContent) && previousSibling.firstChild) {
+                                                               self.selectNode(previousSibling.firstChild, true);
+                                                       } else {
+                                                               self.selectNodeContents(previousSibling, false);
+                                                       }
+                                               }
+                                               // ... or the only child of body and having no child (IE) or only a br child (FF)
+                                       } else if (
+                                               /^(html|body)$/i.test(node.parentNode.nodeName)
                                                && !/\S/.test(node.parentNode.textContent)
                                                && (node.childNodes.length === 0 || (node.childNodes.length === 1 && /^(br)$/i.test(node.firstChild.nodeName)))
                                        ) {
-                                       var parentNode = node.parentNode;
-                                       Dom.removeFromParent(node);
-                                       parentNode.innerHTML = '<br />';
-                                       self.selectNodeContents(parentNode, true);
+                                               var parentNode = node.parentNode;
+                                               Dom.removeFromParent(node);
+                                               parentNode.innerHTML = '<br />';
+                                               self.selectNodeContents(parentNode, true);
+                                       }
                                }
                        }
                }, 10);
index aeaa602..298eb2b 100644 (file)
@@ -587,7 +587,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        while (i < startAncestors.length && i < endAncestors.length && startAncestors[i] === endAncestors[i]) {
                                ++i;
                        }
-                       if ((endBlocks.start === endBlocks.end && /^(body)$/i.test(endBlocks.start.nodeName)) || !startAncestors[i] || !endAncestors[i]) {
+                       if ((endBlocks.start === endBlocks.end && /^(html|body)$/i.test(endBlocks.start.nodeName)) || !startAncestors[i] || !endAncestors[i]) {
                                --i;
                        }
                        if (keepValid) {
@@ -652,7 +652,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        if (endBlocks.start === endBlocks.end) {
                                --index;
                        }
-                       if (!/^(body)$/i.test(startAncestors[index].nodeName)) {
+                       if (!/^(html|body)$/i.test(startAncestors[index].nodeName)) {
                                for (var block = startAncestors[index]; block; block = block.nextSibling) {
                                        if (Dom.isBlockElement(block)) {
                                                switch (buttonId) {
@@ -705,7 +705,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                this.editor.getDomNode().removeMarkup(block);
                        }
                },
-       
+
                insertList: function (buttonId, parentElement) {
                        if (/^(dd)$/i.test(parentElement.nodeName)) {
                                var list = parentElement.appendChild(this.editor.document.createElement((buttonId === 'OrderedList') ? 'ol' : 'ul'));
@@ -753,7 +753,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                // which breaks the range
                        var range = this.editor.getBookMark().moveTo(bookmark);
                        bookmark = this.editor.getBookMark().get(range);
-       
+
                                // Check if the last element has children. If so, outdent those that do not intersect the selection
                        var last = indentedList.lastChild.lastChild;
                        if (last && /^(ol|ul)$/i.test(last.nodeName)) {
@@ -1037,8 +1037,8 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        if (mode === 'wysiwyg' && this.editor.isEditable()) {
                                var statusBarSelection = this.editor.statusBar ? this.editor.statusBar.getSelection() : null;
                                var parentElement = statusBarSelection ? statusBarSelection : this.editor.getSelection().getParentElement();
-                               if (!/^body$/i.test(parentElement.nodeName)) {
-                                       while (parentElement && !Dom.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName)) {
+                               if (!/^(html|body)$/i.test(parentElement.nodeName)) {
+                                       while (parentElement && (!Dom.isBlockElement(parentElement) || /^li$/i.test(parentElement.nodeName))) {
                                                parentElement = parentElement.parentNode;
                                        }
                                        var blockAncestors = Dom.getBlockAncestors(parentElement);
@@ -1081,7 +1081,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                                        break;
                                                case "InsertParagraphBefore" :
                                                case "InsertParagraphAfter"  :
-                                                       button.setDisabled(/^(body)$/i.test(startAncestors[index].nodeName));
+                                                       button.setDisabled(/^(html|body)$/i.test(startAncestors[index].nodeName));
                                                        break;
                                                case "Blockquote" :
                                                        for (var j = blockAncestors.length; --j >= 0;) {
@@ -1103,7 +1103,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                                                                        commandState = false;
                                                                }
                                                        } else {
-                                                               if (/^(body)$/i.test(startAncestors[index].nodeName)) {
+                                                               if (/^(html|body)$/i.test(startAncestors[index].nodeName)) {
                                                                        button.setDisabled(true);
                                                                } else {
                                                                        button.setDisabled(false);
index 597de06..3987160 100644 (file)
@@ -293,7 +293,7 @@ define(['TYPO3/CMS/Rtehtmlarea/HTMLArea/Plugin/Plugin',
                        if (mode === 'wysiwyg' && this.editor.isEditable()) {
                                var statusBarSelection = editor.statusBar ? editor.statusBar.getSelection() : null;
                                var parentElement = statusBarSelection ? statusBarSelection : editor.getSelection().getParentElement();
-                               if (!/^(body)$/i.test(parentElement.nodeName)) {
+                               if (!/^(html|body)$/i.test(parentElement.nodeName)) {
                                        var endBlocks = editor.getSelection().getEndBlocks();
                                        switch (button.itemId) {
                                                case 'Outdent':
index 9562ded..3bcf16b 100644 (file)
@@ -158,7 +158,7 @@ define([
                        var propertyStore = this.filteredProperties.length > 0 ? this.filteredProperties : this.itemprop;
                        this.inheritedType = 'none';
                        var parent = element.parentNode;
-                       while (parent && !/^(body)$/i.test(parent.nodeName)) {
+                       while (parent && !/^(html|body)$/i.test(parent.nodeName)) {
                                if (parent.getAttribute('itemtype')) {
                                        this.inheritedType = parent.getAttribute('itemtype');
                                        break;