Ver código fonte

Merge branch 'master' into t/ckeditor5/479

Aleksander Nowodzinski 7 anos atrás
pai
commit
3eeffa01e0

+ 1 - 1
packages/ckeditor5-editor-decoupled/.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-decoupled/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **Decoupled Editor** – https://github.com/ckeditor/ckeditor5-editor-decoupled <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 - 1
packages/ckeditor5-editor-decoupled/docs/framework/guides/document-editor.md

@@ -46,7 +46,7 @@ DecoupledEditor
 	} );
 ```
 
-You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorwithui~EditorWithUI#event:uiReady `Editor#uiReady`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`.
+You may have noticed that you have to make sure the editor UI is injected into your application after it fires the {@link module:core/editor/editorui~EditorUI#event:ready `EditorUI#ready`} event. The toolbar element can be found under `editor.ui.view.toolbar.element`.
 
 <info-box>
 	Document editor supports the Easy Image plugin provided by [CKEditor Cloud Services](https://ckeditor.com/ckeditor-cloud-services/) out of the box. Please refer to the {@link features/image-upload#easy-image documentation} to learn more.

+ 1 - 12
packages/ckeditor5-editor-decoupled/src/decouplededitor.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.
  */
 
@@ -15,7 +15,6 @@ import DecoupledEditorUIView from './decouplededitoruiview';
 import getDataFromElement from '@ckeditor/ckeditor5-utils/src/dom/getdatafromelement';
 import setDataInElement from '@ckeditor/ckeditor5-utils/src/dom/setdatainelement';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 import { isElement } from 'lodash-es';
 
 /**
@@ -78,16 +77,6 @@ export default class DecoupledEditor extends Editor {
 		this.ui = new DecoupledEditorUI( this, view );
 	}
 
-	/**
-	 * @inheritDoc
-	 */
-	get element() {
-		log.warn( 'deprecated-editor-element: The editor#element is deprecated.' );
-		// This editor has no single "main UI element". Its editable and toolbar are exposed separately and need
-		// to be added to the DOM manually by the consumer.
-		return null;
-	}
-
 	/**
 	 * Destroys the editor instance, releasing all resources used by it.
 	 *

+ 12 - 10
packages/ckeditor5-editor-decoupled/src/decouplededitorui.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.
  */
 
@@ -68,11 +68,15 @@ export default class DecoupledEditorUI 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 DecoupledEditor.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.
+		view.editable.name = editingRoot.rootName;
 
 		// Register the editable UI view in the editor. A single editor instance can aggregate multiple
 		// editable areas (roots) but the decoupled 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
@@ -88,10 +92,6 @@ export default class DecoupledEditorUI extends EditorUI {
 		// 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;
-
 		// 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.
 		editingView.attachDomRoot( editableElement );
@@ -103,7 +103,8 @@ export default class DecoupledEditorUI extends EditorUI {
 		editor.on( 'dataReady', () => {
 			view.editable.enableEditingRootListeners();
 
-			const placeholderText = editor.config.get( 'placeholder' ) || editor.sourceElement.getAttribute( 'placeholder' );
+			const placeholderText = editor.config.get( 'placeholder' ) ||
+				editor.sourceElement && editor.sourceElement.getAttribute( 'placeholder' );
 
 			if ( placeholderText ) {
 				const placeholderElement = getRootPlaceholderElement( editingRoot );
@@ -113,18 +114,19 @@ export default class DecoupledEditorUI extends EditorUI {
 		} );
 
 		this._initToolbar();
-		this.ready();
+		this.fire( 'ready' );
 	}
 
 	/**
 	 * @inheritDoc
 	 */
 	destroy() {
-		const view = this.view;
+		const view = this._view;
 		const editingView = this.editor.editing.view;
 
 		view.editable.disableEditingRootListeners();
 		editingView.detachDomRoot( view.editable.name );
+		view.destroy();
 
 		super.destroy();
 	}

+ 1 - 19
packages/ckeditor5-editor-decoupled/src/decouplededitoruiview.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.
  */
 
@@ -12,8 +12,6 @@ import InlineEditableUIView from '@ckeditor/ckeditor5-ui/src/editableui/inline/i
 import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
 import Template from '@ckeditor/ckeditor5-ui/src/template';
 
-import log from '@ckeditor/ckeditor5-utils/src/log';
-
 /**
  * The decoupled editor UI view. It is a virtual view providing an inline
  * {@link module:editor-decoupled/decouplededitoruiview~DecoupledEditorUIView#editable} and a
@@ -72,20 +70,4 @@ export default class DecoupledEditorUIView extends EditorUIView {
 
 		this.registerChild( [ this.toolbar, 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 DecoupledEditorUIView#editableElement property is deprecated.' );
-		return this.editable.editableElement;
-	}
 }

+ 18 - 29
packages/ckeditor5-editor-decoupled/tests/decouplededitor.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.
  */
 
@@ -20,6 +20,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 { describeMemoryUsage, testMemoryUsage } from '@ckeditor/ckeditor5-core/tests/_utils/memory';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+
 const editorData = '<p><strong>foo</strong> bar</p>';
 
 describe( 'DecoupledEditor', () => {
@@ -44,10 +47,6 @@ describe( 'DecoupledEditor', () => {
 			expect( testUtils.isMixed( DecoupledEditor, DataApiMixin ) ).to.be.true;
 		} );
 
-		it( 'implements the EditorWithUI interface', () => {
-			expect( editor.element ).to.be.null;
-		} );
-
 		it( 'creates main root element', () => {
 			expect( editor.model.document.getRoot( 'main' ) ).to.instanceof( RootElement );
 		} );
@@ -195,7 +194,6 @@ describe( 'DecoupledEditor', () => {
 						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 );
 						}
@@ -206,7 +204,7 @@ describe( 'DecoupledEditor', () => {
 							plugins: [ EventWatcher ]
 						} )
 						.then( newEditor => {
-							expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'uiReady', 'dataReady', 'ready' ] );
+							expect( fired ).to.deep.equal( [ 'pluginsReady', 'ready', 'dataReady', 'ready' ] );
 
 							return newEditor.destroy();
 						} );
@@ -234,28 +232,6 @@ describe( 'DecoupledEditor', () => {
 						} );
 				} );
 
-				it( 'fires uiReady once UI is rendered', () => {
-					let isReady;
-
-					class EventWatcher extends Plugin {
-						init() {
-							this.editor.on( 'uiReady', () => {
-								isReady = this.editor.ui.view.isRendered;
-							} );
-						}
-					}
-
-					return DecoupledEditor
-						.create( getElementOrData(), {
-							plugins: [ EventWatcher ]
-						} )
-						.then( newEditor => {
-							expect( isReady ).to.be.true;
-
-							return newEditor.destroy();
-						} );
-				} );
-
 				it( 'fires ready once UI is rendered', () => {
 					let isReady;
 
@@ -331,4 +307,17 @@ describe( 'DecoupledEditor', () => {
 			} );
 		}
 	} );
+
+	describeMemoryUsage( () => {
+		testMemoryUsage(
+			'should not grow on multiple create/destroy',
+			() => DecoupledEditor
+				.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-decoupled/tests/decouplededitorui.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.
  */
 
@@ -17,7 +17,7 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import utils from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 
 describe( 'DecoupledEditorUI', () => {
-	let editor, view, ui;
+	let editor, view, ui, viewElement;
 
 	testUtils.createSinonSandbox();
 
@@ -31,6 +31,7 @@ describe( 'DecoupledEditorUI', () => {
 
 				ui = editor.ui;
 				view = ui.view;
+				viewElement = view.element;
 			} );
 	} );
 
@@ -151,6 +152,12 @@ describe( 'DecoupledEditorUI', () => {
 		} );
 	} );
 
+	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 );
@@ -160,8 +167,8 @@ describe( 'DecoupledEditorUI', () => {
 			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;
 		} );
 	} );
 } );
