瀏覽代碼

Merge branch 'stable'

Piotrek Koszuliński 6 年之前
父節點
當前提交
84891b9b13
共有 2 個文件被更改,包括 119 次插入80 次删除
  1. 80 8
      packages/ckeditor5-watchdog/docs/features/watchdog.md
  2. 39 72
      packages/ckeditor5-watchdog/src/watchdog.js

+ 80 - 8
packages/ckeditor5-watchdog/docs/features/watchdog.md

@@ -5,13 +5,25 @@ category: features
 
 # Watchdog
 
-The {@link module:watchdog/watchdog~Watchdog} feature allows you to create a wrapper for the editor that will ensure that the editor instance is running. If a {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` error} is thrown by the editor, it tries to restart the editor to the state before the crash. All other errors are transparent to the watchdog. By looking at the error context, the Watchdog restarts only the editor which crashed.
+Every non trivial piece of software has bugs. Despite our high quality standards like 100% code coverage, regression testing and manual tests before every release, CKEditor 5 is not free of bugs. Neither is the browser used by the user, your application in which CKEditor 5 is integrate, or any 3rd party addons that you used.
 
-**Note**: The watchdog does not handle errors during editor initialization (`Editor.create()`) and editor destruction (`editor.destroy()`). Errors at these stages mean that there is a serious problem in the code integrating the editor and such problem cannot be easily fixed restarting the editor.
+In order to limit the effect of an editor crash on the user experience, you can automatically restart the editor with the content saved just before the crash.
 
-**Note**: A new editor instance is created each time the watchdog is restarted. Thus the editor instance should not be kept internally. Use the `watchdog.editor` each time you need to access the editor. It also means that you should not execute any actions on `editor.create` because these actions will not be executed when the editor restarts. Use `watchdog.create` instead, add a plugin and add your code in the `plugin#init`, or listen on `watchdog#restart` to handle restarts.
+The {@link module:watchdog/watchdog~Watchdog} util allows you to do that exactly. It ensures that an editor instance is running, despite a potential crash. It works by detecting that an editor crashed, destroying it and automatically creating a new instance of that editor with the content of the previous editor.
 
-## Basic implementation
+## Usage
+
+<info-box>
+	Note: watchdog can be used only with an {@link builds/guides/integration/advanced-setup#scenario-2-building-from-source editor built from source}.
+</info-box>
+
+Install the [`@ckeditor/ckeditor5-watchdog`](https://www.npmjs.com/package/@ckeditor/ckeditor5-watchdog) package:
+
+```bash
+npm install --save @ckeditor/ckeditor5-watchdog
+```
+
+And then change your `ClassicEditor.create()` call to `watchdog.create()` as follows:
 
 ```js
 import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
@@ -22,14 +34,74 @@ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
 import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
 
+// Create a watchdog for the given editor type.
 const watchdog = Watchdog.for( ClassicEditor );
 
+// Create a new editor instance.
 watchdog.create( document.querySelector( '#editor' ), {
 	plugins: [ Essentials, Paragraph, Bold, Italic ],
 	toolbar: [ 'bold', 'italic', 'alignment' ]
-} )
-	.then( () => {
-		const editor = watchdog.editor;
-	} );
+} );
 ```
 
+In other words, your goal is to create a watchdog instance and make the watchdog create an instance of the editor you want to use. Watchdog will then create a new editor and if it ever crashes restart it by creating a new editor.
+
+<info-box>
+	A new editor instance is created every time the watchdog detects a crash. Thus, the editor instance should not be kept in your application's state. Use the {@link module:watchdog/watchdog~Watchdog#editor Watchdog#editor`} property instead.
+
+	It also means that any code that should be executed for any new editor instance should be either loaded as an editor plugin or executed in the callbacks defined by {@link module:watchdog/watchdog~Watchdog#setCreator `Watchdog#setCreator()`} and {@link module:watchdog/watchdog~Watchdog#setDestructor `Watchdog#setDestructor()`}. Read more about controlling editor creation/destruction in the next section.
+</info-box>
+
+### Controlling editor creation/destruction
+
+For more control over the creation and destruction of editor instances, you can use the {@link module:watchdog/watchdog~Watchdog#setCreator `Watchdog#setCreator()`} and {@link module:watchdog/watchdog~Watchdog#setDestructor `Watchdog#setDestructor()`} methods:
+
+```js
+// Instantiate the watchdog manually (don't use the for() helper).
+const watchdog = new Watchdog();
+
+watchdog.setCreator( ( elementOrData, editorConfig ) => {
+	return ClassicEditor
+		.create( elementOrData, editorConfig )
+		.then( editor => {
+			// Do something with the new editor instance.
+		} );
+} );
+
+watchdog.setDestructor( editor => {
+	// Do something before the editor is destroyed.
+
+	return editor
+		.destroy()
+		.then( () => {
+			// Do something after the editor is destroyed.
+		} );
+ } );
+
+watchdog.create( elementOrData, editorConfig );
+```
+
+### API
+
+Other useful {@link module:watchdog/watchdog~Watchdog methods, properties and events}:
+
+ ```js
+watchdog.on( 'error', () => { console.log( 'Editor crashed.' ) } );
+watchdog.on( 'restart', () => { console.log( 'Editor was restarted.' ) } );
+
+// Destroy the watchdog and the current editor instance.
+watchdog.destroy();
+
+// The current editor instance.
+watchdog.editor;
+
+watchdog.crashes.forEach( crashInfo => console.log( crashInfo ) );
+```
+
+## Limitations
+
+The CKEditor 5 watchdog listens to uncaught errors which can be associated with the editor instance created by that watchdog. Currently, these errors are {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` errors} so ones explicitly thrown by the editor (by its internal checks). This means that not every runtime error which crashed the editor can be caught which, in turn, means that not every crash can be detected.
+
+However, with time, the most "dangerous" places in the API will be covered with checks and `try-catch` blocks (allowing detecting unknown errors).
+
+<info-box>The watchdog does not handle errors thrown during editor initialization (by `Editor.create()`) and editor destruction (`Editor#destroy()`). Errors thrown at these stages mean that there is a serious problem in the code integrating the editor with your application and such problem cannot be easily fixed by restarting the editor.</info-box>

+ 39 - 72
packages/ckeditor5-watchdog/src/watchdog.js

@@ -18,44 +18,8 @@ import areConnectedThroughProperties from '@ckeditor/ckeditor5-utils/src/areconn
 /**
  * A watchdog for CKEditor 5 editors.
  *
- * It keeps an {@link module:core/editor/editor~Editor editor} instance running.
- * If a {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` error}
- * is thrown by the editor, it tries to restart the editor to the state before the crash. All other errors
- * are transparent to the watchdog. By looking at the error context, the Watchdog restarts only the editor which crashed.
- *
- * Note that the watchdog does not handle errors during editor initialization (`Editor.create()`)
- * and editor destruction (`editor.destroy()`). Errors at these stages mean that there is a serious
- * problem in the code integrating the editor and such problem cannot be easily fixed restarting the editor.
- *
- * A new editor instance is created each time the watchdog is restarted. Thus the editor instance should not be kept
- * internally. Use the `watchdog.editor` each time you need to access the editor.
- *
- * Basic usage:
- *
- * 		const watchdog = Watchdog.for( ClassicEditor );
- *
- * 		watchdog.create( elementOrData, editorConfig ).then( editor => {} );
- *
- * Full usage:
- *
- *		const watchdog = new Watchdog();
- *
- *		watchdog.setCreator( ( elementOrData, editorConfig ) => ClassicEditor.create( elementOrData, editorConfig ) );
- *		watchdog.setDestructor( editor => editor.destroy() );
- *
- *		watchdog.create( elementOrData, editorConfig ).then( editor => {} );
- *
- * Other important APIs:
- *
- *		watchdog.on( 'error', () => { console.log( 'Editor crashed.' ) } );
- *		watchdog.on( 'restart', () => { console.log( 'Editor was restarted.' ) } );
- *
- *		watchdog.restart(); // Restarts the editor.
- *		watchdog.destroy(); // Destroys the editor and global error event listeners.
- *
- * 		watchdog.editor; // Current editor instance.
- *
- * 		watchdog.crashes.forEach( crashInfo => console.log( crashInfo ) );
+ * See the {@glink features/watchdog Watchdog} feature guide to learn the rationale behind it and
+ * how to use it.
  */
 export default class Watchdog {
 	/**
@@ -66,11 +30,12 @@ export default class Watchdog {
 	 */
 	constructor( { crashNumberLimit, waitingTime } = {} ) {
 		/**
-		 * An array of crashes saved as an object with the following props:
-		 * * message: String,
-		 * * source: String,
-		 * * lineno: String,
-		 * * colno: String
+		 * An array of crashes saved as an object with the following properties:
+		 *
+		 * * `message`: `String`,
+		 * * `source`: `String`,
+		 * * `lineno`: `String`,
+		 * * `colno`: `String`
 		 *
 		 * @public
 		 * @readonly
@@ -79,7 +44,7 @@ export default class Watchdog {
 		this.crashes = [];
 
 		/**
-		 * Crash number limit (default to 3). After the limit is reached the editor is not restarted by the watchdog.
+		 * Crash number limit (defaults to `3`). After this limit is reached the editor is not restarted by the watchdog.
 		 * This is to prevent an infinite crash loop.
 		 *
 		 * @private
@@ -106,7 +71,7 @@ export default class Watchdog {
 		this._throttledSave = throttle( this._save.bind( this ), waitingTime || 5000 );
 
 		/**
-		 * The current editor.
+		 * The current editor instance.
 		 *
 		 * @private
 		 * @type {module:core/editor/editor~Editor}
@@ -144,18 +109,18 @@ export default class Watchdog {
 		 */
 
 		/**
-		* The editor source element or data.
-		*
-		* @private
-		* @member {HTMLElement|String} #_elementOrData
-		*/
+		 * The editor source element or data.
+		 *
+		 * @private
+		 * @member {HTMLElement|String} #_elementOrData
+		 */
 
 		/**
-		* The editor configuration.
-		*
-		* @private
-		* @member {Object|undefined} #_config
-		*/
+		 * The editor configuration.
+		 *
+		 * @private
+		 * @member {Object|undefined} #_config
+		 */
 	}
 
 	/**
@@ -172,7 +137,7 @@ export default class Watchdog {
 	 * Sets the function that is responsible for editor creation.
 	 * It expects a function that should return a promise.
 	 *
-	 * 		watchdog.setCreator( ( el, config ) => ClassicEditor.create( el, config ) );
+	 *		watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
 	 *
 	 * @param {Function} creator
 	 */
@@ -193,8 +158,8 @@ export default class Watchdog {
 	}
 
 	/**
-	 * Creates a watched editor instance using the creator passed to the {@link #setCreator} method or
-	 * {@link module:watchdog/watchdog~Watchdog.for} helper.
+	 * Creates a watched editor instance using the creator passed to the {@link #setCreator `setCreator()`} method or
+	 * {@link module:watchdog/watchdog~Watchdog.for `Watchdog.for()`} helper.
 	 *
 	 * @param {HTMLElement|String} elementOrData
 	 * @param {module:core/editor/editorconfig~EditorConfig} [config]
@@ -204,26 +169,28 @@ export default class Watchdog {
 	create( elementOrData, config ) {
 		if ( !this._creator ) {
 			/**
-			 * @error watchdog-creator-not-defined
+			 * The watchdog's editor creator is not defined. Define it by using
+			 * {@link module:watchdog/watchdog~Watchdog#setCreator `Watchdog#setCreator()`} or
+			 * the {@link module:watchdog/watchdog~Watchdog.for `Watchdog.for()`} helper.
 			 *
-			 * The watchdog creator is not defined, define it using `watchdog.setCreator()` or `Watchdog.for` helper.
+			 * @error watchdog-creator-not-defined
 			 */
 			throw new CKEditorError(
-				'watchdog-creator-not-defined: The watchdog creator is not defined, define it using `watchdog.setCreator()` ' +
-				'or `Watchdog.for` helper.',
+				'watchdog-creator-not-defined: The watchdog\'s editor creator is not defined.',
 				null
 			);
 		}
 
 		if ( !this._destructor ) {
 			/**
-			 * @error watchdog-destructor-not-defined
+			 * The watchdog's editor destructor is not defined. Define it by using
+			 * {@link module:watchdog/watchdog~Watchdog#setDestructor `Watchdog#setDestructor()`} or
+			 * the {@link module:watchdog/watchdog~Watchdog.for `Watchdog.for()`} helper.
 			 *
-			 * The watchdog destructor is not defined, define it using `watchdog.setDestructor()` or `Watchdog.for` helper.
+			 * @error watchdog-destructor-not-defined
 			 */
 			throw new CKEditorError(
-				'watchdog-destructor-not-defined: The watchdog destructor is not defined, define it using `watchdog.setDestructor()` ' +
-				'or `Watchdog.for` helper.',
+				'watchdog-destructor-not-defined: The watchdog\'s editor destructor is not defined.',
 				null
 			);
 		}
@@ -282,7 +249,7 @@ export default class Watchdog {
 	}
 
 	/**
-	 * Destroys the current editor using the destructor passed to {@link #setDestructor} method.
+	 * Destroys the current editor instance by using the destructor passed to the {@link #setDestructor `setDestructor()`} method.
 	 *
 	 * @returns {Promise.<module:watchdog/watchdog~Watchdog>}
 	 */
@@ -328,7 +295,7 @@ export default class Watchdog {
 
 	/**
 	 * Checks if the event error comes from the editor that is handled by the watchdog (by checking the error context) and
-	 * restarts the editor. It handles {@link module:utils/ckeditorerror~CKEditorError CKEditorError errors} only.
+	 * restarts the editor. It handles {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` errors} only.
 	 *
 	 * @private
 	 * @fires error
@@ -379,14 +346,14 @@ export default class Watchdog {
 	}
 
 	/**
-	 * A shortcut method for creating the Watchdog. For the full usage see the
-	 * {@link ~Watchdog Watchdog class description}.
+	 * A shorthand method for creating an instance of the watchdog. For the full usage see the
+	 * {@link ~Watchdog `Watchdog` class description}.
 	 *
 	 * Usage:
 	 *
-	 * 		const watchdog = Watchdog.for( ClassicEditor );
+	 *		const watchdog = Watchdog.for( ClassicEditor );
 	 *
-	 * 		watchdog.create( elementOrData, config ).then( editor => {} );
+	 *		watchdog.create( elementOrData, config );
 	 *
 	 * @param {*} Editor The editor class.
 	 */