@ckeditor/ckeditor5-engine 31.0.0 → 33.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.
Files changed (117) hide show
  1. package/LICENSE.md +2 -2
  2. package/package.json +25 -25
  3. package/src/controller/datacontroller.js +71 -80
  4. package/src/controller/editingcontroller.js +83 -6
  5. package/src/conversion/conversion.js +15 -14
  6. package/src/conversion/conversionhelpers.js +1 -1
  7. package/src/conversion/downcastdispatcher.js +298 -367
  8. package/src/conversion/downcasthelpers.js +771 -63
  9. package/src/conversion/mapper.js +105 -60
  10. package/src/conversion/modelconsumable.js +85 -35
  11. package/src/conversion/upcastdispatcher.js +3 -6
  12. package/src/conversion/upcasthelpers.js +4 -2
  13. package/src/conversion/viewconsumable.js +1 -1
  14. package/src/dataprocessor/basichtmlwriter.js +1 -1
  15. package/src/dataprocessor/dataprocessor.jsdoc +1 -1
  16. package/src/dataprocessor/htmldataprocessor.js +9 -31
  17. package/src/dataprocessor/htmlwriter.js +1 -1
  18. package/src/dataprocessor/xmldataprocessor.js +1 -1
  19. package/src/dev-utils/model.js +16 -14
  20. package/src/dev-utils/operationreplayer.js +1 -1
  21. package/src/dev-utils/utils.js +1 -1
  22. package/src/dev-utils/view.js +7 -7
  23. package/src/index.js +2 -1
  24. package/src/model/batch.js +77 -10
  25. package/src/model/differ.js +87 -59
  26. package/src/model/document.js +13 -4
  27. package/src/model/documentfragment.js +1 -1
  28. package/src/model/documentselection.js +1 -1
  29. package/src/model/element.js +1 -1
  30. package/src/model/history.js +1 -1
  31. package/src/model/item.jsdoc +1 -1
  32. package/src/model/liveposition.js +1 -1
  33. package/src/model/liverange.js +1 -1
  34. package/src/model/markercollection.js +29 -5
  35. package/src/model/model.js +18 -9
  36. package/src/model/node.js +1 -1
  37. package/src/model/nodelist.js +1 -1
  38. package/src/model/operation/attributeoperation.js +1 -1
  39. package/src/model/operation/detachoperation.js +1 -1
  40. package/src/model/operation/insertoperation.js +1 -1
  41. package/src/model/operation/markeroperation.js +1 -1
  42. package/src/model/operation/mergeoperation.js +1 -1
  43. package/src/model/operation/moveoperation.js +1 -1
  44. package/src/model/operation/nooperation.js +1 -1
  45. package/src/model/operation/operation.js +1 -1
  46. package/src/model/operation/operationfactory.js +1 -1
  47. package/src/model/operation/renameoperation.js +1 -1
  48. package/src/model/operation/rootattributeoperation.js +1 -1
  49. package/src/model/operation/splitoperation.js +1 -1
  50. package/src/model/operation/transform.js +1 -1
  51. package/src/model/operation/utils.js +1 -1
  52. package/src/model/position.js +1 -1
  53. package/src/model/range.js +1 -1
  54. package/src/model/rootelement.js +1 -1
  55. package/src/model/schema.js +1 -1
  56. package/src/model/selection.js +1 -1
  57. package/src/model/text.js +1 -1
  58. package/src/model/textproxy.js +1 -1
  59. package/src/model/treewalker.js +1 -1
  60. package/src/model/utils/autoparagraphing.js +1 -1
  61. package/src/model/utils/deletecontent.js +1 -1
  62. package/src/model/utils/getselectedcontent.js +1 -1
  63. package/src/model/utils/insertcontent.js +1 -1
  64. package/src/model/utils/modifyselection.js +15 -8
  65. package/src/model/utils/selection-post-fixer.js +37 -30
  66. package/src/model/writer.js +17 -27
  67. package/src/view/attributeelement.js +1 -1
  68. package/src/view/containerelement.js +1 -1
  69. package/src/view/document.js +3 -2
  70. package/src/view/documentfragment.js +1 -1
  71. package/src/view/documentselection.js +1 -1
  72. package/src/view/domconverter.js +169 -47
  73. package/src/view/downcastwriter.js +121 -5
  74. package/src/view/editableelement.js +1 -1
  75. package/src/view/element.js +29 -1
  76. package/src/view/elementdefinition.jsdoc +1 -1
  77. package/src/view/emptyelement.js +1 -1
  78. package/src/view/filler.js +1 -1
  79. package/src/view/item.jsdoc +1 -1
  80. package/src/view/matcher.js +15 -14
  81. package/src/view/node.js +1 -1
  82. package/src/view/observer/arrowkeysobserver.js +1 -1
  83. package/src/view/observer/bubblingemittermixin.js +1 -1
  84. package/src/view/observer/bubblingeventinfo.js +1 -1
  85. package/src/view/observer/clickobserver.js +1 -1
  86. package/src/view/observer/compositionobserver.js +1 -1
  87. package/src/view/observer/domeventdata.js +1 -1
  88. package/src/view/observer/domeventobserver.js +1 -1
  89. package/src/view/observer/fakeselectionobserver.js +1 -1
  90. package/src/view/observer/focusobserver.js +1 -1
  91. package/src/view/observer/inputobserver.js +1 -1
  92. package/src/view/observer/keyobserver.js +1 -1
  93. package/src/view/observer/mouseobserver.js +1 -1
  94. package/src/view/observer/mutationobserver.js +1 -1
  95. package/src/view/observer/observer.js +1 -1
  96. package/src/view/observer/selectionobserver.js +3 -3
  97. package/src/view/placeholder.js +1 -1
  98. package/src/view/position.js +1 -1
  99. package/src/view/range.js +1 -1
  100. package/src/view/rawelement.js +1 -1
  101. package/src/view/renderer.js +7 -17
  102. package/src/view/rooteditableelement.js +1 -1
  103. package/src/view/selection.js +1 -1
  104. package/src/view/styles/background.js +1 -1
  105. package/src/view/styles/border.js +1 -1
  106. package/src/view/styles/margin.js +1 -1
  107. package/src/view/styles/padding.js +1 -1
  108. package/src/view/styles/utils.js +1 -1
  109. package/src/view/stylesmap.js +1 -1
  110. package/src/view/text.js +1 -1
  111. package/src/view/textproxy.js +1 -1
  112. package/src/view/treewalker.js +1 -1
  113. package/src/view/uielement.js +1 -1
  114. package/src/view/upcastwriter.js +1 -1
  115. package/src/view/view.js +1 -1
  116. package/theme/placeholder.css +10 -1
  117. package/theme/renderer.css +2 -2
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -27,7 +27,7 @@ import DocumentSelection from './documentselection';
27
27
 
