8
0
Просмотр исходного кода

Merge branch 'master' into t/ckeditor5/479

Aleksander Nowodzinski 7 лет назад
Родитель
Сommit
d57e888c92

+ 1 - 1
packages/ckeditor5-editor-balloon/.eslintrc.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-editor-balloon/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **Balloon Editor** – https://github.com/ckeditor/ckeditor5-editor-balloon <br>
-Copyright (c) 2003-2018, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2019, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
 
 Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
 

+ 1 - 10
packages/ckeditor5-editor-balloon/src/ballooneditor.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -18,7 +18,6 @@ import DataApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin
 import ElementApiMixin from '@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin';
 import attachToForm from '@ckeditor/ckeditor5-core/src/editor/utils/attachtoform';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 import { isElement } from 'lodash-es';
 
 /**
@@ -84,14 +83,6 @@ export default class BalloonEditor extends Editor {
 		attachToForm( this );
 	}
 
-	/**
-	 * @inheritDoc
-	 */
-	get element() {
-		log.warn( 'deprecated-editor-element: The editor#element is deprecated.' );
-		return this.ui.element;
-	}
-
 	/**
 	 * Destroys the editor instance, releasing all resources used by it.
 	 *

+ 11 - 10
packages/ckeditor5-editor-balloon/src/ballooneditorui.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -67,11 +67,15 @@ export default class BalloonEditorUI extends EditorUI {
 
 		// The editable UI element in DOM is available for sure only after the editor UI view has been rendered.
 		// But it can be available earlier if a DOM element has been passed to BalloonEditor.create().
-		const editableElement = editable.editableElement;
+		const editableElement = editable.element;
+
+		// The editable UI and editing root should share the same name. Then name is used
+		// to recognize the particular editable, for instance in ARIA attributes.
+		editable.name = editingRoot.rootName;
 
 		// Register the editable UI view in the editor. A single editor instance can aggregate multiple
 		// editable areas (roots) but the balloon editor has only one.
-		this._editableElements.push( view.editable );
+		this._editableElements.set( editable.name, editableElement );
 
 		// Let the global focus tracker know that the editable UI element is focusable and
 		// belongs to the editor. From now on, the focus tracker will sustain the editor focus
@@ -85,11 +89,7 @@ export default class BalloonEditorUI extends EditorUI {
 		// it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user.
 		// Doing otherwise will result in editable focus styles disappearing, once e.g. the
 		// toolbar gets focused.
-		view.editable.bind( 'isFocused' ).to( this.focusTracker );
-
-		// The editable UI and editing root should share the same name. Then name is used
-		// to recognize the particular editable, for instance in ARIA attributes.
-		view.editable.name = editingRoot.rootName;
+		editable.bind( 'isFocused' ).to( this.focusTracker );
 
 		// Bind the editable UI element to the editing view, making it an end– and entry–point
 		// of the editor's engine. This is where the engine meets the UI.
@@ -100,7 +100,7 @@ export default class BalloonEditorUI extends EditorUI {
 		// element and doing so before data is loaded into the model (ready) would destroy the
 		// original content.
 		editor.on( 'dataReady', () => {
-			view.editable.enableEditingRootListeners();
+			editable.enableEditingRootListeners();
 
 			const placeholderText = editor.config.get( 'placeholder' ) || editor.sourceElement.getAttribute( 'placeholder' );
 
@@ -124,7 +124,7 @@ export default class BalloonEditorUI extends EditorUI {
 			}
 		} );
 
-		this.ready();
+		this.fire( 'ready' );
 	}
 
 	/**
@@ -136,6 +136,7 @@ export default class BalloonEditorUI extends EditorUI {
 
 		view.editable.disableEditingRootListeners();
 		editingView.detachDomRoot( view.editable.name );
+		view.destroy();
 
 		super.destroy();
 	}

+ 1 - 18
packages/ckeditor5-editor-balloon/src/ballooneditoruiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -9,7 +9,6 @@
 
 import EditorUIView from '@ckeditor/ckeditor5-ui/src/editorui/editoruiview';
 import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 
 /**
  * Contextual editor UI view. Uses the {@link module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}.
@@ -44,20 +43,4 @@ export default class BalloonEditorUIView extends EditorUIView {
 
 		this.registerChild( this.editable );
 	}
-
-	/**
-	 * **Deprecated** since `v12.0.0`. The {@link module:ui/editableui/editableuiview~EditableUIView#editableElement
-	 * `EditableUIView editableElement`} could be used instead.
-	 *
-	 * The element which is the main editable element (usually the one with `contentEditable="true"`).
-	 *
-	 * @deprecated v12.0.0 The {@link module:ui/editableui/editableuiview~EditableUIView#editableElement
-	 * `EditableUIView editableElement`} could be used instead.
-	 * @readonly
-	 * @member {HTMLElement} #editableElement
-	 */
-	get editableElement() {
-		log.warn( 'deprecated-ui-view-editableElement: The BalloonEditorUIView#editableElement property is deprecated.' );
-		return this.editable.editableElement;
-	}
 }

