From 53f54248018db2a5bb6f5133ee08ad20f78dbbc6 Mon Sep 17 00:00:00 2001 From: Neil Fraser Date: Thu, 20 Aug 2015 15:46:44 -0700 Subject: [PATCH] Skc memory leaks (PR #159) --- blockly_compressed.js | 35 ++++++++++++++++++----------------- core/flyout.js | 19 +++++++++++-------- core/scrollbar.js | 6 ------ core/toolbox.js | 1 + core/workspace_svg.js | 40 +++++++++++++++++++++++++++++++--------- 5 files changed, 61 insertions(+), 40 deletions(-) diff --git a/blockly_compressed.js b/blockly_compressed.js index 1fd656d47..ffb647805 100644 --- a/blockly_compressed.js +++ b/blockly_compressed.js @@ -933,13 +933,13 @@ Blockly.Input.prototype.appendField=function(a,b){if(!a&&!b)return this;goog.isS Blockly.Input.prototype.appendTitle=function(a,b){console.warn("Deprecated call to appendTitle, use appendField instead.");return this.appendField(a,b)};Blockly.Input.prototype.removeField=function(a){for(var b=0,c;c=this.fieldRow[b];b++)if(c.name===a){c.dispose();this.fieldRow.splice(b,1);this.sourceBlock_.rendered&&(this.sourceBlock_.render(),this.sourceBlock_.bumpNeighbours_());return}goog.asserts.fail('Field "%s" not found.',a)};Blockly.Input.prototype.isVisible=function(){return this.visible_}; Blockly.Input.prototype.setVisible=function(a){var b=[];if(this.visible_==a)return b;for(var c=(this.visible_=a)?"block":"none",d=0,e;e=this.fieldRow[d];d++)e.setVisible(a);this.connection&&(a?b=this.connection.unhideAll():this.connection.hideAll(),d=this.connection.targetBlock())&&(d.getSvgRoot().style.display=c,a||(d.rendered=!1));return b};Blockly.Input.prototype.setCheck=function(a){if(!this.connection)throw"This input does not have a connection.";this.connection.setCheck(a);return this}; Blockly.Input.prototype.setAlign=function(a){this.align=a;this.sourceBlock_.rendered&&this.sourceBlock_.render();return this};Blockly.Input.prototype.init=function(){if(this.sourceBlock_.workspace.rendered)for(var a=0;athis.options.zoomOptions.maxScale?c=this.options.zoomOptions.maxScale/this.scale:dthis.workspace.remainingCapacity()&&(d.enabled=!1);c.push(d);this.isEditable()&&!this.collapsed_&&this.workspace.options.comments&&(d={enabled:!0},this.comment?(d.text=Blockly.Msg.REMOVE_COMMENT,d.callback= function(){b.setCommentText(null)}):(d.text=Blockly.Msg.ADD_COMMENT,d.callback=function(){b.setCommentText("")}),c.push(d));if(!this.collapsed_)for(d=1;d} * @private */ @@ -145,14 +145,14 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { this.hide(); - this.eventWrappers_.concat(Blockly.bindEvent_(this.svgGroup_, - 'wheel', this, this.wheel_)); - this.eventWrappers_.concat( + Array.prototype.push.apply(this.eventWrappers_, + Blockly.bindEvent_(this.svgGroup_, 'wheel', this, this.wheel_)); + Array.prototype.push.apply(this.eventWrappers_, Blockly.bindEvent_(this.targetWorkspace_.getCanvas(), 'blocklyWorkspaceChange', this, this.filterForCapacity_)); // Dragging the flyout up and down. - this.eventWrappers_.concat(Blockly.bindEvent_(this.svgGroup_, - 'mousedown', this, this.onMouseDown_)); + Array.prototype.push.apply(this.eventWrappers_, + Blockly.bindEvent_(this.svgGroup_, 'mousedown', this, this.onMouseDown_)); }; /** @@ -162,12 +162,15 @@ Blockly.Flyout.prototype.init = function(targetWorkspace) { Blockly.Flyout.prototype.dispose = function() { this.hide(); Blockly.unbindEvent_(this.eventWrappers_); - this.eventWrappers_.length = 0; if (this.scrollbar_) { this.scrollbar_.dispose(); this.scrollbar_ = null; } - this.workspace_ = null; + if (this.workspace_) { + this.workspace_.targetWorkspace = null; + this.workspace_.dispose(); + this.workspace_ = null; + } if (this.svgGroup_) { goog.dom.removeNode(this.svgGroup_); this.svgGroup_ = null; diff --git a/core/scrollbar.js b/core/scrollbar.js index 80adc623e..0712df792 100644 --- a/core/scrollbar.js +++ b/core/scrollbar.js @@ -53,8 +53,6 @@ Blockly.ScrollbarPair = function(workspace) { * Unlink from all DOM elements to prevent memory leaks. */ Blockly.ScrollbarPair.prototype.dispose = function() { - Blockly.unbindEvent_(this.onResizeWrapper_); - this.onResizeWrapper_ = null; goog.dom.removeNode(this.corner_); this.corner_ = null; this.workspace_ = null; @@ -190,10 +188,6 @@ if (goog.events.BrowserFeature.TOUCH_ENABLED) { */ Blockly.Scrollbar.prototype.dispose = function() { this.onMouseUpKnob_(); - if (this.onResizeWrapper_) { - Blockly.unbindEvent_(this.onResizeWrapper_); - this.onResizeWrapper_ = null; - } Blockly.unbindEvent_(this.onMouseDownBarWrapper_); this.onMouseDownBarWrapper_ = null; Blockly.unbindEvent_(this.onMouseDownKnobWrapper_); diff --git a/core/toolbox.js b/core/toolbox.js index cdc8f0a82..e975ba2ac 100644 --- a/core/toolbox.js +++ b/core/toolbox.js @@ -147,6 +147,7 @@ Blockly.Toolbox.prototype.dispose = function() { this.flyout_.dispose(); this.tree_.dispose(); goog.dom.removeNode(this.HtmlDiv); + this.workspace_ = null; }; /** diff --git a/core/workspace_svg.js b/core/workspace_svg.js index 2030eec2d..9bea8211e 100644 --- a/core/workspace_svg.js +++ b/core/workspace_svg.js @@ -52,12 +52,20 @@ Blockly.WorkspaceSvg = function(options) { this.setMetrics = options.setMetrics; Blockly.ConnectionDB.init(this); + /** * Database of pre-loaded sounds. * @private * @const */ this.SOUNDS_ = Object.create(null); + + /** + * Opaque data that can be passed to Blockly.unbindEvent_. + * @type {Array.} + * @private + */ + this.eventWrappers_ = []; }; goog.inherits(Blockly.WorkspaceSvg, Blockly.Workspace); @@ -187,6 +195,7 @@ Blockly.WorkspaceSvg.prototype.createDom = function(opt_backgroundClass) { Blockly.WorkspaceSvg.prototype.dispose = function() { // Stop rerendering. this.rendered = false; + Blockly.unbindEvent_(this.eventWrappers_); Blockly.WorkspaceSvg.superClass_.dispose.call(this); if (this.svgGroup_) { goog.dom.removeNode(this.svgGroup_); @@ -206,9 +215,13 @@ Blockly.WorkspaceSvg.prototype.dispose = function() { this.trashcan.dispose(); this.trashcan = null; } - if (this.zoomControls) { - this.zoomControls.dispose(); - this.zoomControls = null; + if (this.scrollbar) { + this.scrollbar.dispose(); + this.scrollbar = null; + } + if (this.zoomControls_) { + this.zoomControls_.dispose(); + this.zoomControls_ = null; } if (!this.options.parentWorkspace) { // Top-most workspace. Dispose of the SVG too. @@ -221,6 +234,7 @@ Blockly.WorkspaceSvg.prototype.dispose = function() { * @private */ Blockly.WorkspaceSvg.prototype.addTrashcan_ = function() { + /** @type {Blockly.Trashcan} */ this.trashcan = new Blockly.Trashcan(this); var svgTrashcan = this.trashcan.createDom(); this.svgGroup_.insertBefore(svgTrashcan, this.svgBlockCanvas_); @@ -232,10 +246,11 @@ Blockly.WorkspaceSvg.prototype.addTrashcan_ = function() { * @private */ Blockly.WorkspaceSvg.prototype.addZoomControls_ = function() { - this.zoomControls = new Blockly.ZoomControls(this); - var svgZoomControls = this.zoomControls.createDom(); + /** @type {Blockly.ZoomControls} */ + this.zoomControls_ = new Blockly.ZoomControls(this); + var svgZoomControls = this.zoomControls_.createDom(); this.svgGroup_.appendChild(svgZoomControls); - this.zoomControls.init(); + this.zoomControls_.init(); }; /** @@ -247,6 +262,7 @@ Blockly.WorkspaceSvg.prototype.addFlyout_ = function() { parentWorkspace: this, RTL: this.RTL }; + /** @type {Blockly.Flyout} */ this.flyout_ = new Blockly.Flyout(workspaceOptions); this.flyout_.autoClose = false; var svgFlyout = this.flyout_.createDom(); @@ -266,8 +282,8 @@ Blockly.WorkspaceSvg.prototype.resize = function() { if (this.trashcan) { this.trashcan.position(); } - if (this.zoomControls) { - this.zoomControls.position(); + if (this.zoomControls_) { + this.zoomControls_.position(); } if (this.scrollbar) { this.scrollbar.resize(); @@ -811,8 +827,10 @@ Blockly.WorkspaceSvg.prototype.updateToolbox = function(tree) { * removeChangeListener. */ Blockly.WorkspaceSvg.prototype.addChangeListener = function(func) { - return Blockly.bindEvent_(this.getCanvas(), + var wrapper = Blockly.bindEvent_(this.getCanvas(), 'blocklyWorkspaceChange', null, func); + Array.prototype.push.apply(this.eventWrappers_, wrapper); + return wrapper; }; /** @@ -821,6 +839,10 @@ Blockly.WorkspaceSvg.prototype.addChangeListener = function(func) { */ Blockly.WorkspaceSvg.prototype.removeChangeListener = function(bindData) { Blockly.unbindEvent_(bindData); + var i = this.eventWrappers_.indexOf(bindData); + if (i != -1) { + this.eventWrappers_.splice(i, 1); + } }; /**