@ckeditor/ckeditor5-watchdog 10.0.1 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -1
- package/LICENSE.md +1 -1
- package/README.md +2 -3
- package/package.json +16 -9
- package/src/contextwatchdog.js +547 -0
- package/src/editorwatchdog.js +327 -0
- package/src/utils/areconnectedthroughproperties.js +80 -0
- package/src/utils/getsubnodes.js +89 -0
- package/src/watchdog.js +212 -235
package/src/watchdog.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright (c) 2003-
|
|
2
|
+
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
|
|
3
3
|
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
|
4
4
|
*/
|
|
5
5
|
|
|
@@ -7,35 +7,30 @@
|
|
|
7
7
|
* @module watchdog/watchdog
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
/* globals
|
|
11
|
-
|
|
12
|
-
import mix from '@ckeditor/ckeditor5-utils/src/mix';
|
|
13
|
-
import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
|
|
14
|
-
import { throttle, cloneDeepWith, isElement } from 'lodash-es';
|
|
15
|
-
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
|
|
16
|
-
import areConnectedThroughProperties from '@ckeditor/ckeditor5-utils/src/areconnectedthroughproperties';
|
|
10
|
+
/* globals window */
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
|
-
*
|
|
13
|
+
* An abstract watchdog class that handles most of the error handling process and the state of the underlying component.
|
|
14
|
+
*
|
|
15
|
+
* See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and how to use it.
|
|
20
16
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
17
|
+
* @private
|
|
18
|
+
* @abstract
|
|
23
19
|
*/
|
|
24
20
|
export default class Watchdog {
|
|
25
21
|
/**
|
|
26
|
-
* @param {
|
|
27
|
-
* @param {Number} [config.crashNumberLimit=3] A threshold specifying the number of crashes
|
|
28
|
-
* when the watchdog stops restarting the editor in case of errors.
|
|
29
|
-
* @param {Number} [config.waitingTime=5000] A minimum amount of milliseconds between saving editor data internally.
|
|
22
|
+
* @param {module:watchdog/watchdog~WatchdogConfig} config The watchdog plugin configuration.
|
|
30
23
|
*/
|
|
31
|
-
constructor(
|
|
24
|
+
constructor( config ) {
|
|
32
25
|
/**
|
|
33
26
|
* An array of crashes saved as an object with the following properties:
|
|
34
27
|
*
|
|
35
28
|
* * `message`: `String`,
|
|
36
|
-
* * `
|
|
37
|
-
* * `
|
|
38
|
-
* * `
|
|
29
|
+
* * `stack`: `String`,
|
|
30
|
+
* * `date`: `Number`,
|
|
31
|
+
* * `filename`: `String | undefined`,
|
|
32
|
+
* * `lineno`: `Number | undefined`,
|
|
33
|
+
* * `colno`: `Number | undefined`,
|
|
39
34
|
*
|
|
40
35
|
* @public
|
|
41
36
|
* @readonly
|
|
@@ -44,339 +39,321 @@ export default class Watchdog {
|
|
|
44
39
|
this.crashes = [];
|
|
45
40
|
|
|
46
41
|
/**
|
|
47
|
-
*
|
|
48
|
-
* This is to prevent an infinite crash loop.
|
|
42
|
+
* Specifies the state of the item watched by the watchdog. The state can be one of the following values:
|
|
49
43
|
*
|
|
50
|
-
*
|
|
44
|
+
* * `initializing` – Before the first initialization, and after crashes, before the item is ready.
|
|
45
|
+
* * `ready` – A state when the user can interact with the item.
|
|
46
|
+
* * `crashed` – A state when an error occurs. It quickly changes to `initializing` or `crashedPermanently`
|
|
47
|
+
* depending on how many and how frequent errors have been caught recently.
|
|
48
|
+
* * `crashedPermanently` – A state when the watchdog stops reacting to errors and keeps the item it is watching crashed,
|
|
49
|
+
* * `destroyed` – A state when the item is manually destroyed by the user after calling `watchdog.destroy()`.
|
|
50
|
+
*
|
|
51
|
+
* @public
|
|
52
|
+
* @member {'initializing'|'ready'|'crashed'|'crashedPermanently'|'destroyed'} #state
|
|
53
|
+
*/
|
|
54
|
+
this.state = 'initializing';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @protected
|
|
51
58
|
* @type {Number}
|
|
59
|
+
* @see module:watchdog/watchdog~WatchdogConfig
|
|
52
60
|
*/
|
|
53
|
-
this._crashNumberLimit = crashNumberLimit
|
|
61
|
+
this._crashNumberLimit = typeof config.crashNumberLimit === 'number' ? config.crashNumberLimit : 3;
|
|
54
62
|
|
|
55
63
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
64
|
+
* Returns the result of the `Date.now()` call. It can be overridden in tests to mock time as some popular
|
|
65
|
+
* approaches like `sinon.useFakeTimers()` do not work well with error handling.
|
|
58
66
|
*
|
|
59
|
-
* @
|
|
60
|
-
* @type {Function}
|
|
67
|
+
* @protected
|
|
61
68
|
*/
|
|
62
|
-
this.
|
|
69
|
+
this._now = Date.now;
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* @private
|
|
69
|
-
* @type {Function}
|
|
72
|
+
* @protected
|
|
73
|
+
* @type {Number}
|
|
74
|
+
* @see module:watchdog/watchdog~WatchdogConfig
|
|
70
75
|
*/
|
|
71
|
-
this.
|
|
76
|
+
this._minimumNonErrorTimePeriod = typeof config.minimumNonErrorTimePeriod === 'number' ? config.minimumNonErrorTimePeriod : 5000;
|
|
72
77
|
|
|
73
78
|
/**
|
|
74
|
-
*
|
|
79
|
+
* Checks if the event error comes from the underlying item and restarts the item.
|
|
75
80
|
*
|
|
76
81
|
* @private
|
|
77
|
-
* @type {
|
|
82
|
+
* @type {Function}
|
|
78
83
|
*/
|
|
79
|
-
this.
|
|
84
|
+
this._boundErrorHandler = evt => {
|
|
85
|
+
// `evt.error` is exposed by EventError while `evt.reason` is available in PromiseRejectionEvent.
|
|
86
|
+
const error = evt.error || evt.reason;
|
|
87
|
+
|
|
88
|
+
// Note that `evt.reason` might be everything that is in the promise rejection.
|
|
89
|
+
// Similarly everything that is thrown lands in `evt.error`.
|
|
90
|
+
if ( error instanceof Error ) {
|
|
91
|
+
this._handleError( error, evt );
|
|
92
|
+
}
|
|
93
|
+
};
|
|
80
94
|
|
|
81
95
|
/**
|
|
82
|
-
* The
|
|
96
|
+
* The creation method.
|
|
83
97
|
*
|
|
84
|
-
* @
|
|
98
|
+
* @protected
|
|
85
99
|
* @member {Function} #_creator
|
|
86
100
|
* @see #setCreator
|
|
87
101
|
*/
|
|
88
102
|
|
|
89
103
|
/**
|
|
90
|
-
* The
|
|
104
|
+
* The destruction method.
|
|
91
105
|
*
|
|
92
|
-
* @
|
|
106
|
+
* @protected
|
|
93
107
|
* @member {Function} #_destructor
|
|
94
108
|
* @see #setDestructor
|
|
95
109
|
*/
|
|
96
110
|
|
|
97
111
|
/**
|
|
98
|
-
* The
|
|
112
|
+
* The watched item.
|
|
99
113
|
*
|
|
100
|
-
* @
|
|
101
|
-
* @
|
|
114
|
+
* @abstract
|
|
115
|
+
* @protected
|
|
116
|
+
* @member {Object|undefined} #_item
|
|
102
117
|
*/
|
|
103
118
|
|
|
104
119
|
/**
|
|
105
|
-
* The
|
|
120
|
+
* The method responsible for restarting the watched item.
|
|
106
121
|
*
|
|
107
|
-
* @
|
|
108
|
-
* @
|
|
122
|
+
* @abstract
|
|
123
|
+
* @protected
|
|
124
|
+
* @method #_restart
|
|
109
125
|
*/
|
|
110
126
|
|
|
111
127
|
/**
|
|
112
|
-
*
|
|
128
|
+
* Traverses the error context and the watched item to find out whether the error should
|
|
129
|
+
* be handled by the given item.
|
|
113
130
|
*
|
|
114
|
-
* @
|
|
115
|
-
* @
|
|
131
|
+
* @abstract
|
|
132
|
+
* @protected
|
|
133
|
+
* @method #_isErrorComingFromThisItem
|
|
134
|
+
* @param {module:utils/ckeditorerror~CKEditorError} error
|
|
116
135
|
*/
|
|
117
136
|
|
|
118
137
|
/**
|
|
119
|
-
*
|
|
138
|
+
* A dictionary of event emitter listeners.
|
|
120
139
|
*
|
|
121
140
|
* @private
|
|
122
|
-
* @
|
|
141
|
+
* @type {Object.<String,Array.<Function>>}
|
|
123
142
|
*/
|
|
124
|
-
|
|
143
|
+
this._listeners = {};
|
|
125
144
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
get editor() {
|
|
133
|
-
return this._editor;
|
|
145
|
+
if ( !this._restart ) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
'The Watchdog class was split into the abstract `Watchdog` class and the `EditorWatchdog` class. ' +
|
|
148
|
+
'Please, use `EditorWatchdog` if you have used the `Watchdog` class previously.'
|
|
149
|
+
);
|
|
150
|
+
}
|
|
134
151
|
}
|
|
135
152
|
|
|
136
153
|
/**
|
|
137
|
-
* Sets the function that is responsible for
|
|
138
|
-
* It expects a function that should return a promise.
|
|
139
|
-
*
|
|
140
|
-
* watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
|
|
154
|
+
* Sets the function that is responsible for creating watched items.
|
|
141
155
|
*
|
|
142
|
-
* @param {Function} creator
|
|
156
|
+
* @param {Function} creator A callback responsible for creating an item. Returns a promise
|
|
157
|
+
* that is resolved when the item is created.
|
|
143
158
|
*/
|
|
144
159
|
setCreator( creator ) {
|
|
145
160
|
this._creator = creator;
|
|
146
161
|
}
|
|
147
162
|
|
|
148
163
|
/**
|
|
149
|
-
* Sets the function that is responsible for
|
|
150
|
-
* It expects a function that should return a promise or `undefined`.
|
|
151
|
-
*
|
|
152
|
-
* watchdog.setDestructor( editor => editor.destroy() );
|
|
164
|
+
* Sets the function that is responsible for destroying watched items.
|
|
153
165
|
*
|
|
154
|
-
* @param {Function} destructor
|
|
166
|
+
* @param {Function} destructor A callback that takes the item and returns the promise
|
|
167
|
+
* to the destroying process.
|
|
155
168
|
*/
|
|
156
169
|
setDestructor( destructor ) {
|
|
157
170
|
this._destructor = destructor;
|
|
158
171
|
}
|
|
159
172
|
|
|
160
173
|
/**
|
|
161
|
-
*
|
|
162
|
-
|
|
174
|
+
* Destroys the watchdog and releases the resources.
|
|
175
|
+
*/
|
|
176
|
+
destroy() {
|
|
177
|
+
this._stopErrorHandling();
|
|
178
|
+
|
|
179
|
+
this._listeners = {};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Starts listening to a specific event name by registering a callback that will be executed
|
|
184
|
+
* whenever an event with a given name fires.
|
|
163
185
|
*
|
|
164
|
-
*
|
|
165
|
-
* @param {module:core/editor/editorconfig~EditorConfig} [config]
|
|
186
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
166
187
|
*
|
|
167
|
-
* @
|
|
188
|
+
* @param {String} eventName The event name.
|
|
189
|
+
* @param {Function} callback A callback which will be added to event listeners.
|
|
168
190
|
*/
|
|
169
|
-
|
|
170
|
-
if ( !this.
|
|
171
|
-
|
|
172
|
-
* The watchdog's editor creator is not defined. Define it by using
|
|
173
|
-
* {@link module:watchdog/watchdog~Watchdog#setCreator `Watchdog#setCreator()`} or
|
|
174
|
-
* the {@link module:watchdog/watchdog~Watchdog.for `Watchdog.for()`} helper.
|
|
175
|
-
*
|
|
176
|
-
* @error watchdog-creator-not-defined
|
|
177
|
-
*/
|
|
178
|
-
throw new CKEditorError(
|
|
179
|
-
'watchdog-creator-not-defined: The watchdog\'s editor creator is not defined.',
|
|
180
|
-
null
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if ( !this._destructor ) {
|
|
185
|
-
/**
|
|
186
|
-
* The watchdog's editor destructor is not defined. Define it by using
|
|
187
|
-
* {@link module:watchdog/watchdog~Watchdog#setDestructor `Watchdog#setDestructor()`} or
|
|
188
|
-
* the {@link module:watchdog/watchdog~Watchdog.for `Watchdog.for()`} helper.
|
|
189
|
-
*
|
|
190
|
-
* @error watchdog-destructor-not-defined
|
|
191
|
-
*/
|
|
192
|
-
throw new CKEditorError(
|
|
193
|
-
'watchdog-destructor-not-defined: The watchdog\'s editor destructor is not defined.',
|
|
194
|
-
null
|
|
195
|
-
);
|
|
191
|
+
on( eventName, callback ) {
|
|
192
|
+
if ( !this._listeners[ eventName ] ) {
|
|
193
|
+
this._listeners[ eventName ] = [];
|
|
196
194
|
}
|
|
197
195
|
|
|
198
|
-
this.
|
|
199
|
-
|
|
200
|
-
// Clone config because it might be shared within multiple watchdog instances. Otherwise
|
|
201
|
-
// when an error occurs in one of these editors the watchdog will restart all of them.
|
|
202
|
-
this._config = cloneDeepWith( config, value => {
|
|
203
|
-
// Leave DOM references.
|
|
204
|
-
return isElement( value ) ? value : undefined;
|
|
205
|
-
} );
|
|
206
|
-
|
|
207
|
-
return Promise.resolve()
|
|
208
|
-
.then( () => this._creator( elementOrData, this._config ) )
|
|
209
|
-
.then( editor => {
|
|
210
|
-
this._editor = editor;
|
|
211
|
-
|
|
212
|
-
window.addEventListener( 'error', this._boundErrorHandler );
|
|
213
|
-
this.listenTo( editor.model.document, 'change:data', this._throttledSave );
|
|
214
|
-
|
|
215
|
-
this._lastDocumentVersion = editor.model.document.version;
|
|
216
|
-
this._data = editor.data.get();
|
|
217
|
-
|
|
218
|
-
return this;
|
|
219
|
-
} );
|
|
196
|
+
this._listeners[ eventName ].push( callback );
|
|
220
197
|
}
|
|
221
198
|
|
|
222
199
|
/**
|
|
223
|
-
*
|
|
224
|
-
*
|
|
200
|
+
* Stops listening to the specified event name by removing the callback from event listeners.
|
|
201
|
+
*
|
|
202
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
225
203
|
*
|
|
226
|
-
* @
|
|
227
|
-
* @
|
|
204
|
+
* @param {String} eventName The event name.
|
|
205
|
+
* @param {Function} callback A callback which will be removed from event listeners.
|
|
228
206
|
*/
|
|
229
|
-
|
|
230
|
-
this.
|
|
231
|
-
|
|
232
|
-
return Promise.resolve()
|
|
233
|
-
.then( () => this.destroy() )
|
|
234
|
-
.catch( err => console.error( 'An error happened during the editor destructing.', err ) )
|
|
235
|
-
.then( () => {
|
|
236
|
-
if ( typeof this._elementOrData === 'string' ) {
|
|
237
|
-
return this.create( this._data, this._config );
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const updatedConfig = Object.assign( {}, this._config, {
|
|
241
|
-
initialData: this._data
|
|
242
|
-
} );
|
|
243
|
-
|
|
244
|
-
return this.create( this._elementOrData, updatedConfig );
|
|
245
|
-
} )
|
|
246
|
-
.then( () => {
|
|
247
|
-
this.fire( 'restart' );
|
|
248
|
-
} );
|
|
207
|
+
off( eventName, callback ) {
|
|
208
|
+
this._listeners[ eventName ] = this._listeners[ eventName ]
|
|
209
|
+
.filter( cb => cb !== callback );
|
|
249
210
|
}
|
|
250
211
|
|
|
251
212
|
/**
|
|
252
|
-
*
|
|
213
|
+
* Fires an event with a given event name and arguments.
|
|
214
|
+
*
|
|
215
|
+
* Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
|
|
253
216
|
*
|
|
254
|
-
* @
|
|
217
|
+
* @protected
|
|
218
|
+
* @param {String} eventName The event name.
|
|
219
|
+
* @param {...*} args Event arguments.
|
|
255
220
|
*/
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
this.stopListening( this._editor.model.document, 'change:data', this._throttledSave );
|
|
259
|
-
|
|
260
|
-
return Promise.resolve()
|
|
261
|
-
.then( () => this._destructor( this._editor ) )
|
|
262
|
-
.then( () => {
|
|
263
|
-
this._editor = null;
|
|
221
|
+
_fire( eventName, ...args ) {
|
|
222
|
+
const callbacks = this._listeners[ eventName ] || [];
|
|
264
223
|
|
|
265
|
-
|
|
266
|
-
|
|
224
|
+
for ( const callback of callbacks ) {
|
|
225
|
+
callback.apply( this, [ null, ...args ] );
|
|
226
|
+
}
|
|
267
227
|
}
|
|
268
228
|
|
|
269
229
|
/**
|
|
270
|
-
*
|
|
271
|
-
* the moment of a crash.
|
|
230
|
+
* Starts error handling by attaching global error handlers.
|
|
272
231
|
*
|
|
273
|
-
* @
|
|
232
|
+
* @protected
|
|
274
233
|
*/
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
// can be the same after that change.
|
|
280
|
-
if ( version === this._lastDocumentVersion ) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
234
|
+
_startErrorHandling() {
|
|
235
|
+
window.addEventListener( 'error', this._boundErrorHandler );
|
|
236
|
+
window.addEventListener( 'unhandledrejection', this._boundErrorHandler );
|
|
237
|
+
}
|
|
283
238
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
);
|
|
293
|
-
}
|
|
239
|
+
/**
|
|
240
|
+
* Stops error handling by detaching global error handlers.
|
|
241
|
+
*
|
|
242
|
+
* @protected
|
|
243
|
+
*/
|
|
244
|
+
_stopErrorHandling() {
|
|
245
|
+
window.removeEventListener( 'error', this._boundErrorHandler );
|
|
246
|
+
window.removeEventListener( 'unhandledrejection', this._boundErrorHandler );
|
|
294
247
|
}
|
|
295
248
|
|
|
296
249
|
/**
|
|
297
|
-
* Checks if
|
|
298
|
-
*
|
|
250
|
+
* Checks if an error comes from the watched item and restarts it.
|
|
251
|
+
* It reacts to {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` errors} only.
|
|
299
252
|
*
|
|
300
253
|
* @private
|
|
301
254
|
* @fires error
|
|
302
|
-
* @param {
|
|
255
|
+
* @param {Error} error Error.
|
|
256
|
+
* @param {ErrorEvent|PromiseRejectionEvent} evt An error event.
|
|
303
257
|
*/
|
|
304
|
-
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
if ( evt.error.context === undefined ) {
|
|
310
|
-
console.error( 'The error is missing its context and Watchdog cannot restart the proper editor.' );
|
|
311
|
-
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// In some cases the editor should not be restarted - e.g. in case of the editor initialization.
|
|
316
|
-
// That's why the `null` was introduced as a correct error context which does cause restarting.
|
|
317
|
-
if ( evt.error.context === null ) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
258
|
+
_handleError( error, evt ) {
|
|
259
|
+
// @if CK_DEBUG // if ( error.is && error.is( 'CKEditorError' ) && error.context === undefined ) {
|
|
260
|
+
// @if CK_DEBUG // console.warn( 'The error is missing its context and Watchdog cannot restart the proper item.' );
|
|
261
|
+
// @if CK_DEBUG // }
|
|
320
262
|
|
|
321
|
-
if ( this.
|
|
263
|
+
if ( this._shouldReactToError( error ) ) {
|
|
322
264
|
this.crashes.push( {
|
|
323
|
-
message:
|
|
324
|
-
|
|
265
|
+
message: error.message,
|
|
266
|
+
stack: error.stack,
|
|
267
|
+
|
|
268
|
+
// `evt.filename`, `evt.lineno` and `evt.colno` are available only in ErrorEvent events
|
|
269
|
+
filename: evt.filename,
|
|
325
270
|
lineno: evt.lineno,
|
|
326
|
-
colno: evt.colno
|
|
271
|
+
colno: evt.colno,
|
|
272
|
+
date: this._now()
|
|
327
273
|
} );
|
|
328
274
|
|
|
329
|
-
this.
|
|
275
|
+
const causesRestart = this._shouldRestart();
|
|
276
|
+
|
|
277
|
+
this.state = 'crashed';
|
|
278
|
+
this._fire( 'stateChange' );
|
|
279
|
+
this._fire( 'error', { error, causesRestart } );
|
|
330
280
|
|
|
331
|
-
if (
|
|
332
|
-
this.
|
|
281
|
+
if ( causesRestart ) {
|
|
282
|
+
this._restart();
|
|
283
|
+
} else {
|
|
284
|
+
this.state = 'crashedPermanently';
|
|
285
|
+
this._fire( 'stateChange' );
|
|
333
286
|
}
|
|
334
287
|
}
|
|
335
288
|
}
|
|
336
289
|
|
|
337
290
|
/**
|
|
338
|
-
*
|
|
339
|
-
* with the current editor.
|
|
291
|
+
* Checks whether an error should be handled by the watchdog.
|
|
340
292
|
*
|
|
341
293
|
* @private
|
|
342
|
-
* @param {
|
|
294
|
+
* @param {Error} error An error that was caught by the error handling process.
|
|
343
295
|
*/
|
|
344
|
-
|
|
345
|
-
return
|
|
296
|
+
_shouldReactToError( error ) {
|
|
297
|
+
return (
|
|
298
|
+
error.is &&
|
|
299
|
+
error.is( 'CKEditorError' ) &&
|
|
300
|
+
error.context !== undefined &&
|
|
301
|
+
|
|
302
|
+
// In some cases the watched item should not be restarted - e.g. during the item initialization.
|
|
303
|
+
// That's why the `null` was introduced as a correct error context which does cause restarting.
|
|
304
|
+
error.context !== null &&
|
|
305
|
+
|
|
306
|
+
// Do not react to errors if the watchdog is in states other than `ready`.
|
|
307
|
+
this.state === 'ready' &&
|
|
308
|
+
|
|
309
|
+
this._isErrorComingFromThisItem( error )
|
|
310
|
+
);
|
|
346
311
|
}
|
|
347
312
|
|
|
348
313
|
/**
|
|
349
|
-
*
|
|
350
|
-
* {@link ~Watchdog `Watchdog` class description}.
|
|
351
|
-
*
|
|
352
|
-
* Usage:
|
|
314
|
+
* Checks if the watchdog should restart the underlying item.
|
|
353
315
|
*
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* watchdog.create( elementOrData, config );
|
|
357
|
-
*
|
|
358
|
-
* @param {*} Editor The editor class.
|
|
316
|
+
* @private
|
|
359
317
|
*/
|
|
360
|
-
|
|
361
|
-
|
|
318
|
+
_shouldRestart() {
|
|
319
|
+
if ( this.crashes.length <= this._crashNumberLimit ) {
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const lastErrorTime = this.crashes[ this.crashes.length - 1 ].date;
|
|
324
|
+
const firstMeaningfulErrorTime = this.crashes[ this.crashes.length - 1 - this._crashNumberLimit ].date;
|
|
362
325
|
|
|
363
|
-
|
|
364
|
-
watchdog.setDestructor( editor => editor.destroy() );
|
|
326
|
+
const averageNonErrorTimePeriod = ( lastErrorTime - firstMeaningfulErrorTime ) / this._crashNumberLimit;
|
|
365
327
|
|
|
366
|
-
return
|
|
328
|
+
return averageNonErrorTimePeriod > this._minimumNonErrorTimePeriod;
|
|
367
329
|
}
|
|
368
330
|
|
|
369
331
|
/**
|
|
370
|
-
* Fired when
|
|
332
|
+
* Fired when a new {@link module:utils/ckeditorerror~CKEditorError `CKEditorError`} error connected to the watchdog instance occurs
|
|
333
|
+
* and the watchdog will react to it.
|
|
371
334
|
*
|
|
372
|
-
*
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* Fired after the watchdog restarts the error in case of a crash or when the `restart()` method was called explicitly.
|
|
335
|
+
* watchdog.on( 'error', ( evt, { error, causesRestart } ) => {
|
|
336
|
+
* console.log( 'An error occurred.' );
|
|
337
|
+
* } );
|
|
377
338
|
*
|
|
378
|
-
* @event
|
|
339
|
+
* @event error
|
|
379
340
|
*/
|
|
380
341
|
}
|
|
381
342
|
|
|
382
|
-
|
|
343
|
+
/**
|
|
344
|
+
* The watchdog plugin configuration.
|
|
345
|
+
*
|
|
346
|
+
* @typedef {Object} WatchdogConfig
|
|
347
|
+
*
|
|
348
|
+
* @property {Number} [crashNumberLimit=3] A threshold specifying the number of watched item crashes
|
|
349
|
+
* when the watchdog stops restarting the item in case of errors.
|
|
350
|
+
* After this limit is reached and the time between the last errors is shorter than `minimumNonErrorTimePeriod`,
|
|
351
|
+
* the watchdog changes its state to `crashedPermanently` and it stops restarting the item. This prevents an infinite restart loop.
|
|
352
|
+
*
|
|
353
|
+
* @property {Number} [minimumNonErrorTimePeriod=5000] An average number of milliseconds between the last watched item errors
|
|
354
|
+
* (defaults to 5000). When the period of time between errors is lower than that and the `crashNumberLimit` is also reached,
|
|
355
|
+
* the watchdog changes its state to `crashedPermanently` and it stops restarting the item. This prevents an infinite restart loop.
|
|
356
|
+
*
|
|
357
|
+
* @property {Number} [saveInterval=5000] A minimum number of milliseconds between saving the editor data internally (defaults to 5000).
|
|
358
|
+
* Note that for large documents this might impact the editor performance.
|
|
359
|
+
*/
|