+ 18 - 34
packages/ckeditor5-editor-balloon/tests/ballooneditor.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -22,6 +22,9 @@ import RootElement from '@ckeditor/ckeditor5-engine/src/model/rootelement';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import log from '@ckeditor/ckeditor5-utils/src/log';
 
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+import { describeMemoryUsage, testMemoryUsage } from '@ckeditor/ckeditor5-core/tests/_utils/memory';
+
 describe( 'BalloonEditor', () => {
 	let editor, editorElement;
 
@@ -122,14 +125,6 @@ describe( 'BalloonEditor', () => {
 					return newEditor.destroy();
 				} );
 		} );
-
-		it( 'editor.element should contain the whole editor (with UI) element', () => {
-			return BalloonEditor.create( '<p>Hello world!</p>', {
-				plugins: [ Paragraph ]
-			} ).then( editor => {
-				expect( editor.editing.view.getDomRoot() ).to.equal( editor.element );
-			} );
-		} );
 	} );
 
 	describe( 'create()', () => {
@@ -158,7 +153,6 @@ describe( 'BalloonEditor', () => {
 		it( 'attaches editable UI as view\'s DOM root', () => {
 			const domRoot = editor.editing.view.getDomRoot();
 
-			expect( domRoot ).to.equal( editor.element );
 			expect( domRoot ).to.equal( editor.ui.view.editable.element );
 		} );
 
@@ -215,7 +209,6 @@ describe( 'BalloonEditor', () => {
 				init() {
 					this.editor.on( 'pluginsReady', spy );
 					this.editor.ui.on( 'ready', spy );
-					this.editor.on( 'uiReady', spy );
 					this.editor.on( 'dataReady', spy );
 					this.editor.on( 'ready', spy );
 				}
@@ -226,7 +219,7 @@ describe( 'BalloonEditor', () => {
 					plugins: [ EventWatcher ]
 				} )
 				.then( newEditor => {
-					expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'uiReady', 'dataReady', 'ready' ] );
+					expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'dataReady', 'ready' ] );
 
 					editor = newEditor;
 				} );
@@ -254,28 +247,6 @@ describe( 'BalloonEditor', () => {
 				} );
 		} );
 
-		it( 'fires uiReady once UI is ready', () => {
-			let isRendered;
-
-			class EventWatcher extends Plugin {
-				init() {
-					this.editor.on( 'uiReady', () => {
-						isRendered = this.editor.ui.view.isRendered;
-					} );
-				}
-			}
-
-			return BalloonEditor
-				.create( editorElement, {
-					plugins: [ EventWatcher ]
-				} )
-				.then( newEditor => {
-					expect( isRendered ).to.be.true;
-
-					editor = newEditor;
-				} );
-		} );
-
 		it( 'fires ready once UI is ready', () => {
 			let isRendered;
 
@@ -339,4 +310,17 @@ describe( 'BalloonEditor', () => {
 				.then( newEditor => newEditor.destroy() );
 		} );
 	} );
