浏览代码

Wrapped the `view.change()` into the try/catch block.

Maciej Bukowski 6 年之前
父节点
当前提交
c220ac73a0
共有 2 个文件被更改,包括 69 次插入19 次删除
  1. 40 19
      packages/ckeditor5-engine/src/view/view.js
  2. 29 0
      packages/ckeditor5-engine/tests/view/view/view.js

+ 40 - 19
packages/ckeditor5-engine/src/view/view.js

@@ -457,29 +457,50 @@ export default class View {
 			);
 		}
 
-		// Recursive call to view.change() method - execute listener immediately.
-		if ( this._ongoingChange ) {
-			return callback( this._writer );
-		}
+		// Use try-catch to catch the
+		try {
+			// Recursive call to view.change() method - execute listener immediately.
+			if ( this._ongoingChange ) {
+				return callback( this._writer );
+			}
 
-		// This lock will assure that all recursive calls to view.change() will end up in same block - one "render"
-		// event for all nested calls.
-		this._ongoingChange = true;
-		const callbackResult = callback( this._writer );
-		this._ongoingChange = false;
+			// This lock will assure that all recursive calls to view.change() will end up in same block - one "render"
+			// event for all nested calls.
+			this._ongoingChange = true;
+			const callbackResult = callback( this._writer );
+			this._ongoingChange = false;
+
+			// This lock is used by editing controller to render changes from outer most model.change() once. As plugins might call
+			// view.change() inside model.change() block - this will ensures that postfixers and rendering are called once after all
+			// changes. Also, we don't need to render anything if there're no changes since last rendering.
+			if ( !this._renderingDisabled && this._hasChangedSinceTheLastRendering ) {
+				this._postFixersInProgress = true;
+				this.document._callPostFixers( this._writer );
+				this._postFixersInProgress = false;
+
+				this.fire( 'render' );
+			}
 
-		// This lock is used by editing controller to render changes from outer most model.change() once. As plugins might call
-		// view.change() inside model.change() block - this will ensures that postfixers and rendering are called once after all changes.
-		// Also, we don't need to render anything if there're no changes since last rendering.
-		if ( !this._renderingDisabled && this._hasChangedSinceTheLastRendering ) {
-			this._postFixersInProgress = true;
-			this.document._callPostFixers( this._writer );
-			this._postFixersInProgress = false;
+			return callbackResult;
+		} catch ( err ) {
+			if ( err.is && err.is( 'CKEditorError' ) ) {
+				throw err;
+			}
 
-			this.fire( 'render' );
+			/**
+			 * An unexpected error occurred inside the `view.change()` block. The `error.data.originalError` property
+			 * shows the original error properties.
+			 *
+			 * @error view-change-unexpected-error
+			 */
+			throw new CKEditorError( 'view-change-unexpected-error', this, {
+				originalError: {
+					message: err.message,
+					stack: err.stack,
+					name: err.name
+				}
+			} );
 		}
-
-		return callbackResult;
 	}
 
 	/**

+ 29 - 0
packages/ckeditor5-engine/tests/view/view/view.js

@@ -25,6 +25,7 @@ import createViewRoot from '../_utils/createroot';
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 import env from '@ckeditor/ckeditor5-utils/src/env';
+import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 describe( 'view', () => {
 	const DEFAULT_OBSERVERS_COUNT = 6;
@@ -802,6 +803,34 @@ describe( 'view', () => {
 			expect( result2 ).to.equal( true );
 			expect( result3 ).to.undefined;
 		} );
+
+		it( 'should catch native errors and wrap them into the CKEditorError errors', () => {
+			const error = new TypeError( 'foo' );
+			error.stack = 'bar';
+
+			expectToThrowCKEditorError( () => {
+				view.change( () => {
+					throw error;
+				} );
+			}, /view-change-unexpected-error/, view, {
+				originalError: {
+					message: 'foo',
+					stack: 'bar',
+					name: 'TypeError'
+				}
+			} );
+		} );
+
+		it( 'should re-throw custom CKEditorError errors', () => {
+			const error = new TypeError( 'foo' );
+			error.stack = 'bar';
+
+			expectToThrowCKEditorError( () => {
+				view.change( () => {
+					throw new CKEditorError( 'foo', view );
+				} );
+			}, /foo/, view );
+		} );
 	} );
 
 	describe( 'createPositionAt()', () => {