Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removed memory leak caused by CKEDITOR.filter.instances #1770

Merged
merged 19 commits into from
Oct 9, 2018
Merged

Conversation

jacekbogdanski
Copy link
Member

@jacekbogdanski jacekbogdanski commented Mar 9, 2018

What is the purpose of this pull request?

Bug fix

Does your PR contain necessary tests?

All patches which change the editor code must include tests. You can always read more
on PR testing,
how to set the testing environment and
how to create tests
in the official CKEditor documentation.

This PR contains

  • Unit tests
  • Manual tests

What changes did you make?

Added additional option to CKEDITOR.filter constructor to pass an editor owning filter instance. Changed editor.destroy function to remove correct editors and refactored plugins using CKEDITOR.filter. With this change we are now able to remove orphaned editor filters and prevent memory leak with CKEDITOR.filter.instances.

Additional parameter doesn't look most beautiful because we should probably go with something like new CKEDITOR.filter( editor, rules) but it would require to provide breaking change for our API.

Based on #1690

Closes #1722, closes #2466

Copy link
Member

@Comandeer Comandeer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix does not cover filters for nested editables, which should be destroyed after destroying the widget, not the whole editor.

CHANGES.md Outdated
@@ -1,6 +1,12 @@
CKEditor 4 Changelog
====================

## CKEDITOR 4.9.1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update version tag.

core/filter.js Outdated
*
* @since 4.1
* @class
* @constructor Creates a filter class instance.
* @param {CKEDITOR.editor/CKEDITOR.filter.allowedContentRules} editorOrRules
* @param {CKEDITOR.editor} owner Editor owning the filter instance.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like it. We already have editor as a first parameter. We could make that change compatible with older versions by allowing to optionally pass rules as a second parameter. It seems like a more elegant solution:

CKEDITOR.filter = function( editorOrRules, rules )

Please also note that optional parameters should be marked with [] (so [owner], not owner).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only issue with such approach is the fact that new CKEDITOR.filter( editor ) creates new filter based on editor's features. However I still think that something like new CKEDITOR.filter( editor, {} ) or new CKEDITOR.filter( editor, null ) is more readable than new CKEDITOR.filter( null, editor ).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's not beautiful. But let's say you would like to create empty filter and setup it by CKEDITOR.filter#allow function e.g

var filter = new CKEDITOR.filter();
filter.allow( {...} );

In that case, you won't be able to create a filter with owning editor - if you try to pass editor as an argument you will create a filter based on editor (https://github.com/ckeditor/ckeditor-dev/blob/major/core/filter.js#L168).