+
+	describeMemoryUsage( () => {
+		testMemoryUsage(
+			'should not grow on multiple create/destroy',
+			() => BalloonEditor
+				.create( document.querySelector( '#mem-editor' ), {
+					plugins: [ ArticlePluginSet ],
+					toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote' ],
+					image: {
+						toolbar: [ 'imageStyle:full', 'imageStyle:side', '|', 'imageTextAlternative' ]
+					}
+				} ) );
+	} );
 } );

+ 11 - 6
packages/ckeditor5-editor-balloon/tests/ballooneditorui.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -16,7 +16,7 @@ import utils from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 describe( 'BalloonEditorUI', () => {
-	let editor, view, ui;
+	let editor, view, ui, viewElement;
 
 	testUtils.createSinonSandbox();
 
@@ -29,6 +29,7 @@ describe( 'BalloonEditorUI', () => {
 				editor = newEditor;
 				ui = editor.ui;
 				view = ui.view;
+				viewElement = view.editable.element;
 			} );
 	} );
 
@@ -124,6 +125,12 @@ describe( 'BalloonEditorUI', () => {
 		} );
 	} );
 
+	describe( 'element()', () => {
+		it( 'returns correct element instance', () => {
+			expect( ui.element ).to.equal( viewElement );
+		} );
+	} );
+
 	describe( 'getEditableElement()', () => {
 		it( 'returns editable element (default)', () => {
 			expect( ui.getEditableElement() ).to.equal( view.editable.element );
@@ -133,8 +140,8 @@ describe( 'BalloonEditorUI', () => {
 			expect( ui.getEditableElement( 'main' ) ).to.equal( view.editable.element );
 		} );
 
-		it( 'returns null if editable with the given name is absent', () => {
-			expect( ui.getEditableElement( 'absent' ) ).to.null;
+		it( 'returns undefined if editable with the given name is absent', () => {
+			expect( ui.getEditableElement( 'absent' ) ).to.be.undefined;
 		} );
 	} );
 } );
@@ -161,8 +168,6 @@ class VirtualBalloonTestEditor extends VirtualTestEditor {
 				editor.initPlugins()
 					.then( () => {
 						editor.ui.init();
-						editor.ui.ready();
-						editor.fire( 'uiReady' );
 						editor.fire( 'dataReady' );
 						editor.fire( 'ready' );
 					} )

+ 1 - 12
packages/ckeditor5-editor-balloon/tests/ballooneditoruiview.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 
@@ -8,7 +8,6 @@ import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/i
 import Locale from '@ckeditor/ckeditor5-utils/src/locale';
 
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 
 describe( 'BalloonEditorUIView', () => {
 	let locale, view;
@@ -45,14 +44,4 @@ describe( 'BalloonEditorUIView', () => {
 			sinon.assert.calledOnce( spy );
 		} );
 	} );
-
-	describe( 'editableElement', () => {
-		it( 'returns editable\'s view element', () => {
-			testUtils.sinon.stub( log, 'warn' ).callsFake( () => {} );
-
-			view.render();
-			expect( view.editableElement.getAttribute( 'contentEditable' ) ).to.equal( 'true' );
-			view.destroy();
-		} );
-	} );
 } );

+ 1 - 1
packages/ckeditor5-editor-balloon/tests/manual/ballooneditor-data.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-editor-balloon/tests/manual/ballooneditor.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 28 - 0
packages/ckeditor5-editor-balloon/tests/manual/memory.html

@@ -0,0 +1,28 @@
+<p>
+	<button id="destroyEditors">Destroy editor</button>
+	<button id="initEditors">Init editors</button>
+</p>
+
+<div id="editor-1" contenteditable="true" class="custom-class" custom-attr="foo">
+	<h2>Editor 1</h2>
+	<p>This is an editor instance. And there's <a href="http://ckeditor.com">some link</a>.</p>
+</div>
+
+<div id="editor-2" class="custom-class" custom-attr="foo">
+	<h2>Editor 2</h2>
+	<p>This is another editor instance.</p>
+	<img src="sample.jpg" />
+	<p>
+		Unlike Editor 1 it doesn't have contenteditable=true initially.
+		Check if it's editable after initializing the editors and back to non-editable after destroying them.
+	</p>
+</div>
+
+<style>
+	.custom-class {
+		margin-top: 100px;
+		margin-left: 100px;
+		margin-bottom: 100px;
+		width: 450px;
+	}
+</style>

+ 80 - 0
packages/ckeditor5-editor-balloon/tests/manual/memory.js

@@ -0,0 +1,80 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console:false, document, window */
+
+import BalloonEditor from '../../src/ballooneditor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+
+window.editors = {};
+
+/*
+ * Memory-leak safe version of balloon editor manual test does not:
+ * - define global variables (such as let editor; in main file scope)
+ * - console.log() objects
+ * - add event listeners with () => {} methods which reference other
+ */
+function initEditors() {
+	init( '#editor-1' );
+	init( '#editor-2' );
+
+	document.getElementById( 'destroyEditors' ).addEventListener( 'click', destroyEditors );
+
+	function init( selector ) {
+		BalloonEditor
+			.create( document.querySelector( selector ), {
+				plugins: [ ArticlePluginSet ],
+				toolbar: {
+					items: [
+						'heading',
+						'|',
+						'bold',
+						'italic',
+						'link',
+						'bulletedList',
+						'numberedList',
+						'blockQuote',
+						'insertTable',
+						'mediaEmbed',
+						'undo',
+						'redo'
+					]
+				},
+				image: {
+					toolbar: [
+						'imageStyle:full',
+						'imageStyle:side',
+						'|',
+						'imageTextAlternative'
+					]
+				},
+				table: {
+					contentToolbar: [
+						'tableColumn',
+						'tableRow',
+						'mergeTableCells'
+					]
+				}
+			} )
+			.then( editor => {
+				window.editors[ selector ] = editor;
+			} )
+			.catch( err => {
+				console.error( err.stack );
+			} );
+	}
+
+	function destroyEditors() {
+		for ( const selector in window.editors ) {
+			window.editors[ selector ].destroy().then( () => {
+				window.editors[ selector ] = undefined;
+			} );
+		}
+
+		document.getElementById( 'destroyEditors' ).removeEventListener( 'click', destroyEditors );
+	}
+}
+
+document.getElementById( 'initEditors' ).addEventListener( 'click', initEditors );

+ 31 - 0
packages/ckeditor5-editor-balloon/tests/manual/memory.md

@@ -0,0 +1,31 @@
+1. Open DevTools on Chrome.
+2. Go to Memory tab.
+3. Select "Heap snapshot" profiling type.
+4. Click "Collect Garbage" (trash icon) and "Clear all profiles" (don't go icon).
+5. Record heap snapshot ("Take heap snapshot" a record icon).
+6. Repeat multiple times:
+    - click "Init editor",
+    - wait to editor show up ,
+    - click "Destroy editor".
+7. Record heap snapshot again.
+8. Compare Snapshot 1 with Snapshot 2 and look for:
+    - "detached" - HTML Elements/DOM nodes
+    - "EventListener" - there should be only 1
+    - Any CKEditor5 classes instances like "Editor" or "LivePosition"
+
+## Notes:
+
+* Browser extensions might attach event listeners to the DOM so the safest way to run this test is by running Chrome from command line:
+
+    ```
+    google-chrome \
+        --enable-precise-memory-info \
+        --disable-extensions \
+        --disable-plugins \
+        --incognito \
+        http://localhost:8125 
+    ```
+
+    The above will run Chrome without extensions or plugins in incognito mode and open manual tests page.
+
+* Close any running Chrome instance beforehand because otherwise it will open a tab in currently opened Chrome (look for "Opening in existing browser session." in command line).