@@ -202,8 +209,6 @@ class VirtualDecoupledTestEditor 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-decoupled/tests/decouplededitoruiview.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.
  */
 
@@ -11,7 +11,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( 'DecoupledEditorUIView', () => {
 	let locale, view;
@@ -126,14 +125,4 @@ describe( 'DecoupledEditorUIView', () => {
 			view.editable.element.remove();
 		} );
 	} );
-
-	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-decoupled/tests/manual/decouplededitor-editable.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-decoupled/tests/manual/decouplededitor.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.
  */
 

+ 49 - 0
packages/ckeditor5-editor-decoupled/tests/manual/memory.html

@@ -0,0 +1,49 @@
+<p>
+	<button id="destroyEditor">Destroy editor</button>
+	<button id="initEditor">Init editor</button>
+</p>
+
+<h2>The toolbar</h2>
+<div class="toolbar-container"></div>
+
+<h2>The editable</h2>
+<div class="editable-container"></div>
+
+<style>
+	.editable-container,
+	.toolbar-container {
+		position: relative;
+		border: 1px solid red;
+	}
+
+	.editable-container::after,
+	.toolbar-container::after {
+		content: attr(class);
+		position: absolute;
+		background: red;
+		color: #fff;
+		top: 0;
+		right: 0;
+		font: 10px/2 monospace;
+		padding: .1em .3em;
+	}
+
+	.toolbar-container{
+		padding: 1em;
+	}
+
+	.editable-container {
+		padding: 3em;
+		overflow-y: scroll;
+		max-height: 300px;
+	}
+
+	.editable-container .ck-editor__editable {
+		min-height: 21cm;
+		padding: 2em;
+		border: 1px #D3D3D3 solid;
+		border-radius: var(--ck-border-radius);
+		background: white;
+		box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
+	}
+</style>

+ 74 - 0
packages/ckeditor5-editor-decoupled/tests/manual/memory.js

@@ -0,0 +1,74 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals console:false, document, window */
+
+import DecoupledEditor from '../../src/decouplededitor';
+import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
+
+const editorData = '<h2>Hello world</h2><p>This is the decoupled editor.</p><img src="sample.jpg" />';
+
+/*
+ * Memory-leak safe version of decoupled 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 initEditor() {
+	DecoupledEditor
+		.create( editorData, {
+			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( newEditor => {
+			window.editor = newEditor;
+
+			document.querySelector( '.toolbar-container' ).appendChild( newEditor.ui.view.toolbar.element );
+			document.querySelector( '.editable-container' ).appendChild( newEditor.ui.view.editable.element );
+
+			document.getElementById( 'destroyEditor' ).addEventListener( 'click', destroyEditor );
+		} )
+		.catch( err => {
+			console.error( err.stack );
+		} );
+
+	function destroyEditor() {
+		window.editor.destroy().then( () => console.log( 'Editor was destroyed' ) );
+		window.editor = null;
+		document.getElementById( 'destroyEditor' ).removeEventListener( 'click', destroyEditor );
+	}
+}
+
+document.getElementById( 'initEditor' ).addEventListener( 'click', initEditor );

+ 31 - 0
packages/ckeditor5-editor-decoupled/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).

BIN
packages/ckeditor5-editor-decoupled/tests/manual/sample.jpg