We have that case in our code (https://github.com/ckeditor/ckeditor-dev/blob/major/plugins/clipboard/plugin.js#L1347) and it's possible that people could use it in that way also.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually you would be still able to create such filter:

var filter = new CKEDITOR.filter( editor, {} );
filter.allow( {} );

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it can be done but with that change, we will provide breaking change for our API. If we are ok with that I also prefer new CKEDITOR.filter( editorOrRules, rules ) signature. WDYT @Comandeer?

* @param {Object} obj
* @return {Array} object values.
*/
objToArray: function( obj ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change does not seem to be directly connected with the fix. Why is it present here?

Copy link
Member Author

@jacekbogdanski jacekbogdanski Apr 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed it because this PR is based on #1690. Now I think maybe it shouldn't be changed here as it's a separate issue.

I'm using this function inside unit tests for proposed changes.

@jacekbogdanski
Copy link
Member Author

Rebased into the latest major.

@jacekbogdanski jacekbogdanski force-pushed the t/1722 branch 2 times, most recently from c1de513 to 045ae8e Compare May 7, 2018 10:18
@jacekbogdanski
Copy link
Member Author

I left CKEDITOR.filter signature unchanged - please refer to #1770 (comment) and if you think we can provide breaking change for API I will refactor the code.
About destroying nested editables inside widget plugin - a problem with this approach is that we are sharing the same filter for each widget instance:
https://github.com/ckeditor/ckeditor-dev/blob/045ae8e15cbfc85872748d737e8e54fb414acbcd/plugins/widget/plugin.js#L2202-L2224
So if we destroy nested editable filter on widget destroy it will become unavailable for other widgets of the same type. It's not a problem when destroying editor because in that case widgets are already / will be destroyed so filters are not needed anymore. I could implement some workaround like destroying filter only if it's not used by widgets but not sure if it's correct way.

@Comandeer
Copy link
Member

Hmm, the main ticket is about destroying filters in nested editables, so it would be nice to actually implement it. I think that workaround to detect any nested editable still using this filter should be enough.

Copy link
Member

@Comandeer Comandeer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rebase onto latest major.

@mlewand mlewand added the review:easy Pull requests that can be reviewed by a Junior Developer before being reviewed by the Reviewer. label Jul 10, 2018
@jacekbogdanski jacekbogdanski self-assigned this Aug 29, 2018
@jacekbogdanski jacekbogdanski changed the base branch from major to master August 29, 2018 10:54
@jacekbogdanski jacekbogdanski removed their assignment Aug 29, 2018
@Comandeer Comandeer self-assigned this Sep 18, 2018
core/filter.js Outdated
*
* @since 4.1
* @class
* @constructor Creates a filter class instance.
* @param {CKEDITOR.editor/CKEDITOR.filter.allowedContentRules} editorOrRules
* @param {CKEDITOR.editor} rules This parameter is available since 4.10.0.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update version tag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this parameter is not of CKEDITOR.editor type.

core/filter.js Outdated
@@ -165,11 +175,12 @@
// Register filter instance.
CKEDITOR.filter.instances[ this.id ] = this;

if ( editorOrRules instanceof CKEDITOR.editor ) {
var editor = this.editor = editorOrRules;
this.editor = editorOrRules instanceof CKEDITOR.editor ? editorOrRules : null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not:

var editor = this.editor = editorOrRules instanceof CKEDITOR.editor ? editorOrRules : null;

? With such version there won't be need to change all references to the editor.


function countFilters( editor ) {
var filters = bender.tools.objToArray( CKEDITOR.filter.instances );
return editor ? filters.filter( function( filter ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function won't work in IE8, as there is no Array.prototype.filter. Our own CKEDITOR.tools.array.filter should be used here.

@@ -0,0 +1,14 @@
@bender-tags: bug, 4.9.1, 1722
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update version tag.

@@ -0,0 +1,14 @@
@bender-tags: widget, bug, 4.10.0, 1722
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update version tag.

@Comandeer Comandeer changed the base branch from master to major October 8, 2018 10:47
@Comandeer
Copy link
Member

Rebased onto latest major and changed base.

Copy link
Member

@Comandeer Comandeer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix looks good, only some bureaucracy left. However it seems that filters are still leaking, due to #1452

@@ -32,6 +32,7 @@ Fixed Issues:
* [#421](https://github.com/ckeditor/ckeditor-dev/issues/421) Fixed: Expandable [Button](https://ckeditor.com/cke4/addon/button) puts `(Selected)` text at the end of the label when clicked.
* [#1454](https://github.com/ckeditor/ckeditor-dev/issues/1454): Fixed: [Upload Widget](https://ckeditor.com/cke4/addon/uploadwidget) [`onAbort`](https://docs.ckeditor.com/ckeditor4/docs/#!/api/CKEDITOR.fileTools.uploadWidgetDefinition-property-onAbort) method is not called when loader is aborted.
* [#1451](https://github.com/ckeditor/ckeditor-dev/issues/1451): Fixed: Context menu is incorrectly positioned when opened with `Shift-F10`.
* [#1722](https://github.com/ckeditor/ckeditor-dev/issues/1722): [`CKEDITOR.filter.instances`](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_filter.html#static-property-instances) is causing memory leaks.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please create followup ticket that will describe changes in API and reference it in "API Changes" section.

core/filter.js Outdated
*
* @since 4.1
* @class
* @constructor Creates a filter class instance.
* @param {CKEDITOR.editor/CKEDITOR.filter.allowedContentRules} editorOrRules
* @param {CKEDITOR.filter.allowedContentRules} rules This parameter is available since 4.10.2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update version and add period at the end.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also mark this parameter as optional.

core/filter.js Outdated
* ```
*
* If filter is only used by a single editor instance, you should pass editor instance alongside with the rules
* so the filter could be removed with {@link CKEDITOR.editor#destroy} method.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be additional info about what will happen if editor is passed without rules as it changes the behaviour of the constructor in not so obvious way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't enough described by the whole https://github.com/ckeditor/ckeditor-dev/blob/013af363d892680f9c0a2f11765aee45ec09ca2b/core/filter.js#L33-L56 docs? 🤔 However, I added additional info that passing only editor ( without rules ) will also prevent memory leaks.

Copy link
Member

@Comandeer Comandeer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review:easy Pull requests that can be reviewed by a Junior Developer before being reviewed by the Reviewer.
Projects
None yet
3 participants