* Fixed bug #7851: htmlArea RTE causes memory leaks in IE
authorStanislas Rolland <typo3@sjbr.ca>
Thu, 13 Mar 2008 23:34:20 +0000 (23:34 +0000)
committerStanislas Rolland <typo3@sjbr.ca>
Thu, 13 Mar 2008 23:34:20 +0000 (23:34 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@3419 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
typo3/sysext/rtehtmlarea/ChangeLog
typo3/sysext/rtehtmlarea/htmlarea/htmlarea-ie.js
typo3/sysext/rtehtmlarea/htmlarea/htmlarea.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/DefaultClean/default-clean.js
typo3/sysext/rtehtmlarea/htmlarea/plugins/TYPO3HtmlParser/typo3html-parser.js

index bc64051..489e62e 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-03-13  Stanislas Rolland  <typo3@sjbr.ca>
+
+       * Fixed bug #7851: htmlArea RTE causes memory leaks in IE
+
 2008-03-12  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Fixed bug #6064 (revisited): htmlArea RTE adds unwanted <p> tags in table cells
index 3b065d1..f0bb894 100644 (file)
@@ -1,3 +1,7 @@
+2008-03-13  Stanislas Rolland  <typo3@sjbr.ca>
+
+       * Fixed bug #7851: htmlArea RTE causes memory leaks in IE
+
 2008-03-12  Stanislas Rolland  <typo3@sjbr.ca>
 
        * Fixed bug #6064 (revisited): htmlArea RTE adds unwanted <p> tags in table cells
index 35f9c76..992bb28 100644 (file)
@@ -3,7 +3,7 @@
 *
 *  (c) 2002-2004, interactivetools.com, inc.
 *  (c) 2003-2004 dynarch.com
-*  (c) 2004-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+*  (c) 2004-2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -39,74 +39,6 @@ HTMLArea.prototype.isEditable = function() {
        return this._doc.body.contentEditable;
 };
 
-/***************************************************
- *  FINAL IE CLEANUP
- ***************************************************/
- HTMLArea._cleanup = function (editor) {
-               // nullify envent handlers
-       for (var handler in editor.eventHandlers) {
-               if (editor.eventHandlers.hasOwnProperty(handler)) {
-                       editor.eventHandlers[handler] = null;
-               }
-       }
-       for (var button in editor.btnList) {
-               if (editor.btnList.hasOwnProperty(button)) {
-                       editor.btnList[button][3] = null;
-               }
-       }
-       for (var dropdown in editor.config.customSelects) {
-               if (editor.config.customSelects.hasOwnProperty(dropdown)) {
-                       editor.config.customSelects[dropdown].action = null;
-                       editor.config.customSelects[dropdown].refresh = null;
-               }
-       }
-       editor.onGenerate = null;
-       HTMLArea._editorEvent = null;
-       if(editor._textArea.form) {
-               editor._textArea.form.__msh_prevOnReset = null;
-               editor._textArea.form._editorNumber = null;
-       }
-       HTMLArea.onload = null;
-       if(HTMLArea._eventCache) {
-               HTMLArea._eventCache.listEvents = null;
-               HTMLArea._eventCache.add = null;
-               HTMLArea._eventCache.flush = null;
-               HTMLArea._eventCache = null;
-       }
-       
-               // cleaning plugin handlers
-       for (var plugin in editor.plugins) {
-               if (editor.plugins.hasOwnProperty(plugin)) {
-                       var pluginInstance = editor.plugins[plugin].instance;
-                       pluginInstance.onGenerate = null;
-                       pluginInstance.onMode = null;
-                       pluginInstance.onKeyPress = null;
-                       pluginInstance.onSelect = null;
-                       pluginInstance.onUpdateTolbar = null;
-               }
-       }
-       
-               // cleaning the toolbar elements
-       for (var txt in editor._toolbarObjects) {
-               if (editor._toolbarObjects.hasOwnProperty(txt)) {
-                       var obj = editor._toolbarObjects[txt];
-                       obj["state"] = null;
-                       document.getElementById(obj["elementId"])._obj = null;
-               }
-       }
-
-               // cleaning the statusbar elements
-       if(editor._statusBarTree.hasChildNodes()) {
-               for (var i = editor._statusBarTree.firstChild; i; i = i.nextSibling) {
-                       if (i.nodeName.toLowerCase() == "a") {
-                               HTMLArea._removeEvents(i, ["click", "contextmenu"], HTMLArea.statusBarHandler);
-                               i.el = null;
-                               i.editor = null;
-                       }
-               }
-       }
-};
-
 /***************************************************
  *  SELECTIONS AND RANGES
  ***************************************************/
index 4543cd3..312555a 100644 (file)
@@ -3,7 +3,7 @@
 *
 *  (c) 2002-2004, interactivetools.com, inc.
 *  (c) 2003-2004 dynarch.com
-*  (c) 2004-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+*  (c) 2004-2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -1187,22 +1187,102 @@ HTMLArea.resetHandler = function(ev) {
  * Clean up event handlers and object references, undo/redo snapshots, update the textarea for submission
  */
 HTMLArea.removeEditorEvents = function(ev) {
-       if(!ev) var ev = window.event;
+       if (!ev) var ev = window.event;
        HTMLArea._stopEvent(ev);
-       for (var ed = RTEarea.length; --ed > 0 ;) {
-               var editor = RTEarea[ed]["editor"];
-               if(editor) {
-                       RTEarea[ed]["editor"] = null;
+       if (HTMLArea._eventCache) {
+               HTMLArea._eventCache.flush();
+       }
+       for (var editorNumber = RTEarea.length; --editorNumber > 0 ;) {
+               var editor = RTEarea[editorNumber].editor;
+               if (editor) {
+                       RTEarea[editorNumber].editor = null;
                                // save the HTML content into the original textarea for submit, back/forward, etc.
                        editor._textArea.value = editor.getHTML();
                                // release undo/redo snapshots
                        window.clearInterval(editor._timerUndo);
                        editor._undoQueue = null;
-                               // release events
-                       if (HTMLArea.is_ie) HTMLArea._cleanup(editor);
+                               // do final cleanup
+                       HTMLArea.cleanup(editor);
+               }
+       }
+};
+
+/*
+ * Clean up a bunch of references in order to avoid memory leakages mainly in IE, but also in Firefox and Opera
+ */
+HTMLArea.cleanup = function (editor) {
+               // nullify envent handlers
+       for (var handler in editor.eventHandlers) {
+               if (editor.eventHandlers.hasOwnProperty(handler)) {
+                       editor.eventHandlers[handler] = null;
+               }
+       }
+       for (var button in editor.btnList) {
+               if (editor.btnList.hasOwnProperty(button)) {
+                       editor.btnList[button][3] = null;
+               }
+       }
+       for (var dropdown in editor.config.customSelects) {
+               if (editor.config.customSelects.hasOwnProperty(dropdown)) {
+                       editor.config.customSelects[dropdown].action = null;
+                       editor.config.customSelects[dropdown].refresh = null;
+               }
+       }
+       for (var hotKey in editor.config.hotKeyList) {
+               if (editor.config.customSelects.hasOwnProperty(hotKey)) {
+                       editor.config.hotKeyList[hotKey].action = null;
+               }
+       }
+       editor.onGenerate = null;
+       HTMLArea._editorEvent = null;
+       if(editor._textArea.form) {
+               editor._textArea.form.__msh_prevOnReset = null;
+               editor._textArea.form._editorNumber = null;
+       }
+       HTMLArea.onload = null;
+       
+               // cleaning plugin handlers
+       for (var plugin in editor.plugins) {
+               if (editor.plugins.hasOwnProperty(plugin)) {
+                       var pluginInstance = editor.plugins[plugin].instance;
+                       pluginInstance.onChange = null;
+                       pluginInstance.onButtonPress = null;
+                       pluginInstance.onGenerate = null;
+                       pluginInstance.onGenerateOnce = null;
+                       pluginInstance.onMode = null;
+                       pluginInstance.onHotKey = null;
+                       pluginInstance.onKeyPress = null;
+                       pluginInstance.onSelect = null;
+                       pluginInstance.onUpdateTolbar = null;
+               }
+       }
+               // cleaning the toolbar elements
+       for (var txt in editor._toolbarObjects) {
+               if (editor._toolbarObjects.hasOwnProperty(txt)) {
+                       var obj = editor._toolbarObjects[txt];
+                       obj.state = null;
+                       obj.cmd = null;
+                       document.getElementById(obj.elementId)._obj = null;
+                       editor._toolbarObjects[txt] = null;
+               }
+       }
+       
+               // cleaning the statusbar elements
+       if (editor._statusBarTree.hasChildNodes()) {
+               for (var i = editor._statusBarTree.firstChild; i; i = i.nextSibling) {
+                       if (i.nodeName.toLowerCase() == "a") {
+                               HTMLArea._removeEvents(i, ["click", "contextmenu"], HTMLArea.statusBarHandler);
+                               i.el = null;
+                               i.editor = null;
+                       }
                }
        }
-       if (HTMLArea._eventCache && !HTMLArea.is_opera) HTMLArea._eventCache.flush();
+               // final cleanup
+       editor._toolbar = null;
+       editor._statusBar = null;
+       editor._statusBarTree =  null;
+       editor._htmlArea = null;
+       editor._iframe = null;
 };
 
 /*
@@ -2095,6 +2175,7 @@ HTMLArea._eventCacheConstructor = function() {
                                        item[2] = null;
                                } catch(e) { }
                        }
+                       listEvents.length = 0;
                }
        });
 };
index 55f885d..912170a 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+*  (c) 2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -40,18 +40,17 @@ DefaultClean = HTMLArea.Plugin.extend({
        configurePlugin : function(editor) {
                
                this.pageTSConfiguration = this.editorConfiguration.buttons.cleanword;
-               this.cleanLaterFunctRef = this.makeFunctionReference("cleanLater");
                
                /*
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : "1.0",
+                       version         : "1.1",
                        developer       : "Stanislas Rolland",
-                       developerUrl    : "http://www.fructifor.ca/",
+                       developerUrl    : "http://www.sjbr.ca/",
                        copyrightOwner  : "Stanislas Rolland",
-                       sponsor         : "Fructifor Inc.",
-                       sponsorUrl      : "http://www.fructifor.ca/",
+                       sponsor         : "SJBR",
+                       sponsorUrl      : "http://www.sjbr.ca/",
                        license         : "GPL"
                };
                this.registerPluginInformation(pluginInformation);
@@ -88,8 +87,7 @@ DefaultClean = HTMLArea.Plugin.extend({
        
        onGenerate : function () {
                var doc = this.editor._doc;
-               var cleanFunctRef = this.makeFunctionReference("wordCleanHandler");
-               HTMLArea._addEvents((HTMLArea.is_ie ? doc.body : doc), ["paste","dragdrop","drop"], cleanFunctRef, true);
+               HTMLArea._addEvents((HTMLArea.is_ie ? doc.body : doc), ["paste","dragdrop","drop"], DefaultClean.wordCleanHandler, true);
        },
        
        clean : function (html) {
@@ -139,33 +137,34 @@ DefaultClean = HTMLArea.Plugin.extend({
                        return true;
                }
                parseTree(html);
-       },
-       
-       cleanLater : function () {
-               this.clean(this.editor._doc.body);
-               if (this.doUpdateToolbar) {
-                       this.editor.updateToolbar();
-               }
-               this.doUpdateToolbar = false;
-       },
-       
-       /*
-       * Handler for paste, dragdrop and drop events
-       */
-       wordCleanHandler : function (ev) {
-               if(!ev) var ev = window.event;
-               var target = (ev.target) ? ev.target : ev.srcElement;
-               var owner = (target.ownerDocument) ? target.ownerDocument : target;
-               while (HTMLArea.is_ie && owner.parentElement ) { // IE5.5 does not report any ownerDocument
-                       owner = owner.parentElement;
-               }
-                       // If we dropped an image dragged from the TYPO3 Image plugin, let's close the dialog window
-               if (typeof(HTMLArea.Dialog) != "undefined" && HTMLArea.Dialog.TYPO3Image) {
-                       HTMLArea.Dialog.TYPO3Image.close();
-               } else {
-                       this.doUpdateToolbar = false;
-                       window.setTimeout(this.cleanLaterFunctRef, 250);
-               }
        }
 });
 
+/*
+ * Closure avoidance for IE
+ */
+DefaultClean.cleanLater = function (editorNumber) {
+       var editor = RTEarea[editorNumber].editor;
+       editor.plugins.DefaultClean.instance.clean(editor._doc.body);
+};
+
+/*
+ * Handler for paste, dragdrop and drop events
+ */
+DefaultClean.wordCleanHandler = function (ev) {
+       if (!ev) var ev = window.event;
+       var target = ev.target ? ev.target : ev.srcElement;
+       var owner = target.ownerDocument ? target.ownerDocument : target;
+       if (HTMLArea.is_ie) { // IE5.5 does not report any ownerDocument
+               while (owner.parentElement) { owner = owner.parentElement; }
+       }
+       var editor = RTEarea[owner._editorNo].editor;
+       
+               // If we dropped an image dragged from the TYPO3 Image plugin, let's close the dialog window
+       if (typeof(HTMLArea.Dialog) != "undefined" && HTMLArea.Dialog.TYPO3Image) {
+               HTMLArea.Dialog.TYPO3Image.close();
+       } else {
+               window.setTimeout("DefaultClean.cleanLater(" + editor._editorNumber + ");", 250);
+       }
+};
+
index b165d04..7912132 100644 (file)
@@ -1,7 +1,7 @@
 /***************************************************************
 *  Copyright notice
 *
-*  (c) 2005-2008 Stanislas Rolland <stanislas.rolland(arobas)fructifor.ca>
+*  (c) 2005-2008 Stanislas Rolland <typo3(arobas)sjbr.ca>
 *  All rights reserved
 *
 *  This script is part of the TYPO3 project. The TYPO3 project is
@@ -41,18 +41,17 @@ TYPO3HtmlParser = HTMLArea.Plugin.extend({
                
                this.pageTSConfiguration = this.editorConfiguration.buttons.cleanword;
                this.parseHtmlModulePath = this.pageTSConfiguration.pathParseHtmlModule;
-               this.cleanLaterFunctRef = this.makeFunctionReference("cleanLater");
                
                /*
                 * Registering plugin "About" information
                 */
                var pluginInformation = {
-                       version         : "1.7",
+                       version         : "1.8",
                        developer       : "Stanislas Rolland",
-                       developerUrl    : "http://www.fructifor.ca/",
+                       developerUrl    : "http://www.sjbr.ca/",
                        copyrightOwner  : "Stanislas Rolland",
-                       sponsor         : "Fructifor Inc.",
-                       sponsorUrl      : "http://www.fructifor.ca/",
+                       sponsor         : "SJBR",
+                       sponsorUrl      : "http://www.sjbr.ca/",
                        license         : "GPL"
                };
                this.registerPluginInformation(pluginInformation);
@@ -90,7 +89,7 @@ TYPO3HtmlParser = HTMLArea.Plugin.extend({
        onGenerate : function () {
                var doc = this.editor._doc;
                var cleanFunctRef = this.makeFunctionReference("wordCleanHandler");
-               HTMLArea._addEvents((HTMLArea.is_ie ? doc.body : doc), ["paste","dragdrop","drop"], cleanFunctRef, true);
+               HTMLArea._addEvents((HTMLArea.is_ie ? doc.body : doc), ["paste","dragdrop","drop"], TYPO3HtmlParser.wordCleanHandler, true);
        },
        
        clean : function(body, bookmark) {
@@ -106,29 +105,35 @@ TYPO3HtmlParser = HTMLArea.Plugin.extend({
                                        editor.selectRange(editor.moveToBookmark(bookmark));
                                }
                );
-       },
-       
-       cleanLater : function () {
-               var bookmark = this.editor.getBookmark(this.editor._createRange(this.editor._getSelection()));
-               this.clean(this.editor._doc.body, bookmark);
-       },
-       
-       /*
-       * Handler for paste, dragdrop and drop events
-       */
-       wordCleanHandler : function (ev) {
-               if(!ev) var ev = window.event;
-               var target = (ev.target) ? ev.target : ev.srcElement;
-               var owner = (target.ownerDocument) ? target.ownerDocument : target;
-               while (HTMLArea.is_ie && owner.parentElement ) { // IE5.5 does not report any ownerDocument
-                       owner = owner.parentElement;
-               }
-                       // If we dropped an image dragged from the TYPO3 Image plugin, let's close the dialog window
-               if (typeof(HTMLArea.Dialog) != "undefined" && HTMLArea.Dialog.TYPO3Image) {
-                       HTMLArea.Dialog.TYPO3Image.close();
-               } else {
-                       window.setTimeout(this.cleanLaterFunctRef, 250);
-               }
        }
 });
 
+/*
+ * Closure avoidance for IE
+ */
+TYPO3HtmlParser.cleanLater = function (editorNumber) {
+       var editor = RTEarea[editorNumber].editor;
+       var bookmark = editor.getBookmark(editor._createRange(editor._getSelection()));
+       editor.plugins.TYPO3HtmlParser.instance.clean(editor._doc.body, bookmark);
+};
+
+/*
+ * Handler for paste, dragdrop and drop events
+ */
+TYPO3HtmlParser.wordCleanHandler = function (ev) {
+       if (!ev) var ev = window.event;
+       var target = ev.target ? ev.target : ev.srcElement;
+       var owner = target.ownerDocument ? target.ownerDocument : target;
+       if (HTMLArea.is_ie) { // IE5.5 does not report any ownerDocument
+               while (owner.parentElement) { owner = owner.parentElement; }
+       }
+       var editor = RTEarea[owner._editorNo].editor;
+       
+               // If we dropped an image dragged from the TYPO3 Image plugin, let's close the dialog window
+       if (typeof(HTMLArea.Dialog) != "undefined" && HTMLArea.Dialog.TYPO3Image) {
+               HTMLArea.Dialog.TYPO3Image.close();
+       } else {
+               window.setTimeout("TYPO3HtmlParser.cleanLater(" + editor._editorNumber + ");", 250);
+       }
+};
+