28
28
  import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
29
29
 
30
- import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
30
+ import CKEditorError, { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
31
31
 
32
32
  /**
33
33
  * The model can only be modified by using the writer. It should be used whenever you want to create a node, modify
@@ -968,30 +968,8 @@ export default class Writer {
968
968
  * As the first parameter you can set marker name or instance. If none of them is provided, new marker, with a unique
969
969
  * name is created and returned.
970
970
  *
971
- * As the second parameter you can set the new marker data or leave this parameter as empty which will just refresh
972
- * the marker by triggering downcast conversion for it. Refreshing the marker is useful when you want to change
973
- * the marker {@link module:engine/view/element~Element view element} without changing any marker data.
974
- *
975
- * let isCommentActive = false;
976
- *
977
- * model.conversion.markerToHighlight( {
978
- * model: 'comment',
979
- * view: data => {
980
- * const classes = [ 'comment-marker' ];
981
- *
982
- * if ( isCommentActive ) {
983
- * classes.push( 'comment-marker--active' );
984
- * }
985
- *
986
- * return { classes };
987
- * }
988
- * } );
989
- *
990
- * // Change the property that indicates if marker is displayed as active or not.
991
- * isCommentActive = true;
992
- *
993
- * // And refresh the marker to convert it with additional class.
994
- * model.change( writer => writer.updateMarker( 'comment' ) );
971
+ * **Note**: If you want to change the {@link module:engine/view/element~Element view element} of the marker while its data in the model
972
+ * remains the same, use the dedicated {@link module:engine/controller/editingcontroller~EditingController#reconvertMarker} method.
995
973
  *
996
974
  * The `options.usingOperation` parameter lets you change if the marker should be managed by operations or not. See
997
975
  * {@link module:engine/model/markercollection~Marker marker class description} to learn about the difference between
@@ -1037,7 +1015,7 @@ export default class Writer {
1037
1015
 
1038
1016
  if ( !currentMarker ) {
1039
1017
  /**
1040
- * Marker with provided name does not exists.
1018
+ * Marker with provided name does not exist and will not be updated.
1041
1019
  *
1042
1020
  * @error writer-updatemarker-marker-not-exists
1043
1021
  */
@@ -1045,6 +1023,18 @@ export default class Writer {
1045
1023
  }
1046
1024
 
1047
1025
  if ( !options ) {
1026
+ /**
1027
+ * The usage of `writer.updateMarker()` only to reconvert (refresh) a
1028
+ * {@link module:engine/model/markercollection~Marker model marker} was deprecated and may not work in the future.
1029
+ * Please update your code to use
1030
+ * {@link module:engine/controller/editingcontroller~EditingController#reconvertMarker `editor.editing.reconvertMarker()`}
1031
+ * instead.
1032
+ *
1033
+ * @error writer-updatemarker-reconvert-using-editingcontroller
1034
+ * @param {String} markerName The name of the updated marker.
1035
+ */
1036
+ logWarning( 'writer-updatemarker-reconvert-using-editingcontroller', { markerName } );
1037
+
1048
1038
  this.model.markers._refresh( currentMarker );
1049
1039
 
1050
1040
  return;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -141,7 +141,8 @@ export default class Document {
141
141
  *
142
142
  * * adding or removing attribute from elements,
143
143
  * * changes inside of {@link module:engine/view/uielement~UIElement UI elements},
144
- * * {@link module:engine/model/differ~Differ#refreshItem marking some of the model elements to be re-converted}.
144
+ * * {@link module:engine/controller/editingcontroller~EditingController#reconvertItem marking some of the model elements to be
145
+ * re-converted}.
145
146
  *
146
147
  * Try to avoid changes which touch view structure:
147
148
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -24,13 +24,18 @@ import {
24
24
  } from './filler';
25
25
 
26
26
  import global from '@ckeditor/ckeditor5-utils/src/dom/global';
27
+ import { logWarning } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
27
28
  import indexOf from '@ckeditor/ckeditor5-utils/src/dom/indexof';
28
29
  import getAncestors from '@ckeditor/ckeditor5-utils/src/dom/getancestors';
29
30
  import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
31
+ import isComment from '@ckeditor/ckeditor5-utils/src/dom/iscomment';
30
32
 
31
33
  const BR_FILLER_REF = BR_FILLER( document ); // eslint-disable-line new-cap
32
34
  const NBSP_FILLER_REF = NBSP_FILLER( document ); // eslint-disable-line new-cap
33
35
  const MARKED_NBSP_FILLER_REF = MARKED_NBSP_FILLER( document ); // eslint-disable-line new-cap
36
+ const UNSAFE_ATTRIBUTE_NAME_PREFIX = 'data-ck-unsafe-attribute-';
37
+ const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
38
+ const UNSAFE_ELEMENTS = [ 'script', 'style' ];
34
39
 
35
40
  /**
36
41
  * `DomConverter` is a set of tools to do transformations between DOM nodes and view nodes. It also handles
@@ -72,14 +77,6 @@ export default class DomConverter {
72
77
  */
73
78
  this.renderingMode = options.renderingMode || 'editing';
74
79
 
75
- /**
76
- * Main switch for new rendering approach in the editing view.
77
- *
78
- * @protected
79
- * @member {Boolean}
80
- */
81
- this.experimentalRenderingMode = false;
82
-
83
80
  /**
84
81
  * The mode of a block filler used by the DOM converter.
85
82
  *
@@ -242,21 +239,47 @@ export default class DomConverter {
242
239
  }
243
240
 
244
241
  /**
245
- * Decides whether given pair of attribute key and value should be passed further down the pipeline.
242
+ * Decides whether a given pair of attribute key and value should be passed further down the pipeline.
246
243
  *
247
244
  * @param {String} attributeKey
248
245
  * @param {String} attributeValue
246
+ * @param {String} elementName Element name in lower case.
249
247
  * @returns {Boolean}
250
248
  */
251
- shouldRenderAttribute( attributeKey, attributeValue ) {
252
- if ( !this.experimentalRenderingMode || this.renderingMode === 'data' ) {
249
+ shouldRenderAttribute( attributeKey, attributeValue, elementName ) {
250
+ if ( this.renderingMode === 'data' ) {
253
251
  return true;
254
252
  }
255
253
 
256
- return !( attributeKey.toLowerCase().startsWith( 'on' ) ||
257
- attributeValue.match( /(\b)(on\S+)(\s*)=|javascript:|(<\s*)(\/*)script/i ) ||
258
- attributeValue.match( /data:(?!image\/(png|jpeg|gif|webp))/i )
259
- );
254
+ attributeKey = attributeKey.toLowerCase();
255
+
256
+ if ( attributeKey.startsWith( 'on' ) ) {
257
+ return false;
258
+ }
259
+
260
+ if (
261
+ attributeKey === 'srcdoc' &&
262
+ attributeValue.match( /\bon\S+\s*=|javascript:|<\s*\/*script/i )
263
+ ) {
264
+ return false;
265
+ }
266
+
267
+ if (
268
+ elementName === 'img' &&
269
+ ( attributeKey === 'src' || attributeKey === 'srcset' )
270
+ ) {
271
+ return true;
272
+ }
273
+
274
+ if ( elementName === 'source' && attributeKey === 'srcset' ) {
275
+ return true;
276
+ }
277
+
278
+ if ( attributeValue.match( /^\s*(javascript:|data:(image\/svg|text\/x?html))/i ) ) {
279
+ return false;
280
+ }
281
+
282
+ return true;
260
283
  }
261
284
 
262
285
  /**
@@ -267,7 +290,7 @@ export default class DomConverter {
267
290
  */
268
291
  setContentOf( domElement, html ) {
269
292
  // For data pipeline we pass the HTML as-is.
270
- if ( !this.experimentalRenderingMode || this.renderingMode === 'data' ) {
293
+ if ( this.renderingMode === 'data' ) {
271
294
  domElement.innerHTML = html;
272
295
 
273
296
  return;
@@ -294,17 +317,15 @@ export default class DomConverter {
294
317
  for ( const currentNode of nodes ) {
295
318
  // Go through nodes to remove those that are prohibited in editing pipeline.
296
319
  for ( const attributeName of currentNode.getAttributeNames() ) {
297
- const attributeValue = currentNode.getAttribute( attributeName );
298
-
299
- if ( !this.shouldRenderAttribute( attributeName, attributeValue ) ) {
300
- currentNode.removeAttribute( attributeName );
301
- }
320
+ this.setDomElementAttribute( currentNode, attributeName, currentNode.getAttribute( attributeName ) );
302
321
  }
303
322
 
304
323
  const elementName = currentNode.tagName.toLowerCase();
305
324
 
306
325
  // There are certain nodes, that should be renamed to <span> in editing pipeline.
307
326
  if ( this._shouldRenameElement( elementName ) ) {
327
+ _logUnsafeElement( elementName );
328
+
308
329
  currentNode.replaceWith( this._createReplacementDomElement( elementName, currentNode ) );
309
330
  }
310
331
  }
@@ -364,6 +385,8 @@ export default class DomConverter {
364
385
  } else {
365
386
  // Create DOM element.
366
387
  if ( this._shouldRenameElement( viewNode.name ) ) {
388
+ _logUnsafeElement( viewNode.name );
389
+
367
390
  domElement = this._createReplacementDomElement( viewNode.name );
368
391
  } else if ( viewNode.hasAttribute( 'xmlns' ) ) {
369
392
  domElement = domDocument.createElementNS( viewNode.getAttribute( 'xmlns' ), viewNode.name );
@@ -383,13 +406,7 @@ export default class DomConverter {
383
406
 
384
407
  // Copy element's attributes.
385
408
  for ( const key of viewNode.getAttributeKeys() ) {
386
- const value = viewNode.getAttribute( key );
387
-
388
- if ( !this.shouldRenderAttribute( key, value ) ) {
389
- continue;
390
- }
391
-
392
- domElement.setAttribute( key, value );
409
+ this.setDomElementAttribute( domElement, key, viewNode.getAttribute( key ), viewNode );
393
410
  }
394
411
  }
395
412
 
@@ -403,6 +420,60 @@ export default class DomConverter {
403
420
  }
404
421
  }
405
422
 
423
+ /**
424
+ * Sets the attribute on a DOM element.
425
+ *
426
+ * **Note**: To remove the attribute, use {@link #removeDomElementAttribute}.
427
+ *
428
+ * @param {HTMLElement} domElement The DOM element the attribute should be set on.
429
+ * @param {String} key The name of the attribute.
430
+ * @param {String} value The value of the attribute.
431
+ * @param {module:engine/view/element~Element} [relatedViewElement] The view element related to the `domElement` (if there is any).
432
+ * It helps decide whether the attribute set is unsafe. For instance, view elements created via the
433
+ * {@link module:engine/view/downcastwriter~DowncastWriter} methods can allow certain attributes that would normally be filtered out.
434
+ */
435
+ setDomElementAttribute( domElement, key, value, relatedViewElement = null ) {
436
+ const shouldRenderAttribute = this.shouldRenderAttribute( key, value, domElement.tagName.toLowerCase() ) ||
437
+ relatedViewElement && relatedViewElement.shouldRenderUnsafeAttribute( key );
438
+
439
+ if ( !shouldRenderAttribute ) {
440
+ logWarning( 'domconverter-unsafe-attribute-detected', { domElement, key, value } );
441
+ }
442
+
443
+ // The old value was safe but the new value is unsafe.
444
+ if ( domElement.hasAttribute( key ) && !shouldRenderAttribute ) {
445
+ domElement.removeAttribute( key );
446
+ }
447
+ // The old value was unsafe (but prefixed) but the new value will be safe (will be unprefixed).
448
+ else if ( domElement.hasAttribute( UNSAFE_ATTRIBUTE_NAME_PREFIX + key ) && shouldRenderAttribute ) {
449
+ domElement.removeAttribute( UNSAFE_ATTRIBUTE_NAME_PREFIX + key );
450
+ }
451
+
452
+ // If the attribute should not be rendered, rename it (instead of removing) to give developers some idea of what
453
+ // is going on (https://github.com/ckeditor/ckeditor5/issues/10801).
454
+ domElement.setAttribute( shouldRenderAttribute ? key : UNSAFE_ATTRIBUTE_NAME_PREFIX + key, value );
455
+ }
456
+
457
+ /**
458
+ * Removes an attribute from a DOM element.
459
+ *
460
+ * **Note**: To set the attribute, use {@link #setDomElementAttribute}.
461
+ *
462
+ * @param {HTMLElement} domElement The DOM element the attribute should be removed from.
463
+ * @param {String} key The name of the attribute.
464
+ */
465
+ removeDomElementAttribute( domElement, key ) {
466
+ // See #_createReplacementDomElement() to learn what this is.
467
+ if ( key == UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE ) {
468
+ return;
469
+ }
470
+
471
+ domElement.removeAttribute( key );
472
+
473
+ // See setDomElementAttribute() to learn what this is.
474
+ domElement.removeAttribute( UNSAFE_ATTRIBUTE_NAME_PREFIX + key );
475
+ }
476
+
406
477
  /**
407
478
  * Converts children of the view element to DOM using the
408
479
  * {@link module:engine/view/domconverter~DomConverter#viewToDom} method.
@@ -548,7 +619,7 @@ export default class DomConverter {
548
619
  return hostElement;
549
620
  }
550
621
 
551
- if ( this.isComment( domNode ) && options.skipComments ) {
622
+ if ( isComment( domNode ) && options.skipComments ) {
552
623
  return null;
553
624
  }
554
625
 
@@ -593,8 +664,8 @@ export default class DomConverter {
593
664
 
594
665
  // Treat this element's content as a raw data if it was registered as such.
595
666
  // Comment node is also treated as an element with raw data.
596
- if ( this._isViewElementWithRawContent( viewElement, options ) || this.isComment( domNode ) ) {
597
- const rawContent = this.isComment( domNode ) ? domNode.data : domNode.innerHTML;
667
+ if ( this._isViewElementWithRawContent( viewElement, options ) || isComment( domNode ) ) {
668
+ const rawContent = isComment( domNode ) ? domNode.data : domNode.innerHTML;
598
669
 
599
670
  viewElement._setCustomProperty( '$rawContent', rawContent );
600
671
 
@@ -963,16 +1034,6 @@ export default class DomConverter {
963
1034
  return node && node.nodeType == Node.DOCUMENT_FRAGMENT_NODE;
964
1035
  }
965
1036
 
966
- /**
967
- * Returns `true` when `node.nodeType` equals `Node.COMMENT_NODE`.
968
- *
969
- * @param {Node} node Node to check.
970
- * @returns {Boolean}
971
- */
972
- isComment( node ) {
973
- return node && node.nodeType == Node.COMMENT_NODE;
974
- }
975
-
976
1037
  /**
977
1038
  * Checks if the node is an instance of the block filler for this DOM converter.
978
1039
  *
@@ -1457,7 +1518,7 @@ export default class DomConverter {
1457
1518
  * @returns {Element}
1458
1519
  */
1459
1520
  _createViewElement( node, options ) {
1460
- if ( this.isComment( node ) ) {
1521
+ if ( isComment( node ) ) {
1461
1522
  return new ViewUIElement( this.document, '$comment' );
1462
1523
  }
1463
1524
 
@@ -1479,18 +1540,20 @@ export default class DomConverter {
1479
1540
  }
1480
1541
 
1481
1542
  /**
1482
- * Checks whether given element name should be renamed in a current rendering mode.
1543
+ * Checks whether a given element name should be renamed in a current rendering mode.
1483
1544
  *
1484
1545
  * @private
1485
1546
  * @param {String} elementName The name of view element.
1486
1547
  * @returns {Boolean}
1487
1548
  */
1488
1549
  _shouldRenameElement( elementName ) {
1489
- return this.experimentalRenderingMode && this.renderingMode == 'editing' && elementName == 'script';
1550
+ const name = elementName.toLowerCase();
1551
+
1552
+ return this.renderingMode === 'editing' && UNSAFE_ELEMENTS.includes( name );
1490
1553
  }
1491
1554
 
1492
1555
  /**
1493
- * Return a <span> element with special attribute holding the name of the original element.
1556
+ * Return a <span> element with a special attribute holding the name of the original element.
1494
1557
  * Optionally, copy all the attributes of the original element if that element is provided.
1495
1558
  *
1496
1559
  * @private
@@ -1502,7 +1565,7 @@ export default class DomConverter {
1502
1565
  const newDomElement = document.createElement( 'span' );
1503
1566
 
1504
1567
  // Mark the span replacing a script as hidden.
1505
- newDomElement.setAttribute( 'data-ck-hidden', elementName );
1568
+ newDomElement.setAttribute( UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE, elementName );
1506
1569
 
1507
1570
  if ( originalDomElement ) {
1508
1571
  while ( originalDomElement.firstChild ) {
@@ -1566,6 +1629,20 @@ function hasBlockParent( domNode, blockElements ) {
1566
1629
  return parent && parent.tagName && blockElements.includes( parent.tagName.toLowerCase() );
1567
1630
  }
1568
1631
 
1632
+ // Log to console the information about element that was replaced.
1633
+ // Check UNSAFE_ELEMENTS for all recognized unsafe elements.
1634
+ //
1635
+ // @param {String} elementName The name of the view element
1636
+ function _logUnsafeElement( elementName ) {
1637
+ if ( elementName === 'script' ) {
1638
+ logWarning( 'domconverter-unsafe-script-element-detected' );
1639
+ }
1640
+
1641
+ if ( elementName === 'style' ) {
1642
+ logWarning( 'domconverter-unsafe-style-element-detected' );
1643
+ }
1644
+ }
1645
+
1569
1646
  /**
1570
1647
  * Enum representing the type of the block filler.
1571
1648
  *
@@ -1578,3 +1655,48 @@ function hasBlockParent( domNode, blockElements ) {
1578
1655
  *
1579
1656
  * @typedef {String} module:engine/view/filler~BlockFillerMode
1580
1657
  */
1658
+
1659
+ /**
1660
+ * While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<script>` element that may
1661
+ * disrupt the editing experience. To avoid this, the `<script>` element was replaced with `<span data-ck-unsafe-element="script"></span>`.
1662
+ *
1663
+ * @error domconverter-unsafe-script-element-detected
1664
+ */
1665
+
1666
+ /**
1667
+ * While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<style>` element that may affect
1668
+ * the editing experience. To avoid this, the `<style>` element was replaced with `<span data-ck-unsafe-element="style"></span>`.
1669
+ *
1670
+ * @error domconverter-unsafe-style-element-detected
1671
+ */
1672
+
1673
+ /**
1674
+ * The {@link module:engine/view/domconverter~DomConverter} detected an interactive attribute in the
1675
+ * {@glink framework/guides/architecture/editing-engine#editing-pipeline editing pipeline}. For the best
1676
+ * editing experience, the attribute was renamed to `data-ck-unsafe-attribute-[original attribute name]`.
1677
+ *
1678
+ * If you are the author of the plugin that generated this attribute and you want it to be preserved
1679
+ * in the editing pipeline, you can configure this when creating the element
1680
+ * using {@link module:engine/view/downcastwriter~DowncastWriter} during the
1681
+ * {@glink framework/guides/architecture/editing-engine#conversion model–view conversion}. Methods such as
1682
+ * {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement},
1683
+ * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement}, or
1684
+ * {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement}
1685
+ * accept an option that will disable filtering of specific attributes:
1686
+ *
1687
+ * const paragraph = writer.createContainerElement( 'p',
1688
+ * {
1689
+ * class: 'clickable-paragraph',
1690
+ * onclick: 'alert( "Paragraph clicked!" )'
1691
+ * },
1692
+ * {
1693
+ * // Make sure the "onclick" attribute will pass through.
1694
+ * renderUnsafeAttributes: [ 'onclick' ]
1695
+ * }
1696
+ * );
1697
+ *
1698
+ * @error domconverter-unsafe-attribute-detected
1699
+ * @param {HTMLElement} domElement The DOM element the attribute was set on.
1700
+ * @param {String} key The original name of the attribute
1701
+ * @param {String} value The value of the original attribute
1702
+ */