浏览代码

Check for non-existent roots in `DataController` `get()`, `set()` and `init()` methods.

Krzysztof Krztoń 7 年之前
父节点
当前提交
4b27135da4

+ 59 - 19
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -113,6 +113,21 @@ export default class DataController {
 	 * @returns {String} Output data.
 	 */
 	get( rootName = 'main' ) {
+		if ( !_checkIfRootsExists( [ rootName ], this.model.document.getRootNames() ) ) {
+			/**
+			 * Cannot get data from non-existing root. This error is thrown when {@link #get DataController#get() method}
+			 * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
+			 * calling {@link #get} like:
+			 *
+			 * 		data.get( 'root2' );
+			 *
+			 * will throw this error.
+			 *
+			 * @error trying-to-get-data-from-non-existing-root
+			 */
+			throw new CKEditorError( 'trying-to-get-data-from-non-existing-root: Attempting to get data from non-existing root.' );
+		}
+
 		// Get model range.
 		return this.stringify( this.model.document.getRoot( rootName ) );
 	}
@@ -205,6 +220,21 @@ export default class DataController {
 			initialData = data;
 		}
 
+		if ( !_checkIfRootsExists( Object.keys( initialData ), this.model.document.getRootNames() ) ) {
+			/**
+			 * Cannot init data on non-existing root. This error is thrown when {@link #init DataController#init() method}
+			 * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
+			 * calling {@link #init} like:
+			 *
+			 * 		data.init( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
+			 *
+			 * will throw this error.
+			 *
+			 * @error trying-to-init-data-on-non-existing-root
+			 */
+			throw new CKEditorError( 'trying-to-init-data-on-non-existing-root: Attempting to init data on non-existing root.' );
+		}
+
 		this.model.enqueueChange( 'transparent', writer => {
 			for ( const rootName of Object.keys( initialData ) ) {
 				const modelRoot = this.model.document.getRoot( rootName );
@@ -235,25 +265,19 @@ export default class DataController {
 			initialData = data;
 		}
 
-		const rootNames = this.model.document.getRootNames();
-
-		// Check if all roots exists. Thrown an error if there is non-existing one before changing any data.
-		for ( const rootName of Object.keys( initialData ) ) {
-			if ( !rootNames.includes( rootName ) ) {
-				/**
-				 * Cannot set data on non-existing root. This error is thrown when {@link #set DataController#set() method}
-				 * is called with absent root name. For example, if there is an editor instance only with the `main` root,
-				 * calling {@link #set} like:
-				 *
-				 * 		data.set( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
-				 *
-			 	 * will throw this error.
-				 *
-				 * @error trying-to-set-data-on-non-existing-root
-				 */
-				throw new CKEditorError( 'trying-to-set-data-on-non-existing-root: ' +
-					`Attempting to set data on non-existing "${ rootName }" root.` );
-			}
+		if ( !_checkIfRootsExists( Object.keys( initialData ), this.model.document.getRootNames() ) ) {
+			/**
+			 * Cannot set data on non-existing root. This error is thrown when {@link #set DataController#set() method}
+			 * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
+			 * calling {@link #set} like:
+			 *
+			 * 		data.set( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
+			 *
+			 * will throw this error.
+			 *
+			 * @error trying-to-set-data-on-non-existing-root
+			 */
+			throw new CKEditorError( 'trying-to-set-data-on-non-existing-root: Attempting to set data on non-existing root.' );
 		}
 
 		this.model.enqueueChange( 'transparent', writer => {
@@ -347,3 +371,19 @@ function _getMarkersRelativeToElement( element ) {
 
 	return result;
 }
+
+// Check if all provided root names are present in the given list of editor roots.
+//
+// @private
+// @param {Array.<String>} rootNames Root names to check.
+// @param {Array.<String>} editorRoots List of editor root names.
+// @returns {Boolean} Whether all provided root names are present in the editor.
+function _checkIfRootsExists( rootNames, editorRoots ) {
+	for ( const rootName of rootNames ) {
+		if ( !editorRoots.includes( rootName ) ) {
+			return false;
+		}
+	}
+
+	return true;
+}

+ 33 - 2
packages/ckeditor5-engine/tests/controller/datacontroller.js

@@ -218,6 +218,28 @@ describe( 'DataController', () => {
 
 			return promise;
 		} );
+
+		it( 'should throw an error when non-existent root is used (single)', () => {
+			expect( () => {
+				data.init( { nonexistent: '<p>Bar</p>' } );
+			} ).to.throw(
+				CKEditorError,
+				'trying-to-init-data-on-non-existing-root: Attempting to init data on non-existing root.'
+			);
+		} );
+
+		it( 'should throw an error when non-existent root is used (one of many)', () => {
+			schema.extend( '$text', { allowIn: '$root' } );
+
+			expect( () => {
+				data.init( { main: 'bar', nonexistent: '<p>Bar</p>' } );
+			} ).to.throw(
+				CKEditorError,
+				'trying-to-init-data-on-non-existing-root: Attempting to init data on non-existing root.'
+			);
+
+			expect( getData( model, { withoutSelection: true } ) ).to.equal( '' );
+		} );
 	} );
 
 	describe( 'set()', () => {
@@ -287,7 +309,7 @@ describe( 'DataController', () => {
 				data.set( { nonexistent: '<p>Bar</p>' } );
 			} ).to.throw(
 				CKEditorError,
-				'trying-to-set-data-on-non-existing-root: Attempting to set data on non-existing "nonexistent" root.'
+				'trying-to-set-data-on-non-existing-root: Attempting to set data on non-existing root.'
 			);
 		} );
 
@@ -299,7 +321,7 @@ describe( 'DataController', () => {
 				data.set( { main: 'bar', nonexistent: '<p>Bar</p>' } );
 			} ).to.throw(
 				CKEditorError,
-				'trying-to-set-data-on-non-existing-root: Attempting to set data on non-existing "nonexistent" root.'
+				'trying-to-set-data-on-non-existing-root: Attempting to set data on non-existing root.'
 			);
 
 			expect( getData( model, { withoutSelection: true } ) ).to.equal( 'foo' );
@@ -374,6 +396,15 @@ describe( 'DataController', () => {
 			expect( data.get( 'main' ) ).to.equal( '<p>foo</p>' );
 			expect( data.get( 'title' ) ).to.equal( 'Bar' );
 		} );
+
+		it( 'should throw an error when non-existent root is used', () => {
+			expect( () => {
+				data.get( 'nonexistent' );
+			} ).to.throw(
+				CKEditorError,
+				'trying-to-get-data-from-non-existing-root: Attempting to get data from non-existing root.'
+			);
+		} );
 	} );
 
 	describe( 'stringify()', () => {