Преглед изворни кода

Merge branch 'master' into i/6356

Aleksander Nowodzinski пре 5 година
родитељ
комит
1e57ddb284
100 измењених фајлова са 1517 додато и 1319 уклоњено
  1. 31 8
      packages/ckeditor5-engine/src/controller/datacontroller.js
  2. 4 4
      packages/ckeditor5-engine/src/controller/editingcontroller.js
  3. 6 5
      packages/ckeditor5-engine/src/conversion/downcasthelpers.js
  4. 15 6
      packages/ckeditor5-engine/src/conversion/viewconsumable.js
  5. 4 2
      packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js
  6. 3 2
      packages/ckeditor5-engine/src/dataprocessor/xmldataprocessor.js
  7. 5 4
      packages/ckeditor5-engine/src/dev-utils/model.js
  8. 12 4
      packages/ckeditor5-engine/src/dev-utils/view.js
  9. 1 1
      packages/ckeditor5-engine/src/model/model.js
  10. 4 12
      packages/ckeditor5-engine/src/model/node.js
  11. 4 6
      packages/ckeditor5-engine/src/model/rootelement.js
  12. 4 3
      packages/ckeditor5-engine/src/model/selection.js
  13. 0 11
      packages/ckeditor5-engine/src/model/textproxy.js
  14. 7 2
      packages/ckeditor5-engine/src/view/attributeelement.js
  15. 7 2
      packages/ckeditor5-engine/src/view/containerelement.js
  16. 11 18
      packages/ckeditor5-engine/src/view/document.js
  17. 15 6
      packages/ckeditor5-engine/src/view/documentfragment.js
  18. 12 5
      packages/ckeditor5-engine/src/view/domconverter.js
  19. 18 13
      packages/ckeditor5-engine/src/view/downcastwriter.js
  20. 14 55
      packages/ckeditor5-engine/src/view/editableelement.js
  21. 19 17
      packages/ckeditor5-engine/src/view/element.js
  22. 6 3
      packages/ckeditor5-engine/src/view/emptyelement.js
  23. 19 15
      packages/ckeditor5-engine/src/view/node.js
  24. 4 4
      packages/ckeditor5-engine/src/view/placeholder.js
  25. 3 2
      packages/ckeditor5-engine/src/view/rooteditableelement.js
  26. 1 1
      packages/ckeditor5-engine/src/view/styles/background.js
  27. 1 1
      packages/ckeditor5-engine/src/view/styles/border.js
  28. 1 1
      packages/ckeditor5-engine/src/view/styles/margin.js
  29. 1 1
      packages/ckeditor5-engine/src/view/styles/padding.js
  30. 21 69
      packages/ckeditor5-engine/src/view/stylesmap.js
  31. 5 4
      packages/ckeditor5-engine/src/view/text.js
  32. 5 2
      packages/ckeditor5-engine/src/view/uielement.js
  33. 20 7
      packages/ckeditor5-engine/src/view/upcastwriter.js
  34. 6 3
      packages/ckeditor5-engine/src/view/view.js
  35. 28 7
      packages/ckeditor5-engine/tests/controller/datacontroller.js
  36. 5 4
      packages/ckeditor5-engine/tests/controller/editingcontroller.js
  37. 2 1
      packages/ckeditor5-engine/tests/conversion/conversion.js
  38. 4 3
      packages/ckeditor5-engine/tests/conversion/downcastdispatcher.js
  39. 47 28
      packages/ckeditor5-engine/tests/conversion/downcasthelpers.js
  40. 70 62
      packages/ckeditor5-engine/tests/conversion/mapper.js
  41. 37 34
      packages/ckeditor5-engine/tests/conversion/upcastdispatcher.js
  42. 105 85
      packages/ckeditor5-engine/tests/conversion/upcasthelpers.js
  43. 31 33
      packages/ckeditor5-engine/tests/conversion/viewconsumable.js
  44. 10 3
      packages/ckeditor5-engine/tests/dataprocessor/htmldataprocessor.js
  45. 8 5
      packages/ckeditor5-engine/tests/dataprocessor/xmldataprocessor.js
  46. 119 105
      packages/ckeditor5-engine/tests/dev-utils/view.js
  47. 51 19
      packages/ckeditor5-engine/tests/model/node.js
  48. 0 5
      packages/ckeditor5-engine/tests/model/textproxy.js
  49. 2 1
      packages/ckeditor5-engine/tests/tickets/1323.js
  50. 2 0
      packages/ckeditor5-engine/tests/view/_utils/createdocumentmock.js
  51. 1 2
      packages/ckeditor5-engine/tests/view/_utils/createroot.js
  52. 26 18
      packages/ckeditor5-engine/tests/view/attributeelement.js
  53. 10 2
      packages/ckeditor5-engine/tests/view/containerelement.js
  54. 2 13
      packages/ckeditor5-engine/tests/view/document.js
  55. 45 37
      packages/ckeditor5-engine/tests/view/documentfragment.js
  56. 18 16
      packages/ckeditor5-engine/tests/view/documentselection.js
  57. 19 16
      packages/ckeditor5-engine/tests/view/domconverter/binding.js
  58. 11 9
      packages/ckeditor5-engine/tests/view/domconverter/dom-to-view.js
  59. 15 12
      packages/ckeditor5-engine/tests/view/domconverter/domconverter.js
  60. 9 6
      packages/ckeditor5-engine/tests/view/domconverter/uielement.js
  61. 131 125
      packages/ckeditor5-engine/tests/view/domconverter/view-to-dom.js
  62. 15 14
      packages/ckeditor5-engine/tests/view/downcastwriter/breakattributes.js
  63. 5 3
      packages/ckeditor5-engine/tests/view/downcastwriter/breakcontainer.js
  64. 17 16
      packages/ckeditor5-engine/tests/view/downcastwriter/clear.js
  65. 16 14
      packages/ckeditor5-engine/tests/view/downcastwriter/insert.js
  66. 7 5
      packages/ckeditor5-engine/tests/view/downcastwriter/mergeattributes.js
  67. 2 1
      packages/ckeditor5-engine/tests/view/downcastwriter/mergecontainers.js
  68. 17 15
      packages/ckeditor5-engine/tests/view/downcastwriter/move.js
  69. 13 12
      packages/ckeditor5-engine/tests/view/downcastwriter/remove.js
  70. 2 1
      packages/ckeditor5-engine/tests/view/downcastwriter/rename.js
  71. 15 14
      packages/ckeditor5-engine/tests/view/downcastwriter/unwrap.js
  72. 19 18
      packages/ckeditor5-engine/tests/view/downcastwriter/wrap.js
  73. 2 1
      packages/ckeditor5-engine/tests/view/downcastwriter/writer.js
  74. 25 53
      packages/ckeditor5-engine/tests/view/editableelement.js
  75. 82 60
      packages/ckeditor5-engine/tests/view/element.js
  76. 9 6
      packages/ckeditor5-engine/tests/view/emptyelement.js
  77. 2 1
      packages/ckeditor5-engine/tests/view/manual/clickobserver.js
  78. 2 1
      packages/ckeditor5-engine/tests/view/manual/fakeselection.js
  79. 2 1
      packages/ckeditor5-engine/tests/view/manual/focus.js
  80. 2 1
      packages/ckeditor5-engine/tests/view/manual/focusobserver.js
  81. 2 1
      packages/ckeditor5-engine/tests/view/manual/immutable.js
  82. 2 1
      packages/ckeditor5-engine/tests/view/manual/inline-filler.js
  83. 2 1
      packages/ckeditor5-engine/tests/view/manual/keyobserver.js
  84. 2 1
      packages/ckeditor5-engine/tests/view/manual/mutationobserver.js
  85. 2 1
      packages/ckeditor5-engine/tests/view/manual/noselection-iframe.js
  86. 2 1
      packages/ckeditor5-engine/tests/view/manual/noselection.js
  87. 2 1
      packages/ckeditor5-engine/tests/view/manual/selectionobserver.js
  88. 2 1
      packages/ckeditor5-engine/tests/view/manual/x-index.js
  89. 47 39
      packages/ckeditor5-engine/tests/view/matcher.js
  90. 73 70
      packages/ckeditor5-engine/tests/view/node.js
  91. 2 1
      packages/ckeditor5-engine/tests/view/observer/clickobserver.js
  92. 2 1
      packages/ckeditor5-engine/tests/view/observer/compositionobserver.js
  93. 2 1
      packages/ckeditor5-engine/tests/view/observer/domeventdata.js
  94. 3 2
      packages/ckeditor5-engine/tests/view/observer/domeventobserver.js
  95. 2 1
      packages/ckeditor5-engine/tests/view/observer/fakeselectionobserver.js
  96. 3 2
      packages/ckeditor5-engine/tests/view/observer/focusobserver.js
  97. 4 2
      packages/ckeditor5-engine/tests/view/observer/inputobserver.js
  98. 2 1
      packages/ckeditor5-engine/tests/view/observer/keyobserver.js
  99. 2 1
      packages/ckeditor5-engine/tests/view/observer/mouseobserver.js
  100. 8 3
      packages/ckeditor5-engine/tests/view/observer/mutationobserver.js

+ 31 - 8
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -47,10 +47,9 @@ export default class DataController {
 	 * Creates a data controller instance.
 	 *
 	 * @param {module:engine/model/model~Model} model Data model.
-	 * @param {module:engine/dataprocessor/dataprocessor~DataProcessor} [dataProcessor] Data processor that should be used
-	 * by the controller.
+	 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
 	 */
-	constructor( model, dataProcessor ) {
+	constructor( model, stylesProcessor ) {
 		/**
 		 * Data model.
 		 *
@@ -60,12 +59,19 @@ export default class DataController {
 		this.model = model;
 
 		/**
-		 * Data processor used during the conversion.
+		 * Styles processor used during the conversion.
 		 *
 		 * @readonly
-		 * @member {module:engine/dataprocessor/dataprocessor~DataProcessor}
+		 * @member {module:engine/view/stylesmap~StylesProcessor}
 		 */
-		this.processor = dataProcessor;
+		this.stylesProcessor = stylesProcessor;
+
+		/**
+		 * Data processor used during the conversion.
+		 *
+		 * @member {module:engine/dataprocessor/dataprocessor~DataProcessor} #processor
+		 */
+		this.processor;
 
 		/**
 		 * Mapper used for the conversion. It has no permanent bindings, because they are created when getting data and
@@ -187,12 +193,13 @@ export default class DataController {
 
 		// First, convert elements.
 		const modelRange = ModelRange._createIn( modelElementOrFragment );
+		const viewDocument = new ViewDocument( this.stylesProcessor );
 
-		const viewDocumentFragment = new ViewDocumentFragment();
+		const viewDocumentFragment = new ViewDocumentFragment( viewDocument );
 
 		// Create separate ViewDowncastWriter just for data conversion purposes.
 		// We have no view controller and rendering do DOM in DataController so view.change() block is not used here.
-		const viewWriter = new ViewDowncastWriter( new ViewDocument() );
+		const viewWriter = new ViewDowncastWriter( viewDocument );
 		this.mapper.bindElements( modelElementOrFragment, viewDocumentFragment );
 
 		this.downcastDispatcher.convertInsert( modelRange, viewWriter );
@@ -371,6 +378,22 @@ export default class DataController {
 		} );
 	}
 
+	/**
+	 * Adds a style processor normalization rules.
+	 *
+	 * You can implement your own rules as well as use one of the available processor rules:
+	 *
+	 * * background: {@link module:engine/view/styles/background~addBackgroundRules}
+	 * * border: {@link module:engine/view/styles/border~addBorderRules}
+	 * * margin: {@link module:engine/view/styles/margin~addMarginRules}
+	 * * padding: {@link module:engine/view/styles/padding~addPaddingRules}
+	 *
+	 * @param {Function} callback
+	 */
+	addStyleProcessorRules( callback ) {
+		callback( this.stylesProcessor );
+	}
+
 	/**
 	 * Removes all event listeners set by the DataController.
 	 */

+ 4 - 4
packages/ckeditor5-engine/src/controller/editingcontroller.js

@@ -31,8 +31,9 @@ export default class EditingController {
 	 * Creates an editing controller instance.
 	 *
 	 * @param {module:engine/model/model~Model} model Editing model.
+	 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
 	 */
-	constructor( model ) {
+	constructor( model, stylesProcessor ) {
 		/**
 		 * Editor model.
 		 *
@@ -47,7 +48,7 @@ export default class EditingController {
 		 * @readonly
 		 * @member {module:engine/view/view~View}
 		 */
-		this.view = new View();
+		this.view = new View( stylesProcessor );
 
 		/**
 		 * Mapper which describes the model-view binding.
@@ -115,10 +116,9 @@ export default class EditingController {
 				return null;
 			}
 
-			const viewRoot = new RootEditableElement( root.name );
+			const viewRoot = new RootEditableElement( this.view.document, root.name );
 
 			viewRoot.rootName = root.rootName;
-			viewRoot._document = this.view.document;
 			this.mapper.bindElements( root, viewRoot );
 
 			return viewRoot;

+ 6 - 5
packages/ckeditor5-engine/src/conversion/downcasthelpers.js

@@ -416,11 +416,12 @@ export function remove() {
  * provided by the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor} object. If a priority
  * is not provided in the descriptor, the default priority will be used.
  *
+ * @param {module:engine/view/downcastwriter~DowncastWriter} writer
  * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
  * @returns {module:engine/view/attributeelement~AttributeElement}
  */
-export function createViewElementFromHighlightDescriptor( descriptor ) {
-	const viewElement = new ViewAttributeElement( 'span', descriptor.attributes );
+export function createViewElementFromHighlightDescriptor( writer, descriptor ) {
+	const viewElement = writer.createAttributeElement( 'span', descriptor.attributes );
 
 	if ( descriptor.classes ) {
 		viewElement._addClass( descriptor.classes );
@@ -543,7 +544,7 @@ export function clearAttributes() {
 			// Not collapsed selection should not have artifacts.
 			if ( range.isCollapsed ) {
 				// Position might be in the node removed by the view writer.
-				if ( range.end.parent.document ) {
+				if ( range.end.parent.isAttached() ) {
 					conversionApi.writer.mergeAttributes( range.start );
 				}
 			}
@@ -919,8 +920,8 @@ function highlightText( highlightDescriptor ) {
 			return;
 		}
 
-		const viewElement = createViewElementFromHighlightDescriptor( descriptor );
 		const viewWriter = conversionApi.writer;
+		const viewElement = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 		const viewSelection = viewWriter.document.selection;
 
 		if ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection ) {
@@ -1034,7 +1035,7 @@ function removeHighlight( highlightDescriptor ) {
 		}
 
 		// View element that will be used to unwrap `AttributeElement`s.
-		const viewHighlightElement = createViewElementFromHighlightDescriptor( descriptor );
+		const viewHighlightElement = createViewElementFromHighlightDescriptor( conversionApi.writer, descriptor );
 
 		// Get all elements bound with given marker name.
 		const elements = conversionApi.mapper.markerNameToElements( data.markerName );

+ 15 - 6
packages/ckeditor5-engine/src/conversion/viewconsumable.js

@@ -9,7 +9,6 @@
 
 import { isArray } from 'lodash-es';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
-import StylesMap from '../view/stylesmap';
 
 /**
  * Class used for handling consumption of view {@link module:engine/view/element~Element elements},
@@ -89,7 +88,7 @@ export default class ViewConsumable {
 
 		// For elements create new ViewElementConsumables or update already existing one.
 		if ( !this._consumables.has( element ) ) {
-			elementConsumables = new ViewElementConsumables();
+			elementConsumables = new ViewElementConsumables( element );
 			this._consumables.set( element, elementConsumables );
 		} else {
 			elementConsumables = this._consumables.get( element );
@@ -239,6 +238,7 @@ export default class ViewConsumable {
 	 */
 	static consumablesFromElement( element ) {
 		const consumables = {
+			element,
 			name: true,
 			attributes: [],
 			classes: [],
@@ -284,7 +284,7 @@ export default class ViewConsumable {
 	 */
 	static createFrom( from, instance ) {
 		if ( !instance ) {
-			instance = new ViewConsumable();
+			instance = new ViewConsumable( from );
 		}
 
 		if ( from.is( 'text' ) ) {
@@ -319,8 +319,17 @@ export default class ViewConsumable {
 class ViewElementConsumables {
 	/**
 	 * Creates ViewElementConsumables instance.
+	 *
+	 * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} from View node or document fragment
+	 * from which `ViewElementConsumables` is being created.
 	 */
-	constructor() {
+	constructor( from ) {
+		/**
+		 * @readonly
+		 * @member {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
+		 */
+		this.element = from;
+
 		/**
 		 * Flag indicating if name of the element can be consumed.
 		 *
@@ -510,7 +519,7 @@ class ViewElementConsumables {
 			consumables.set( name, true );
 
 			if ( type === 'styles' ) {
-				for ( const alsoName of StylesMap.getRelatedStyles( name ) ) {
+				for ( const alsoName of this.element.document.stylesProcessor.getRelatedStyles( name ) ) {
 					consumables.set( alsoName, true );
 				}
 			}
@@ -577,7 +586,7 @@ class ViewElementConsumables {
 				consumables.set( name, false );
 
 				if ( type == 'styles' ) {
-					for ( const toConsume of StylesMap.getRelatedStyles( name ) ) {
+					for ( const toConsume of this.element.document.stylesProcessor.getRelatedStyles( name ) ) {
 						consumables.set( toConsume, false );
 					}
 				}

+ 4 - 2
packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js

@@ -21,8 +21,10 @@ import DomConverter from '../view/domconverter';
 export default class HtmlDataProcessor {
 	/**
 	 * Creates a new instance of the HTML data processor class.
+	 *
+	 * @param {module:engine/view/document~Document} document The view document instance.
 	 */
-	constructor() {
+	constructor( document ) {
 		/**
 		 * A DOM parser instance used to parse an HTML string to an HTML document.
 		 *
@@ -37,7 +39,7 @@ export default class HtmlDataProcessor {
 		 * @private
 		 * @member {module:engine/view/domconverter~DomConverter}
 		 */
-		this._domConverter = new DomConverter( { blockFillerMode: 'nbsp' } );
+		this._domConverter = new DomConverter( document, { blockFillerMode: 'nbsp' } );
 
 		/**
 		 * A basic HTML writer instance used to convert DOM elements to an HTML string.

+ 3 - 2
packages/ckeditor5-engine/src/dataprocessor/xmldataprocessor.js

@@ -24,10 +24,11 @@ export default class XmlDataProcessor {
 	/**
 	 * Creates a new instance of the XML data processor class.
 	 *
+	 * @param {module:engine/view/document~Document} document The view document instance.
 	 * @param {Object} options Configuration options.
 	 * @param {Array<String>} [options.namespaces=[]] A list of namespaces allowed to use in the XML input.
 	 */
-	constructor( options = {} ) {
+	constructor( document, options = {} ) {
 		/**
 		 * A list of namespaces allowed to use in the XML input.
 		 *
@@ -53,7 +54,7 @@ export default class XmlDataProcessor {
 		 * @private
 		 * @member {module:engine/view/domconverter~DomConverter}
 		 */
-		this._domConverter = new DomConverter( { blockFillerMode: 'nbsp' } );
+		this._domConverter = new DomConverter( document, { blockFillerMode: 'nbsp' } );
 
 		/**
 		 * A basic HTML writer instance used to convert DOM elements to an XML string.

+ 5 - 4
packages/ckeditor5-engine/src/dev-utils/model.js

@@ -39,6 +39,7 @@ import {
 
 import { isPlainObject } from 'lodash-es';
 import toMap from '@ckeditor/ckeditor5-utils/src/tomap';
+import { StylesProcessor } from '../view/stylesmap';
 
 /**
  * Writes the content of a model {@link module:engine/model/document~Document document} to an HTML-like string.
@@ -210,12 +211,12 @@ export function stringify( node, selectionOrPositionOrRange = null, markers = nu
 
 	// Set up conversion.
 	// Create a temporary view controller.
-	const view = new View();
+	const stylesProcessor = new StylesProcessor();
+	const view = new View( stylesProcessor );
 	const viewDocument = view.document;
-	const viewRoot = new ViewRootEditableElement( 'div' );
+	const viewRoot = new ViewRootEditableElement( viewDocument, 'div' );
 
 	// Create a temporary root element in view document.
-	viewRoot._document = view.document;
 	viewRoot.rootName = 'main';
 	viewDocument.roots.add( viewRoot );
 
@@ -242,7 +243,7 @@ export function stringify( node, selectionOrPositionOrRange = null, markers = nu
 		// Stringify object types values for properly display as an output string.
 		const attributes = convertAttributes( modelItem.getAttributes(), stringifyAttributeValue );
 
-		return new ViewContainerElement( modelItem.name, attributes );
+		return new ViewContainerElement( viewDocument, modelItem.name, attributes );
 	} ) );
 
 	downcastDispatcher.on( 'selection', convertRangeSelection() );

+ 12 - 4
packages/ckeditor5-engine/src/dev-utils/view.js

@@ -14,6 +14,7 @@
  */
 
 import View from '../view/view';
+import ViewDocument from '../view/document';
 import ViewDocumentFragment from '../view/documentfragment';
 import XmlDataProcessor from '../dataprocessor/xmldataprocessor';
 import ViewElement from '../view/element';
@@ -24,6 +25,7 @@ import AttributeElement from '../view/attributeelement';
 import ContainerElement from '../view/containerelement';
 import EmptyElement from '../view/emptyelement';
 import UIElement from '../view/uielement';
+import { StylesProcessor } from '../view/stylesmap';
 
 const ELEMENT_RANGE_START_TOKEN = '[';
 const ELEMENT_RANGE_END_TOKEN = ']';
@@ -321,16 +323,19 @@ export function stringify( node, selectionOrPositionOrRange = null, options = {}
  * this node will be used as the root for all parsed nodes.
  * @param {Boolean} [options.sameSelectionCharacters=false] When set to `false`, the selection inside the text should be marked using
  * `{` and `}` and the selection outside the ext using `[` and `]`. When set to `true`, both should be marked with `[` and `]` only.
+ * @param {module:engine/view/stylesmap~StylesProcessor} [options.stylesProcessor] Styles processor.
  * @returns {module:engine/view/text~Text|module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|Object}
  * Returns the parsed view node or an object with two fields: `view` and `selection` when selection ranges were included in the data
  * to parse.
  */
 export function parse( data, options = {} ) {
+	const viewDocument = new ViewDocument( new StylesProcessor() );
+
 	options.order = options.order || [];
 	const rangeParser = new RangeParser( {
 		sameSelectionCharacters: options.sameSelectionCharacters
 	} );
-	const processor = new XmlDataProcessor( {
+	const processor = new XmlDataProcessor( viewDocument, {
 		namespaces: Object.keys( allowedTypes )
 	} );
 
@@ -927,7 +932,10 @@ class ViewStringify {
 function _convertViewElements( rootNode ) {
 	if ( rootNode.is( 'element' ) || rootNode.is( 'documentFragment' ) ) {
 		// Convert element or leave document fragment.
-		const convertedElement = rootNode.is( 'documentFragment' ) ? new ViewDocumentFragment() : _convertElement( rootNode );
+
+		const convertedElement = rootNode.is( 'documentFragment' ) ?
+			new ViewDocumentFragment( rootNode.document ) :
+			_convertElement( rootNode.document, rootNode );
 
 		// Convert all child nodes.
 		// Cache the nodes in array. Otherwise, we would skip some nodes because during iteration we move nodes
@@ -973,10 +981,10 @@ function _convertViewElements( rootNode ) {
 // module:engine/view/emptyelement~EmptyElement|module:engine/view/uielement~UIElement|
 // module:engine/view/containerelement~ContainerElement} A tree view
 // element converted according to its name.
-function _convertElement( viewElement ) {
+function _convertElement( viewDocument, viewElement ) {
 	const info = _convertElementNameAndInfo( viewElement );
 	const ElementConstructor = allowedTypes[ info.type ];
-	const newElement = ElementConstructor ? new ElementConstructor( info.name ) : new ViewElement( info.name );
+	const newElement = ElementConstructor ? new ElementConstructor( viewDocument, info.name ) : new ViewElement( viewDocument, info.name );
 
 	if ( newElement.is( 'attributeElement' ) ) {
 		if ( info.priority !== null ) {

+ 1 - 1
packages/ckeditor5-engine/src/model/model.js

@@ -334,7 +334,7 @@ export default class Model {
 	 *
 	 *		// You can create your own HtmlDataProcessor instance or use editor.data.processor
 	 *		// if you have not overridden the default one (which is the HtmlDataProcessor instance).
-	 *		const htmlDP = new HtmlDataProcessor();
+	 *		const htmlDP = new HtmlDataProcessor( viewDocument );
 	 *
 	 *		// Convert an HTML string to a view document fragment:
 	 *		const viewFragment = htmlDP.toView( htmlString );

+ 4 - 12
packages/ckeditor5-engine/src/model/node.js

@@ -189,20 +189,12 @@ export default class Node {
 	}
 
 	/**
-	 * {@link module:engine/model/document~Document Document} that owns this node or `null` if the node has no parent or is inside
-	 * a {@link module:engine/model/documentfragment~DocumentFragment DocumentFragment}.
+	 * Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
 	 *
-	 * @readonly
-	 * @type {module:engine/model/document~Document|null}
+	 * @returns {Boolean}
 	 */
-	get document() {
-		// This is a top element of a sub-tree.
-		if ( this.root == this ) {
-			return null;
-		}
-
-		// Root may be `DocumentFragment` which does not have document property.
-		return this.root.document || null;
+	isAttached() {
+		return this.root.is( 'rootElement' );
 	}
 
 	/**

+ 4 - 6
packages/ckeditor5-engine/src/model/rootelement.js

@@ -17,12 +17,12 @@ export default class RootElement extends Element {
 	/**
 	 * Creates root element.
 	 *
-	 * @param {module:engine/model/document~Document} doc Document that is an owner of this root.
+	 * @param {module:engine/model/document~Document} document Document that is an owner of this root.
 	 * @param {String} name Node name.
 	 * @param {String} [rootName='main'] Unique root name used to identify this root
 	 * element by {@link module:engine/model/document~Document}.
 	 */
-	constructor( doc, name, rootName = 'main' ) {
+	constructor( document, name, rootName = 'main' ) {
 		super( name );
 
 		/**
@@ -31,7 +31,7 @@ export default class RootElement extends Element {
 		 * @private
 		 * @member {module:engine/model/document~Document}
 		 */
-		this._doc = doc;
+		this._document = document;
 
 		/**
 		 * Unique root name used to identify this root element by {@link module:engine/model/document~Document}.
@@ -45,13 +45,11 @@ export default class RootElement extends Element {
 	/**
 	 * {@link module:engine/model/document~Document Document} that owns this root element.
 	 *
-	 * In contrary, to {@link module:engine/model/node~Node node}, root element always have a `document`.
-	 *
 	 * @readonly
 	 * @type {module:engine/model/document~Document|null}
 	 */
 	get document() {
-		return this._doc;
+		return this._document;
 	}
 
 	/**

+ 4 - 3
packages/ckeditor5-engine/src/model/selection.js

@@ -829,7 +829,7 @@ function isUnvisitedBlock( element, visited ) {
 
 	visited.add( element );
 
-	return element.document.model.schema.isBlock( element ) && element.parent;
+	return element.root.document.model.schema.isBlock( element ) && element.parent;
 }
 
 // Checks if the given element is a $block was not previously visited and is a top block in a range.
@@ -841,7 +841,8 @@ function isUnvisitedTopBlock( element, visited, range ) {
 // It will search until first ancestor that is a limit element.
 // Marks all ancestors as already visited to not include any of them later on.
 function getParentBlock( position, visited ) {
-	const schema = position.parent.document.model.schema;
+	const element = position.parent;
+	const schema = element.root.document.model.schema;
 
 	const ancestors = position.parent.getAncestors( { parentFirst: true, includeSelf: true } );
 
@@ -887,7 +888,7 @@ function isTopBlockInRange( block, range ) {
 // @param {module:engine/model/node~Node} node
 // @returns {module:engine/model/node~Node|undefined}
 function findAncestorBlock( node ) {
-	const schema = node.document.model.schema;
+	const schema = node.root.document.model.schema;
 
 	let parent = node.parent;
 

+ 0 - 11
packages/ckeditor5-engine/src/model/textproxy.js

@@ -163,17 +163,6 @@ export default class TextProxy {
 		return this.textNode.root;
 	}
 
-	/**
-	 * {@link module:engine/model/document~Document Document} that owns text node represented by this text proxy or `null` if the text node
-	 * has no parent or is inside a {@link module:engine/model/documentfragment~DocumentFragment DocumentFragment}.
-	 *
-	 * @readonly
-	 * @type {module:engine/model/document~Document|null}
-	 */
-	get document() {
-		return this.textNode.document;
-	}
-
 	/**
 	 * Checks whether this object is of the given.
 	 *

+ 7 - 2
packages/ckeditor5-engine/src/view/attributeelement.js

@@ -33,9 +33,14 @@ export default class AttributeElement extends Element {
 	 * @see module:engine/view/downcastwriter~DowncastWriter#createAttributeElement
 	 * @see module:engine/view/element~Element
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
+	 * @param {String} name Node name.
+	 * @param {Object|Iterable} [attrs] Collection of attributes.
+	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
+	 * A list of nodes to be inserted into created element.
 	 */
-	constructor( name, attrs, children ) {
-		super( name, attrs, children );
+	constructor( document, name, attrs, children ) {
+		super( document, name, attrs, children );
 
 		/**
 		 * Returns block {@link module:engine/view/filler filler} offset or `null` if block filler is not needed.

+ 7 - 2
packages/ckeditor5-engine/src/view/containerelement.js

@@ -37,9 +37,14 @@ export default class ContainerElement extends Element {
 	 * @see module:engine/view/downcastwriter~DowncastWriter#createContainerElement
 	 * @see module:engine/view/element~Element
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
+	 * @param {String} name Node name.
+	 * @param {Object|Iterable} [attrs] Collection of attributes.
+	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
+	 * A list of nodes to be inserted into created element.
 	 */
-	constructor( name, attrs, children ) {
-		super( name, attrs, children );
+	constructor( document, name, attrs, children ) {
+		super( document, name, attrs, children );
 
 		/**
 		 * Returns block {@link module:engine/view/filler filler} offset or `null` if block filler is not needed.

+ 11 - 18
packages/ckeditor5-engine/src/view/document.js

@@ -11,7 +11,6 @@ import DocumentSelection from './documentselection';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
-import StylesMap from './stylesmap';
 
 // @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
 
@@ -24,8 +23,10 @@ import StylesMap from './stylesmap';
 export default class Document {
 	/**
 	 * Creates a Document instance.
+	 *
+	 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
 	 */
-	constructor() {
+	constructor( stylesProcessor ) {
 		/**
 		 * Selection done on this document.
 		 *
@@ -47,6 +48,14 @@ export default class Document {
 		 */
 		this.roots = new Collection( { idProperty: 'rootName' } );
 
+		/**
+		 * The styles processor instance used by this document when normalizing styles.
+		 *
+		 * @readonly
+		 * @member {module:engine/view/stylesmap~StylesProcessor}
+		 */
+		this.stylesProcessor = stylesProcessor;
+
 		/**
 		 * Defines whether document is in read-only mode.
 		 *
@@ -161,22 +170,6 @@ export default class Document {
 		this.stopListening();
 	}
 
-	/**
-	 * Adds a style processor normalization rules.
-	 *
-	 * The available style processors:
-	 *
-	 * * background: {@link module:engine/view/styles/background~addBackgroundRules}
-	 * * border: {@link module:engine/view/styles/border~addBorderRules}
-	 * * margin: {@link module:engine/view/styles/margin~addMarginRules}
-	 * * padding: {@link module:engine/view/styles/padding~addPaddingRules}
-	 *
-	 * @param {Function} callback
-	 */
-	addStyleProcessorRules( callback ) {
-		callback( StylesMap._styleProcessor );
-	}
-
 	/**
 	 * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
 	 *

+ 15 - 6
packages/ckeditor5-engine/src/view/documentfragment.js

@@ -25,10 +25,19 @@ export default class DocumentFragment {
 	 * Creates new DocumentFragment instance.
 	 *
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document to which this document fragment belongs.
 	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
 	 * A list of nodes to be inserted into the created document fragment.
 	 */
-	constructor( children ) {
+	constructor( document, children ) {
+		/**
+		 * The document to which this document fragment belongs.
+		 *
+		 * @readonly
+		 * @member {module:engine/view/document~Document}
+		 */
+		this.document = document;
+
 		/**
 		 * Array of child nodes.
 		 *
@@ -164,7 +173,7 @@ export default class DocumentFragment {
 		this._fireChange( 'children', this );
 		let count = 0;
 
-		const nodes = normalize( items );
+		const nodes = normalize( this.document, items );
 
 		for ( const node of nodes ) {
 			// If node that is being added to this element is already inside another element, first remove it from the old parent.
@@ -238,10 +247,10 @@ mix( DocumentFragment, EmitterMixin );
 //
 // @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>}
 // @returns {Iterable.<module:engine/view/node~Node>}
-function normalize( nodes ) {
+function normalize( document, nodes ) {
 	// Separate condition because string is iterable.
 	if ( typeof nodes == 'string' ) {
-		return [ new Text( nodes ) ];
+		return [ new Text( document, nodes ) ];
 	}
 
 	if ( !isIterable( nodes ) ) {
@@ -252,11 +261,11 @@ function normalize( nodes ) {
 	return Array.from( nodes )
 		.map( node => {
 			if ( typeof node == 'string' ) {
-				return new Text( node );
+				return new Text( document, node );
 			}
 
 			if ( node instanceof TextProxy ) {
-				return new Text( node.data );
+				return new Text( document, node.data );
 			}
 
 			return node;

+ 12 - 5
packages/ckeditor5-engine/src/view/domconverter.js

@@ -44,10 +44,17 @@ export default class DomConverter {
 	/**
 	 * Creates DOM converter.
 	 *
+	 * @param {module:engine/view/document~Document} document The view document instance.
 	 * @param {Object} options Object with configuration options.
 	 * @param {module:engine/view/filler~BlockFillerMode} [options.blockFillerMode='br'] The type of the block filler to use.
 	 */
-	constructor( options = {} ) {
+	constructor( document, options = {} ) {
+		/**
+		 * @readonly
+		 * @type {module:engine/view/document~Document}
+		 */
+		this.document = document;
+
 		/**
 		 * The mode of a block filler used by DOM converter.
 		 *
@@ -399,7 +406,7 @@ export default class DomConverter {
 			} else {
 				const textData = this._processDataFromDomText( domNode );
 
-				return textData === '' ? null : new ViewText( textData );
+				return textData === '' ? null : new ViewText( this.document, textData );
 			}
 		} else if ( this.isComment( domNode ) ) {
 			return null;
@@ -412,7 +419,7 @@ export default class DomConverter {
 
 			if ( this.isDocumentFragment( domNode ) ) {
 				// Create view document fragment.
-				viewElement = new ViewDocumentFragment();
+				viewElement = new ViewDocumentFragment( this.document );
 
 				if ( options.bind ) {
 					this.bindDocumentFragments( domNode, viewElement );
@@ -420,7 +427,7 @@ export default class DomConverter {
 			} else {
 				// Create view element.
 				const viewName = options.keepOriginalCase ? domNode.tagName : domNode.tagName.toLowerCase();
-				viewElement = new ViewElement( viewName );
+				viewElement = new ViewElement( this.document, viewName );
 
 				if ( options.bind ) {
 					this.bindElements( domNode, viewElement );
@@ -800,7 +807,7 @@ export default class DomConverter {
 	/**
 	 * Checks if the node is an instance of the block filler for this DOM converter.
 	 *
-	 *		const converter = new DomConverter( { blockFillerMode: 'br' } );
+	 *		const converter = new DomConverter( viewDocument, { blockFillerMode: 'br' } );
 	 *
 	 *		converter.isBlockFiller( BR_FILLER( document ) ); // true
 	 *		converter.isBlockFiller( NBSP_FILLER( document ) ); // false

+ 18 - 13
packages/ckeditor5-engine/src/view/downcastwriter.js

@@ -37,8 +37,13 @@ import { isPlainObject } from 'lodash-es';
  * section of the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide.
  */
 export default class DowncastWriter {
+	/**
+	 * @param {module:engine/view/document~Document} document The view document instance.
+	 */
 	constructor( document ) {
 		/**
+		 * The view document instance in which this writer operates.
+		 *
 		 * @readonly
 		 * @type {module:engine/view/document~Document}
 		 */
@@ -145,7 +150,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/text~Text} The created text node.
 	 */
 	createText( data ) {
-		return new Text( data );
+		return new Text( this.document, data );
 	}
 
 	/**
@@ -168,7 +173,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/attributeelement~AttributeElement} Created element.
 	 */
 	createAttributeElement( name, attributes, options = {} ) {
-		const attributeElement = new AttributeElement( name, attributes );
+		const attributeElement = new AttributeElement( this.document, name, attributes );
 
 		if ( options.priority ) {
 			attributeElement._priority = options.priority;
@@ -200,7 +205,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/containerelement~ContainerElement} Created element.
 	 */
 	createContainerElement( name, attributes ) {
-		return new ContainerElement( name, attributes );
+		return new ContainerElement( this.document, name, attributes );
 	}
 
 	/**
@@ -214,7 +219,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/editableelement~EditableElement} Created element.
 	 */
 	createEditableElement( name, attributes ) {
-		const editableElement = new EditableElement( name, attributes );
+		const editableElement = new EditableElement( this.document, name, attributes );
 		editableElement._document = this.document;
 
 		return editableElement;
@@ -231,7 +236,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/emptyelement~EmptyElement} Created element.
 	 */
 	createEmptyElement( name, attributes ) {
-		return new EmptyElement( name, attributes );
+		return new EmptyElement( this.document, name, attributes );
 	}
 
 	/**
@@ -255,7 +260,7 @@ export default class DowncastWriter {
 	 * @returns {module:engine/view/uielement~UIElement} Created element.
 	 */
 	createUIElement( name, attributes, renderFunction ) {
-		const uiElement = new UIElement( name, attributes );
+		const uiElement = new UIElement( this.document, name, attributes );
 
 		if ( renderFunction ) {
 			uiElement.render = renderFunction;
@@ -325,7 +330,7 @@ export default class DowncastWriter {
 	 *		}, element );
 	 *
 	 * **Note**: The passed style can be normalized if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.
 	 *
 	 * @param {String|Object} property Property name or object with key - value pairs.
@@ -347,7 +352,7 @@ export default class DowncastWriter {
 	 *		writer.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles.
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
 	 *
 	 * @param {Array.<String>|String} property
@@ -686,7 +691,7 @@ export default class DowncastWriter {
 
 		// If range is collapsed - nothing to remove.
 		if ( range.isCollapsed ) {
-			return new DocumentFragment();
+			return new DocumentFragment( this.document );
 		}
 
 		// Break attributes at range start and end.
@@ -708,7 +713,7 @@ export default class DowncastWriter {
 		range.end = mergePosition.clone();
 
 		// Return removed nodes.
-		return new DocumentFragment( removed );
+		return new DocumentFragment( this.document, removed );
 	}
 
 	/**
@@ -914,7 +919,7 @@ export default class DowncastWriter {
 	 * @param {module:engine/view/containerelement~ContainerElement} viewElement Element to be renamed.
 	 */
 	rename( newName, viewElement ) {
-		const newElement = new ContainerElement( newName, viewElement.getAttributes() );
+		const newElement = new ContainerElement( this.document, newName, viewElement.getAttributes() );
 
 		this.insert( Position._createAfter( viewElement ), newElement );
 		this.move( Range._createIn( viewElement ), Position._createAt( newElement, 0 ) );
@@ -985,7 +990,7 @@ export default class DowncastWriter {
 	/**
 	 * Creates a range spanning from `start` position to `end` position.
 	 *
-	 * **Note:** This factory method creates it's own {@link module:engine/view/position~Position} instances basing on passed values.
+	 * **Note:** This factory method creates its own {@link module:engine/view/position~Position} instances basing on passed values.
 	 *
 	 * @param {module:engine/view/position~Position} start Start position.
 	 * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
@@ -1807,7 +1812,7 @@ function breakTextNode( position ) {
 	position.parent._data = position.parent.data.slice( 0, position.offset );
 
 	// Insert new text node after position's parent text node.
-	position.parent.parent._insertChild( position.parent.index + 1, new Text( textToMove ) );
+	position.parent.parent._insertChild( position.parent.index + 1, new Text( position.root.document, textToMove ) );
 
 	// Return new position between two newly created text nodes.
 	return new Position( position.parent.parent, position.parent.index + 1 );

+ 14 - 55
packages/ckeditor5-engine/src/view/editableelement.js

@@ -8,12 +8,9 @@
  */
 
 import ContainerElement from './containerelement';
-import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 
-const documentSymbol = Symbol( 'document' );
-
 /**
  * Editable element which can be a {@link module:engine/view/rooteditableelement~RootEditableElement root}
  * or nested editable area in the editor.
@@ -33,8 +30,8 @@ export default class EditableElement extends ContainerElement {
 	 * @see module:engine/view/downcastwriter~DowncastWriter#createEditableElement
 	 * @protected
 	 */
-	constructor( name, attrs, children ) {
-		super( name, attrs, children );
+	constructor( document, name, attrs, children ) {
+		super( document, name, attrs, children );
 
 		/**
 		 * Whether the editable is in read-write or read-only mode.
@@ -56,14 +53,18 @@ export default class EditableElement extends ContainerElement {
 		 */
 		this.set( 'isFocused', false );
 
-		/**
-		 * The {@link module:engine/view/document~Document} which is an owner of this root.
-		 * Can only by set once.
-		 * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-editableelement-document-already-set`
-		 * when document is already set.
-		 *
-		 * @member {module:engine/view/document~Document} #document
-		 */
+		this.bind( 'isReadOnly' ).to( document );
+
+		this.bind( 'isFocused' ).to(
+			document,
+			'isFocused',
+			isFocused => isFocused && document.selection.editableElement == this
+		);
+
+		// Update focus state based on selection changes.
+		this.listenTo( document.selection, 'change', () => {
+			this.isFocused = document.isFocused && document.selection.editableElement == this;
+		} );
 	}
 
 	/**
@@ -105,48 +106,6 @@ export default class EditableElement extends ContainerElement {
 	destroy() {
 		this.stopListening();
 	}
-
-	/**
-	 * Returns document associated with the editable.
-	 *
-	 * @readonly
-	 * @returns {module:engine/view/document~Document}
-	 */
-	get document() {
-		return this.getCustomProperty( documentSymbol );
-	}
-
-	/**
-	 * Sets document of this editable element.
-	 *
-	 * @protected
-	 * @param {module:engine/view/document~Document} document
-	 */
-	set _document( document ) {
-		if ( this.getCustomProperty( documentSymbol ) ) {
-			/**
-			 * View document is already set. It can only be set once.
-			 *
-			 * @error view-editableelement-document-already-set
-			 */
-			throw new CKEditorError( 'view-editableelement-document-already-set: View document is already set.', this );
-		}
-
-		this._setCustomProperty( documentSymbol, document );
-
-		this.bind( 'isReadOnly' ).to( document );
-
-		this.bind( 'isFocused' ).to(
-			document,
-			'isFocused',
-			isFocused => isFocused && document.selection.editableElement == this
-		);
-
-		// Update focus state based on selection changes.
-		this.listenTo( document.selection, 'change', () => {
-			this.isFocused = document.isFocused && document.selection.editableElement == this;
-		} );
-	}
 }
 
 mix( EditableElement, ObservableMixin );

+ 19 - 17
packages/ckeditor5-engine/src/view/element.js

@@ -48,18 +48,19 @@ export default class Element extends Node {
 	 *
 	 * Attributes can be passed in various formats:
 	 *
-	 *		new Element( 'div', { class: 'editor', contentEditable: 'true' } ); // object
-	 *		new Element( 'div', [ [ 'class', 'editor' ], [ 'contentEditable', 'true' ] ] ); // map-like iterator
-	 *		new Element( 'div', mapOfAttributes ); // map
+	 *		new Element( viewDocument, 'div', { class: 'editor', contentEditable: 'true' } ); // object
+	 *		new Element( viewDocument, 'div', [ [ 'class', 'editor' ], [ 'contentEditable', 'true' ] ] ); // map-like iterator
+	 *		new Element( viewDocument, 'div', mapOfAttributes ); // map
 	 *
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
 	 * @param {String} name Node name.
 	 * @param {Object|Iterable} [attrs] Collection of attributes.
 	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
 	 * A list of nodes to be inserted into created element.
 	 */
-	constructor( name, attrs, children ) {
-		super();
+	constructor( document, name, attrs, children ) {
+		super( document );
 
 		/**
 		 * Name of the element.
@@ -110,7 +111,7 @@ export default class Element extends Node {
 		 * @protected
 		 * @member {module:engine/view/stylesmap~StylesMap} module:engine/view/element~Element#_styles
 		 */
-		this._styles = new StylesMap();
+		this._styles = new StylesMap( this.document.stylesProcessor );
 
 		if ( this._attrs.has( 'style' ) ) {
 			// Remove style attribute and handle it by styles map.
@@ -380,13 +381,13 @@ export default class Element extends Node {
 	 * If the style does not exist `undefined` is returned.
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#getAsString `StylesMap#getAsString()`} for details.
 	 *
 	 * For an element with style set to `'margin:1px'`:
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		const element = view.change( writer => {
 	 *			const element = writer.createElement();
@@ -428,7 +429,7 @@ export default class Element extends Node {
 	 * Will return a `2px` string.
 	 *
 	 * **Note**: This method will return normalized values only if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#getNormalized `StylesMap#getNormalized()`} for details.
 	 *
 	 *
@@ -563,7 +564,7 @@ export default class Element extends Node {
 		}
 
 		// ContainerElement and AttributeElement should be also cloned properly.
-		const cloned = new this.constructor( this.name, this._attrs, childrenClone );
+		const cloned = new this.constructor( this.document, this.name, this._attrs, childrenClone );
 
 		// Classes and styles are cloned separately - this solution is faster than adding them back to attributes and
 		// parse once again in constructor.
@@ -610,7 +611,7 @@ export default class Element extends Node {
 		this._fireChange( 'children', this );
 		let count = 0;
 
-		const nodes = normalize( items );
+		const nodes = normalize( this.document, items );
 
 		for ( const node of nodes ) {
 			// If node that is being added to this element is already inside another element, first remove it from the old parent.
@@ -619,6 +620,7 @@ export default class Element extends Node {
 			}
 
 			node.parent = this;
+			node.document = this.document;
 
 			this._children.splice( index, 0, node );
 			index++;
@@ -755,7 +757,7 @@ export default class Element extends Node {
 	 *		} );
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.
 	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#setStyle
@@ -777,7 +779,7 @@ export default class Element extends Node {
 	 *		element._removeStyle( [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles.
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
 	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#removeStyle
@@ -886,10 +888,10 @@ function parseClasses( classesSet, classesString ) {
 //
 // @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>}
 // @returns {Iterable.<module:engine/view/node~Node>}
-function normalize( nodes ) {
+function normalize( document, nodes ) {
 	// Separate condition because string is iterable.
 	if ( typeof nodes == 'string' ) {
-		return [ new Text( nodes ) ];
+		return [ new Text( document, nodes ) ];
 	}
 
 	if ( !isIterable( nodes ) ) {
@@ -900,11 +902,11 @@ function normalize( nodes ) {
 	return Array.from( nodes )
 		.map( node => {
 			if ( typeof node == 'string' ) {
-				return new Text( node );
+				return new Text( document, node );
 			}
 
 			if ( node instanceof TextProxy ) {
-				return new Text( node.data );
+				return new Text( document, node.data );
 			}
 
 			return node;

+ 6 - 3
packages/ckeditor5-engine/src/view/emptyelement.js

@@ -28,11 +28,14 @@ export default class EmptyElement extends Element {
 	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#createEmptyElement
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
 	 * @param {String} name Node name.
-	 * @param {Object|Iterable} [attributes] Collection of attributes.
+	 * @param {Object|Iterable} [attrs] Collection of attributes.
+	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
+	 * A list of nodes to be inserted into created element.
 	 */
-	constructor( name, attributes, children ) {
-		super( name, attributes, children );
+	constructor( document, name, attrs, children ) {
+		super( document, name, attrs, children );
 
 		/**
 		 * Returns `null` because filler is not needed for EmptyElements.

+ 19 - 15
packages/ckeditor5-engine/src/view/node.js

@@ -17,19 +17,30 @@ import { clone } from 'lodash-es';
 import '@ckeditor/ckeditor5-utils/src/version';
 
 /**
- * Abstract tree view node class.
+ * Abstract view node class.
  *
  * This is an abstract class. Its constructor should not be used directly.
- * Use the {@link module:engine/view/element~Element} class to create view elements
- * or {@link module:engine/view/text~Text} class to create view text nodes.
+ * Use the {@link module:engine/view/downcastwriter~DowncastWriter} or {@link module:engine/view/upcastwriter~UpcastWriter}
+ * to create new instances of view nodes.
  *
  * @abstract
  */
 export default class Node {
 	/**
 	 * Creates a tree view node.
+	 *
+	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this node belongs.
 	 */
-	constructor() {
+	constructor( document ) {
+		/**
+		 * The document instance to which this node belongs.
+		 *
+		 * @readonly
+		 * @member {module:engine/view/document~Document}
+		 */
+		this.document = document;
+
 		/**
 		 * Parent element. Null by default. Set by {@link module:engine/view/element~Element#_insertChild}.
 		 *
@@ -109,19 +120,12 @@ export default class Node {
 	}
 
 	/**
-	 * {@link module:engine/view/document~Document View document} that owns this node, or `null` if the node is inside
-	 * {@link module:engine/view/documentfragment~DocumentFragment document fragment}.
+	 * Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
 	 *
-	 * @readonly
-	 * @type {module:engine/view/document~Document|null}
+	 * @returns {Boolean}
 	 */
-	get document() {
-		// Parent might be Node, null or DocumentFragment.
-		if ( this.parent instanceof Node ) {
-			return this.parent.document;
-		} else {
-			return null;
-		}
+	isAttached() {
+		return this.root.is( 'rootElement' );
 	}
 
 	/**

+ 4 - 4
packages/ckeditor5-engine/src/view/placeholder.js

@@ -144,10 +144,7 @@ export function hidePlaceholder( writer, element ) {
  * @returns {Boolean}
  */
 export function needsPlaceholder( element ) {
-	const doc = element.document;
-
-	// The element was removed from document.
-	if ( !doc ) {
+	if ( !element.isAttached() ) {
 		return false;
 	}
 
@@ -155,6 +152,8 @@ export function needsPlaceholder( element ) {
 	const isEmptyish = !Array.from( element.getChildren() )
 		.some( element => !element.is( 'uiElement' ) );
 
+	const doc = element.document;
+
 	// If the element is empty and the document is blurred.
 	if ( !doc.isFocused && isEmptyish ) {
 		return true;
@@ -201,6 +200,7 @@ function updateDocumentPlaceholders( doc, writer ) {
 // @returns {Boolean} True if any changes were made to the view document.
 function updatePlaceholder( writer, element, config ) {
 	const { text, isDirectHost } = config;
+
 	const hostElement = isDirectHost ? element : getChildPlaceholderHostSubstitute( element );
 	let wasViewModified = false;
 

+ 3 - 2
packages/ckeditor5-engine/src/view/rooteditableelement.js

@@ -22,10 +22,11 @@ export default class RootEditableElement extends EditableElement {
 	/**
 	 * Creates root editable element.
 	 *
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
 	 * @param {String} name Node name.
 	 */
-	constructor( name ) {
-		super( name );
+	constructor( document, name ) {
+		super( document, name );
 
 		/**
 		 * Name of this root inside {@link module:engine/view/document~Document} that is an owner of this root. If no

+ 1 - 1
packages/ckeditor5-engine/src/view/styles/background.js

@@ -12,7 +12,7 @@ import { getShorthandValues, isAttachment, isColor, isPosition, isRepeat, isURL
 /**
  * Adds a background CSS styles processing rules.
  *
- *		editor.editing.view.document.addStyleProcessorRules( addBackgroundRules );
+ *		editor.data.addStyleProcessorRules( addBackgroundRules );
  *
  * The normalized value is stored as:
  *

+ 1 - 1
packages/ckeditor5-engine/src/view/styles/border.js

@@ -12,7 +12,7 @@ import { getShorthandValues, getBoxSidesValueReducer, getBoxSidesValues, isLengt
 /**
  * Adds a border CSS styles processing rules.
  *
- *		editor.editing.view.document.addStyleProcessorRules( addBorderRules );
+ *		editor.data.addStyleProcessorRules( addBorderRules );
  *
  * This rules merges all [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) styles notation shorthands:
  *

+ 1 - 1
packages/ckeditor5-engine/src/view/styles/margin.js

@@ -12,7 +12,7 @@ import { getPositionShorthandNormalizer, getBoxSidesValueReducer } from './utils
 /**
  * Adds a margin CSS styles processing rules.
  *
- *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+ *		editor.data.addStyleProcessorRules( addMarginRules );
  *
  * The normalized value is stored as:
  *

+ 1 - 1
packages/ckeditor5-engine/src/view/styles/padding.js

@@ -12,7 +12,7 @@ import { getPositionShorthandNormalizer, getBoxSidesValueReducer } from './utils
 /**
  * Adds a margin CSS styles processing rules.
  *
- *		editor.editing.view.document.addStyleProcessorRules( addPaddingRules );
+ *		editor.data.addStyleProcessorRules( addPaddingRules );
  *
  * The normalized value is stored as:
  *

+ 21 - 69
packages/ckeditor5-engine/src/view/stylesmap.js

@@ -17,28 +17,28 @@ import { get, isObject, merge, set, unset } from 'lodash-es';
 export default class StylesMap {
 	/**
 	 * Creates Styles instance.
+	 *
+	 * @param {module:engine/view/stylesmap~StylesProcessor} styleProcessor
 	 */
-	constructor() {
+	constructor( styleProcessor ) {
 		/**
 		 * Keeps an internal representation of styles map. Normalized styles are kept as object tree to allow unified modification and
 		 * value access model using lodash's get, set, unset, etc methods.
 		 *
 		 * When no style processor rules are defined the it acts as simple key-value storage.
 		 *
-		 * @type {Object}
 		 * @private
+		 * @type {Object}
 		 */
 		this._styles = {};
 
-		// Hide _styleProcessor from the watchdog by making this property non-enumarable. Watchdog checks errors for their editor origin
-		// by checking if two objects are connected through properties. Using singleton is against this check as it would detect
-		// that two editors are connected through single style processor instance.
-		Object.defineProperty( this, '_styleProcessor', {
-			get() {
-				return StylesMap._styleProcessor;
-			},
-			enumerable: false
-		} );
+		/**
+		 * An instance of the {@link module:engine/view/stylesmap~StylesProcessor}.
+		 *
+		 * @private
+		 * @member {module:engine/view/stylesmap~StylesProcessor}
+		 */
+		this._styleProcessor = styleProcessor;
 	}
 
 	/**
@@ -94,7 +94,7 @@ export default class StylesMap {
 	 * **Note**: This check supports normalized style names.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		styles.setTo( 'margin:2px;' );
 	 *
@@ -139,11 +139,11 @@ export default class StylesMap {
 	 *			'margin-right': '1em'
 	 *		} );
 	 *
-	 * ***Note**:* This method uses {@link module:engine/view/document~Document#addStyleProcessorRules enabled style processor rules}
-	 * to normalize passed values.
+	 * ***Note**:* This method uses {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules
+	 * enabled style processor rules} to normalize passed values.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		styles.set( 'margin', '2px' );
 	 *
@@ -192,11 +192,11 @@ export default class StylesMap {
 	 *
 	 *		styles.toString();   // -> 'margin-right:2px;'
 	 *
-	 * ***Note**:* This method uses {@link module:engine/view/document~Document#addStyleProcessorRules enabled style processor rules}
-	 * to normalize passed values.
+	 * ***Note**:* This method uses {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules
+	 * enabled style processor rules} to normalize passed values.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		styles.setTo( 'margin:1px' );
 	 *
@@ -220,7 +220,7 @@ export default class StylesMap {
 	 * Returns a normalized style object or a single value.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		const styles = new Styles();
 	 *		styles.setTo( 'margin:1px 2px 3em;' );
@@ -256,7 +256,7 @@ export default class StylesMap {
 	 * **Note**: This method supports normalized styles if defined.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		styles.set( 'margin' , '1px' );
 	 *		styles.set( 'background', '#f00' );
@@ -282,7 +282,7 @@ export default class StylesMap {
 	 * Returns property as a value string or undefined if property is not set.
 	 *
 	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *		editor.data.addStyleProcessorRules( addMarginRules );
 	 *
 	 *		const styles = new Styles();
 	 *		styles.setTo( 'margin:1px;' );
@@ -371,28 +371,6 @@ export default class StylesMap {
 		this._styles = {};
 	}
 
-	/**
-	 * Returns related style names.
-	 *
-	 *		// Enable 'margin' shorthand processing:
-	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
-	 *
-	 *		StylesMap.getRelatedStyles( 'margin' );
-	 *		// will return: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ];
-	 *
-	 *		StylesMap.getRelatedStyles( 'margin-top' );
-	 *		// will return: [ 'margin' ];
-	 *
-	 * **Note**: To define new style relations load an existing style processor (as shown above) or use
-	 * {@link module:engine/view/stylesmap~StylesProcessor#setStyleRelation `StylesProcessor.setStyleRelation()`}.
-	 *
-	 * @param {String} name
-	 * @returns {Array.<String>}
-	 */
-	static getRelatedStyles( name ) {
-		return this._styleProcessor.getRelatedStyles( name );
-	}
-
 	/**
 	 * Returns normalized styles entries for further processing.
 	 *
@@ -439,32 +417,6 @@ export default class StylesMap {
 			this.remove( parentPath );
 		}
 	}
-
-	/**
-	 * Returns global StylesProcessor instance.
-	 *
-	 * @returns {module:engine/view/stylesmap~StylesProcessor}
-	 * @private
-	 */
-	static get _styleProcessor() {
-		if ( !this._processor ) {
-			this._processor = new StylesProcessor();
-		}
-
-		return this._processor;
-	}
-
-	/**
-	 * Set new StylesProcessor instance.
-	 *
-	 * This is an internal method used mostly in tests.
-	 *
-	 * @param processor
-	 * @protected
-	 */
-	static _setProcessor( processor ) {
-		this._processor = processor;
-	}
 }
 
 /**

+ 5 - 4
packages/ckeditor5-engine/src/view/text.js

@@ -12,7 +12,7 @@ import Node from './node';
 /**
  * Tree view text node.
  *
- * The constructor of this class shouldn't be used directly. To create new Text instances
+ * The constructor of this class should not be used directly. To create a new text node instance
  * use the {@link module:engine/view/downcastwriter~DowncastWriter#createText `DowncastWriter#createText()`}
  * method when working on data downcasted from the model or the
  * {@link module:engine/view/upcastwriter~UpcastWriter#createText `UpcastWriter#createText()`}
@@ -25,10 +25,11 @@ export default class Text extends Node {
 	 * Creates a tree view text node.
 	 *
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this text node belongs.
 	 * @param {String} data The text's data.
 	 */
-	constructor( data ) {
-		super();
+	constructor( document, data ) {
+		super( document );
 
 		/**
 		 * The text content.
@@ -125,7 +126,7 @@ export default class Text extends Node {
 	 * @returns {module:engine/view/text~Text} Text node that is a clone of this node.
 	 */
 	_clone() {
-		return new Text( this.data );
+		return new Text( this.document, this.data );
 	}
 
 	// @if CK_DEBUG_ENGINE // toString() {

+ 5 - 2
packages/ckeditor5-engine/src/view/uielement.js

@@ -41,11 +41,14 @@ export default class UIElement extends Element {
 	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#createUIElement
 	 * @protected
+	 * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
 	 * @param {String} name Node name.
 	 * @param {Object|Iterable} [attributes] Collection of attributes.
+	 * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
+	 * A list of nodes to be inserted into created element.
 	 */
-	constructor( name, attributes, children ) {
-		super( name, attributes, children );
+	constructor( document, name, attributes, children ) {
+		super( document, name, attributes, children );
 
 		/**
 		 * Returns `null` because filler is not needed for UIElements.

+ 20 - 7
packages/ckeditor5-engine/src/view/upcastwriter.js

@@ -29,12 +29,25 @@ import Selection from './selection';
  * Unlike `DowncastWriter`, which is available in the {@link module:engine/view/view~View#change `View#change()`} block,
  * `UpcastWriter` can be created wherever you need it:
  *
- *		const writer = new UpcastWriter();
+ *		const writer = new UpcastWriter( viewDocument );
  *		const text = writer.createText( 'foo!' );
  *
  *		writer.appendChild( text, someViewElement );
  */
 export default class UpcastWriter {
+	/**
+	 * @param {module:engine/view/document~Document} document The view document instance in which this upcast writer operates.
+	 */
+	constructor( document ) {
+		/**
+		 * The view document instance in which this upcast writer operates.
+		 *
+		 * @readonly
+		 * @type {module:engine/view/document~Document}
+		 */
+		this.document = document;
+	}
+
 	/**
 	 * Creates a new {@link module:engine/view/documentfragment~DocumentFragment} instance.
 	 *
@@ -43,7 +56,7 @@ export default class UpcastWriter {
 	 * @returns {module:engine/view/documentfragment~DocumentFragment} The created document fragment.
 	 */
 	createDocumentFragment( children ) {
-		return new DocumentFragment( children );
+		return new DocumentFragment( this.document, children );
 	}
 
 	/**
@@ -62,7 +75,7 @@ export default class UpcastWriter {
 	 * @returns {module:engine/view/element~Element} Created element.
 	 */
 	createElement( name, attrs, children ) {
-		return new Element( name, attrs, children );
+		return new Element( this.document, name, attrs, children );
 	}
 
 	/**
@@ -72,7 +85,7 @@ export default class UpcastWriter {
 	 * @returns {module:engine/view/text~Text} The created text node.
 	 */
 	createText( data ) {
-		return new Text( data );
+		return new Text( this.document, data );
 	}
 
 	/**
@@ -201,7 +214,7 @@ export default class UpcastWriter {
 	 * was not replaced (happens for detached elements).
 	 */
 	rename( newName, element ) {
-		const newElement = new Element( newName, element.getAttributes(), element.getChildren() );
+		const newElement = new Element( this.document, newName, element.getAttributes(), element.getChildren() );
 
 		return this.replace( element, newElement ) ? newElement : null;
 	}
@@ -271,7 +284,7 @@ export default class UpcastWriter {
 	 *		} );
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.
 	 *
 	 * @see module:engine/view/element~Element#_setStyle
@@ -293,7 +306,7 @@ export default class UpcastWriter {
 	 *		writer.removeStyle( element, [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles.
 	 *
 	 * **Note**: This method can work with normalized style names if
-	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
 	 * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
 	 *
 	 * @see module:engine/view/element~Element#_removeStyle

+ 6 - 3
packages/ckeditor5-engine/src/view/view.js

@@ -62,14 +62,17 @@ import env from '@ckeditor/ckeditor5-utils/src/env';
  * @mixes module:utils/observablemixin~ObservableMixin
  */
 export default class View {
-	constructor() {
+	/**
+	 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
+	 */
+	constructor( stylesProcessor ) {
 		/**
 		 * Instance of the {@link module:engine/view/document~Document} associated with this view controller.
 		 *
 		 * @readonly
 		 * @type {module:engine/view/document~Document}
 		 */
-		this.document = new Document();
+		this.document = new Document( stylesProcessor );
 
 		/**
 		 * Instance of the {@link module:engine/view/domconverter~DomConverter domConverter} used by
@@ -79,7 +82,7 @@ export default class View {
 		 * @readonly
 		 * @type {module:engine/view/domconverter~DomConverter}
 		 */
-		this.domConverter = new DomConverter();
+		this.domConverter = new DomConverter( this.document );
 
 		/**
 		 * Roots of the DOM tree. Map on the `HTMLElement`s with roots names as keys.

+ 28 - 7
packages/ckeditor5-engine/tests/controller/datacontroller.js

@@ -11,6 +11,7 @@ import HtmlDataProcessor from '../../src/dataprocessor/htmldataprocessor';
 
 import ModelDocumentFragment from '../../src/model/documentfragment';
 import ViewDocumentFragment from '../../src/view/documentfragment';
+import ViewDocument from '../../src/view/document';
 
 import { getData, setData, stringify, parse as parseModel } from '../../src/dev-utils/model';
 import { parse as parseView, stringify as stringifyView } from '../../src/dev-utils/view';
@@ -20,11 +21,13 @@ import count from '@ckeditor/ckeditor5-utils/src/count';
 import UpcastHelpers from '../../src/conversion/upcasthelpers';
 import DowncastHelpers from '../../src/conversion/downcasthelpers';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'DataController', () => {
-	let model, modelDocument, htmlDataProcessor, data, schema, upcastHelpers, downcastHelpers;
+	let model, modelDocument, htmlDataProcessor, data, schema, upcastHelpers, downcastHelpers, viewDocument;
 
 	beforeEach( () => {
+		const stylesProcessor = new StylesProcessor();
 		model = new Model();
 
 		schema = model.schema;
@@ -35,19 +38,23 @@ describe( 'DataController', () => {
 
 		schema.register( '$title', { inheritAllFrom: '$root' } );
 
-		htmlDataProcessor = new HtmlDataProcessor();
+		viewDocument = new ViewDocument( stylesProcessor );
+		htmlDataProcessor = new HtmlDataProcessor( viewDocument );
 
-		data = new DataController( model, htmlDataProcessor );
+		data = new DataController( model, stylesProcessor );
+		data.processor = htmlDataProcessor;
 
 		upcastHelpers = new UpcastHelpers( [ data.upcastDispatcher ] );
 		downcastHelpers = new DowncastHelpers( [ data.downcastDispatcher ] );
 	} );
 
 	describe( 'constructor()', () => {
-		it( 'works without data processor', () => {
-			const data = new DataController( model );
+		it( 'sets the model and styles processor properties', () => {
+			const stylesProcessor = new StylesProcessor();
+			const data = new DataController( model, stylesProcessor );
 
-			expect( data.processor ).to.be.undefined;
+			expect( data.model ).to.equal( model );
+			expect( data.stylesProcessor ).to.equal( stylesProcessor );
 		} );
 	} );
 
@@ -139,7 +146,7 @@ describe( 'DataController', () => {
 			schema.register( 'inlineRoot' );
 			schema.extend( '$text', { allowIn: 'inlineRoot' } );
 
-			const viewFragment = new ViewDocumentFragment( [ parseView( 'foo' ) ] );
+			const viewFragment = new ViewDocumentFragment( viewDocument, [ parseView( 'foo' ) ] );
 
 			// Model fragment in root.
 			expect( stringify( data.toModel( viewFragment ) ) ).to.equal( '' );
@@ -569,4 +576,18 @@ describe( 'DataController', () => {
 			expect( data ).to.respondTo( 'destroy' );
 		} );
 	} );
+
+	describe( 'addStyleProcessorRules()', () => {
+		it( 'should execute callback with an instance of StyleProcessor as the first argument', () => {
+			const stylesProcessor = new StylesProcessor();
+			const data = new DataController( model, stylesProcessor );
+
+			const spy = sinon.spy();
+
+			data.addStyleProcessorRules( spy );
+
+			sinon.assert.calledOnce( spy );
+			sinon.assert.calledWithExactly( spy, stylesProcessor );
+		} );
+	} );
 } );

+ 5 - 4
packages/ckeditor5-engine/tests/controller/editingcontroller.js

@@ -22,6 +22,7 @@ import ModelDocumentFragment from '../../src/model/documentfragment';
 
 import { getData as getModelData, parse } from '../../src/dev-utils/model';
 import { getData as getViewData } from '../../src/dev-utils/view';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'EditingController', () => {
 	describe( 'constructor()', () => {
@@ -29,7 +30,7 @@ describe( 'EditingController', () => {
 
 		beforeEach( () => {
 			model = new Model();
-			editing = new EditingController( model );
+			editing = new EditingController( model, new StylesProcessor() );
 		} );
 
 		afterEach( () => {
@@ -77,7 +78,7 @@ describe( 'EditingController', () => {
 			model = new Model();
 			modelRoot = model.document.createRoot();
 
-			editing = new EditingController( model );
+			editing = new EditingController( model, new StylesProcessor() );
 
 			domRoot = document.createElement( 'div' );
 			domRoot.contentEditable = true;
@@ -477,7 +478,7 @@ describe( 'EditingController', () => {
 			model.document.createRoot();
 			model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
 
-			const editing = new EditingController( model );
+			const editing = new EditingController( model, new StylesProcessor() );
 
 			const spy = sinon.spy();
 
@@ -501,7 +502,7 @@ describe( 'EditingController', () => {
 			model.document.createRoot();
 			model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
 
-			const editing = new EditingController( model );
+			const editing = new EditingController( model, new StylesProcessor() );
 
 			const spy = sinon.spy( editing.view, 'destroy' );
 

+ 2 - 1
packages/ckeditor5-engine/tests/conversion/conversion.js

@@ -18,6 +18,7 @@ import { parse as viewParse, stringify as viewStringify } from '../../src/dev-ut
 import { stringify as modelStringify } from '../../src/dev-utils/model';
 import ConversionHelpers from '../../src/conversion/conversionhelpers';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Conversion', () => {
 	let conversion, downcastDispA, upcastDispaA, downcastDispB;
@@ -121,7 +122,7 @@ describe( 'Conversion', () => {
 
 		beforeEach( () => {
 			model = new Model();
-			const controller = new EditingController( model );
+			const controller = new EditingController( model, new StylesProcessor() );
 
 			const modelDoc = model.document;
 			modelRoot = modelDoc.createRoot();

+ 4 - 3
packages/ckeditor5-engine/tests/conversion/downcastdispatcher.js

@@ -13,13 +13,14 @@ import ModelRange from '../../src/model/range';
 
 import View from '../../src/view/view';
 import ViewContainerElement from '../../src/view/containerelement';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'DowncastDispatcher', () => {
 	let dispatcher, doc, root, differStub, model, view, mapper;
 
 	beforeEach( () => {
 		model = new Model();
-		view = new View();
+		view = new View( new StylesProcessor() );
 		doc = model.document;
 		mapper = new Mapper();
 		dispatcher = new DowncastDispatcher( { mapper } );
@@ -414,8 +415,8 @@ describe( 'DowncastDispatcher', () => {
 			root._appendChild( [ image ] );
 
 			// Create view elements that will be "mapped" to model elements.
-			const viewCaption = new ViewContainerElement( 'caption' );
-			const viewFigure = new ViewContainerElement( 'figure', null, viewCaption );
+			const viewCaption = new ViewContainerElement( view.document, 'caption' );
+			const viewFigure = new ViewContainerElement( view.document, 'figure', null, viewCaption );
 
 			// Create custom highlight handler mock.
 			viewFigure._setCustomProperty( 'addHighlight', () => {} );

+ 47 - 28
packages/ckeditor5-engine/tests/conversion/downcasthelpers.js

@@ -16,6 +16,7 @@ import ViewAttributeElement from '../../src/view/attributeelement';
 import ViewContainerElement from '../../src/view/containerelement';
 import ViewUIElement from '../../src/view/uielement';
 import ViewText from '../../src/view/text';
+import ViewDocument from '../../src/view/document';
 
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
@@ -34,18 +35,18 @@ import View from '../../src/view/view';
 import createViewRoot from '../view/_utils/createroot';
 import { setData as setModelData } from '../../src/dev-utils/model';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
+import DowncastWriter from '../../src/view/downcastwriter';
 
 describe( 'DowncastHelpers', () => {
-	let model, modelRoot, viewRoot, downcastHelpers, controller;
-
-	let modelRootStart;
+	let model, modelRoot, viewRoot, downcastHelpers, controller, modelRootStart;
 
 	beforeEach( () => {
 		model = new Model();
 		const modelDoc = model.document;
 		modelRoot = modelDoc.createRoot();
 
-		controller = new EditingController( model );
+		controller = new EditingController( model, new StylesProcessor() );
 
 		// Set name of view root the same as dom root.
 		// This is a mock of attaching view root to dom root.
@@ -1319,13 +1320,15 @@ describe( 'DowncastHelpers', () => {
 					converterPriority: 7
 				};
 
-				let markerRange;
+				let markerRange, viewDocument;
 
 				beforeEach( () => {
+					viewDocument = new ViewDocument( new StylesProcessor() );
+
 					downcastHelpers.elementToElement( {
 						model: 'div',
 						view: () => {
-							const viewContainer = new ViewContainerElement( 'div' );
+							const viewContainer = new ViewContainerElement( viewDocument, 'div' );
 
 							viewContainer._setCustomProperty( 'addHighlight', ( element, descriptor, writer ) => {
 								writer.addClass( descriptor.classes, element );
@@ -1453,7 +1456,7 @@ describe( 'downcast converters', () => {
 		modelDoc = model.document;
 		modelRoot = modelDoc.createRoot();
 
-		controller = new EditingController( model );
+		controller = new EditingController( model, new StylesProcessor() );
 
 		viewRoot = controller.view.document.getRoot();
 		// Set name of view root the same as dom root.
@@ -1499,6 +1502,12 @@ describe( 'downcast converters', () => {
 
 	// Remove converter is by default already added in `EditingController` instance.
 	describe( 'remove()', () => {
+		let viewDocument;
+
+		beforeEach( () => {
+			viewDocument = new ViewDocument( new StylesProcessor() );
+		} );
+
 		it( 'should remove items from view accordingly to changes in model #1', () => {
 			const modelElement = new ModelElement( 'paragraph', null, new ModelText( 'foobar' ) );
 
@@ -1552,9 +1561,9 @@ describe( 'downcast converters', () => {
 		it( 'should not remove view ui elements that are placed next to removed content', () => {
 			modelRoot._appendChild( new ModelText( 'fozbar' ) );
 			viewRoot._appendChild( [
-				new ViewText( 'foz' ),
-				new ViewUIElement( 'span' ),
-				new ViewText( 'bar' )
+				new ViewText( viewDocument, 'foz' ),
+				new ViewUIElement( viewDocument, 'span' ),
+				new ViewText( viewDocument, 'bar' )
 			] );
 
 			// Remove 'b'.
@@ -1579,9 +1588,9 @@ describe( 'downcast converters', () => {
 		it( 'should remove correct amount of text when it is split by view ui element', () => {
 			modelRoot._appendChild( new ModelText( 'fozbar' ) );
 			viewRoot._appendChild( [
-				new ViewText( 'foz' ),
-				new ViewUIElement( 'span' ),
-				new ViewText( 'bar' )
+				new ViewText( viewDocument, 'foz' ),
+				new ViewUIElement( viewDocument, 'span' ),
+				new ViewText( viewDocument, 'bar' )
 			] );
 
 			// Remove 'z<span></span>b'.
@@ -1649,10 +1658,10 @@ describe( 'downcast converters', () => {
 			const modelP1 = new ModelElement( 'paragraph' );
 			const modelP2 = new ModelElement( 'paragraph' );
 
-			const viewP1 = new ViewContainerElement( 'p' );
-			const viewUi1 = new ViewUIElement( 'span' );
-			const viewUi2 = new ViewUIElement( 'span' );
-			const viewP2 = new ViewContainerElement( 'p' );
+			const viewP1 = new ViewContainerElement( viewDocument, 'p' );
+			const viewUi1 = new ViewUIElement( viewDocument, 'span' );
+			const viewUi2 = new ViewUIElement( viewDocument, 'span' );
+			const viewP2 = new ViewContainerElement( viewDocument, 'p' );
 
 			modelRoot._appendChild( [ modelP1, modelP2 ] );
 			viewRoot._appendChild( [ viewP1, viewUi1, viewUi2, viewP2 ] );
@@ -1685,13 +1694,19 @@ describe( 'downcast converters', () => {
 	} );
 
 	describe( 'createViewElementFromHighlightDescriptor()', () => {
+		let viewWriter;
+
+		beforeEach( () => {
+			viewWriter = new DowncastWriter( controller.view.document );
+		} );
+
 		it( 'should return attribute element from descriptor object', () => {
 			const descriptor = {
 				classes: 'foo-class',
 				attributes: { one: '1', two: '2' },
 				priority: 7
 			};
-			const element = createViewElementFromHighlightDescriptor( descriptor );
+			const element = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 
 			expect( element.is( 'attributeElement' ) ).to.be.true;
 			expect( element.name ).to.equal( 'span' );
@@ -1709,7 +1724,7 @@ describe( 'downcast converters', () => {
 				attributes: { one: '1', two: '2' },
 				priority: 7
 			};
-			const element = createViewElementFromHighlightDescriptor( descriptor );
+			const element = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 
 			expect( element.is( 'attributeElement' ) ).to.be.true;
 			expect( element.name ).to.equal( 'span' );
@@ -1727,7 +1742,7 @@ describe( 'downcast converters', () => {
 				attributes: { one: '1', two: '2' },
 				priority: 7
 			};
-			const element = createViewElementFromHighlightDescriptor( descriptor );
+			const element = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 
 			expect( element.is( 'attributeElement' ) ).to.be.true;
 			expect( element.name ).to.equal( 'span' );
@@ -1743,7 +1758,7 @@ describe( 'downcast converters', () => {
 				classes: 'foo-class',
 				attributes: { one: '1', two: '2' }
 			};
-			const element = createViewElementFromHighlightDescriptor( descriptor );
+			const element = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 
 			expect( element.is( 'attributeElement' ) ).to.be.true;
 			expect( element.name ).to.equal( 'span' );
@@ -1760,7 +1775,7 @@ describe( 'downcast converters', () => {
 				classes: 'foo-class',
 				priority: 7
 			};
-			const element = createViewElementFromHighlightDescriptor( descriptor );
+			const element = createViewElementFromHighlightDescriptor( viewWriter, descriptor );
 
 			expect( element.is( 'attributeElement' ) ).to.be.true;
 			expect( element.name ).to.equal( 'span' );
@@ -1781,7 +1796,7 @@ describe( 'downcast selection converters', () => {
 
 		model.schema.extend( '$text', { allowIn: '$root' } );
 
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDoc = view.document;
 		viewRoot = createViewRoot( viewDoc );
 		viewSelection = viewDoc.selection;
@@ -1908,7 +1923,11 @@ describe( 'downcast selection converters', () => {
 		} );
 
 		describe( 'collapsed selection', () => {
-			let marker;
+			let marker, viewDocument;
+
+			beforeEach( () => {
+				viewDocument = new ViewDocument( new StylesProcessor() );
+			} );
 
 			it( 'in container', () => {
 				testSelection(
@@ -2040,8 +2059,8 @@ describe( 'downcast selection converters', () => {
 
 				// Add two ui elements to view.
 				viewRoot._appendChild( [
-					new ViewUIElement( 'span' ),
-					new ViewUIElement( 'span' )
+					new ViewUIElement( viewDocument, 'span' ),
+					new ViewUIElement( viewDocument, 'span' )
 				] );
 
 				model.change( writer => {
@@ -2073,7 +2092,7 @@ describe( 'downcast selection converters', () => {
 					dispatcher.convertInsert( model.createRangeIn( modelRoot ), writer );
 
 					// Add ui element to view.
-					const uiElement = new ViewUIElement( 'span' );
+					const uiElement = new ViewUIElement( viewDocument, 'span' );
 					viewRoot._insertChild( 1, uiElement );
 
 					dispatcher.convertSelection( docSelection, model.markers, writer );
@@ -2098,7 +2117,7 @@ describe( 'downcast selection converters', () => {
 					dispatcher.convertInsert( model.createRangeIn( modelRoot ), writer );
 
 					// Add ui element to view.
-					const uiElement = new ViewUIElement( 'span' );
+					const uiElement = new ViewUIElement( viewDocument, 'span' );
 					viewRoot._insertChild( 1, uiElement, writer );
 					dispatcher.convertSelection( docSelection, model.markers, writer );
 				} );

+ 70 - 62
packages/ckeditor5-engine/tests/conversion/mapper.js

@@ -11,19 +11,27 @@ import ModelText from '../../src/model/text';
 import ModelPosition from '../../src/model/position';
 import ModelRange from '../../src/model/range';
 
+import ViewDocument from '../../src/view/document';
 import ViewElement from '../../src/view/element';
 import ViewUIElement from '../../src/view/uielement';
 import ViewText from '../../src/view/text';
 import ViewPosition from '../../src/view/position';
 import ViewRange from '../../src/view/range';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Mapper', () => {
+	let viewDocument;
+
+	beforeEach( () => {
+		viewDocument = new ViewDocument( new StylesProcessor() );
+	} );
+
 	describe( 'clearBindings', () => {
 		it( 'should remove all mapping', () => {
-			const viewA = new ViewElement( 'a' );
-			const viewB = new ViewElement( 'b' );
-			const viewC = new ViewElement( 'c' );
-			const viewD = new ViewElement( 'd' );
+			const viewA = new ViewElement( viewDocument, 'a' );
+			const viewB = new ViewElement( viewDocument, 'b' );
+			const viewC = new ViewElement( viewDocument, 'c' );
+			const viewD = new ViewElement( viewDocument, 'd' );
 
 			const modelA = new ModelElement( 'a' );
 			const modelB = new ModelElement( 'b' );
@@ -69,7 +77,7 @@ describe( 'Mapper', () => {
 
 	describe( 'unbindModelElement', () => {
 		it( 'should remove binding between given model element and view element that it was bound to', () => {
-			const viewA = new ViewElement( 'a' );
+			const viewA = new ViewElement( viewDocument, 'a' );
 			const modelA = new ModelElement( 'a' );
 
 			const mapper = new Mapper();
@@ -85,7 +93,7 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should not remove binding between view and model element if view element got rebound', () => {
-			const viewA = new ViewElement( 'a' );
+			const viewA = new ViewElement( viewDocument, 'a' );
 			const modelA = new ModelElement( 'a' );
 			const modelB = new ModelElement( 'b' );
 
@@ -105,7 +113,7 @@ describe( 'Mapper', () => {
 
 	describe( 'unbindViewElement', () => {
 		it( 'should remove binding between given view element and model element that it was bound to', () => {
-			const viewA = new ViewElement( 'a' );
+			const viewA = new ViewElement( viewDocument, 'a' );
 			const modelA = new ModelElement( 'a' );
 
 			const mapper = new Mapper();
@@ -121,8 +129,8 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should not remove binding between model and view element if model element got rebound', () => {
-			const viewA = new ViewElement( 'a' );
-			const viewB = new ViewElement( 'b' );
+			const viewA = new ViewElement( viewDocument, 'a' );
+			const viewB = new ViewElement( viewDocument, 'b' );
 			const modelA = new ModelElement( 'a' );
 
 			const mapper = new Mapper();
@@ -201,21 +209,21 @@ describe( 'Mapper', () => {
 				new ModelText( 'zz' )
 			] );
 
-			viewTextB = new ViewText( 'b' );
-			viewTextO = new ViewText( 'o' );
-			viewTextM = new ViewText( 'm' );
-			viewTextX = new ViewText( 'x' );
-			viewTextY = new ViewText( 'y' );
-			viewTextZZ = new ViewText( 'zz' );
-			viewTextFOO = new ViewText( 'foo' );
-			viewTextBAR = new ViewText( 'bar' );
-			viewImg = new ViewElement( 'img' );
-			viewSup = new ViewElement( 'sup', {}, [ viewTextO ] );
-			viewU = new ViewElement( 'u', {}, [ viewTextB, viewSup, viewTextM ] );
-			viewI = new ViewElement( 'i', {}, [ viewTextFOO ] );
-			viewB = new ViewElement( 'b', {}, [ viewI ] );
-			viewP = new ViewElement( 'p', {}, [ viewTextY, viewB, viewTextBAR, viewImg, viewU ] );
-			viewDiv = new ViewElement( 'div', {}, [ viewTextX, viewP, viewTextZZ ] );
+			viewTextB = new ViewText( viewDocument, 'b' );
+			viewTextO = new ViewText( viewDocument, 'o' );
+			viewTextM = new ViewText( viewDocument, 'm' );
+			viewTextX = new ViewText( viewDocument, 'x' );
+			viewTextY = new ViewText( viewDocument, 'y' );
+			viewTextZZ = new ViewText( viewDocument, 'zz' );
+			viewTextFOO = new ViewText( viewDocument, 'foo' );
+			viewTextBAR = new ViewText( viewDocument, 'bar' );
+			viewImg = new ViewElement( viewDocument, 'img' );
+			viewSup = new ViewElement( viewDocument, 'sup', {}, [ viewTextO ] );
+			viewU = new ViewElement( viewDocument, 'u', {}, [ viewTextB, viewSup, viewTextM ] );
+			viewI = new ViewElement( viewDocument, 'i', {}, [ viewTextFOO ] );
+			viewB = new ViewElement( viewDocument, 'b', {}, [ viewI ] );
+			viewP = new ViewElement( viewDocument, 'p', {}, [ viewTextY, viewB, viewTextBAR, viewImg, viewU ] );
+			viewDiv = new ViewElement( viewDocument, 'div', {}, [ viewTextX, viewP, viewTextZZ ] );
 
 			mapper = new Mapper();
 			mapper.bindElements( modelP, viewP );
@@ -467,17 +475,17 @@ describe( 'Mapper', () => {
 			modelDiv = new ModelRootElement();
 			modelDiv._appendChild( [ new ModelText( 'x' ), modelWidget, new ModelText( 'zz' ) ] );
 
-			viewTextX = new ViewText( 'y' );
-			viewTextZZ = new ViewText( 'zz' );
-			viewTextFOO = new ViewText( 'foo' );
-			viewTextLABEL = new ViewText( 'label' );
+			viewTextX = new ViewText( viewDocument, 'y' );
+			viewTextZZ = new ViewText( viewDocument, 'zz' );
+			viewTextFOO = new ViewText( viewDocument, 'foo' );
+			viewTextLABEL = new ViewText( viewDocument, 'label' );
 
-			viewImg = new ViewElement( 'img' );
-			viewMask = new ViewElement( 'mask', {}, [ viewTextLABEL ] );
-			viewCaption = new ViewElement( 'caption', {}, [ viewTextFOO ] );
-			viewWrapper = new ViewElement( 'wrapper', {}, [ viewImg, viewCaption ] );
-			viewWidget = new ViewElement( 'widget', [ viewMask, viewWrapper ] );
-			viewDiv = new ViewElement( 'div', {}, [ viewTextX, viewWidget, viewTextZZ ] );
+			viewImg = new ViewElement( viewDocument, 'img' );
+			viewMask = new ViewElement( viewDocument, 'mask', {}, [ viewTextLABEL ] );
+			viewCaption = new ViewElement( viewDocument, 'caption', {}, [ viewTextFOO ] );
+			viewWrapper = new ViewElement( viewDocument, 'wrapper', {}, [ viewImg, viewCaption ] );
+			viewWidget = new ViewElement( viewDocument, 'widget', [ viewMask, viewWrapper ] );
+			viewDiv = new ViewElement( viewDocument, 'div', {}, [ viewTextX, viewWidget, viewTextZZ ] );
 
 			mapper = new Mapper();
 			mapper.bindElements( modelDiv, viewDiv );
@@ -574,15 +582,15 @@ describe( 'Mapper', () => {
 
 			modelRoot = new ModelRootElement( 'root', null, [ modelListItem1, modelListItem11, modelListItem12, modelListItem2 ] );
 
-			viewListItem11 = new ViewElement( 'li', null, new ViewText( 'bbb' ) );
-			viewListItem12 = new ViewElement( 'li', null, new ViewText( 'ccc' ) );
-			viewListNested = new ViewElement( 'ul', null, [ viewListItem11, viewListItem12 ] );
+			viewListItem11 = new ViewElement( viewDocument, 'li', null, new ViewText( viewDocument, 'bbb' ) );
+			viewListItem12 = new ViewElement( viewDocument, 'li', null, new ViewText( viewDocument, 'ccc' ) );
+			viewListNested = new ViewElement( viewDocument, 'ul', null, [ viewListItem11, viewListItem12 ] );
 
-			viewListItem1 = new ViewElement( 'li', null, [ new ViewText( 'aaa' ), viewListNested ] );
-			viewListItem2 = new ViewElement( 'li', null, new ViewText( 'ddd' ) );
-			viewList = new ViewElement( 'ul', null, [ viewListItem1, viewListItem2 ] );
+			viewListItem1 = new ViewElement( viewDocument, 'li', null, [ new ViewText( viewDocument, 'aaa' ), viewListNested ] );
+			viewListItem2 = new ViewElement( viewDocument, 'li', null, new ViewText( viewDocument, 'ddd' ) );
+			viewList = new ViewElement( viewDocument, 'ul', null, [ viewListItem1, viewListItem2 ] );
 
-			viewRoot = new ViewElement( 'div', null, viewList );
+			viewRoot = new ViewElement( viewDocument, 'div', null, viewList );
 
 			mapper = new Mapper();
 			mapper.bindElements( modelRoot, viewRoot );
@@ -632,7 +640,7 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should bind element to a marker name', () => {
-			const view = new ViewElement( 'a' );
+			const view = new ViewElement( viewDocument, 'a' );
 
 			mapper.bindElementToMarker( view, 'marker' );
 
@@ -644,9 +652,9 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should bind multiple elements to a marker name', () => {
-			const viewA = new ViewElement( 'a' );
-			const viewB = new ViewElement( 'b' );
-			const viewC = new ViewElement( 'c' );
+			const viewA = new ViewElement( viewDocument, 'a' );
+			const viewB = new ViewElement( viewDocument, 'b' );
+			const viewC = new ViewElement( viewDocument, 'c' );
 
 			mapper.bindElementToMarker( viewA, 'marker' );
 			mapper.bindElementToMarker( viewB, 'marker' );
@@ -658,8 +666,8 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should unbind element from a marker name', () => {
-			const viewA = new ViewElement( 'a' );
-			const viewB = new ViewElement( 'b' );
+			const viewA = new ViewElement( viewDocument, 'a' );
+			const viewB = new ViewElement( viewDocument, 'b' );
 
 			mapper.bindElementToMarker( viewA, 'marker' );
 			mapper.bindElementToMarker( viewA, 'markerB' );
@@ -701,7 +709,7 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should return length according to callback added by registerViewToModelLength', () => {
-			const viewElement = new ViewElement( 'span' );
+			const viewElement = new ViewElement( viewDocument, 'span' );
 
 			mapper.registerViewToModelLength( 'span', () => 4 );
 
@@ -709,7 +717,7 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should return 1 for mapped elements', () => {
-			const viewElement = new ViewElement( 'span' );
+			const viewElement = new ViewElement( viewDocument, 'span' );
 			const modelElement = new ModelElement( 'span' );
 			mapper.bindElements( modelElement, viewElement );
 
@@ -717,24 +725,24 @@ describe( 'Mapper', () => {
 		} );
 
 		it( 'should return 0 for ui elements', () => {
-			const viewUiElement = new ViewUIElement( 'span' );
+			const viewUiElement = new ViewUIElement( viewDocument, 'span' );
 
 			expect( mapper.getModelLength( viewUiElement ) ).to.equal( 0 );
 		} );
 
 		it( 'should return length of data for text nodes', () => {
-			const viewText = new ViewText( 'foo' );
+			const viewText = new ViewText( viewDocument, 'foo' );
 
 			expect( mapper.getModelLength( viewText ) ).to.equal( 3 );
 		} );
 
 		it( 'should return sum of length of children for unmapped element', () => {
 			const modelP = new ModelElement( 'p' );
-			const viewP = new ViewElement( 'p' );
-			const viewUi = new ViewUIElement( 'span' );
-			const viewFoo = new ViewText( 'foo' );
-			const viewCallback = new ViewElement( 'xxx' );
-			const viewDiv = new ViewElement( 'div', null, [ viewP, viewUi, viewFoo, viewCallback ] );
+			const viewP = new ViewElement( viewDocument, 'p' );
+			const viewUi = new ViewUIElement( viewDocument, 'span' );
+			const viewFoo = new ViewText( viewDocument, 'foo' );
+			const viewCallback = new ViewElement( viewDocument, 'xxx' );
+			const viewDiv = new ViewElement( viewDocument, 'div', null, [ viewP, viewUi, viewFoo, viewCallback ] );
 
 			mapper.bindElements( modelP, viewP );
 			mapper.registerViewToModelLength( 'xxx', () => 2 );
@@ -750,10 +758,10 @@ describe( 'Mapper', () => {
 			const modelP = new ModelElement( 'p' );
 			const modelDiv = new ModelElement( 'div' );
 
-			const viewText = new ViewText( 'foo' );
-			const viewSpan = new ViewElement( 'span', null, viewText );
-			const viewP = new ViewElement( 'p', null, viewSpan );
-			const viewDiv = new ViewElement( 'div', null, viewP );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewSpan = new ViewElement( viewDocument, 'span', null, viewText );
+			const viewP = new ViewElement( viewDocument, 'p', null, viewSpan );
+			const viewDiv = new ViewElement( viewDocument, 'div', null, viewP );
 
 			mapper.bindElements( modelP, viewP );
 			mapper.bindElements( modelDiv, viewDiv );
@@ -770,8 +778,8 @@ describe( 'Mapper', () => {
 
 	describe( 'flushUnboundMarkerNames()', () => {
 		it( 'should return marker names of markers which elements has been unbound and clear that list', () => {
-			const viewA = new ViewElement( 'a' );
-			const viewB = new ViewElement( 'b' );
+			const viewA = new ViewElement( viewDocument, 'a' );
+			const viewB = new ViewElement( viewDocument, 'b' );
 
 			const mapper = new Mapper();
 

+ 37 - 34
packages/ckeditor5-engine/tests/conversion/upcastdispatcher.js

@@ -8,6 +8,7 @@ import ViewContainerElement from '../../src/view/containerelement';
 import ViewElement from '../../src/view/element';
 import ViewDocumentFragment from '../../src/view/documentfragment';
 import ViewText from '../../src/view/text';
+import ViewDocument from '../../src/view/document';
 
 import Model from '../../src/model/model';
 import ModelText from '../../src/model/text';
@@ -20,12 +21,14 @@ import ModelWriter from '../../src/model/writer';
 
 import first from '@ckeditor/ckeditor5-utils/src/first';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'UpcastDispatcher', () => {
-	let model;
+	let model, viewDocument;
 
 	beforeEach( () => {
 		model = new Model();
+		viewDocument = new ViewDocument( new StylesProcessor() );
 	} );
 
 	describe( 'constructor()', () => {
@@ -56,7 +59,7 @@ describe( 'UpcastDispatcher', () => {
 		} );
 
 		it( 'should create api for a conversion process', () => {
-			const viewElement = new ViewContainerElement( 'p', null, new ViewText( 'foobar' ) );
+			const viewElement = new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'foobar' ) );
 
 			// To be sure that both converters was called.
 			const spy = sinon.spy();
@@ -108,16 +111,16 @@ describe( 'UpcastDispatcher', () => {
 		it( 'should fire viewCleanup event on converted view part', () => {
 			sinon.spy( dispatcher, 'fire' );
 
-			const viewP = new ViewContainerElement( 'p' );
+			const viewP = new ViewContainerElement( viewDocument, 'p' );
 			model.change( writer => dispatcher.convert( viewP, writer ) );
 
 			expect( dispatcher.fire.calledWith( 'viewCleanup', viewP ) ).to.be.true;
 		} );
 
 		it( 'should fire proper events', () => {
-			const viewText = new ViewText( 'foobar' );
-			const viewElement = new ViewContainerElement( 'p', null, viewText );
-			const viewFragment = new ViewDocumentFragment( viewElement );
+			const viewText = new ViewText( viewDocument, 'foobar' );
+			const viewElement = new ViewContainerElement( viewDocument, 'p', null, viewText );
+			const viewFragment = new ViewDocumentFragment( viewDocument, viewElement );
 
 			sinon.spy( dispatcher, 'fire' );
 
@@ -134,7 +137,7 @@ describe( 'UpcastDispatcher', () => {
 
 		it( 'should convert ViewText', () => {
 			const spy = sinon.spy();
-			const viewText = new ViewText( 'foobar' );
+			const viewText = new ViewText( viewDocument, 'foobar' );
 
 			dispatcher.on( 'text', ( evt, data, conversionApi ) => {
 				// Check if this method has been fired.
@@ -170,7 +173,7 @@ describe( 'UpcastDispatcher', () => {
 
 		it( 'should convert ViewContainerElement', () => {
 			const spy = sinon.spy();
-			const viewElement = new ViewContainerElement( 'p', { attrKey: 'attrValue' } );
+			const viewElement = new ViewContainerElement( viewDocument, 'p', { attrKey: 'attrValue' } );
 
 			dispatcher.on( 'element', ( evt, data, conversionApi ) => {
 				// Check if this method has been fired.
@@ -208,7 +211,7 @@ describe( 'UpcastDispatcher', () => {
 
 		it( 'should convert ViewDocumentFragment', () => {
 			const spy = sinon.spy();
-			const viewFragment = new ViewDocumentFragment();
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 				// Check if this method has been fired.
@@ -242,9 +245,9 @@ describe( 'UpcastDispatcher', () => {
 		} );
 
 		it( 'should remove empty elements that was created as a result of split', () => {
-			const viewElement = new ViewElement( 'div', null, [
-				new ViewElement( 'p', null, [
-					new ViewElement( 'img' )
+			const viewElement = new ViewElement( viewDocument, 'div', null, [
+				new ViewElement( viewDocument, 'p', null, [
+					new ViewElement( viewDocument, 'img' )
 				] )
 			] );
 
@@ -289,7 +292,7 @@ describe( 'UpcastDispatcher', () => {
 		} );
 
 		it( 'should extract temporary markers elements from converter element and create static markers list', () => {
-			const viewFragment = new ViewDocumentFragment();
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
 				// Create model fragment.
@@ -326,7 +329,7 @@ describe( 'UpcastDispatcher', () => {
 			dispatcher = new UpcastDispatcher( { schema: model.schema } );
 
 			const spy = sinon.spy();
-			const viewElement = new ViewContainerElement( 'third' );
+			const viewElement = new ViewContainerElement( viewDocument, 'third' );
 			let checkChildResult;
 
 			model.schema.register( 'first', {
@@ -377,8 +380,8 @@ describe( 'UpcastDispatcher', () => {
 			spyP = sinon.spy();
 			spyText = sinon.spy();
 
-			viewP = new ViewContainerElement( 'p' );
-			viewText = new ViewText( 'foobar' );
+			viewP = new ViewContainerElement( viewDocument, 'p' );
+			viewText = new ViewText( viewDocument, 'foobar' );
 			modelP = new ModelElement( 'paragraph' );
 			modelText = new ModelText( 'foobar' );
 
@@ -404,9 +407,9 @@ describe( 'UpcastDispatcher', () => {
 			spyNull = sinon.spy();
 			spyArray = sinon.spy();
 
-			viewDiv = new ViewContainerElement( 'div' ); // Will not be recognized and not converted.
-			viewNull = new ViewContainerElement( 'null' ); // Will return `null` in `data.modelRange` upon conversion.
-			viewArray = new ViewContainerElement( 'array' ); // Will return an array in `data.modelRange` upon conversion.
+			viewDiv = new ViewContainerElement( viewDocument, 'div' ); // Will not be recognized and not converted.
+			viewNull = new ViewContainerElement( viewDocument, 'null' ); // Will return `null` in `data.modelRange` upon conversion.
+			viewArray = new ViewContainerElement( viewDocument, 'array' ); // Will return an array in `data.modelRange` upon conversion.
 
 			dispatcher.on( 'element:null', ( evt, data ) => {
 				spyNull();
@@ -444,7 +447,7 @@ describe( 'UpcastDispatcher', () => {
 					expect( textResult.modelCursor.path ).to.deep.equal( [ 7 ] );
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 
 				expect( spy.calledOnce ).to.be.true;
 				expect( spyP.calledOnce ).to.be.true;
@@ -459,7 +462,7 @@ describe( 'UpcastDispatcher', () => {
 					expect( conversionApi.convertItem( viewNull, data.modelCursor ).modelRange ).to.equal( null );
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 
 				expect( spy.calledOnce ).to.be.true;
 				expect( spyNull.calledOnce ).to.be.true;
@@ -473,7 +476,7 @@ describe( 'UpcastDispatcher', () => {
 				} );
 
 				expectToThrowCKEditorError( () => {
-					model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+					model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 				}, /^view-conversion-dispatcher-incorrect-result/, model );
 
 				expect( spy.calledOnce ).to.be.true;
@@ -501,7 +504,7 @@ describe( 'UpcastDispatcher', () => {
 					expect( result.modelCursor.path ).to.deep.equal( [ 7 ] );
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment( [ viewP, viewText ] ), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument, [ viewP, viewText ] ), writer ) );
 
 				expect( spy.calledOnce ).to.be.true;
 				expect( spyP.calledOnce ).to.be.true;
@@ -534,7 +537,7 @@ describe( 'UpcastDispatcher', () => {
 					spy();
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 				sinon.assert.calledOnce( spy );
 			} );
 
@@ -571,7 +574,7 @@ describe( 'UpcastDispatcher', () => {
 					spy();
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 				sinon.assert.calledOnce( spy );
 			} );
 
@@ -591,7 +594,7 @@ describe( 'UpcastDispatcher', () => {
 					spy();
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
 				sinon.assert.calledOnce( spy );
 			} );
 
@@ -610,7 +613,7 @@ describe( 'UpcastDispatcher', () => {
 					spy();
 				} );
 
-				model.change( writer => dispatcher.convert( new ViewDocumentFragment(), writer, [ '$root', 'paragraph' ] ) );
+				model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer, [ '$root', 'paragraph' ] ) );
 				sinon.assert.calledOnce( spy );
 			} );
 		} );
@@ -633,7 +636,7 @@ describe( 'UpcastDispatcher', () => {
 					evt.stop();
 				}, { priority: 'high' } );
 
-				const viewElement = new ViewElement( 'p' );
+				const viewElement = new ViewElement( viewDocument, 'p' );
 
 				model.change( writer => dispatcher.convert( viewElement, writer ) );
 
@@ -695,12 +698,12 @@ describe( 'UpcastDispatcher', () => {
 					evt.stop();
 				}, { priority: 'high' } );
 
-				const viewElement = new ViewElement( 'p', null, [
-					new ViewText( 'foo' ),
-					new ViewElement( 'image' ),
-					new ViewText( 'bar' ),
-					new ViewElement( 'image' ),
-					new ViewText( 'xyz' )
+				const viewElement = new ViewElement( viewDocument, 'p', null, [
+					new ViewText( viewDocument, 'foo' ),
+					new ViewElement( viewDocument, 'image' ),
+					new ViewText( viewDocument, 'bar' ),
+					new ViewElement( viewDocument, 'image' ),
+					new ViewText( viewDocument, 'xyz' )
 				] );
 
 				model.change( writer => dispatcher.convert( viewElement, writer, [ '$root' ] ) );

+ 105 - 85
packages/ckeditor5-engine/tests/conversion/upcasthelpers.js

@@ -10,6 +10,7 @@ import ViewDocumentFragment from '../../src/view/documentfragment';
 import ViewText from '../../src/view/text';
 import ViewUIElement from '../../src/view/uielement';
 import ViewAttributeElement from '../../src/view/attributeelement';
+import ViewDocument from '../../src/view/document';
 
 import Model from '../../src/model/model';
 import ModelDocumentFragment from '../../src/model/documentfragment';
@@ -27,12 +28,14 @@ import { setData as viewSetData } from '../../src/dev-utils/view';
 import Mapper from '../../src/conversion/mapper';
 import ViewSelection from '../../src/view/selection';
 import ViewRange from '../../src/view/range';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'UpcastHelpers', () => {
-	let upcastDispatcher, model, schema, upcastHelpers;
+	let upcastDispatcher, model, schema, upcastHelpers, viewDocument;
 
 	beforeEach( () => {
 		model = new Model();
+		viewDocument = new ViewDocument( new StylesProcessor() );
 
 		schema = model.schema;
 
@@ -61,7 +64,7 @@ describe( 'UpcastHelpers', () => {
 		it( 'config.view is a string', () => {
 			upcastHelpers.elementToElement( { view: 'p', model: 'paragraph' } );
 
-			expectResult( new ViewContainerElement( 'p' ), '<paragraph></paragraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'p' ), '<paragraph></paragraph>' );
 		} );
 
 		it( 'can be overwritten using converterPriority', () => {
@@ -72,7 +75,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToElement( { view: 'p', model: 'p' } );
 			upcastHelpers.elementToElement( { view: 'p', model: 'paragraph', converterPriority: 'high' } );
 
-			expectResult( new ViewContainerElement( 'p' ), '<paragraph></paragraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'p' ), '<paragraph></paragraph>' );
 		} );
 
 		it( 'config.view is an object', () => {
@@ -88,8 +91,8 @@ describe( 'UpcastHelpers', () => {
 				model: 'fancyParagraph'
 			} );
 
-			expectResult( new ViewContainerElement( 'p', { class: 'fancy' } ), '<fancyParagraph></fancyParagraph>' );
-			expectResult( new ViewContainerElement( 'p' ), '' );
+			expectResult( new ViewContainerElement( viewDocument, 'p', { class: 'fancy' } ), '<fancyParagraph></fancyParagraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'p' ), '' );
 		} );
 
 		it( 'config.model is a function', () => {
@@ -108,8 +111,11 @@ describe( 'UpcastHelpers', () => {
 				}
 			} );
 
-			expectResult( new ViewContainerElement( 'p', { class: 'heading', 'data-level': 2 } ), '<heading level="2"></heading>' );
-			expectResult( new ViewContainerElement( 'p', { 'data-level': 2 } ), '' );
+			expectResult(
+				new ViewContainerElement( viewDocument, 'p', { class: 'heading', 'data-level': 2 } ),
+				'<heading level="2"></heading>'
+			);
+			expectResult( new ViewContainerElement( viewDocument, 'p', { 'data-level': 2 } ), '' );
 		} );
 
 		it( 'config.view is not set - should fire conversion for every element', () => {
@@ -117,20 +123,23 @@ describe( 'UpcastHelpers', () => {
 				model: 'paragraph'
 			} );
 
-			expectResult( new ViewContainerElement( 'p' ), '<paragraph></paragraph>' );
-			expectResult( new ViewContainerElement( 'foo' ), '<paragraph></paragraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'p' ), '<paragraph></paragraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'foo' ), '<paragraph></paragraph>' );
 		} );
 
 		it( 'should fire conversion of the element children', () => {
 			upcastHelpers.elementToElement( { view: 'p', model: 'paragraph' } );
 
-			expectResult( new ViewContainerElement( 'p', null, new ViewText( 'foo' ) ), '<paragraph>foo</paragraph>' );
+			expectResult(
+				new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'foo' ) ),
+				'<paragraph>foo</paragraph>'
+			);
 		} );
 
 		it( 'should not insert a model element if it is not allowed by schema', () => {
 			upcastHelpers.elementToElement( { view: 'h2', model: 'heading' } );
 
-			expectResult( new ViewContainerElement( 'h2' ), '' );
+			expectResult( new ViewContainerElement( viewDocument, 'h2' ), '' );
 		} );
 
 		it( 'should auto-break elements', () => {
@@ -142,10 +151,10 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToElement( { view: 'h2', model: 'heading' } );
 
 			expectResult(
-				new ViewContainerElement( 'p', null, [
-					new ViewText( 'Foo' ),
-					new ViewContainerElement( 'h2', null, new ViewText( 'Xyz' ) ),
-					new ViewText( 'Bar' )
+				new ViewContainerElement( viewDocument, 'p', null, [
+					new ViewText( viewDocument, 'Foo' ),
+					new ViewContainerElement( viewDocument, 'h2', null, new ViewText( viewDocument, 'Xyz' ) ),
+					new ViewText( viewDocument, 'Bar' )
 				] ),
 				'<paragraph>Foo</paragraph><heading>Xyz</heading><paragraph>Bar</paragraph>'
 			);
@@ -155,7 +164,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToElement( { view: 'p', model: 'paragraph' } );
 			upcastHelpers.elementToElement( { view: 'p', model: () => null, converterPriority: 'high' } );
 
-			expectResult( new ViewContainerElement( 'p' ), '<paragraph></paragraph>' );
+			expectResult( new ViewContainerElement( viewDocument, 'p' ), '<paragraph></paragraph>' );
 		} );
 	} );
 
@@ -168,7 +177,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToAttribute( { view: 'strong', model: 'bold' } );
 
 			expectResult(
-				new ViewAttributeElement( 'strong', null, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'strong', null, new ViewText( viewDocument, 'foo' ) ),
 				'<$text bold="true">foo</$text>'
 			);
 		} );
@@ -178,7 +187,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToAttribute( { view: 'strong', model: 'bold', converterPriority: 'high' } );
 
 			expectResult(
-				new ViewAttributeElement( 'strong', null, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'strong', null, new ViewText( viewDocument, 'foo' ) ),
 				'<$text bold="true">foo</$text>'
 			);
 		} );
@@ -193,11 +202,11 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'span', { class: 'bold' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'span', { class: 'bold' }, new ViewText( viewDocument, 'foo' ) ),
 				'<$text bold="true">foo</$text>'
 			);
 
-			expectResult( new ViewAttributeElement( 'span', {}, new ViewText( 'foo' ) ), 'foo' );
+			expectResult( new ViewAttributeElement( viewDocument, 'span', {}, new ViewText( viewDocument, 'foo' ) ), 'foo' );
 		} );
 
 		it( 'model attribute value is given', () => {
@@ -217,11 +226,11 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'span', { class: 'styled styled-dark' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'span', { class: 'styled styled-dark' }, new ViewText( viewDocument, 'foo' ) ),
 				'<$text styled="dark">foo</$text>'
 			);
 
-			expectResult( new ViewAttributeElement( 'span', {}, new ViewText( 'foo' ) ), 'foo' );
+			expectResult( new ViewAttributeElement( viewDocument, 'span', {}, new ViewText( viewDocument, 'foo' ) ), 'foo' );
 		} );
 
 		it( 'model attribute value is a function', () => {
@@ -254,17 +263,17 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'span', { style: 'font-size:9px' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'span', { style: 'font-size:9px' }, new ViewText( viewDocument, 'foo' ) ),
 				'<$text fontSize="small">foo</$text>'
 			);
 
 			expectResult(
-				new ViewAttributeElement( 'span', { style: 'font-size:12px' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'span', { style: 'font-size:12px' }, new ViewText( viewDocument, 'foo' ) ),
 				'foo'
 			);
 
 			expectResult(
-				new ViewAttributeElement( 'span', { style: 'font-size:14px' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'span', { style: 'font-size:14px' }, new ViewText( viewDocument, 'foo' ) ),
 				'<$text fontSize="big">foo</$text>'
 			);
 		} );
@@ -273,7 +282,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToAttribute( { view: 'em', model: 'italic' } );
 
 			expectResult(
-				new ViewAttributeElement( 'em', null, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'em', null, new ViewText( viewDocument, 'foo' ) ),
 				'foo'
 			);
 		} );
@@ -290,7 +299,7 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'strong', null, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'strong', null, new ViewText( viewDocument, 'foo' ) ),
 				'<$text bold="true">foo</$text>'
 			);
 		} );
@@ -307,7 +316,12 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'span', { class: 'attrib-a', style: 'color:attrib-b;' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement(
+					viewDocument,
+					'span',
+					{ class: 'attrib-a', style: 'color:attrib-b;' },
+					new ViewText( viewDocument, 'foo' )
+				),
 				'<$text attribA="true" attribB="true">foo</$text>'
 			);
 		} );
@@ -329,7 +343,7 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'strong', { class: 'foo' }, new ViewText( 'foo' ) ),
+				new ViewAttributeElement( viewDocument, 'strong', { class: 'foo' }, new ViewText( viewDocument, 'foo' ) ),
 				'<$text attribB="true" bold="true">foo</$text>'
 			);
 		} );
@@ -345,9 +359,10 @@ describe( 'UpcastHelpers', () => {
 
 			expectResult(
 				new ViewAttributeElement(
+					viewDocument,
 					'strong',
 					null,
-					new ViewContainerElement( 'p', null, new ViewText( 'Foo' ) )
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'Foo' ) )
 				),
 				'<paragraph><$text bold="true">Foo</$text></paragraph>'
 			);
@@ -375,7 +390,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.attributeToAttribute( { view: 'src', model: 'source' } );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				new ViewAttributeElement( viewDocument, 'img', { src: 'foo.jpg' } ),
 				'<image source="foo.jpg"></image>'
 			);
 		} );
@@ -388,7 +403,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.attributeToAttribute( { view: { key: 'src' }, model: 'source' } );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				new ViewAttributeElement( viewDocument, 'img', { src: 'foo.jpg' } ),
 				'<image source="foo.jpg"></image>'
 			);
 		} );
@@ -401,7 +416,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.attributeToAttribute( { view: { name: 'img', key: 'src' }, model: { name: 'image', key: 'source' } } );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				new ViewAttributeElement( viewDocument, 'img', { src: 'foo.jpg' } ),
 				'<image source="foo.jpg"></image>'
 			);
 		} );
@@ -415,7 +430,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.attributeToAttribute( { view: { key: 'src' }, model: 'source', converterPriority: 'normal' } );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				new ViewAttributeElement( viewDocument, 'img', { src: 'foo.jpg' } ),
 				'<image source="foo.jpg"></image>'
 			);
 		} );
@@ -434,7 +449,7 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { 'data-style': 'dark' } ),
+				new ViewAttributeElement( viewDocument, 'img', { 'data-style': 'dark' } ),
 				'<image styled="dark"></image>'
 			);
 		} );
@@ -459,17 +474,17 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToElement( { view: 'p', model: 'paragraph' } );
 
 			expectResult(
-				new ViewContainerElement( 'img', { class: 'styled-dark' } ),
+				new ViewContainerElement( viewDocument, 'img', { class: 'styled-dark' } ),
 				'<image styled="dark"></image>'
 			);
 
 			expectResult(
-				new ViewContainerElement( 'img', { class: 'styled-xxx' } ),
+				new ViewContainerElement( viewDocument, 'img', { class: 'styled-xxx' } ),
 				'<image></image>'
 			);
 
 			expectResult(
-				new ViewContainerElement( 'p', { class: 'styled-dark' } ),
+				new ViewContainerElement( viewDocument, 'p', { class: 'styled-dark' } ),
 				'<paragraph></paragraph>'
 			);
 		} );
@@ -496,7 +511,7 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { 'class': 'styled-dark' } ),
+				new ViewAttributeElement( viewDocument, 'img', { 'class': 'styled-dark' } ),
 				'<image styled="dark"></image>'
 			);
 		} );
@@ -505,7 +520,7 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.attributeToAttribute( { view: 'src', model: 'source' } );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { src: 'foo.jpg' } ),
+				new ViewAttributeElement( viewDocument, 'img', { src: 'foo.jpg' } ),
 				'<image></image>'
 			);
 		} );
@@ -538,7 +553,7 @@ describe( 'UpcastHelpers', () => {
 			} );
 
 			expectResult(
-				new ViewAttributeElement( 'img', { class: 'styled' } ),
+				new ViewAttributeElement( viewDocument, 'img', { class: 'styled' } ),
 				'<image styled="true"></image>'
 			);
 		} );
@@ -559,9 +574,10 @@ describe( 'UpcastHelpers', () => {
 
 			expectResult(
 				new ViewContainerElement(
+					viewDocument,
 					'div',
 					{ class: 'border' },
-					new ViewContainerElement( 'div', { class: 'shade' } )
+					new ViewContainerElement( viewDocument, 'div', { class: 'shade' } )
 				),
 				'<div border="border"><div shade="shade"></div></div>'
 			);
@@ -576,12 +592,12 @@ describe( 'UpcastHelpers', () => {
 		it( 'config.view is a string', () => {
 			upcastHelpers.elementToMarker( { view: 'marker-search', model: 'search' } );
 
-			const frag = new ViewDocumentFragment( [
-				new ViewText( 'fo' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'oba' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'r' )
+			const frag = new ViewDocumentFragment( viewDocument, [
+				new ViewText( viewDocument, 'fo' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'oba' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'r' )
 			] );
 
 			const marker = { name: 'search', start: [ 2 ], end: [ 5 ] };
@@ -593,12 +609,12 @@ describe( 'UpcastHelpers', () => {
 			upcastHelpers.elementToMarker( { view: 'marker-search', model: 'search-result' } );
 			upcastHelpers.elementToMarker( { view: 'marker-search', model: 'search', converterPriority: 'high' } );
 
-			const frag = new ViewDocumentFragment( [
-				new ViewText( 'fo' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'oba' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'r' )
+			const frag = new ViewDocumentFragment( viewDocument, [
+				new ViewText( viewDocument, 'fo' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'oba' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'r' )
 			] );
 
 			const marker = { name: 'search', start: [ 2 ], end: [ 5 ] };
@@ -615,12 +631,12 @@ describe( 'UpcastHelpers', () => {
 				model: 'search'
 			} );
 
-			const frag = new ViewDocumentFragment( [
-				new ViewText( 'f' ),
-				new ViewUIElement( 'span', { 'data-marker': 'search' } ),
-				new ViewText( 'oob' ),
-				new ViewUIElement( 'span', { 'data-marker': 'search' } ),
-				new ViewText( 'ar' )
+			const frag = new ViewDocumentFragment( viewDocument, [
+				new ViewText( viewDocument, 'f' ),
+				new ViewUIElement( viewDocument, 'span', { 'data-marker': 'search' } ),
+				new ViewText( viewDocument, 'oob' ),
+				new ViewUIElement( viewDocument, 'span', { 'data-marker': 'search' } ),
+				new ViewText( viewDocument, 'ar' )
 			] );
 
 			const marker = { name: 'search', start: [ 1 ], end: [ 4 ] };
@@ -634,12 +650,12 @@ describe( 'UpcastHelpers', () => {
 				model: viewElement => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
 			} );
 
-			const frag = new ViewDocumentFragment( [
-				new ViewText( 'foo' ),
-				new ViewUIElement( 'comment', { 'data-comment-id': 4 } ),
-				new ViewText( 'b' ),
-				new ViewUIElement( 'comment', { 'data-comment-id': 4 } ),
-				new ViewText( 'ar' )
+			const frag = new ViewDocumentFragment( viewDocument, [
+				new ViewText( viewDocument, 'foo' ),
+				new ViewUIElement( viewDocument, 'comment', { 'data-comment-id': 4 } ),
+				new ViewText( viewDocument, 'b' ),
+				new ViewUIElement( viewDocument, 'comment', { 'data-comment-id': 4 } ),
+				new ViewText( viewDocument, 'ar' )
 			] );
 
 			const marker = { name: 'comment:4', start: [ 3 ], end: [ 4 ] };
@@ -652,12 +668,12 @@ describe( 'UpcastHelpers', () => {
 
 			upcastHelpers.elementToMarker( { view: 'marker-search', model: 'search' } );
 
-			const element = new ViewContainerElement( 'p', null, [
-				new ViewText( 'fo' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'oba' ),
-				new ViewUIElement( 'marker-search' ),
-				new ViewText( 'r' )
+			const element = new ViewContainerElement( viewDocument, 'p', null, [
+				new ViewText( viewDocument, 'fo' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'oba' ),
+				new ViewUIElement( viewDocument, 'marker-search' ),
+				new ViewText( viewDocument, 'r' )
 			] );
 
 			const marker = { name: 'search', start: [ 0, 2 ], end: [ 0, 5 ] };
@@ -683,10 +699,11 @@ describe( 'UpcastHelpers', () => {
 } );
 
 describe( 'upcast-converters', () => {
-	let dispatcher, schema, context, model;
+	let dispatcher, schema, context, model, viewDocument;
 
 	beforeEach( () => {
 		model = new Model();
+		viewDocument = new ViewDocument( new StylesProcessor() );
 		schema = model.schema;
 
 		schema.register( 'paragraph', { inheritAllFrom: '$block' } );
@@ -699,7 +716,7 @@ describe( 'upcast-converters', () => {
 
 	describe( 'convertText()', () => {
 		it( 'should return converter converting ViewText to ModelText', () => {
-			const viewText = new ViewText( 'foobar' );
+			const viewText = new ViewText( viewDocument, 'foobar' );
 
 			dispatcher.on( 'text', convertText() );
 
@@ -711,7 +728,7 @@ describe( 'upcast-converters', () => {
 		} );
 
 		it( 'should not convert already consumed texts', () => {
-			const viewText = new ViewText( 'foofuckbafuckr' );
+			const viewText = new ViewText( viewDocument, 'foofuckbafuckr' );
 
 			// Default converter for elements. Returns just converted children. Added with lowest priority.
 			dispatcher.on( 'text', convertText(), { priority: 'lowest' } );
@@ -739,7 +756,7 @@ describe( 'upcast-converters', () => {
 				}
 			} );
 
-			const viewText = new ViewText( 'foobar' );
+			const viewText = new ViewText( viewDocument, 'foobar' );
 			dispatcher.on( 'text', convertText() );
 			let conversionResult = model.change( writer => dispatcher.convert( viewText, writer, context ) );
 
@@ -755,7 +772,7 @@ describe( 'upcast-converters', () => {
 		} );
 
 		it( 'should support unicode', () => {
-			const viewText = new ViewText( 'நிலைக்கு' );
+			const viewText = new ViewText( viewDocument, 'நிலைக்கு' );
 
 			dispatcher.on( 'text', convertText() );
 
@@ -769,9 +786,9 @@ describe( 'upcast-converters', () => {
 
 	describe( 'convertToModelFragment()', () => {
 		it( 'should return converter converting whole ViewDocumentFragment to ModelDocumentFragment', () => {
-			const viewFragment = new ViewDocumentFragment( [
-				new ViewContainerElement( 'p', null, new ViewText( 'foo' ) ),
-				new ViewText( 'bar' )
+			const viewFragment = new ViewDocumentFragment( viewDocument, [
+				new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'foo' ) ),
+				new ViewText( viewDocument, 'bar' )
 			] );
 
 			// To get any meaningful results we have to actually convert something.
@@ -788,7 +805,7 @@ describe( 'upcast-converters', () => {
 		} );
 
 		it( 'should not convert already consumed (converted) changes', () => {
-			const viewP = new ViewContainerElement( 'p', null, new ViewText( 'foo' ) );
+			const viewP = new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'foo' ) );
 
 			// To get any meaningful results we have to actually convert something.
 			dispatcher.on( 'text', convertText() );
@@ -818,9 +835,12 @@ describe( 'upcast-converters', () => {
 
 		it( 'should forward correct modelCursor', () => {
 			const spy = sinon.spy();
-			const view = new ViewDocumentFragment( [
-				new ViewContainerElement( 'div', null, [ new ViewText( 'abc' ), new ViewContainerElement( 'foo' ) ] ),
-				new ViewContainerElement( 'bar' )
+			const view = new ViewDocumentFragment( viewDocument, [
+				new ViewContainerElement( viewDocument, 'div', null, [
+					new ViewText( viewDocument, 'abc' ),
+					new ViewContainerElement( viewDocument, 'foo' )
+				] ),
+				new ViewContainerElement( viewDocument, 'bar' )
 			] );
 			const position = ModelPosition._createAt( new ModelElement( 'element' ), 0 );
 
@@ -857,7 +877,7 @@ describe( 'upcast-converters', () => {
 
 			modelSetData( model, '<paragraph>foo</paragraph><paragraph>bar</paragraph>' );
 
-			view = new View();
+			view = new View( new StylesProcessor() );
 			viewDocument = view.document;
 			viewRoot = createViewRoot( viewDocument, 'div', 'main' );
 

+ 31 - 33
packages/ckeditor5-engine/tests/conversion/viewconsumable.js

@@ -3,21 +3,29 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
+import ViewDocument from '../../src/view/document';
 import ViewElement from '../../src/view/element';
 import ViewText from '../../src/view/text';
 import ViewDocumentFragment from '../../src/view/documentfragment';
 import ViewConsumable from '../../src/conversion/viewconsumable';
-import StylesMap, { StylesProcessor } from '../../src/view/stylesmap';
 import { addBorderRules } from '../../src/view/styles/border';
 import { addMarginRules } from '../../src/view/styles/margin';
 import { addPaddingRules } from '../../src/view/styles/padding';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'ViewConsumable', () => {
-	let viewConsumable, el;
+	let viewConsumable, el, viewDocument;
 
 	beforeEach( () => {
+		const stylesProcessor = new StylesProcessor();
+		viewDocument = new ViewDocument( stylesProcessor );
+
+		addBorderRules( stylesProcessor );
+		addMarginRules( stylesProcessor );
+		addPaddingRules( stylesProcessor );
+
 		viewConsumable = new ViewConsumable();
-		el = new ViewElement( 'p' );
+		el = new ViewElement( viewDocument, 'p' );
 	} );
 
 	describe( 'add', () => {
@@ -28,14 +36,14 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should allow to add text node', () => {
-			const text = new ViewText( 'foobar' );
+			const text = new ViewText( viewDocument, 'foobar' );
 			viewConsumable.add( text );
 
 			expect( viewConsumable.test( text ) ).to.be.true;
 		} );
 
 		it( 'should allow to add document fragment', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 			viewConsumable.add( fragment );
 			expect( viewConsumable.test( fragment ) ).to.be.true;
 		} );
@@ -102,7 +110,7 @@ describe( 'ViewConsumable', () => {
 
 	describe( 'test', () => {
 		it( 'should test element name', () => {
-			const el2 = new ViewElement( 'p' );
+			const el2 = new ViewElement( viewDocument, 'p' );
 
 			viewConsumable.add( el, { name: true } );
 
@@ -111,8 +119,8 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should test text nodes', () => {
-			const text1 = new ViewText();
-			const text2 = new ViewText();
+			const text1 = new ViewText( viewDocument );
+			const text2 = new ViewText( viewDocument );
 
 			viewConsumable.add( text1 );
 
@@ -121,8 +129,8 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should test document fragments', () => {
-			const fragment1 = new ViewDocumentFragment();
-			const fragment2 = new ViewDocumentFragment();
+			const fragment1 = new ViewDocumentFragment( viewDocument );
+			const fragment2 = new ViewDocumentFragment( viewDocument );
 
 			viewConsumable.add( fragment1 );
 
@@ -131,7 +139,7 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should test attribute, classes and styles', () => {
-			const el = new ViewElement( 'p' );
+			const el = new ViewElement( viewDocument, 'p' );
 
 			viewConsumable.add( el, { attributes: 'href', classes: 'foobar', styles: 'color' } );
 
@@ -241,7 +249,7 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should consume text node', () => {
-			const text = new ViewText();
+			const text = new ViewText( viewDocument );
 			viewConsumable.add( text );
 			const consumed = viewConsumable.consume( text );
 			expect( consumed ).to.be.true;
@@ -250,7 +258,7 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should consume document fragment', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 			viewConsumable.add( fragment );
 			const consumed = viewConsumable.consume( fragment );
 			expect( consumed ).to.be.true;
@@ -357,8 +365,8 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should revert text node', () => {
-			const text1 = new ViewText();
-			const text2 = new ViewText();
+			const text1 = new ViewText( viewDocument );
+			const text2 = new ViewText( viewDocument );
 
 			viewConsumable.add( text1 );
 			viewConsumable.consume( text1 );
@@ -371,8 +379,8 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should revert document fragment', () => {
-			const fragment1 = new ViewDocumentFragment();
-			const fragment2 = new ViewDocumentFragment();
+			const fragment1 = new ViewDocumentFragment( viewDocument );
+			const fragment2 = new ViewDocumentFragment( viewDocument );
 
 			viewConsumable.add( fragment1 );
 			viewConsumable.consume( fragment1 );
@@ -528,7 +536,7 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should return new ViewConsumable instance from document fragment', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 			const newConsumable = ViewConsumable.createFrom( fragment );
 
 			expect( newConsumable ).to.be.instanceof( ViewConsumable );
@@ -536,11 +544,11 @@ describe( 'ViewConsumable', () => {
 		} );
 
 		it( 'should add all child elements', () => {
-			const text1 = new ViewText( 'foo' );
-			const text2 = new ViewText( 'bar' );
-			const child1 = new ViewElement( 'p', { 'title': 'baz' }, [ text1 ] );
-			const child2 = new ViewElement( 'p' );
-			const child3 = new ViewElement( 'p', { 'style': 'top:10px;', 'class': 'qux bar' }, [ text2, child2 ] );
+			const text1 = new ViewText( viewDocument, 'foo' );
+			const text2 = new ViewText( viewDocument, 'bar' );
+			const child1 = new ViewElement( viewDocument, 'p', { 'title': 'baz' }, [ text1 ] );
+			const child2 = new ViewElement( viewDocument, 'p' );
+			const child3 = new ViewElement( viewDocument, 'p', { 'style': 'top:10px;', 'class': 'qux bar' }, [ text2, child2 ] );
 			el._appendChild( [ child1, child3 ] );
 
 			const newConsumable = ViewConsumable.createFrom( el );
@@ -555,16 +563,6 @@ describe( 'ViewConsumable', () => {
 	} );
 
 	describe( 'style shorthands handling', () => {
-		before( () => {
-			const stylesProcessor = new StylesProcessor();
-
-			StylesMap._setProcessor( stylesProcessor );
-
-			addBorderRules( stylesProcessor );
-			addMarginRules( stylesProcessor );
-			addPaddingRules( stylesProcessor );
-		} );
-
 		describe( 'add', () => {
 			it( 'should add padding shorthands', () => {
 				viewConsumable.add( el, { styles: [ 'margin' ] } );

+ 10 - 3
packages/ckeditor5-engine/tests/dataprocessor/htmldataprocessor.js

@@ -9,9 +9,16 @@ import HtmlDataProcessor from '../../src/dataprocessor/htmldataprocessor';
 import xssTemplates from '../../tests/dataprocessor/_utils/xsstemplates';
 import ViewDocumentFragment from '../../src/view/documentfragment';
 import { stringify, parse } from '../../src/dev-utils/view';
+import { StylesProcessor } from '../../src/view/stylesmap';
+import ViewDocument from '../../src/view/document';
 
 describe( 'HtmlDataProcessor', () => {
-	const dataProcessor = new HtmlDataProcessor();
+	let dataProcessor, viewDocument;
+
+	beforeEach( () => {
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		dataProcessor = new HtmlDataProcessor( viewDocument );
+	} );
 
 	describe( 'toView()', () => {
 		it( 'should return empty DocumentFragment when empty string is passed', () => {
@@ -89,13 +96,13 @@ describe( 'HtmlDataProcessor', () => {
 
 	describe( 'toData()', () => {
 		it( 'should return empty string when empty DocumentFragment is passed', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 
 			expect( dataProcessor.toData( fragment ) ).to.equal( '' );
 		} );
 
 		it( 'should return text if document fragment with single text node is passed', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 			fragment._appendChild( parse( 'foo bar' ) );
 
 			expect( dataProcessor.toData( fragment ) ).to.equal( 'foo bar' );

+ 8 - 5
packages/ckeditor5-engine/tests/dataprocessor/xmldataprocessor.js

@@ -8,13 +8,16 @@
 import XmlDataProcessor from '../../src/dataprocessor/xmldataprocessor';
 import xssTemplates from '../../tests/dataprocessor/_utils/xsstemplates';
 import ViewDocumentFragment from '../../src/view/documentfragment';
+import ViewDocument from '../../src/view/document';
 import { stringify, parse } from '../../src/dev-utils/view';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'XmlDataProcessor', () => {
-	let dataProcessor;
+	let dataProcessor, viewDocument;
 
 	beforeEach( () => {
-		dataProcessor = new XmlDataProcessor();
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		dataProcessor = new XmlDataProcessor( viewDocument );
 	} );
 
 	describe( 'toView', () => {
@@ -43,7 +46,7 @@ describe( 'XmlDataProcessor', () => {
 		} );
 
 		it( 'should allow to use registered namespaces', () => {
-			dataProcessor = new XmlDataProcessor( {
+			dataProcessor = new XmlDataProcessor( viewDocument, {
 				namespaces: [ 'foo', 'bar' ]
 			} );
 
@@ -82,13 +85,13 @@ describe( 'XmlDataProcessor', () => {
 
 	describe( 'toData', () => {
 		it( 'should return empty string when empty DocumentFragment is passed', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 
 			expect( dataProcessor.toData( fragment ) ).to.equal( '' );
 		} );
 
 		it( 'should return text if document fragment with single text node is passed', () => {
-			const fragment = new ViewDocumentFragment();
+			const fragment = new ViewDocumentFragment( viewDocument );
 			fragment._appendChild( parse( 'foo bar' ) );
 
 			expect( dataProcessor.toData( fragment ) ).to.equal( 'foo bar' );

+ 119 - 105
packages/ckeditor5-engine/tests/dev-utils/view.js

@@ -6,6 +6,7 @@
 /* globals document */
 
 import { parse, stringify, getData, setData } from '../../src/dev-utils/view';
+import ViewDocument from '../../src/view/document';
 import DocumentFragment from '../../src/view/documentfragment';
 import Position from '../../src/view/position';
 import Element from '../../src/view/element';
@@ -19,6 +20,7 @@ import Range from '../../src/view/range';
 import View from '../../src/view/view';
 import XmlDataProcessor from '../../src/dataprocessor/xmldataprocessor';
 import createViewRoot from '../view/_utils/createroot';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'view test utils', () => {
 	describe( 'getData, setData', () => {
@@ -30,7 +32,7 @@ describe( 'view test utils', () => {
 			it( 'should use stringify method', () => {
 				const element = document.createElement( 'div' );
 				const stringifySpy = sinon.spy( getData, '_stringify' );
-				const view = new View();
+				const view = new View( new StylesProcessor() );
 				const viewDocument = view.document;
 				const options = {
 					showType: false,
@@ -39,7 +41,7 @@ describe( 'view test utils', () => {
 					renderUIElements: false
 				};
 				const root = createAttachedRoot( viewDocument, element );
-				root._appendChild( new Element( 'p' ) );
+				root._appendChild( new Element( viewDocument, 'p' ) );
 
 				expect( getData( view, options ) ).to.equal( '<p></p>' );
 				sinon.assert.calledOnce( stringifySpy );
@@ -57,11 +59,11 @@ describe( 'view test utils', () => {
 			it( 'should use stringify method with selection', () => {
 				const element = document.createElement( 'div' );
 				const stringifySpy = sinon.spy( getData, '_stringify' );
-				const view = new View();
+				const view = new View( new StylesProcessor() );
 				const viewDocument = view.document;
 				const options = { showType: false, showPriority: false };
 				const root = createAttachedRoot( viewDocument, element );
-				root._appendChild( new Element( 'p' ) );
+				root._appendChild( new Element( viewDocument, 'p' ) );
 
 				view.change( writer => {
 					writer.setSelection( Range._createFromParentsAndOffsets( root, 0, root, 1 ) );
@@ -88,7 +90,7 @@ describe( 'view test utils', () => {
 
 		describe( 'setData', () => {
 			it( 'should use parse method', () => {
-				const view = new View();
+				const view = new View( new StylesProcessor() );
 				const viewDocument = view.document;
 				const data = 'foobar<b>baz</b>';
 				const parseSpy = sinon.spy( setData, '_parse' );
@@ -107,7 +109,7 @@ describe( 'view test utils', () => {
 			} );
 
 			it( 'should use parse method with selection', () => {
-				const view = new View();
+				const view = new View( new StylesProcessor() );
 				const viewDocument = view.document;
 				const data = '[<b>baz</b>]';
 				const parseSpy = sinon.spy( setData, '_parse' );
@@ -133,25 +135,31 @@ describe( 'view test utils', () => {
 	} );
 
 	describe( 'stringify', () => {
+		let viewDocument;
+
+		beforeEach( () => {
+			viewDocument = new ViewDocument( new StylesProcessor() );
+		} );
+
 		it( 'should write text', () => {
-			const text = new Text( 'foobar' );
+			const text = new Text( viewDocument, 'foobar' );
 			expect( stringify( text ) ).to.equal( 'foobar' );
 		} );
 
 		it( 'should write elements and texts', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 
 			expect( stringify( p ) ).to.equal( '<p><b>foobar</b></p>' );
 		} );
 
 		it( 'should write elements with attributes (attributes in alphabetical order)', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', {
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', {
 				foo: 'bar'
 			}, text );
-			const p = new Element( 'p', {
+			const p = new Element( viewDocument, 'p', {
 				baz: 'qux',
 				bar: 'taz',
 				class: 'short wide'
@@ -161,20 +169,20 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should write selection ranges inside elements', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const p = new Element( 'p', null, [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const p = new Element( viewDocument, 'p', null, [ b1, b2 ] );
 			const range = Range._createFromParentsAndOffsets( p, 1, p, 2 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection ) ).to.equal( '<p><b>foobar</b>[<b>bazqux</b>]</p>' );
 		} );
 
 		it( 'should support unicode', () => {
-			const text = new Text( 'நிலைக்கு' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'நிலைக்கு' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 			const range = Range._createFromParentsAndOffsets( p, 0, text, 4 );
 			const selection = new DocumentSelection( [ range ] );
 
@@ -182,30 +190,30 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should write collapsed selection ranges inside elements', () => {
-			const text = new Text( 'foobar' );
-			const p = new Element( 'p', null, text );
+			const text = new Text( viewDocument, 'foobar' );
+			const p = new Element( viewDocument, 'p', null, text );
 			const range = Range._createFromParentsAndOffsets( p, 0, p, 0 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection ) ).to.equal( '<p>[]foobar</p>' );
 		} );
 
 		it( 'should write selection ranges inside text', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const p = new Element( 'p', null, [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const p = new Element( viewDocument, 'p', null, [ b1, b2 ] );
 			const range = Range._createFromParentsAndOffsets( text1, 1, text1, 5 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection ) ).to.equal( '<p><b>f{ooba}r</b><b>bazqux</b></p>' );
 		} );
 
 		it( 'should write selection ranges inside text represented by `[` and `]` characters', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const p = new Element( 'p', null, [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const p = new Element( viewDocument, 'p', null, [ b1, b2 ] );
 			const range = Range._createFromParentsAndOffsets( text1, 1, text1, 5 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection, { sameSelectionCharacters: true } ) )
@@ -213,108 +221,108 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should write collapsed selection ranges inside texts', () => {
-			const text = new Text( 'foobar' );
-			const p = new Element( 'p', null, text );
+			const text = new Text( viewDocument, 'foobar' );
+			const p = new Element( viewDocument, 'p', null, text );
 			const range = Range._createFromParentsAndOffsets( text, 0, text, 0 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection ) ).to.equal( '<p>{}foobar</p>' );
 		} );
 
 		it( 'should write ranges that start inside text end ends between elements', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const p = new Element( 'p', null, [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const p = new Element( viewDocument, 'p', null, [ b1, b2 ] );
 			const range = Range._createFromParentsAndOffsets( p, 0, text2, 5 );
 			const selection = new DocumentSelection( [ range ] );
 			expect( stringify( p, selection ) ).to.equal( '<p>[<b>foobar</b><b>bazqu}x</b></p>' );
 		} );
 
 		it( 'should write elements types as namespaces when needed', () => {
-			const text = new Text( 'foobar' );
-			const b = new AttributeElement( 'b', null, text );
-			const p = new ContainerElement( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new AttributeElement( viewDocument, 'b', null, text );
+			const p = new ContainerElement( viewDocument, 'p', null, b );
 
 			expect( stringify( p, null, { showType: true } ) )
 				.to.equal( '<container:p><attribute:b>foobar</attribute:b></container:p>' );
 		} );
 
 		it( 'should not write element type when type is not specified', () => {
-			const p = new Element( 'p' );
+			const p = new Element( viewDocument, 'p' );
 			expect( stringify( p, null, { showType: true } ) ).to.equal( '<p></p>' );
 		} );
 
 		it( 'should write elements priorities when needed', () => {
-			const text = new Text( 'foobar' );
-			const b = new AttributeElement( 'b', null, text );
-			const p = new ContainerElement( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new AttributeElement( viewDocument, 'b', null, text );
+			const p = new ContainerElement( viewDocument, 'p', null, b );
 
 			expect( stringify( p, null, { showPriority: true } ) )
 				.to.equal( '<p><b view-priority="10">foobar</b></p>' );
 		} );
 
 		it( 'should write elements id when needed', () => {
-			const text = new Text( 'foobar' );
-			const span = new AttributeElement( 'span', null, text );
+			const text = new Text( viewDocument, 'foobar' );
+			const span = new AttributeElement( viewDocument, 'span', null, text );
 			span._id = 'foo';
-			const p = new ContainerElement( 'p', null, span );
+			const p = new ContainerElement( viewDocument, 'p', null, span );
 
 			expect( stringify( p, null, { showAttributeElementId: true } ) )
 				.to.equal( '<p><span view-id="foo">foobar</span></p>' );
 		} );
 
 		it( 'should parse DocumentFragment as root', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const fragment = new DocumentFragment( [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const fragment = new DocumentFragment( viewDocument, [ b1, b2 ] );
 			expect( stringify( fragment, null ) ).to.equal( '<b>foobar</b><b>bazqux</b>' );
 		} );
 
 		it( 'should not write ranges outside elements - end position outside element', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 			const range = Range._createFromParentsAndOffsets( p, 0, p, 5 );
 
 			expect( stringify( p, range ) ).to.equal( '<p>[<b>foobar</b></p>' );
 		} );
 
 		it( 'should not write ranges outside elements - start position outside element', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 			const range = Range._createFromParentsAndOffsets( p, -1, p, 1 );
 
 			expect( stringify( p, range ) ).to.equal( '<p><b>foobar</b>]</p>' );
 		} );
 
 		it( 'should not write ranges outside elements - end position outside text', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 			const range = Range._createFromParentsAndOffsets( text, 0, text, 7 );
 
 			expect( stringify( p, range ) ).to.equal( '<p><b>{foobar</b></p>' );
 		} );
 
 		it( 'should not write ranges outside elements - start position outside text', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', null, text );
-			const p = new Element( 'p', null, b );
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', null, text );
+			const p = new Element( viewDocument, 'p', null, b );
 			const range = Range._createFromParentsAndOffsets( text, -1, text, 2 );
 
 			expect( stringify( p, range ) ).to.equal( '<p><b>fo}obar</b></p>' );
 		} );
 
 		it( 'should write multiple ranges from selection #1', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b1 = new Element( 'b', null, text1 );
-			const b2 = new Element( 'b', null, text2 );
-			const p = new Element( 'p', null, [ b1, b2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b1 = new Element( viewDocument, 'b', null, text1 );
+			const b2 = new Element( viewDocument, 'b', null, text2 );
+			const p = new Element( viewDocument, 'p', null, [ b1, b2 ] );
 			const range1 = Range._createFromParentsAndOffsets( p, 0, p, 1 );
 			const range2 = Range._createFromParentsAndOffsets( p, 1, p, 1 );
 			const selection = new DocumentSelection( [ range2, range1 ] );
@@ -323,10 +331,10 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should write multiple ranges from selection #2', () => {
-			const text1 = new Text( 'foobar' );
-			const text2 = new Text( 'bazqux' );
-			const b = new Element( 'b', null, text1 );
-			const p = new Element( 'p', null, [ b, text2 ] );
+			const text1 = new Text( viewDocument, 'foobar' );
+			const text2 = new Text( viewDocument, 'bazqux' );
+			const b = new Element( viewDocument, 'b', null, text1 );
+			const p = new Element( viewDocument, 'p', null, [ b, text2 ] );
 			const range1 = Range._createFromParentsAndOffsets( p, 0, p, 1 );
 			const range2 = Range._createFromParentsAndOffsets( text2, 0, text2, 3 );
 			const range3 = Range._createFromParentsAndOffsets( text2, 3, text2, 4 );
@@ -337,35 +345,35 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should use Position instance instead of Selection', () => {
-			const text = new Text( 'foobar' );
+			const text = new Text( viewDocument, 'foobar' );
 			const position = new Position( text, 3 );
 			const string = stringify( text, position );
 			expect( string ).to.equal( 'foo{}bar' );
 		} );
 
 		it( 'should use Range instance instead of Selection', () => {
-			const text = new Text( 'foobar' );
+			const text = new Text( viewDocument, 'foobar' );
 			const range = Range._createFromParentsAndOffsets( text, 3, text, 4 );
 			const string = stringify( text, range );
 			expect( string ).to.equal( 'foo{b}ar' );
 		} );
 
 		it( 'should stringify EmptyElement', () => {
-			const img = new EmptyElement( 'img' );
-			const p = new ContainerElement( 'p', null, img );
+			const img = new EmptyElement( viewDocument, 'img' );
+			const p = new ContainerElement( viewDocument, 'p', null, img );
 			expect( stringify( p, null, { showType: true } ) )
 				.to.equal( '<container:p><empty:img></empty:img></container:p>' );
 		} );
 
 		it( 'should stringify UIElement', () => {
-			const span = new UIElement( 'span' );
-			const p = new ContainerElement( 'p', null, span );
+			const span = new UIElement( viewDocument, 'span' );
+			const p = new ContainerElement( viewDocument, 'p', null, span );
 			expect( stringify( p, null, { showType: true } ) )
 				.to.equal( '<container:p><ui:span></ui:span></container:p>' );
 		} );
 
 		it( 'should not stringify inner UIElement content (renderUIElements=false)', () => {
-			const span = new UIElement( 'span' );
+			const span = new UIElement( viewDocument, 'span' );
 
 			span.render = function( domDocument ) {
 				const domElement = this.toDomElement( domDocument );
@@ -375,13 +383,13 @@ describe( 'view test utils', () => {
 				return domElement;
 			};
 
-			const p = new ContainerElement( 'p', null, span );
+			const p = new ContainerElement( viewDocument, 'p', null, span );
 			expect( stringify( p, null, { showType: true } ) )
 				.to.equal( '<container:p><ui:span></ui:span></container:p>' );
 		} );
 
 		it( 'should stringify UIElement, (renderUIElements=true)', () => {
-			const span = new UIElement( 'span' );
+			const span = new UIElement( viewDocument, 'span' );
 
 			span.render = function( domDocument ) {
 				const domElement = this.toDomElement( domDocument );
@@ -391,14 +399,14 @@ describe( 'view test utils', () => {
 				return domElement;
 			};
 
-			const p = new ContainerElement( 'p', null, span );
+			const p = new ContainerElement( viewDocument, 'p', null, span );
 			expect( stringify( p, null, { showType: true, renderUIElements: true } ) )
 				.to.equal( '<container:p><ui:span><b>foo</b></ui:span></container:p>' );
 		} );
 
 		it( 'should sort classes in specified element', () => {
-			const text = new Text( 'foobar' );
-			const b = new Element( 'b', {
+			const text = new Text( viewDocument, 'foobar' );
+			const b = new Element( viewDocument, 'b', {
 				class: 'zz xx aa'
 			}, text );
 
@@ -406,8 +414,8 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should sort styles in specified element', () => {
-			const text = new Text( 'foobar' );
-			const i = new Element( 'i', {
+			const text = new Text( viewDocument, 'foobar' );
+			const i = new Element( viewDocument, 'i', {
 				style: 'text-decoration: underline; font-weight: bold'
 			}, text );
 
@@ -416,6 +424,12 @@ describe( 'view test utils', () => {
 	} );
 
 	describe( 'parse', () => {
+		let viewDocument;
+
+		beforeEach( () => {
+			viewDocument = new ViewDocument( new StylesProcessor() );
+		} );
+
 		it( 'should return empty DocumentFragment for empty string', () => {
 			const fragment = parse( '' );
 
@@ -445,8 +459,8 @@ describe( 'view test utils', () => {
 			const view = parse( '<b></b><i></i>' );
 			expect( view ).to.be.instanceOf( DocumentFragment );
 			expect( view.childCount ).to.equal( 2 );
-			expect( view.getChild( 0 ).isSimilar( new Element( 'b' ) ) ).to.be.true;
-			expect( view.getChild( 1 ).isSimilar( new Element( 'i' ) ) ).to.be.true;
+			expect( view.getChild( 0 ).isSimilar( new Element( viewDocument, 'b' ) ) ).to.be.true;
+			expect( view.getChild( 1 ).isSimilar( new Element( viewDocument, 'i' ) ) ).to.be.true;
 		} );
 
 		it( 'should parse text', () => {
@@ -463,7 +477,7 @@ describe( 'view test utils', () => {
 
 		it( 'should parse elements and texts', () => {
 			const view = parse( '<b>foobar</b>' );
-			const element = new Element( 'b' );
+			const element = new Element( viewDocument, 'b' );
 
 			expect( view ).to.be.instanceof( Element );
 			expect( view.isSimilar( element ) ).to.be.true;
@@ -475,7 +489,7 @@ describe( 'view test utils', () => {
 
 		it( 'should parse element attributes', () => {
 			const view = parse( '<b name="foo" title="bar" class="foo bar" style="color:red;"></b>' );
-			const element = new Element( 'b', { name: 'foo', title: 'bar', class: 'foo bar', style: 'color:red;' } );
+			const element = new Element( viewDocument, 'b', { name: 'foo', title: 'bar', class: 'foo bar', style: 'color:red;' } );
 
 			expect( view ).to.be.instanceof( Element );
 			expect( view.isSimilar( element ) ).to.be.true;
@@ -484,9 +498,9 @@ describe( 'view test utils', () => {
 
 		it( 'should parse element type', () => {
 			const view1 = parse( '<attribute:b></attribute:b>' );
-			const attribute = new AttributeElement( 'b' );
+			const attribute = new AttributeElement( viewDocument, 'b' );
 			const view2 = parse( '<container:p></container:p>' );
-			const container = new ContainerElement( 'p' );
+			const container = new ContainerElement( viewDocument, 'p' );
 
 			expect( view1 ).to.be.instanceof( AttributeElement );
 			expect( view1.isSimilar( attribute ) ).to.be.true;
@@ -496,10 +510,10 @@ describe( 'view test utils', () => {
 
 		it( 'should parse element priority', () => {
 			const parsed1 = parse( '<b view-priority="12"></b>' );
-			const attribute1 = new AttributeElement( 'b' );
+			const attribute1 = new AttributeElement( viewDocument, 'b' );
 			attribute1._priority = 12;
 			const parsed2 = parse( '<attribute:b view-priority="44"></attribute:b>' );
-			const attribute2 = new AttributeElement( 'b' );
+			const attribute2 = new AttributeElement( viewDocument, 'b' );
 			attribute2._priority = 44;
 
 			parsed1.isSimilar( attribute1 );
@@ -517,7 +531,7 @@ describe( 'view test utils', () => {
 
 		it( 'should paste nested elements and texts', () => {
 			const parsed = parse( '<container:p>foo<b view-priority="12">bar<i view-priority="25">qux</i></b></container:p>' );
-			expect( parsed.isSimilar( new ContainerElement( 'p' ) ) ).to.be.true;
+			expect( parsed.isSimilar( new ContainerElement( viewDocument, 'p' ) ) ).to.be.true;
 			expect( parsed.childCount ).to.equal( 2 );
 			expect( parsed.getChild( 0 ) ).to.be.instanceof( Text ).and.have.property( 'data' ).that.equal( 'foo' );
 			const b = parsed.getChild( 1 );
@@ -584,7 +598,7 @@ describe( 'view test utils', () => {
 
 		it( 'should parse ranges #1', () => {
 			const { view, selection } = parse( '<container:p>foo{bar]</container:p>' );
-			expect( view.isSimilar( new ContainerElement( 'p' ) ) ).to.be.true;
+			expect( view.isSimilar( new ContainerElement( viewDocument, 'p' ) ) ).to.be.true;
 			expect( view.childCount ).to.equal( 1 );
 			const text = view.getChild( 0 );
 			expect( text ).to.be.instanceof( Text );
@@ -595,13 +609,13 @@ describe( 'view test utils', () => {
 
 		it( 'should parse ranges #2', () => {
 			const { view, selection } = parse( '<attribute:b>[foob}ar<i>{baz</i>]</attribute:b>' );
-			expect( view.isSimilar( new AttributeElement( 'b' ) ) ).to.be.true;
+			expect( view.isSimilar( new AttributeElement( viewDocument, 'b' ) ) ).to.be.true;
 			expect( view.childCount ).to.equal( 2 );
 			const text1 = view.getChild( 0 );
 			expect( text1 ).to.be.instanceof( Text );
 			expect( text1.data ).to.equal( 'foobar' );
 			const i = view.getChild( 1 );
-			expect( i.isSimilar( new Element( 'i' ) ) ).to.be.true;
+			expect( i.isSimilar( new Element( viewDocument, 'i' ) ) ).to.be.true;
 			expect( i.childCount ).to.equal( 1 );
 			const text2 = i.getChild( 0 );
 			expect( text2 ).to.be.instanceof( Text );
@@ -677,7 +691,7 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should throw when wrong type is provided', () => {
-			sinon.stub( XmlDataProcessor.prototype, 'toView' ).returns( new ContainerElement( 'invalidType:b' ) );
+			sinon.stub( XmlDataProcessor.prototype, 'toView' ).returns( new ContainerElement( viewDocument, 'invalidType:b' ) );
 
 			expect( () => {
 				parse( 'sth' );
@@ -687,14 +701,14 @@ describe( 'view test utils', () => {
 		} );
 
 		it( 'should use provided root element #1', () => {
-			const root = new Element( 'p' );
+			const root = new Element( viewDocument, 'p' );
 			const data = parse( '<span>text</span>', { rootElement: root } );
 
 			expect( stringify( data ) ).to.equal( '<p><span>text</span></p>' );
 		} );
 
 		it( 'should use provided root element #2', () => {
-			const root = new Element( 'p' );
+			const root = new Element( viewDocument, 'p' );
 			const data = parse( '<span>text</span><b>test</b>', { rootElement: root } );
 
 			expect( stringify( data ) ).to.equal( '<p><span>text</span><b>test</b></p>' );

+ 51 - 19
packages/ckeditor5-engine/tests/model/node.js

@@ -8,8 +8,10 @@ import DocumentFragment from '../../src/model/documentfragment';
 import Node from '../../src/model/node';
 import Element from '../../src/model/element';
 import Text from '../../src/model/text';
+import RootElement from '../../src/model/rootelement';
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import ModelTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/modeltesteditor';
 
 describe( 'Node', () => {
 	let doc, root, node,
@@ -75,25 +77,6 @@ describe( 'Node', () => {
 
 			expect( node ).to.have.property( 'previousSibling' ).that.is.null;
 		} );
-
-		it( 'document', () => {
-			expect( root ).to.have.property( 'document' ).that.equals( doc );
-
-			expect( one ).to.have.property( 'document' ).that.equals( doc );
-			expect( two ).to.have.property( 'document' ).that.equals( doc );
-			expect( three ).to.have.property( 'document' ).that.equals( doc );
-
-			expect( textBA ).to.have.property( 'document' ).that.equals( doc );
-			expect( img ).to.have.property( 'document' ).that.equals( doc );
-			expect( textR ).to.have.property( 'document' ).that.equals( doc );
-
-			expect( node ).to.have.property( 'document' ).that.is.null;
-
-			// DocumentFragment does not have document property, so node's document property should be null.
-			const docFrag = new DocumentFragment();
-			docFrag._appendChild( node );
-			expect( node ).to.have.property( 'document' ).that.is.null;
-		} );
 	} );
 
 	describe( 'constructor()', () => {
@@ -405,6 +388,55 @@ describe( 'Node', () => {
 		} );
 	} );
 
+	describe( 'isAttached()', () => {
+		it( 'returns false for a fresh node', () => {
+			const char = new Text( 'x' );
+			const el = new Element( 'one' );
+
+			expect( char.isAttached() ).to.equal( false );
+			expect( el.isAttached() ).to.equal( false );
+		} );
+
+		it( 'returns true for the root element', () => {
+			const model = new Model();
+			const root = new RootElement( model.document, 'root' );
+
+			expect( root.isAttached() ).to.equal( true );
+		} );
+
+		it( 'returns false for a node attached to a document fragment', () => {
+			const foo = new Text( 'foo' );
+			new DocumentFragment( [ foo ] ); // eslint-disable-line no-new
+
+			expect( foo.isAttached() ).to.equal( false );
+		} );
+
+		it( 'returns true for a node moved to graveyard', () => {
+			return ModelTestEditor.create()
+				.then( editor => {
+					const model = editor.model;
+					const root = model.document.getRoot();
+
+					// Allow "paragraph" element to be added as a child in block elements.
+					model.schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+
+					const node = model.change( writer => writer.createElement( 'paragraph' ) );
+
+					expect( node.isAttached() ).to.equal( false );
+
+					model.change( writer => writer.append( node, root ) );
+
+					expect( node.isAttached() ).to.equal( true );
+
+					model.change( writer => writer.remove( node ) );
+
+					expect( node.isAttached() ).to.equal( true );
+
+					return editor.destroy();
+				} );
+		} );
+	} );
+
 	describe( 'attributes interface', () => {
 		const node = new Node( { foo: 'bar' } );
 

+ 0 - 5
packages/ckeditor5-engine/tests/model/textproxy.js

@@ -38,11 +38,6 @@ describe( 'TextProxy', () => {
 		expect( textProxyNoParent ).to.have.property( 'root' ).that.equals( textNoParent );
 	} );
 
-	it( 'should have document property', () => {
-		expect( textProxy ).to.have.property( 'document' ).that.equals( doc );
-		expect( textProxyNoParent ).to.have.property( 'document' ).that.equals( null );
-	} );
-
 	it( 'should have parent property', () => {
 		expect( textProxy ).to.have.property( 'parent' ).that.equals( element );
 		expect( textProxyNoParent ).to.have.property( 'parent' ).that.equals( null );

+ 2 - 1
packages/ckeditor5-engine/tests/tickets/1323.js

@@ -9,6 +9,7 @@ import Model from '../../src/model/model';
 import ModelText from '../../src/model/text';
 
 import MarkerOperation from '../../src/model/operation/markeroperation';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Bug ckeditor5-engine@1323', () => {
 	describe( 'constructor()', () => {
@@ -16,7 +17,7 @@ describe( 'Bug ckeditor5-engine@1323', () => {
 
 		beforeEach( () => {
 			model = new Model();
-			editing = new EditingController( model );
+			editing = new EditingController( model, new StylesProcessor() );
 			root = model.document.createRoot();
 			root._appendChild( new ModelText( 'foo' ) );
 			range = model.createRange( model.createPositionAt( root, 0 ), model.createPositionAt( root, 0 ) );

+ 2 - 0
packages/ckeditor5-engine/tests/view/_utils/createdocumentmock.js

@@ -5,6 +5,7 @@
 
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import DocumentSelection from '../../../src/view/documentselection';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 /**
  * Creates {@link module:engine/view/document~Document view Document} mock.
@@ -16,6 +17,7 @@ export default function createDocumentMock() {
 	doc.set( 'isFocused', false );
 	doc.set( 'isReadOnly', false );
 	doc.selection = new DocumentSelection();
+	doc.stylesProcessor = new StylesProcessor();
 
 	return doc;
 }

+ 1 - 2
packages/ckeditor5-engine/tests/view/_utils/createroot.js

@@ -14,9 +14,8 @@ import RootEditableElement from '../../../src/view/rooteditableelement';
  * @returns {module:engine/view/rooteditableelement~RootEditableElement} Root element.
  */
 export default function createRoot( doc, name = 'div', rootName = 'main' ) {
-	const root = new RootEditableElement( name );
+	const root = new RootEditableElement( doc, name );
 
-	root._document = doc;
 	root.rootName = rootName;
 	doc.roots.add( root );
 

+ 26 - 18
packages/ckeditor5-engine/tests/view/attributeelement.js

@@ -5,14 +5,21 @@
 
 import AttributeElement from '../../src/view/attributeelement';
 import Element from '../../src/view/element';
+import Document from '../../src/view/document';
 import { parse } from '../../src/dev-utils/view';
-
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'AttributeElement', () => {
+	let document;
+
+	beforeEach( () => {
+		document = new Document( new StylesProcessor() );
+	} );
+
 	describe( 'constructor()', () => {
 		it( 'should create element with default priority', () => {
-			const el = new AttributeElement( 'strong' );
+			const el = new AttributeElement( document, 'strong' );
 
 			expect( el ).to.be.an.instanceof( AttributeElement );
 			expect( el ).to.be.an.instanceof( Element );
@@ -25,7 +32,7 @@ describe( 'AttributeElement', () => {
 		let el;
 
 		before( () => {
-			el = new AttributeElement( 'span' );
+			el = new AttributeElement( document, 'span' );
 		} );
 
 		it( 'should return true for attributeElement/element, also with correct name and element name', () => {
@@ -60,7 +67,7 @@ describe( 'AttributeElement', () => {
 
 	describe( '_clone()', () => {
 		it( 'should clone element with priority', () => {
-			const el = new AttributeElement( 'b' );
+			const el = new AttributeElement( document, 'b' );
 			el._priority = 7;
 
 			const clone = el._clone();
@@ -73,32 +80,32 @@ describe( 'AttributeElement', () => {
 
 	describe( 'isSimilar', () => {
 		it( 'should return true if priorities are the same', () => {
-			const b1 = new AttributeElement( 'b' );
+			const b1 = new AttributeElement( document, 'b' );
 			b1._priority = 7;
 
-			const b2 = new AttributeElement( 'b' );
+			const b2 = new AttributeElement( document, 'b' );
 			b2._priority = 7;
 
 			expect( b1.isSimilar( b2 ) ).to.be.true;
 		} );
 
 		it( 'should return false if priorities are different', () => {
-			const b1 = new AttributeElement( 'b' );
+			const b1 = new AttributeElement( document, 'b' );
 			b1._priority = 7;
 
-			const b2 = new AttributeElement( 'b' ); // default priority
+			const b2 = new AttributeElement( document, 'b' ); // default priority
 
 			expect( b1.isSimilar( b2 ) ).to.be.false;
 		} );
 
 		it( 'should return true if ids are the same even if other properties are different', () => {
-			const element1 = new AttributeElement( 'b' );
+			const element1 = new AttributeElement( document, 'b' );
 			element1._id = 'xyz';
 
-			const element2 = new AttributeElement( 'b', { foo: 'bar' } );
+			const element2 = new AttributeElement( document, 'b', { foo: 'bar' } );
 			element2._id = 'xyz';
 
-			const element3 = new AttributeElement( 'span' );
+			const element3 = new AttributeElement( document, 'span' );
 			element3._id = 'xyz';
 
 			expect( element1.isSimilar( element2 ) ).to.be.true;
@@ -106,11 +113,11 @@ describe( 'AttributeElement', () => {
 		} );
 
 		it( 'should return false if ids are different even if other properties are same', () => {
-			const element1 = new AttributeElement( 'span', { foo: 'bar' } );
+			const element1 = new AttributeElement( document, 'span', { foo: 'bar' } );
 			element1._priority = 3;
 			element1._id = 'foo';
 
-			const element2 = new AttributeElement( 'span', { foo: 'bar' } );
+			const element2 = new AttributeElement( document, 'span', { foo: 'bar' } );
 			element2._priority = 3;
 			element2._id = 'bar';
 
@@ -121,8 +128,8 @@ describe( 'AttributeElement', () => {
 	// More tests are available in DowncastWriter tests.
 	describe( 'getElementsWithSameId', () => {
 		it( 'should return a copy of _clonesGroup set', () => {
-			const attributeA = new AttributeElement( 'b' );
-			const attributeB = new AttributeElement( 'b' );
+			const attributeA = new AttributeElement( document, 'b' );
+			const attributeB = new AttributeElement( document, 'b' );
 
 			attributeA._id = 'foo';
 			attributeB._id = 'foo';
@@ -135,7 +142,7 @@ describe( 'AttributeElement', () => {
 		} );
 
 		it( 'should throw if attribute element has no id', () => {
-			const attribute = new AttributeElement( 'b' );
+			const attribute = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				attribute.getElementsWithSameId();
@@ -194,14 +201,15 @@ describe( 'AttributeElement', () => {
 		} );
 
 		it( 'should return null if there is no parent', () => {
-			const attribute = new AttributeElement( 'b' );
+			const attribute = new AttributeElement( document, 'b' );
 
 			expect( attribute.getFillerOffset() ).to.be.null;
 		} );
 
 		it( 'should return offset after all children if it is the only nested element in the container and has UIElement inside', () => {
 			const { selection } = parse(
-				'<container:p><attribute:b><attribute:i>[]<ui:span></ui:span></attribute:i></attribute:b></container:p>' );
+				'<container:p><attribute:b><attribute:i>[]<ui:span></ui:span></attribute:i></attribute:b></container:p>'
+			);
 			const attribute = selection.getFirstPosition().parent;
 
 			expect( attribute.getFillerOffset() ).to.equal( 1 );

+ 10 - 2
packages/ckeditor5-engine/tests/view/containerelement.js

@@ -5,12 +5,20 @@
 
 import { default as ContainerElement, getFillerOffset } from '../../src/view/containerelement';
 import Element from '../../src/view/element';
+import Document from '../../src/view/document';
 import { parse } from '../../src/dev-utils/view';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'ContainerElement', () => {
+	let document;
+
+	beforeEach( () => {
+		document = new Document( new StylesProcessor() );
+	} );
+
 	describe( 'constructor()', () => {
 		it( 'should create element with default priority', () => {
-			const el = new ContainerElement( 'p' );
+			const el = new ContainerElement( document, 'p' );
 
 			expect( el ).to.be.an.instanceof( ContainerElement );
 			expect( el ).to.be.an.instanceof( Element );
@@ -22,7 +30,7 @@ describe( 'ContainerElement', () => {
 		let el;
 
 		before( () => {
-			el = new ContainerElement( 'p' );
+			el = new ContainerElement( document, 'p' );
 		} );
 
 		it( 'should return true for containerElement/element, also with correct name and element name', () => {

+ 2 - 13
packages/ckeditor5-engine/tests/view/document.js

@@ -10,7 +10,7 @@ import Document from '../../src/view/document';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import createViewRoot from './_utils/createroot';
-import StylesMap from '../../src/view/stylesmap';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Document', () => {
 	let domRoot, viewDocument;
@@ -24,7 +24,7 @@ describe( 'Document', () => {
 		} );
 		document.body.appendChild( domRoot );
 
-		viewDocument = new Document();
+		viewDocument = new Document( new StylesProcessor() );
 	} );
 
 	afterEach( () => {
@@ -92,15 +92,4 @@ describe( 'Document', () => {
 			expect( calls ).to.equal( 4 );
 		} );
 	} );
-
-	describe( 'addStyleProcessorRules()', () => {
-		it( 'should ', () => {
-			const spy = sinon.spy();
-
-			viewDocument.addStyleProcessorRules( spy );
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWithExactly( spy, StylesMap._styleProcessor );
-		} );
-	} );
 } );

+ 45 - 37
packages/ckeditor5-engine/tests/view/documentfragment.js

@@ -8,27 +8,35 @@ import Element from '../../src/view/element';
 import Node from '../../src/view/node';
 import Text from '../../src/view/text';
 import TextProxy from '../../src/view/textproxy';
+import Document from '../../src/view/document';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'DocumentFragment', () => {
+	let document;
+
+	beforeEach( () => {
+		document = new Document( new StylesProcessor() );
+	} );
+
 	describe( 'constructor()', () => {
 		it( 'should create DocumentFragment without children', () => {
-			const fragment = new DocumentFragment();
+			const fragment = new DocumentFragment( document );
 
 			expect( fragment ).to.be.an.instanceof( DocumentFragment );
 			expect( fragment.childCount ).to.equal( 0 );
 		} );
 
-		it( 'should create DocumentFragment with child node', () => {
-			const child = new Element( 'p' );
-			const fragment = new DocumentFragment( child );
+		it( 'should create DocumentFragment  document,with child node', () => {
+			const child = new Element( document, 'p' );
+			const fragment = new DocumentFragment( document, child );
 
 			expect( fragment.childCount ).to.equal( 1 );
 			expect( fragment.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'p' );
 		} );
 
-		it( 'should create DocumentFragment with multiple nodes', () => {
-			const children = [ new Element( 'p' ), new Element( 'div' ) ];
-			const fragment = new DocumentFragment( children );
+		it( 'should create DocumentFragment  document,with multiple nodes', () => {
+			const children = [ new Element( document, 'p' ), new Element( document, 'div' ) ];
+			const fragment = new DocumentFragment( document, children );
 
 			expect( fragment.childCount ).to.equal( 2 );
 			expect( fragment.getChild( 0 ) ).to.have.property( 'name' ).that.equals( 'p' );
@@ -38,8 +46,8 @@ describe( 'DocumentFragment', () => {
 
 	describe( 'iterator', () => {
 		it( 'should iterate over all nodes added to document fragment', () => {
-			const children = [ new Element( 'p' ), new Element( 'div' ) ];
-			const fragment = new DocumentFragment( children );
+			const children = [ new Element( document, 'p' ), new Element( document, 'div' ) ];
+			const fragment = new DocumentFragment( document, children );
 
 			const arr = Array.from( fragment );
 
@@ -51,7 +59,7 @@ describe( 'DocumentFragment', () => {
 
 	describe( 'getRoot', () => {
 		it( 'should return document fragment', () => {
-			const fragment = new DocumentFragment();
+			const fragment = new DocumentFragment( document );
 
 			expect( fragment.root ).to.equal( fragment );
 		} );
@@ -59,13 +67,13 @@ describe( 'DocumentFragment', () => {
 
 	describe( 'isEmpty', () => {
 		it( 'should return true if there are no children in document fragment', () => {
-			const fragment = new DocumentFragment();
+			const fragment = new DocumentFragment( document );
 
 			expect( fragment.isEmpty ).to.be.true;
 		} );
 
 		it( 'should return false if there are children in document fragment', () => {
-			const fragment = new DocumentFragment( [ new Element( 'p' ) ] );
+			const fragment = new DocumentFragment( document, [ new Element( document, 'p' ) ] );
 
 			expect( fragment.isEmpty ).to.be.false;
 		} );
@@ -75,7 +83,7 @@ describe( 'DocumentFragment', () => {
 		let frag;
 
 		before( () => {
-			frag = new DocumentFragment();
+			frag = new DocumentFragment( document );
 		} );
 
 		it( 'should return true for documentFragment', () => {
@@ -102,11 +110,11 @@ describe( 'DocumentFragment', () => {
 		let fragment, el1, el2, el3, el4;
 
 		beforeEach( () => {
-			fragment = new DocumentFragment();
-			el1 = new Element( 'el1' );
-			el2 = new Element( 'el2' );
-			el3 = new Element( 'el3' );
-			el4 = new Element( 'el4' );
+			fragment = new DocumentFragment( document );
+			el1 = new Element( document, 'el1' );
+			el2 = new Element( document, 'el2' );
+			el3 = new Element( document, 'el3' );
+			el4 = new Element( document, 'el4' );
 		} );
 
 		describe( 'insertion', () => {
@@ -129,7 +137,7 @@ describe( 'DocumentFragment', () => {
 				expect( fragment.getChild( 0 ) ).to.have.property( 'data' ).that.equals( 'abc' );
 
 				fragment._removeChildren( 0, 1 );
-				fragment._insertChild( 0, [ new Element( 'p' ), 'abc' ] );
+				fragment._insertChild( 0, [ new Element( document, 'p' ), 'abc' ] );
 
 				expect( fragment.childCount ).to.equal( 2 );
 				expect( fragment.getChild( 1 ) ).to.have.property( 'data' ).that.equals( 'abc' );
@@ -168,8 +176,8 @@ describe( 'DocumentFragment', () => {
 			} );
 
 			it( 'should accept and correctly handle text proxies', () => {
-				const frag = new DocumentFragment();
-				const text = new Text( 'abcxyz' );
+				const frag = new DocumentFragment( document );
+				const text = new Text( document, 'abcxyz' );
 				const textProxy = new TextProxy( text, 2, 3 );
 
 				frag._insertChild( 0, textProxy );
@@ -260,10 +268,10 @@ describe( 'DocumentFragment', () => {
 
 	describe( 'node methods when inserted to fragment', () => {
 		it( 'index should return proper value', () => {
-			const node1 = new Node();
-			const node2 = new Node();
-			const node3 = new Node();
-			const fragment = new DocumentFragment( [ node1, node2, node3 ] );
+			const node1 = new Node( document );
+			const node2 = new Node( document );
+			const node3 = new Node( document );
+			const fragment = new DocumentFragment( document, [ node1, node2, node3 ] );
 
 			expect( node1.index ).to.equal( 0 );
 			expect( node2.index ).to.equal( 1 );
@@ -274,10 +282,10 @@ describe( 'DocumentFragment', () => {
 		} );
 
 		it( 'nextSibling should return proper node', () => {
-			const node1 = new Node();
-			const node2 = new Node();
-			const node3 = new Node();
-			new DocumentFragment( [ node1, node2, node3 ] ); // eslint-disable-line no-new
+			const node1 = new Node( document );
+			const node2 = new Node( document );
+			const node3 = new Node( document );
+			new DocumentFragment( document, [ node1, node2, node3 ] ); // eslint-disable-line no-new
 
 			expect( node1.nextSibling ).to.equal( node2 );
 			expect( node2.nextSibling ).to.equal( node3 );
@@ -285,10 +293,10 @@ describe( 'DocumentFragment', () => {
 		} );
 
 		it( 'previousSibling should return proper node', () => {
-			const node1 = new Node();
-			const node2 = new Node();
-			const node3 = new Node();
-			new DocumentFragment( [ node1, node2, node3 ] ); // eslint-disable-line no-new
+			const node1 = new Node( document );
+			const node2 = new Node( document );
+			const node3 = new Node( document );
+			new DocumentFragment( document, [ node1, node2, node3 ] ); // eslint-disable-line no-new
 
 			expect( node1.previousSibling ).to.be.null;
 			expect( node2.previousSibling ).to.equal( node1 );
@@ -296,10 +304,10 @@ describe( 'DocumentFragment', () => {
 		} );
 
 		it( '_remove() should remove node from fragment', () => {
-			const node1 = new Node();
-			const node2 = new Node();
-			const node3 = new Node();
-			const fragment = new DocumentFragment( [ node1, node2, node3 ] );
+			const node1 = new Node( document );
+			const node2 = new Node( document );
+			const node3 = new Node( document );
+			const fragment = new DocumentFragment( document, [ node1, node2, node3 ] );
 
 			node1._remove();
 			node3._remove();

+ 18 - 16
packages/ckeditor5-engine/tests/view/documentselection.js

@@ -15,15 +15,17 @@ import createViewRoot from './_utils/createroot';
 import { parse } from '../../src/dev-utils/view';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'DocumentSelection', () => {
-	let documentSelection, el, range1, range2, range3;
+	let documentSelection, el, range1, range2, range3, document;
 
 	testUtils.createSinonSandbox();
 
 	beforeEach( () => {
-		const text = new Text( 'xxxxxxxxxxxxxxxxxxxx' );
-		el = new Element( 'p', null, text );
+		document = new Document( new StylesProcessor() );
+		const text = new Text( document, 'xxxxxxxxxxxxxxxxxxxx' );
+		el = new Element( document, 'p', null, text );
 
 		documentSelection = new DocumentSelection();
 
@@ -845,8 +847,8 @@ describe( 'DocumentSelection', () => {
 			} );
 
 			it( 'should collapse selection at node and offset', () => {
-				const foo = new Text( 'foo' );
-				const p = new Element( 'p', null, foo );
+				const foo = new Text( document, 'foo' );
+				const p = new Element( document, 'p', null, foo );
 
 				documentSelection._setTo( foo, 0 );
 				let range = documentSelection.getFirstRange();
@@ -864,7 +866,7 @@ describe( 'DocumentSelection', () => {
 			} );
 
 			it( 'should throw an error when the second parameter is not passed and first is an item', () => {
-				const foo = new Text( 'foo' );
+				const foo = new Text( document, 'foo' );
 
 				expectToThrowCKEditorError( () => {
 					documentSelection._setTo( foo );
@@ -872,8 +874,8 @@ describe( 'DocumentSelection', () => {
 			} );
 
 			it( 'should collapse selection at node and flag', () => {
-				const foo = new Text( 'foo' );
-				const p = new Element( 'p', null, foo );
+				const foo = new Text( document, 'foo' );
+				const p = new Element( document, 'p', null, foo );
 
 				documentSelection._setTo( foo, 'end' );
 				let range = documentSelection.getFirstRange();
@@ -1029,10 +1031,10 @@ describe( 'DocumentSelection', () => {
 		} );
 
 		it( 'should allow setting selection on an item', () => {
-			const textNode1 = new Text( 'foo' );
-			const textNode2 = new Text( 'bar' );
-			const textNode3 = new Text( 'baz' );
-			const element = new Element( 'p', null, [ textNode1, textNode2, textNode3 ] );
+			const textNode1 = new Text( document, 'foo' );
+			const textNode2 = new Text( document, 'bar' );
+			const textNode3 = new Text( document, 'baz' );
+			const element = new Element( document, 'p', null, [ textNode1, textNode2, textNode3 ] );
 
 			documentSelection._setTo( textNode2, 'on' );
 
@@ -1045,7 +1047,7 @@ describe( 'DocumentSelection', () => {
 		} );
 
 		it( 'should allow setting selection inside an element', () => {
-			const element = new Element( 'p', null, [ new Text( 'foo' ), new Text( 'bar' ) ] );
+			const element = new Element( document, 'p', null, [ new Text( document, 'foo' ), new Text( document, 'bar' ) ] );
 
 			documentSelection._setTo( element, 'in' );
 
@@ -1058,7 +1060,7 @@ describe( 'DocumentSelection', () => {
 		} );
 
 		it( 'should allow setting backward selection inside an element', () => {
-			const element = new Element( 'p', null, [ new Text( 'foo' ), new Text( 'bar' ) ] );
+			const element = new Element( document, 'p', null, [ new Text( document, 'foo' ), new Text( document, 'bar' ) ] );
 
 			documentSelection._setTo( element, 'in', { backward: true } );
 
@@ -1084,10 +1086,10 @@ describe( 'DocumentSelection', () => {
 		} );
 
 		it( 'should return EditableElement when selection is placed inside', () => {
-			const viewDocument = new Document();
+			const viewDocument = new Document( new StylesProcessor() );
 			documentSelection._setTo( viewDocument.selection );
 			const root = createViewRoot( viewDocument, 'div', 'main' );
-			const element = new Element( 'p' );
+			const element = new Element( document, 'p' );
 			root._appendChild( element );
 
 			documentSelection._setTo( Range._createFromParentsAndOffsets( element, 0, element, 0 ) );

+ 19 - 16
packages/ckeditor5-engine/tests/view/domconverter/binding.js

@@ -9,23 +9,26 @@ import ViewElement from '../../../src/view/element';
 import ViewDocumentSelection from '../../../src/view/documentselection';
 import DomConverter from '../../../src/view/domconverter';
 import ViewDocumentFragment from '../../../src/view/documentfragment';
+import ViewDocument from '../../../src/view/document';
 import { INLINE_FILLER } from '../../../src/view/filler';
 
 import { parse } from '../../../src/dev-utils/view';
 
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DomConverter', () => {
-	let converter;
+	let converter, viewDocument;
 
-	before( () => {
-		converter = new DomConverter();
+	beforeEach( () => {
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		converter = new DomConverter( viewDocument );
 	} );
 
 	describe( 'bindElements()', () => {
 		it( 'should bind elements', () => {
 			const domElement = document.createElement( 'p' );
-			const viewElement = new ViewElement( 'p' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
 
 			converter.bindElements( domElement, viewElement );
 
@@ -37,7 +40,7 @@ describe( 'DomConverter', () => {
 	describe( 'bindDocumentFragments()', () => {
 		it( 'should bind document fragments', () => {
 			const domFragment = document.createDocumentFragment();
-			const viewFragment = new ViewDocumentFragment();
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			converter.bindDocumentFragments( domFragment, viewFragment );
 
@@ -49,7 +52,7 @@ describe( 'DomConverter', () => {
 	describe( 'mapDomToView()', () => {
 		it( 'should return corresponding view element if element is passed', () => {
 			const domElement = document.createElement( 'p' );
-			const viewElement = new ViewElement( 'p' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
 
 			converter.bindElements( domElement, viewElement );
 
@@ -77,7 +80,7 @@ describe( 'DomConverter', () => {
 			const domText = document.createTextNode( 'foo' );
 			const domP = createElement( document, 'p', null, [ domImg, domText ] );
 
-			const viewImg = new ViewElement( 'img' );
+			const viewImg = new ViewElement( viewDocument, 'img' );
 
 			converter.bindElements( domImg, viewImg );
 
@@ -173,7 +176,7 @@ describe( 'DomConverter', () => {
 	describe( 'mapViewToDom()', () => {
 		it( 'should return corresponding DOM element if element was passed', () => {
 			const domElement = document.createElement( 'p' );
-			const viewElement = new ViewElement( 'p' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
 
 			converter.bindElements( domElement, viewElement );
 
@@ -182,7 +185,7 @@ describe( 'DomConverter', () => {
 
 		it( 'should return corresponding DOM document fragment', () => {
 			const domFragment = document.createDocumentFragment();
-			const viewFragment = new ViewDocumentFragment();
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			converter.bindElements( domFragment, viewFragment );
 
@@ -203,7 +206,7 @@ describe( 'DomConverter', () => {
 			domP.appendChild( domImg );
 			domP.appendChild( domText );
 
-			const viewImg = new ViewElement( 'img' );
+			const viewImg = new ViewElement( viewDocument, 'img' );
 
 			converter.bindElements( domImg, viewImg );
 
@@ -267,7 +270,7 @@ describe( 'DomConverter', () => {
 		let domEl, selection, viewElement;
 
 		beforeEach( () => {
-			viewElement = new ViewElement();
+			viewElement = new ViewElement( viewDocument );
 			domEl = document.createElement( 'div' );
 			selection = new ViewDocumentSelection( viewElement, 'in' );
 			converter.bindFakeSelection( domEl, selection );
@@ -282,7 +285,7 @@ describe( 'DomConverter', () => {
 		it( 'should keep a copy of selection', () => {
 			const selectionCopy = new ViewDocumentSelection( selection );
 
-			selection._setTo( new ViewElement(), 'in', { backward: true } );
+			selection._setTo( new ViewElement( viewDocument ), 'in', { backward: true } );
 			const bindSelection = converter.fakeSelectionToView( domEl );
 
 			expect( bindSelection ).to.not.equal( selection );
@@ -294,7 +297,7 @@ describe( 'DomConverter', () => {
 	describe( 'unbindDomElement', () => {
 		it( 'should unbind elements', () => {
 			const domElement = document.createElement( 'p' );
-			const viewElement = new ViewElement( 'p' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
 
 			converter.bindElements( domElement, viewElement );
 
@@ -312,8 +315,8 @@ describe( 'DomConverter', () => {
 			const domChild = document.createElement( 'span' );
 			domElement.appendChild( domChild );
 
-			const viewElement = new ViewElement( 'p' );
-			const viewChild = new ViewElement( 'span' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
+			const viewChild = new ViewElement( viewDocument, 'span' );
 
 			converter.bindElements( domElement, viewElement );
 			converter.bindElements( domChild, viewChild );
@@ -329,7 +332,7 @@ describe( 'DomConverter', () => {
 
 		it( 'should do nothing if there are no elements bind', () => {
 			const domElement = document.createElement( 'p' );
-			const viewElement = new ViewElement( 'p' );
+			const viewElement = new ViewElement( viewDocument, 'p' );
 
 			expect( converter.mapDomToView( domElement ) ).to.be.undefined;
 			expect( converter.mapViewToDom( viewElement ) ).to.be.undefined;

+ 11 - 9
packages/ckeditor5-engine/tests/view/domconverter/dom-to-view.js

@@ -6,21 +6,23 @@
 /* globals document */
 
 import ViewElement from '../../../src/view/element';
+import ViewDocument from '../../../src/view/document';
 import ViewDocumentSelection from '../../../src/view/documentselection';
 import DomConverter from '../../../src/view/domconverter';
 import ViewDocumentFragment from '../../../src/view/documentfragment';
 import { BR_FILLER, INLINE_FILLER, INLINE_FILLER_LENGTH, NBSP_FILLER } from '../../../src/view/filler';
-
+import { StylesProcessor } from '../../../src/view/stylesmap';
 import { parse, stringify } from '../../../src/dev-utils/view';
 
 import count from '@ckeditor/ckeditor5-utils/src/count';
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
 
 describe( 'DomConverter', () => {
-	let converter;
+	let converter, viewDocument;
 
 	before( () => {
-		converter = new DomConverter();
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		converter = new DomConverter( viewDocument );
 	} );
 
 	describe( 'domToView()', () => {
@@ -29,7 +31,7 @@ describe( 'DomConverter', () => {
 			const domText = document.createTextNode( 'foo' );
 			const domP = createElement( document, 'p', { 'class': 'foo' }, [ domImg, domText ] );
 
-			const viewImg = new ViewElement( 'img' );
+			const viewImg = new ViewElement( viewDocument, 'img' );
 
 			converter.bindElements( domImg, viewImg );
 
@@ -90,7 +92,7 @@ describe( 'DomConverter', () => {
 			const domText = document.createTextNode( 'foo' );
 			const domP = createElement( document, 'p', { 'class': 'foo' }, [ domImg, domText ] );
 
-			const viewImg = new ViewElement( 'img' );
+			const viewImg = new ViewElement( viewDocument, 'img' );
 
 			converter.bindElements( domImg, viewImg );
 
@@ -133,7 +135,7 @@ describe( 'DomConverter', () => {
 			domFragment.appendChild( domImg );
 			domFragment.appendChild( domText );
 
-			const viewImg = new ViewElement( 'img' );
+			const viewImg = new ViewElement( viewDocument, 'img' );
 
 			converter.bindElements( domImg, viewImg );
 
@@ -763,7 +765,7 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should converter position inside block filler', () => {
-			const converter = new DomConverter( { blockFillerMode: 'nbsp' } );
+			const converter = new DomConverter( viewDocument, { blockFillerMode: 'nbsp' } );
 			const domFiller = NBSP_FILLER( document ); // eslint-disable-line new-cap
 			const domP = createElement( document, 'p', null, domFiller );
 
@@ -1001,7 +1003,7 @@ describe( 'DomConverter', () => {
 			domContainer.innerHTML = 'fake selection container';
 			document.body.appendChild( domContainer );
 
-			const viewSelection = new ViewDocumentSelection( new ViewElement(), 'in' );
+			const viewSelection = new ViewDocumentSelection( new ViewElement( viewDocument ), 'in' );
 			converter.bindFakeSelection( domContainer, viewSelection );
 
 			const domRange = document.createRange();
@@ -1022,7 +1024,7 @@ describe( 'DomConverter', () => {
 			domContainer.innerHTML = 'fake selection container';
 			document.body.appendChild( domContainer );
 
-			const viewSelection = new ViewDocumentSelection( new ViewElement(), 'in' );
+			const viewSelection = new ViewDocumentSelection( new ViewElement( viewDocument ), 'in' );
 			converter.bindFakeSelection( domContainer, viewSelection );
 
 			const domRange = document.createRange();

+ 15 - 12
packages/ckeditor5-engine/tests/view/domconverter/domconverter.js

@@ -13,14 +13,16 @@ import ViewContainerElement from '../../../src/view/containerelement';
 import { BR_FILLER, INLINE_FILLER, INLINE_FILLER_LENGTH, NBSP_FILLER } from '../../../src/view/filler';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DomConverter', () => {
-	let converter;
+	let converter, viewDocument;
 
 	testUtils.createSinonSandbox();
 
 	beforeEach( () => {
-		converter = new DomConverter();
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		converter = new DomConverter( viewDocument );
 	} );
 
 	describe( 'constructor()', () => {
@@ -29,7 +31,7 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create converter with defined block mode filler', () => {
-			converter = new DomConverter( { blockFillerMode: 'nbsp' } );
+			converter = new DomConverter( viewDocument, { blockFillerMode: 'nbsp' } );
 			expect( converter.blockFillerMode ).to.equal( 'nbsp' );
 		} );
 	} );
@@ -38,9 +40,8 @@ describe( 'DomConverter', () => {
 		let viewEditable, domEditable, domEditableParent, viewDocument;
 
 		beforeEach( () => {
-			viewDocument = new ViewDocument();
-			viewEditable = new ViewEditable( 'div' );
-			viewEditable._document = viewDocument;
+			viewDocument = new ViewDocument( new StylesProcessor() );
+			viewEditable = new ViewEditable( viewDocument, 'div' );
 
 			domEditable = document.createElement( 'div' );
 			domEditableParent = document.createElement( 'div' );
@@ -192,7 +193,7 @@ describe( 'DomConverter', () => {
 			return sel;
 		}
 
-		let domP, domFillerTextNode, domUiSpan, domUiDeepSpan;
+		let domP, domFillerTextNode, domUiSpan, domUiDeepSpan, viewDocument;
 
 		beforeEach( () => {
 			// <p>INLINE_FILLERfoo<span></span></p>.
@@ -203,8 +204,10 @@ describe( 'DomConverter', () => {
 			domUiDeepSpan = document.createElement( 'span' );
 			domUiSpan.appendChild( domUiDeepSpan );
 
-			const viewUiSpan = new ViewUIElement( 'span' );
-			const viewElementSpan = new ViewContainerElement( 'span' );
+			viewDocument = new ViewDocument( new StylesProcessor() );
+
+			const viewUiSpan = new ViewUIElement( viewDocument, 'span' );
+			const viewElementSpan = new ViewContainerElement( viewDocument, 'span' );
 
 			domP.appendChild( domFillerTextNode );
 			domP.appendChild( domUiSpan );
@@ -294,7 +297,7 @@ describe( 'DomConverter', () => {
 	describe( 'isBlockFiller()', () => {
 		describe( 'mode "nbsp"', () => {
 			beforeEach( () => {
-				converter = new DomConverter( { blockFillerMode: 'nbsp' } );
+				converter = new DomConverter( viewDocument, { blockFillerMode: 'nbsp' } );
 			} );
 
 			it( 'should return true if the node is an nbsp filler and is a single child of a block level element', () => {
@@ -377,7 +380,7 @@ describe( 'DomConverter', () => {
 
 		describe( 'mode "br"', () => {
 			beforeEach( () => {
-				converter = new DomConverter( { blockFillerMode: 'br' } );
+				converter = new DomConverter( viewDocument, { blockFillerMode: 'br' } );
 			} );
 
 			it( 'should return true if the node is an instance of the BR block filler', () => {
@@ -389,7 +392,7 @@ describe( 'DomConverter', () => {
 			} );
 
 			it( 'should return false if the node is an instance of the NBSP block filler', () => {
-				converter = new DomConverter( { blockFillerMode: 'br' } );
+				converter = new DomConverter( viewDocument, { blockFillerMode: 'br' } );
 				const nbspFillerInstance = NBSP_FILLER( document ); // eslint-disable-line new-cap
 				// NBSP must be check inside a context.
 				const context = document.createElement( 'div' );

+ 9 - 6
packages/ckeditor5-engine/tests/view/domconverter/uielement.js

@@ -8,12 +8,14 @@
 import ViewUIElement from '../../../src/view/uielement';
 import ViewContainer from '../../../src/view/containerelement';
 import DomConverter from '../../../src/view/domconverter';
+import ViewDocument from '../../../src/view/document';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DOMConverter UIElement integration', () => {
-	let converter;
+	let converter, viewDocument;
 
 	function createUIElement( name ) {
-		const element = new ViewUIElement( name );
+		const element = new ViewUIElement( viewDocument, name );
 
 		element.render = function( domDocument ) {
 			const root = this.toDomElement( domDocument );
@@ -26,12 +28,13 @@ describe( 'DOMConverter UIElement integration', () => {
 	}
 
 	beforeEach( () => {
-		converter = new DomConverter();
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		converter = new DomConverter( viewDocument );
 	} );
 
 	describe( 'viewToDom()', () => {
 		it( 'should create DOM element from UIElement', () => {
-			const uiElement = new ViewUIElement( 'div' );
+			const uiElement = new ViewUIElement( viewDocument, 'div' );
 			const domElement = converter.viewToDom( uiElement, document );
 
 			expect( domElement ).to.be.instanceOf( HTMLElement );
@@ -81,7 +84,7 @@ describe( 'DOMConverter UIElement integration', () => {
 	describe( 'domPositionToView()', () => {
 		it( 'should convert position inside UIElement to position before it', () => {
 			const uiElement = createUIElement( 'h1' );
-			const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
+			const container = new ViewContainer( viewDocument, 'div', null, [ new ViewContainer( viewDocument, 'div' ), uiElement ] );
 			const domContainer = converter.viewToDom( container, document, { bind: true } );
 
 			const viewPosition = converter.domPositionToView( domContainer.childNodes[ 1 ], 0 );
@@ -92,7 +95,7 @@ describe( 'DOMConverter UIElement integration', () => {
 
 		it( 'should convert position inside UIElement children to position before UIElement', () => {
 			const uiElement = createUIElement( 'h1' );
-			const container = new ViewContainer( 'div', null, [ new ViewContainer( 'div' ), uiElement ] );
+			const container = new ViewContainer( viewDocument, 'div', null, [ new ViewContainer( viewDocument, 'div' ), uiElement ] );
 			const domContainer = converter.viewToDom( container, document, { bind: true } );
 
 			const viewPosition = converter.domPositionToView( domContainer.childNodes[ 1 ].childNodes[ 0 ], 1 );

+ 131 - 125
packages/ckeditor5-engine/tests/view/domconverter/view-to-dom.js

@@ -13,24 +13,27 @@ import ViewAttributeElement from '../../../src/view/attributeelement';
 import ViewEmptyElement from '../../../src/view/emptyelement';
 import DomConverter from '../../../src/view/domconverter';
 import ViewDocumentFragment from '../../../src/view/documentfragment';
+import ViewDocument from '../../../src/view/document';
 import { INLINE_FILLER, INLINE_FILLER_LENGTH } from '../../../src/view/filler';
 
 import { parse } from '../../../src/dev-utils/view';
 
 import createElement from '@ckeditor/ckeditor5-utils/src/dom/createelement';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DomConverter', () => {
-	let converter;
+	let converter, viewDocument;
 
-	before( () => {
-		converter = new DomConverter();
+	beforeEach( () => {
+		viewDocument = new ViewDocument( new StylesProcessor() );
+		converter = new DomConverter( viewDocument );
 	} );
 
 	describe( 'viewToDom()', () => {
 		it( 'should create tree of DOM elements from view elements', () => {
-			const viewImg = new ViewElement( 'img' );
-			const viewText = new ViewText( 'foo' );
-			const viewP = new ViewElement( 'p', { class: 'foo' } );
+			const viewImg = new ViewElement( viewDocument, 'img' );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewP = new ViewElement( viewDocument, 'p', { class: 'foo' } );
 
 			viewP._appendChild( viewImg );
 			viewP._appendChild( viewText );
@@ -56,9 +59,9 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create tree of DOM elements from view elements and bind elements', () => {
-			const viewImg = new ViewElement( 'img' );
-			const viewText = new ViewText( 'foo' );
-			const viewP = new ViewElement( 'p', { class: 'foo' } );
+			const viewImg = new ViewElement( viewDocument, 'img' );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewP = new ViewElement( viewDocument, 'p', { class: 'foo' } );
 
 			viewP._appendChild( viewImg );
 			viewP._appendChild( viewText );
@@ -80,8 +83,8 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should support unicode', () => {
-			const viewText = new ViewText( 'நிலைக்கு' );
-			const viewP = new ViewElement( 'p', null, viewText );
+			const viewText = new ViewText( viewDocument, 'நிலைக்கு' );
+			const viewP = new ViewElement( viewDocument, 'p', null, viewText );
 
 			const domP = converter.viewToDom( viewP, document, { bind: true } );
 
@@ -93,9 +96,9 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create tree of DOM elements from view element without children', () => {
-			const viewImg = new ViewElement( 'img' );
-			const viewText = new ViewText( 'foo' );
-			const viewP = new ViewElement( 'p', { class: 'foo' } );
+			const viewImg = new ViewElement( viewDocument, 'img' );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewP = new ViewElement( viewDocument, 'p', { class: 'foo' } );
 
 			viewP._appendChild( viewImg );
 			viewP._appendChild( viewText );
@@ -117,9 +120,9 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create DOM document fragment from view document fragment and bind elements', () => {
-			const viewImg = new ViewElement( 'img' );
-			const viewText = new ViewText( 'foo' );
-			const viewFragment = new ViewDocumentFragment();
+			const viewImg = new ViewElement( viewDocument, 'img' );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			viewFragment._appendChild( viewImg );
 			viewFragment._appendChild( viewText );
@@ -136,9 +139,9 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create DOM document fragment from view document without children', () => {
-			const viewImg = new ViewElement( 'img' );
-			const viewText = new ViewText( 'foo' );
-			const viewFragment = new ViewDocumentFragment();
+			const viewImg = new ViewElement( viewDocument, 'img' );
+			const viewText = new ViewText( viewDocument, 'foo' );
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			viewFragment._appendChild( viewImg );
 			viewFragment._appendChild( viewText );
@@ -157,7 +160,7 @@ describe( 'DomConverter', () => {
 
 		it( 'should return already bind document fragment', () => {
 			const domFragment = document.createDocumentFragment();
-			const viewFragment = new ViewDocumentFragment();
+			const viewFragment = new ViewDocumentFragment( viewDocument );
 
 			converter.bindDocumentFragments( domFragment, viewFragment );
 
@@ -167,7 +170,7 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should create DOM text node from view text node', () => {
-			const viewTextNode = new ViewText( 'foo' );
+			const viewTextNode = new ViewText( viewDocument, 'foo' );
 			const domTextNode = converter.viewToDom( viewTextNode, document );
 
 			expect( domTextNode ).to.be.instanceof( Text );
@@ -176,7 +179,7 @@ describe( 'DomConverter', () => {
 
 		it( 'should create namespaced elements', () => {
 			const namespace = 'http://www.w3.org/2000/svg';
-			const viewSvg = new ViewElement( 'svg', { xmlns: namespace } );
+			const viewSvg = new ViewElement( viewDocument, 'svg', { xmlns: namespace } );
 
 			const domSvg = converter.viewToDom( viewSvg, document );
 
@@ -185,10 +188,10 @@ describe( 'DomConverter', () => {
 
 		describe( 'it should convert spaces to &nbsp;', () => {
 			it( 'at the beginning of each container element', () => {
-				const viewDiv = new ViewContainerElement( 'div', null, [
-					new ViewContainerElement( 'p', null, new ViewText( ' foo' ) ),
-					new ViewContainerElement( 'p', null, new ViewText( 'bar' ) ),
-					new ViewContainerElement( 'p', null, new ViewText( ' xxx' ) )
+				const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, ' foo' ) ),
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'bar' ) ),
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, ' xxx' ) )
 				] );
 
 				const domDiv = converter.viewToDom( viewDiv, document );
@@ -197,10 +200,10 @@ describe( 'DomConverter', () => {
 			} );
 
 			it( 'at the end of each container element', () => {
-				const viewDiv = new ViewContainerElement( 'div', null, [
-					new ViewContainerElement( 'p', null, new ViewText( 'foo ' ) ),
-					new ViewContainerElement( 'p', null, new ViewText( 'bar' ) ),
-					new ViewContainerElement( 'p', null, new ViewText( 'xxx ' ) )
+				const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'foo ' ) ),
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'bar' ) ),
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, 'xxx ' ) )
 				] );
 
 				const domDiv = converter.viewToDom( viewDiv, document );
@@ -209,12 +212,12 @@ describe( 'DomConverter', () => {
 			} );
 
 			it( 'when there are multiple spaces next to each other or between attribute elements', () => {
-				const viewDiv = new ViewContainerElement( 'div', null, [
-					new ViewText( 'x  x   x x ' ),
-					new ViewAttributeElement( 'b', null, new ViewText( ' x ' ) ),
-					new ViewAttributeElement( 'i', null,
-						new ViewAttributeElement( 'b', null,
-							new ViewAttributeElement( 'u', null, new ViewText( ' x' ) )
+				const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+					new ViewText( viewDocument, 'x  x   x x ' ),
+					new ViewAttributeElement( viewDocument, 'b', null, new ViewText( viewDocument, ' x ' ) ),
+					new ViewAttributeElement( viewDocument, 'i', null,
+						new ViewAttributeElement( viewDocument, 'b', null,
+							new ViewAttributeElement( viewDocument, 'u', null, new ViewText( viewDocument, ' x' ) )
 						)
 					)
 				] );
@@ -225,17 +228,17 @@ describe( 'DomConverter', () => {
 			} );
 
 			it( 'all together', () => {
-				const viewDiv = new ViewContainerElement( 'div', null, [
-					new ViewContainerElement( 'p', null, [
-						new ViewText( ' x  x   x x ' ),
-						new ViewAttributeElement( 'b', null, new ViewText( ' x ' ) ),
-						new ViewAttributeElement( 'i', null,
-							new ViewAttributeElement( 'b', null,
-								new ViewAttributeElement( 'u', null, new ViewText( ' x ' ) )
+				const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+					new ViewContainerElement( viewDocument, 'p', null, [
+						new ViewText( viewDocument, ' x  x   x x ' ),
+						new ViewAttributeElement( viewDocument, 'b', null, new ViewText( viewDocument, ' x ' ) ),
+						new ViewAttributeElement( viewDocument, 'i', null,
+							new ViewAttributeElement( viewDocument, 'b', null,
+								new ViewAttributeElement( viewDocument, 'u', null, new ViewText( viewDocument, ' x ' ) )
 							)
 						)
 					] ),
-					new ViewContainerElement( 'p', null, new ViewText( '  x  ' ) )
+					new ViewContainerElement( viewDocument, 'p', null, new ViewText( viewDocument, '  x  ' ) )
 				] );
 
 				const domDiv = converter.viewToDom( viewDiv, document );
@@ -251,10 +254,10 @@ describe( 'DomConverter', () => {
 				}
 
 				it( 'spaces in a text node: ' + inputTexts.join( '|' ) + ' -> ' + output, () => {
-					const viewElement = new ViewContainerElement( 'p' );
+					const viewElement = new ViewContainerElement( viewDocument, 'p' );
 
 					for ( const text of inputTexts ) {
-						viewElement._appendChild( new ViewText( text.replace( /_/g, '\u00A0' ) ) );
+						viewElement._appendChild( new ViewText( viewDocument, text.replace( /_/g, '\u00A0' ) ) );
 					}
 
 					const domElement = converter.viewToDom( viewElement, document );
@@ -415,15 +418,18 @@ describe( 'DomConverter', () => {
 			testConvert( [ '   ', '   ' ], '_ _ __' );
 
 			it( 'not in preformatted blocks', () => {
-				const viewPre = new ViewContainerElement( 'pre', null, [ new ViewText( '   foo   ' ), new ViewText( ' bar ' ) ] );
+				const viewPre = new ViewContainerElement( viewDocument, 'pre', null, [
+					new ViewText( viewDocument, '   foo   ' ),
+					new ViewText( viewDocument, ' bar ' )
+				] );
 				const domPre = converter.viewToDom( viewPre, document );
 
 				expect( domPre.innerHTML ).to.equal( '   foo    bar ' );
 			} );
 
 			it( 'not in a preformatted block followed by a text', () => {
-				const viewPre = new ViewAttributeElement( 'pre', null, new ViewText( 'foo   ' ) );
-				const viewDiv = new ViewContainerElement( 'div', null, [ viewPre, new ViewText( ' bar' ) ] );
+				const viewPre = new ViewAttributeElement( viewDocument, 'pre', null, new ViewText( viewDocument, 'foo   ' ) );
+				const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [ viewPre, new ViewText( viewDocument, ' bar' ) ] );
 				const domDiv = converter.viewToDom( viewDiv, document );
 
 				expect( domDiv.innerHTML ).to.equal( '<pre>foo   </pre> bar' );
@@ -431,10 +437,10 @@ describe( 'DomConverter', () => {
 
 			describe( 'around <br>s', () => {
 				it( 'before <br> – a single space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -442,10 +448,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'before <br> – two spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo  ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo  ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -453,10 +459,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'before <br> – three spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo   ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo   ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -464,10 +470,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'before <br> – only a space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( ' ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, ' ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -475,10 +481,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'before <br> – only two spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( '  ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, '  ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -486,10 +492,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'before <br> – only three spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( '   ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, '   ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -497,10 +503,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – a single space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( ' bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, ' bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -508,10 +514,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – two spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '  bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '  bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -519,10 +525,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – three spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '   bar' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '   bar' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -530,10 +536,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – only a space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( ' ' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, ' ' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -541,10 +547,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – only two spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '  ' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '  ' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -552,10 +558,10 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'after <br> – only three spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewText( 'foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '   ' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewText( viewDocument, 'foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '   ' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -563,11 +569,11 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'between <br>s – a single space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewEmptyElement( 'br' ),
-						new ViewText( ' ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, ' ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -575,11 +581,11 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'between <br>s – only two spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '  ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '  ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -587,11 +593,11 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'between <br>s – only three spaces', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewEmptyElement( 'br' ),
-						new ViewText( '   ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, '   ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -599,11 +605,11 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'between <br>s – space and text', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewEmptyElement( 'br' ),
-						new ViewText( ' foo' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, ' foo' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -611,11 +617,11 @@ describe( 'DomConverter', () => {
 				} );
 
 				it( 'between <br>s – text and space', () => {
-					const viewDiv = new ViewContainerElement( 'div', null, [
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo ' ),
-						new ViewEmptyElement( 'br' ),
-						new ViewText( 'foo' )
+					const viewDiv = new ViewContainerElement( viewDocument, 'div', null, [
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo ' ),
+						new ViewEmptyElement( viewDocument, 'br' ),
+						new ViewText( viewDocument, 'foo' )
 					] );
 					const domDiv = converter.viewToDom( viewDiv, document );
 
@@ -793,7 +799,7 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should return null if view position is in a view text node that has not been rendered to DOM', () => {
-			const viewText = new ViewText( 'foo' );
+			const viewText = new ViewText( viewDocument, 'foo' );
 			const viewPosition = new ViewPosition( viewText, 1 );
 			const domPosition = converter.viewPositionToDom( viewPosition );
 
@@ -801,7 +807,7 @@ describe( 'DomConverter', () => {
 		} );
 
 		it( 'should return null if view position is in a view element that has not been rendered to DOM', () => {
-			const viewElement = new ViewContainerElement( 'div' );
+			const viewElement = new ViewContainerElement( viewDocument, 'div' );
 			const viewPosition = new ViewPosition( viewElement, 0 );
 			const domPosition = converter.viewPositionToDom( viewPosition );
 

+ 15 - 14
packages/ckeditor5-engine/tests/view/downcastwriter/breakattributes.js

@@ -14,13 +14,14 @@ import Range from '../../../src/view/range';
 import Position from '../../../src/view/position';
 
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'breakAttributes()', () => {
 		let writer, document;
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
@@ -152,8 +153,8 @@ describe( 'DowncastWriter', () => {
 			}
 
 			it( 'should throw when range placed in two containers', () => {
-				const p1 = new ContainerElement( 'p' );
-				const p2 = new ContainerElement( 'p' );
+				const p1 = new ContainerElement( document, 'p' );
+				const p2 = new ContainerElement( document, 'p' );
 
 				expectToThrowCKEditorError( () => {
 					writer.breakAttributes( Range._createFromParentsAndOffsets( p1, 0, p2, 0 ) );
@@ -161,7 +162,7 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw when range has no parent container', () => {
-				const el = new AttributeElement( 'b' );
+				const el = new AttributeElement( document, 'b' );
 
 				expectToThrowCKEditorError( () => {
 					writer.breakAttributes( Range._createFromParentsAndOffsets( el, 0, el, 0 ) );
@@ -241,8 +242,8 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if breaking inside EmptyElement #1', () => {
-				const img = new EmptyElement( 'img' );
-				new ContainerElement( 'p', null, img ); // eslint-disable-line no-new
+				const img = new EmptyElement( document, 'img' );
+				new ContainerElement( document, 'p', null, img ); // eslint-disable-line no-new
 				const position = new Position( img, 0 );
 
 				expectToThrowCKEditorError( () => {
@@ -251,9 +252,9 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if breaking inside EmptyElement #2', () => {
-				const img = new EmptyElement( 'img' );
-				const b = new AttributeElement( 'b' );
-				new ContainerElement( 'p', null, [ img, b ] ); // eslint-disable-line no-new
+				const img = new EmptyElement( document, 'img' );
+				const b = new AttributeElement( document, 'b' );
+				new ContainerElement( document, 'p', null, [ img, b ] ); // eslint-disable-line no-new
 				const range = Range._createFromParentsAndOffsets( img, 0, b, 0 );
 
 				expectToThrowCKEditorError( () => {
@@ -262,8 +263,8 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if breaking inside UIElement #1', () => {
-				const span = new UIElement( 'span' );
-				new ContainerElement( 'p', null, span ); // eslint-disable-line no-new
+				const span = new UIElement( document, 'span' );
+				new ContainerElement( document, 'p', null, span ); // eslint-disable-line no-new
 				const position = new Position( span, 0 );
 
 				expectToThrowCKEditorError( () => {
@@ -272,9 +273,9 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if breaking inside UIElement #2', () => {
-				const span = new UIElement( 'span' );
-				const b = new AttributeElement( 'b' );
-				new ContainerElement( 'p', null, [ span, b ] ); // eslint-disable-line no-new
+				const span = new UIElement( document, 'span' );
+				const b = new AttributeElement( document, 'b' );
+				new ContainerElement( document, 'p', null, [ span, b ] ); // eslint-disable-line no-new
 				const range = Range._createFromParentsAndOffsets( span, 0, b, 0 );
 
 				expectToThrowCKEditorError( () => {

+ 5 - 3
packages/ckeditor5-engine/tests/view/downcastwriter/breakcontainer.js

@@ -10,10 +10,11 @@ import ContainerElement from '../../../src/view/containerelement';
 import Position from '../../../src/view/position';
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'breakContainer()', () => {
-		let writer;
+		let writer, document;
 
 		// Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 		// test break position.
@@ -28,7 +29,8 @@ describe( 'DowncastWriter', () => {
 		}
 
 		before( () => {
-			writer = new DowncastWriter( new Document() );
+			document = new Document( new StylesProcessor() );
+			writer = new DowncastWriter( document );
 		} );
 
 		it( 'break inside element - should break container element at given position', () => {
@@ -73,7 +75,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if position parent is root', () => {
-			const element = new ContainerElement( 'div' );
+			const element = new ContainerElement( document, 'div' );
 			const position = Position._createAt( element, 0 );
 
 			expectToThrowCKEditorError( () => {

+ 17 - 16
packages/ckeditor5-engine/tests/view/downcastwriter/clear.js

@@ -13,6 +13,7 @@ import UIElement from '../../../src/view/uielement';
 
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'clear()', () => {
@@ -32,13 +33,13 @@ describe( 'DowncastWriter', () => {
 		}
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
 		it( 'should throw when range placed in two containers', () => {
-			const p1 = new ContainerElement( 'p' );
-			const p2 = new ContainerElement( 'p' );
+			const p1 = new ContainerElement( document, 'p' );
+			const p2 = new ContainerElement( document, 'p' );
 
 			expectToThrowCKEditorError( () => {
 				writer.clear( Range._createFromParentsAndOffsets( p1, 0, p2, 0 ) );
@@ -46,7 +47,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when range has no parent container', () => {
-			const el = new AttributeElement( 'b' );
+			const el = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.clear( Range._createFromParentsAndOffsets( el, 0, el, 0 ) );
@@ -54,7 +55,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove matched element from range', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -64,7 +65,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove matched element from range when range is inside text node', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -74,7 +75,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove multiple matched elements from range', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -84,7 +85,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove multiple matched elements from range when range is inside text node', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -94,7 +95,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove only matched element', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -104,7 +105,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove part of node when range ends inside this node', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -114,7 +115,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove part of node when range starts inside this node', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -124,7 +125,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove part of node when range starts and ends inside this node', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -134,7 +135,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should merge after removing', () => {
-			const elementToRemove = new AttributeElement( 'b' );
+			const elementToRemove = new AttributeElement( document, 'b' );
 
 			testDowncast(
 				elementToRemove,
@@ -146,7 +147,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove EmptyElement', () => {
-			const elementToRemove = new EmptyElement( 'img' );
+			const elementToRemove = new EmptyElement( document, 'img' );
 
 			testDowncast(
 				elementToRemove,
@@ -156,7 +157,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove UIElement', () => {
-			const elementToRemove = new UIElement( 'span' );
+			const elementToRemove = new UIElement( document, 'span' );
 
 			testDowncast(
 				elementToRemove,
@@ -166,7 +167,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should remove ContainerElement', () => {
-			const elementToRemove = new ContainerElement( 'p' );
+			const elementToRemove = new ContainerElement( document, 'p' );
 
 			testDowncast(
 				elementToRemove,

+ 16 - 14
packages/ckeditor5-engine/tests/view/downcastwriter/insert.js

@@ -14,6 +14,7 @@ import { stringify, parse } from '../../../src/dev-utils/view';
 import AttributeElement from '../../../src/view/attributeelement';
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'insert()', () => {
@@ -29,11 +30,12 @@ describe( 'DowncastWriter', () => {
 			const { view, selection } = parse( input );
 
 			const newRange = writer.insert( selection.getFirstPosition(), nodesToInsert );
+
 			expect( stringify( view.root, newRange, { showType: true, showPriority: true } ) ).to.equal( expected );
 		}
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
@@ -154,8 +156,8 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when inserting Element', () => {
-			const element = new Element( 'b' );
-			const container = new ContainerElement( 'p' );
+			const element = new Element( document, 'b' );
+			const container = new ContainerElement( document, 'p' );
 			const position = new Position( container, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -164,9 +166,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when Element is inserted as child node', () => {
-			const element = new Element( 'b' );
-			const root = new ContainerElement( 'p', null, element );
-			const container = new ContainerElement( 'p' );
+			const element = new Element( document, 'b' );
+			const root = new ContainerElement( document, 'p', null, element );
+			const container = new ContainerElement( document, 'p' );
 			const position = new Position( container, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -175,9 +177,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when position is not placed inside container', () => {
-			const element = new Element( 'b' );
+			const element = new Element( document, 'b' );
 			const position = new Position( element, 0 );
-			const attributeElement = new AttributeElement( 'i' );
+			const attributeElement = new AttributeElement( document, 'i' );
 
 			expectToThrowCKEditorError( () => {
 				writer.insert( position, attributeElement );
@@ -193,10 +195,10 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if trying to insert inside EmptyElement', () => {
-			const emptyElement = new EmptyElement( 'img' );
-			new ContainerElement( 'p', null, emptyElement ); // eslint-disable-line no-new
+			const emptyElement = new EmptyElement( document, 'img' );
+			new ContainerElement( document, 'p', null, emptyElement ); // eslint-disable-line no-new
 			const position = new Position( emptyElement, 0 );
-			const attributeElement = new AttributeElement( 'i' );
+			const attributeElement = new AttributeElement( document, 'i' );
 
 			expectToThrowCKEditorError( () => {
 				writer.insert( position, attributeElement );
@@ -204,10 +206,10 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if trying to insert inside UIElement', () => {
-			const uiElement = new UIElement( 'span' );
-			new ContainerElement( 'p', null, uiElement ); // eslint-disable-line no-new
+			const uiElement = new UIElement( document, 'span' );
+			new ContainerElement( document, 'p', null, uiElement ); // eslint-disable-line no-new
 			const position = new Position( uiElement, 0 );
-			const attributeElement = new AttributeElement( 'i' );
+			const attributeElement = new AttributeElement( document, 'i' );
 
 			expectToThrowCKEditorError( () => {
 				writer.insert( position, attributeElement );

+ 7 - 5
packages/ckeditor5-engine/tests/view/downcastwriter/mergeattributes.js

@@ -9,10 +9,11 @@ import Text from '../../../src/view/text';
 import Position from '../../../src/view/position';
 import { stringify, parse } from '../../../src/dev-utils/view';
 import Document from '../../../src/view/document';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'mergeAttributes', () => {
-		let writer;
+		let writer, document;
 
 		// Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 		// test merge position.
@@ -26,7 +27,8 @@ describe( 'DowncastWriter', () => {
 		}
 
 		before( () => {
-			writer = new DowncastWriter( new Document() );
+			document = new Document( new StylesProcessor() );
+			writer = new DowncastWriter( document );
 		} );
 
 		it( 'should not merge if inside text node', () => {
@@ -63,9 +65,9 @@ describe( 'DowncastWriter', () => {
 
 		it( 'should merge when placed between two text nodes', () => {
 			// <p>foobar</p> -> <p>foo|bar</p>
-			const t1 = new Text( 'foo' );
-			const t2 = new Text( 'bar' );
-			const p = new ContainerElement( 'p', null, [ t1, t2 ] );
+			const t1 = new Text( document, 'foo' );
+			const t2 = new Text( document, 'bar' );
+			const p = new ContainerElement( document, 'p', null, [ t1, t2 ] );
 			const position = new Position( p, 1 );
 
 			const newPosition = writer.mergeAttributes( position );

+ 2 - 1
packages/ckeditor5-engine/tests/view/downcastwriter/mergecontainers.js

@@ -8,6 +8,7 @@ import { stringify, parse } from '../../../src/dev-utils/view';
 
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'mergeContainers()', () => {
@@ -26,7 +27,7 @@ describe( 'DowncastWriter', () => {
 		}
 
 		before( () => {
-			writer = new DowncastWriter( new Document() );
+			writer = new DowncastWriter( new Document( new StylesProcessor() ) );
 		} );
 
 		it( 'should merge two container elements - position between elements', () => {

+ 17 - 15
packages/ckeditor5-engine/tests/view/downcastwriter/move.js

@@ -16,10 +16,11 @@ import Position from '../../../src/view/position';
 import Document from '../../../src/view/document';
 import Mapper from '../../../src/conversion/mapper';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'move()', () => {
-		let writer;
+		let writer, document;
 
 		// Executes test using `parse` and `stringify` utils functions. Uses range delimiters `[]{}` to create and
 		// test ranges.
@@ -38,7 +39,8 @@ describe( 'DowncastWriter', () => {
 		}
 
 		before( () => {
-			writer = new DowncastWriter( new Document() );
+			document = new Document( new StylesProcessor() );
+			writer = new DowncastWriter( document );
 		} );
 
 		it( 'should move single text node', () => {
@@ -148,12 +150,12 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if trying to move to EmptyElement', () => {
-			const srcAttribute = new AttributeElement( 'b' );
-			const srcContainer = new ContainerElement( 'p', null, srcAttribute );
+			const srcAttribute = new AttributeElement( document, 'b' );
+			const srcContainer = new ContainerElement( document, 'p', null, srcAttribute );
 			const srcRange = Range._createFromParentsAndOffsets( srcContainer, 0, srcContainer, 1 );
 
-			const dstEmpty = new EmptyElement( 'img' );
-			new ContainerElement( 'p', null, dstEmpty ); // eslint-disable-line no-new
+			const dstEmpty = new EmptyElement( document, 'img' );
+			new ContainerElement( document, 'p', null, dstEmpty ); // eslint-disable-line no-new
 			const dstPosition = new Position( dstEmpty, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -171,12 +173,12 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if trying to move to UIElement', () => {
-			const srcAttribute = new AttributeElement( 'b' );
-			const srcContainer = new ContainerElement( 'p', null, srcAttribute );
+			const srcAttribute = new AttributeElement( document, 'b' );
+			const srcContainer = new ContainerElement( document, 'p', null, srcAttribute );
 			const srcRange = Range._createFromParentsAndOffsets( srcContainer, 0, srcContainer, 1 );
 
-			const dstUI = new UIElement( 'span' );
-			new ContainerElement( 'p', null, dstUI ); // eslint-disable-line no-new
+			const dstUI = new UIElement( document, 'span' );
+			new ContainerElement( document, 'p', null, dstUI ); // eslint-disable-line no-new
 			const dstPosition = new Position( dstUI, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -187,16 +189,16 @@ describe( 'DowncastWriter', () => {
 		it( 'should not break marker mappings if marker element was split and the original element was removed', () => {
 			const mapper = new Mapper();
 
-			const srcContainer = new ContainerElement( 'p' );
-			const dstContainer = new ContainerElement( 'p' );
+			const srcContainer = new ContainerElement( document, 'p' );
+			const dstContainer = new ContainerElement( document, 'p' );
 
-			const root = new RootEditableElement( 'div' );
+			const root = new RootEditableElement( document, 'div' );
 			root._appendChild( [ srcContainer, dstContainer ] );
 
-			const attrElemA = new AttributeElement( 'span' );
+			const attrElemA = new AttributeElement( document, 'span' );
 			attrElemA._id = 'foo';
 
-			const attrElemB = new AttributeElement( 'span' );
+			const attrElemB = new AttributeElement( document, 'span' );
 			attrElemB._id = 'foo';
 
 			writer.insert( new Position( srcContainer, 0 ), [ attrElemA, attrElemB ] );

+ 13 - 12
packages/ckeditor5-engine/tests/view/downcastwriter/remove.js

@@ -14,6 +14,7 @@ import UIElement from '../../../src/view/uielement';
 
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'remove()', () => {
@@ -38,13 +39,13 @@ describe( 'DowncastWriter', () => {
 		}
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
 		it( 'should throw when range placed in two containers', () => {
-			const p1 = new ContainerElement( 'p' );
-			const p2 = new ContainerElement( 'p' );
+			const p1 = new ContainerElement( document, 'p' );
+			const p2 = new ContainerElement( document, 'p' );
 
 			expectToThrowCKEditorError( () => {
 				writer.remove( Range._createFromParentsAndOffsets( p1, 0, p2, 0 ) );
@@ -52,7 +53,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when range has no parent container', () => {
-			const el = new AttributeElement( 'b' );
+			const el = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.remove( Range._createFromParentsAndOffsets( el, 0, el, 0 ) );
@@ -60,7 +61,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should return empty DocumentFragment when range is collapsed', () => {
-			const p = new ContainerElement( 'p' );
+			const p = new ContainerElement( document, 'p' );
 			const range = Range._createFromParentsAndOffsets( p, 0, p, 0 );
 			const fragment = writer.remove( range );
 
@@ -130,9 +131,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if range is placed inside EmptyElement', () => {
-			const emptyElement = new EmptyElement( 'img' );
-			const attributeElement = new AttributeElement( 'b' );
-			new ContainerElement( 'p', null, [ emptyElement, attributeElement ] ); // eslint-disable-line no-new
+			const emptyElement = new EmptyElement( document, 'img' );
+			const attributeElement = new AttributeElement( document, 'b' );
+			new ContainerElement( document, 'p', null, [ emptyElement, attributeElement ] ); // eslint-disable-line no-new
 			const range = Range._createFromParentsAndOffsets( emptyElement, 0, attributeElement, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -149,9 +150,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if range is placed inside UIElement', () => {
-			const uiElement = new UIElement( 'span' );
-			const attributeElement = new AttributeElement( 'b' );
-			new ContainerElement( 'p', null, [ uiElement, attributeElement ] ); // eslint-disable-line no-new
+			const uiElement = new UIElement( document, 'span' );
+			const attributeElement = new AttributeElement( document, 'b' );
+			new ContainerElement( document, 'p', null, [ uiElement, attributeElement ] ); // eslint-disable-line no-new
 			const range = Range._createFromParentsAndOffsets( uiElement, 0, attributeElement, 0 );
 
 			expectToThrowCKEditorError( () => {
@@ -201,7 +202,7 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when item has no parent container', () => {
-			const el = new AttributeElement( 'b' );
+			const el = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.remove( el );

+ 2 - 1
packages/ckeditor5-engine/tests/view/downcastwriter/rename.js

@@ -6,13 +6,14 @@
 import DowncastWriter from '../../../src/view/downcastwriter';
 import { parse } from '../../../src/dev-utils/view';
 import Document from '../../../src/view/document';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'rename()', () => {
 		let root, foo, writer;
 
 		before( () => {
-			writer = new DowncastWriter( new Document() );
+			writer = new DowncastWriter( new Document( new StylesProcessor() ) );
 		} );
 
 		beforeEach( () => {

+ 15 - 14
packages/ckeditor5-engine/tests/view/downcastwriter/unwrap.js

@@ -16,6 +16,7 @@ import Text from '../../../src/view/text';
 import { stringify, parse } from '../../../src/dev-utils/view';
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'unwrap()', () => {
@@ -34,7 +35,7 @@ describe( 'DowncastWriter', () => {
 		}
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
@@ -55,12 +56,12 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw error when element is not instance of AttributeElement', () => {
-			const container = new ContainerElement( 'p', null, new AttributeElement( 'b', null, new Text( 'foo' ) ) );
+			const container = new ContainerElement( document, 'p', null, new AttributeElement( document, 'b', null, new Text( 'foo' ) ) );
 			const range = new Range(
 				new Position( container, 0 ),
 				new Position( container, 1 )
 			);
-			const b = new Element( 'b' );
+			const b = new Element( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.unwrap( range, b );
@@ -68,13 +69,13 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw error when range placed in two containers', () => {
-			const container1 = new ContainerElement( 'p' );
-			const container2 = new ContainerElement( 'p' );
+			const container1 = new ContainerElement( document, 'p' );
+			const container2 = new ContainerElement( document, 'p' );
 			const range = new Range(
 				new Position( container1, 0 ),
 				new Position( container2, 1 )
 			);
-			const b = new AttributeElement( 'b' );
+			const b = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.unwrap( range, b );
@@ -82,8 +83,8 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw when range has no parent container', () => {
-			const el = new AttributeElement( 'b' );
-			const b = new AttributeElement( 'b' );
+			const el = new AttributeElement( document, 'b' );
+			const b = new AttributeElement( document, 'b' );
 
 			expectToThrowCKEditorError( () => {
 				writer.unwrap( Range._createFromParentsAndOffsets( el, 0, el, 0 ), b );
@@ -396,9 +397,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if range is inside EmptyElement', () => {
-			const empty = new EmptyElement( 'img' );
-			const attribute = new AttributeElement( 'b' );
-			const container = new ContainerElement( 'p', null, [ empty, attribute ] );
+			const empty = new EmptyElement( document, 'img' );
+			const attribute = new AttributeElement( document, 'b' );
+			const container = new ContainerElement( document, 'p', null, [ empty, attribute ] );
 			const range = Range._createFromParentsAndOffsets( empty, 0, container, 2 );
 
 			expectToThrowCKEditorError( () => {
@@ -415,9 +416,9 @@ describe( 'DowncastWriter', () => {
 		} );
 
 		it( 'should throw if range is placed inside UIElement', () => {
-			const uiElement = new UIElement( 'span' );
-			const attribute = new AttributeElement( 'b' );
-			const container = new ContainerElement( 'p', null, [ uiElement, attribute ] );
+			const uiElement = new UIElement( document, 'span' );
+			const attribute = new AttributeElement( document, 'b' );
+			const container = new ContainerElement( document, 'p', null, [ uiElement, attribute ] );
 			const range = Range._createFromParentsAndOffsets( uiElement, 0, container, 2 );
 
 			expectToThrowCKEditorError( () => {

+ 19 - 18
packages/ckeditor5-engine/tests/view/downcastwriter/wrap.js

@@ -19,13 +19,14 @@ import { stringify, parse } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
 import Document from '../../../src/view/document';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	describe( 'wrap()', () => {
 		let writer, document;
 
 		beforeEach( () => {
-			document = new Document();
+			document = new Document( new StylesProcessor() );
 			writer = new DowncastWriter( document );
 		} );
 
@@ -62,12 +63,12 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw error when element is not instance of AttributeElement', () => {
-				const container = new ContainerElement( 'p', null, new Text( 'foo' ) );
+				const container = new ContainerElement( document, 'p', null, new Text( 'foo' ) );
 				const range = new Range(
 					new Position( container, 0 ),
 					new Position( container, 1 )
 				);
-				const b = new Element( 'b' );
+				const b = new Element( document, 'b' );
 
 				expectToThrowCKEditorError( () => {
 					writer.wrap( range, b );
@@ -75,13 +76,13 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw error when range placed in two containers', () => {
-				const container1 = new ContainerElement( 'p' );
-				const container2 = new ContainerElement( 'p' );
+				const container1 = new ContainerElement( document, 'p' );
+				const container2 = new ContainerElement( document, 'p' );
 				const range = new Range(
 					new Position( container1, 0 ),
 					new Position( container2, 1 )
 				);
-				const b = new AttributeElement( 'b' );
+				const b = new AttributeElement( document, 'b' );
 
 				expectToThrowCKEditorError( () => {
 					writer.wrap( range, b );
@@ -89,8 +90,8 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw when range has no parent container', () => {
-				const el = new AttributeElement( 'b' );
-				const b = new AttributeElement( 'b' );
+				const el = new AttributeElement( document, 'b' );
+				const b = new AttributeElement( document, 'b' );
 
 				expectToThrowCKEditorError( () => {
 					writer.wrap( Range._createFromParentsAndOffsets( el, 0, el, 0 ), b );
@@ -394,12 +395,12 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if range is inside EmptyElement', () => {
-				const emptyElement = new EmptyElement( 'img' );
-				const container = new ContainerElement( 'p', null, emptyElement );
+				const emptyElement = new EmptyElement( document, 'img' );
+				const container = new ContainerElement( document, 'p', null, emptyElement );
 				const range = Range._createFromParentsAndOffsets( emptyElement, 0, container, 1 );
 
 				expectToThrowCKEditorError( () => {
-					writer.wrap( range, new AttributeElement( 'b' ) );
+					writer.wrap( range, new AttributeElement( document, 'b' ) );
 				}, 'view-writer-cannot-break-empty-element', document );
 			} );
 
@@ -412,12 +413,12 @@ describe( 'DowncastWriter', () => {
 			} );
 
 			it( 'should throw if range is inside UIElement', () => {
-				const uiElement = new UIElement( 'span' );
-				const container = new ContainerElement( 'p', null, uiElement );
+				const uiElement = new UIElement( document, 'span' );
+				const container = new ContainerElement( document, 'p', null, uiElement );
 				const range = Range._createFromParentsAndOffsets( uiElement, 0, container, 1 );
 
 				expectToThrowCKEditorError( () => {
-					writer.wrap( range, new AttributeElement( 'b' ) );
+					writer.wrap( range, new AttributeElement( document, 'b' ) );
 				}, 'view-writer-cannot-break-ui-element', document );
 			} );
 
@@ -520,7 +521,7 @@ describe( 'DowncastWriter', () => {
 			let view, viewDocument, viewRoot;
 
 			beforeEach( () => {
-				view = new View();
+				view = new View( new StylesProcessor() );
 				viewDocument = view.document;
 				viewRoot = createViewRoot( viewDocument );
 			} );
@@ -543,15 +544,15 @@ describe( 'DowncastWriter', () => {
 				const newPosition = writer.wrap( selection.getFirstRange(), parse( wrapAttribute ) );
 
 				// Moving parsed elements to a document fragment so the view root is not shown in `stringify`.
-				const viewChildren = new DocumentFragment( view.getChildren() );
+				const viewChildren = new DocumentFragment( viewDocument, view.getChildren() );
 
 				expect( stringify( viewChildren, newPosition, { showType: true, showPriority: true } ) ).to.equal( expected );
 			}
 
 			it( 'should throw error when element is not instance of AttributeElement', () => {
-				const container = new ContainerElement( 'p', null, new Text( 'foo' ) );
+				const container = new ContainerElement( document, 'p', null, new Text( 'foo' ) );
 				const position = new Position( container, 0 );
-				const b = new Element( 'b' );
+				const b = new Element( document, 'b' );
 
 				expectToThrowCKEditorError( () => {
 					writer.wrap( new Range( position ), b );

+ 2 - 1
packages/ckeditor5-engine/tests/view/downcastwriter/writer.js

@@ -11,13 +11,14 @@ import ViewRange from '../../../src/view/range';
 import createViewRoot from '../_utils/createroot';
 import ViewElement from '../../../src/view/element';
 import ViewSelection from '../../../src/view/selection';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DowncastWriter', () => {
 	let writer, attributes, root, doc;
 
 	beforeEach( () => {
 		attributes = { foo: 'bar', baz: 'quz' };
-		doc = new Document();
+		doc = new Document( new StylesProcessor() );
 		root = createViewRoot( doc );
 		writer = new DowncastWriter( doc );
 	} );

+ 25 - 53
packages/ckeditor5-engine/tests/view/editableelement.js

@@ -7,14 +7,15 @@ import createDocumentMock from '../../tests/view/_utils/createdocumentmock';
 
 import EditableElement from '../../src/view/editableelement';
 import Range from '../../src/view/range';
-import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import Document from '../../src/view/document';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'EditableElement', () => {
 	describe( 'is', () => {
 		let el;
 
 		before( () => {
-			el = new EditableElement( 'div' );
+			el = new EditableElement( new Document( new StylesProcessor() ), 'div' );
 		} );
 
 		it( 'should return true for containerElement/editable/element, also with correct name and element name', () => {
@@ -52,58 +53,20 @@ describe( 'EditableElement', () => {
 		} );
 	} );
 
-	describe( 'document', () => {
-		let element, docMock;
-
-		beforeEach( () => {
-			element = new EditableElement( 'div' );
-			docMock = createDocumentMock();
-		} );
-
-		it( 'should allow to set document', () => {
-			element._document = docMock;
-
-			expect( element.document ).to.equal( docMock );
-		} );
-
-		it( 'should return undefined if document is not set', () => {
-			expect( element.document ).to.be.undefined;
-		} );
-
-		it( 'should throw if trying to set document again', () => {
-			element._document = docMock;
-			const newDoc = createDocumentMock();
-
-			expectToThrowCKEditorError( () => {
-				element._document = newDoc;
-			}, 'view-editableelement-document-already-set: View document is already set.', docMock );
-		} );
-
-		it( 'should be cloned properly', () => {
-			element._document = docMock;
-			const newElement = element._clone();
-
-			expect( newElement.document ).to.equal( docMock );
-		} );
-	} );
-
 	describe( 'isFocused', () => {
 		let docMock, viewMain, viewHeader;
 
 		beforeEach( () => {
 			docMock = createDocumentMock();
 
-			viewMain = new EditableElement( 'div' );
-			viewMain._document = docMock;
+			viewMain = new EditableElement( docMock, 'div' );
 
-			viewHeader = new EditableElement( 'h1' );
-			viewHeader._document = docMock;
+			viewHeader = new EditableElement( docMock, 'h1' );
 			viewHeader.rootName = 'header';
 		} );
 
 		it( 'should be observable', () => {
-			const root = new EditableElement( 'div' );
-			root._document = createDocumentMock();
+			const root = new EditableElement( docMock, 'div' );
 
 			expect( root.isFocused ).to.be.false;
 
@@ -155,9 +118,14 @@ describe( 'EditableElement', () => {
 	} );
 
 	describe( 'isReadOnly', () => {
+		let docMock;
+
+		beforeEach( () => {
+			docMock = createDocumentMock();
+		} );
+
 		it( 'should be observable', () => {
-			const root = new EditableElement( 'div' );
-			root._document = createDocumentMock();
+			const root = new EditableElement( docMock, 'div' );
 
 			expect( root.isReadOnly ).to.be.false;
 
@@ -173,8 +141,7 @@ describe( 'EditableElement', () => {
 		} );
 
 		it( 'should be bound to the document#isReadOnly', () => {
-			const root = new EditableElement( 'div' );
-			root._document = createDocumentMock();
+			const root = new EditableElement( docMock, 'div' );
 
 			root.document.isReadOnly = false;
 
@@ -186,13 +153,18 @@ describe( 'EditableElement', () => {
 		} );
 	} );
 
-	describe( 'getDocument', () => {
-		it( 'should return document', () => {
-			const docMock = createDocumentMock();
-			const root = new EditableElement( 'div' );
-			root._document = docMock;
+	describe( 'document', () => {
+		let element, docMock;
+
+		beforeEach( () => {
+			docMock = createDocumentMock();
+			element = new EditableElement( docMock, 'div' );
+		} );
 
-			expect( root.document ).to.equal( docMock );
+		it( 'should be cloned properly', () => {
+			const newElement = element._clone();
+
+			expect( newElement.document ).to.equal( docMock );
 		} );
 	} );
 } );

+ 82 - 60
packages/ckeditor5-engine/tests/view/element.js

@@ -8,11 +8,19 @@ import Node from '../../src/view/node';
 import Element from '../../src/view/element';
 import Text from '../../src/view/text';
 import TextProxy from '../../src/view/textproxy';
+import Document from '../../src/view/document';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Element', () => {
+	let document;
+
+	beforeEach( () => {
+		document = new Document( new StylesProcessor() );
+	} );
+
 	describe( 'constructor()', () => {
 		it( 'should create element without attributes', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 
 			expect( el ).to.be.an.instanceof( Node );
 			expect( el ).to.have.property( 'name' ).that.equals( 'p' );
@@ -21,7 +29,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should create element with attributes as plain object', () => {
-			const el = new Element( 'p', { foo: 'bar' } );
+			const el = new Element( document, 'p', { foo: 'bar' } );
 
 			expect( el ).to.have.property( 'name' ).that.equals( 'p' );
 			expect( count( el.getAttributeKeys() ) ).to.equal( 1 );
@@ -32,7 +40,7 @@ describe( 'Element', () => {
 			const attrs = new Map();
 			attrs.set( 'foo', 'bar' );
 
-			const el = new Element( 'p', attrs );
+			const el = new Element( document, 'p', attrs );
 
 			expect( el ).to.have.property( 'name' ).that.equals( 'p' );
 			expect( count( el.getAttributeKeys() ) ).to.equal( 1 );
@@ -40,7 +48,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should stringify attributes', () => {
-			const el = new Element( 'p', { foo: true, bar: null, object: {} } );
+			const el = new Element( document, 'p', { foo: true, bar: null, object: {} } );
 
 			expect( el.getAttribute( 'foo' ) ).to.equal( 'true' );
 			expect( el.getAttribute( 'bar' ) ).to.be.undefined;
@@ -48,8 +56,8 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should create element with children', () => {
-			const child = new Element( 'p', { foo: 'bar' } );
-			const parent = new Element( 'div', [], [ child ] );
+			const child = new Element( document, 'p', { foo: 'bar' } );
+			const parent = new Element( document, 'div', [], [ child ] );
 
 			expect( parent ).to.have.property( 'name' ).that.equals( 'div' );
 			expect( parent.childCount ).to.equal( 1 );
@@ -57,7 +65,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should move class attribute to class set ', () => {
-			const el = new Element( 'p', { id: 'test', class: 'one two three' } );
+			const el = new Element( document, 'p', { id: 'test', class: 'one two three' } );
 
 			expect( el._attrs.has( 'class' ) ).to.be.false;
 			expect( el._attrs.has( 'id' ) ).to.be.true;
@@ -67,7 +75,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should move style attribute to style proxy', () => {
-			const el = new Element( 'p', { id: 'test', style: 'one: style1; two:style2 ; three : url(http://ckeditor.com)' } );
+			const el = new Element( document, 'p', { id: 'test', style: 'one: style1; two:style2 ; three : url(http://ckeditor.com)' } );
 
 			expect( el._attrs.has( 'style' ) ).to.be.false;
 			expect( el._attrs.has( 'id' ) ).to.be.true;
@@ -85,7 +93,7 @@ describe( 'Element', () => {
 		let el;
 
 		before( () => {
-			el = new Element( 'p' );
+			el = new Element( document, 'p' );
 		} );
 
 		it( 'should return true for node, element, element with correct name and element name', () => {
@@ -120,13 +128,13 @@ describe( 'Element', () => {
 
 	describe( 'isEmpty', () => {
 		it( 'should return true if there are no children in element', () => {
-			const element = new Element( 'p' );
+			const element = new Element( document, 'p' );
 
 			expect( element.isEmpty ).to.be.true;
 		} );
 
 		it( 'should return false if there are children in element', () => {
-			const fragment = new Element( 'p', null, new Element( 'img' ) );
+			const fragment = new Element( document, 'p', null, new Element( document, 'img' ) );
 
 			expect( fragment.isEmpty ).to.be.false;
 		} );
@@ -134,7 +142,7 @@ describe( 'Element', () => {
 
 	describe( '_clone()', () => {
 		it( 'should clone element', () => {
-			const el = new Element( 'p', { attr1: 'foo', attr2: 'bar' } );
+			const el = new Element( document, 'p', { attr1: 'foo', attr2: 'bar' } );
 			const clone = el._clone();
 
 			expect( clone ).to.not.equal( el );
@@ -144,9 +152,9 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should deeply clone element', () => {
-			const el = new Element( 'p', { attr1: 'foo', attr2: 'bar' }, [
-				new Element( 'b', { attr: 'baz' } ),
-				new Element( 'span', { attr: 'qux' } )
+			const el = new Element( document, 'p', { attr1: 'foo', attr2: 'bar' }, [
+				new Element( document, 'b', { attr: 'baz' } ),
+				new Element( document, 'span', { attr: 'qux' } )
 			] );
 			const count = el.childCount;
 			const clone = el._clone( true );
@@ -168,9 +176,9 @@ describe( 'Element', () => {
 		} );
 
 		it( 'shouldn\'t clone any children when deep copy is not performed', () => {
-			const el = new Element( 'p', { attr1: 'foo', attr2: 'bar' }, [
-				new Element( 'b', { attr: 'baz' } ),
-				new Element( 'span', { attr: 'qux' } )
+			const el = new Element( document, 'p', { attr1: 'foo', attr2: 'bar' }, [
+				new Element( document, 'b', { attr: 'baz' } ),
+				new Element( document, 'span', { attr: 'qux' } )
 			] );
 			const clone = el._clone( false );
 
@@ -182,7 +190,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should clone class attribute', () => {
-			const el = new Element( 'p', { foo: 'bar' } );
+			const el = new Element( document, 'p', { foo: 'bar' } );
 			el._addClass( [ 'baz', 'qux' ] );
 			const clone = el._clone( false );
 
@@ -193,7 +201,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should clone style attribute', () => {
-			const el = new Element( 'p', { style: 'color: red; font-size: 12px;' } );
+			const el = new Element( document, 'p', { style: 'color: red; font-size: 12px;' } );
 			const clone = el._clone( false );
 
 			expect( clone ).to.not.equal( el );
@@ -205,7 +213,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should clone custom properties', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 			const symbol = Symbol( 'custom' );
 			el._setCustomProperty( 'foo', 'bar' );
 			el._setCustomProperty( symbol, 'baz' );
@@ -217,7 +225,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should clone getFillerOffset', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 			const fm = () => 'foo bar';
 
 			expect( el.getFillerOffset ).to.be.undefined;
@@ -230,7 +238,12 @@ describe( 'Element', () => {
 	} );
 
 	describe( 'isSimilar()', () => {
-		const el = new Element( 'p', { foo: 'bar' } );
+		let el;
+
+		beforeEach( () => {
+			el = new Element( document, 'p', { foo: 'bar' } );
+		} );
+
 		it( 'should return false when comparing to non-element', () => {
 			expect( el.isSimilar( null ) ).to.be.false;
 			expect( el.isSimilar( {} ) ).to.be.false;
@@ -241,7 +254,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should return true for element with same attributes and name', () => {
-			const other = new Element( 'p', { foo: 'bar' } );
+			const other = new Element( document, 'p', { foo: 'bar' } );
 			expect( el.isSimilar( other ) ).to.be.true;
 		} );
 
@@ -265,10 +278,10 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should compare class attribute', () => {
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'p' );
-			const el3 = new Element( 'p' );
-			const el4 = new Element( 'p' );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'p' );
+			const el3 = new Element( document, 'p' );
+			const el4 = new Element( document, 'p' );
 
 			el1._addClass( [ 'foo', 'bar' ] );
 			el2._addClass( [ 'bar', 'foo' ] );
@@ -284,8 +297,8 @@ describe( 'Element', () => {
 			let element, other;
 
 			beforeEach( () => {
-				element = new Element( 'p' );
-				other = new Element( 'p' );
+				element = new Element( document, 'p' );
+				other = new Element( document, 'p' );
 
 				element._setStyle( 'color', 'red' );
 				element._setStyle( 'top', '10px' );
@@ -338,11 +351,11 @@ describe( 'Element', () => {
 		let parent, el1, el2, el3, el4;
 
 		beforeEach( () => {
-			parent = new Element( 'p' );
-			el1 = new Element( 'el1' );
-			el2 = new Element( 'el2' );
-			el3 = new Element( 'el3' );
-			el4 = new Element( 'el4' );
+			parent = new Element( document, 'p' );
+			el1 = new Element( document, 'el1' );
+			el2 = new Element( document, 'el2' );
+			el3 = new Element( document, 'el3' );
+			el4 = new Element( document, 'el4' );
 		} );
 
 		describe( 'insertion', () => {
@@ -365,7 +378,7 @@ describe( 'Element', () => {
 				expect( parent.getChild( 0 ) ).to.have.property( 'data' ).that.equals( 'abc' );
 
 				parent._removeChildren( 0, 1 );
-				parent._insertChild( 0, [ new Element( 'p' ), 'abc' ] );
+				parent._insertChild( 0, [ new Element( document, 'p' ), 'abc' ] );
 
 				expect( parent.childCount ).to.equal( 2 );
 				expect( parent.getChild( 1 ) ).to.have.property( 'data' ).that.equals( 'abc' );
@@ -386,8 +399,8 @@ describe( 'Element', () => {
 			} );
 
 			it( 'should accept and correctly handle text proxies', () => {
-				const element = new Element( 'div' );
-				const text = new Text( 'abcxyz' );
+				const element = new Element( document, 'div' );
+				const text = new Text( document, 'abcxyz' );
 				const textProxy = new TextProxy( text, 2, 3 );
 
 				element._insertChild( 0, textProxy );
@@ -396,6 +409,15 @@ describe( 'Element', () => {
 				expect( element.getChild( 0 ) ).to.be.instanceof( Text );
 				expect( element.getChild( 0 ).data ).to.equal( 'cxy' );
 			} );
+
+			it( 'set proper #document on inserted children', () => {
+				const anotherDocument = new Document( new StylesProcessor() );
+				const anotherEl = new Element( anotherDocument, 'p' );
+
+				parent._insertChild( 0, anotherEl );
+
+				expect( anotherEl.document ).to.be.equal( document );
+			} );
 		} );
 
 		describe( 'getChildIndex', () => {
@@ -469,7 +491,7 @@ describe( 'Element', () => {
 		let el;
 
 		beforeEach( () => {
-			el = new Element( 'p' );
+			el = new Element( document, 'p' );
 		} );
 
 		describe( '_setAttribute', () => {
@@ -662,7 +684,7 @@ describe( 'Element', () => {
 
 			it( 'should remove class attribute', () => {
 				el._addClass( [ 'foo', 'bar' ] );
-				const el2 = new Element( 'p' );
+				const el2 = new Element( document, 'p' );
 				const removed1 = el._removeAttribute( 'class' );
 				const removed2 = el2._removeAttribute( 'class' );
 
@@ -676,7 +698,7 @@ describe( 'Element', () => {
 			it( 'should remove style attribute', () => {
 				el._setStyle( 'color', 'red' );
 				el._setStyle( 'position', 'fixed' );
-				const el2 = new Element( 'p' );
+				const el2 = new Element( document, 'p' );
 				const removed1 = el._removeAttribute( 'style' );
 				const removed2 = el2._removeAttribute( 'style' );
 
@@ -693,7 +715,7 @@ describe( 'Element', () => {
 		let el;
 
 		beforeEach( () => {
-			el = new Element( 'p' );
+			el = new Element( document, 'p' );
 		} );
 
 		describe( '_addClass()', () => {
@@ -791,7 +813,7 @@ describe( 'Element', () => {
 		let el;
 
 		beforeEach( () => {
-			el = new Element( 'p' );
+			el = new Element( document, 'p' );
 		} );
 
 		describe( '_setStyle()', () => {
@@ -919,29 +941,29 @@ describe( 'Element', () => {
 
 	describe( 'findAncestor', () => {
 		it( 'should return null if element have no ancestor', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 
 			expect( el.findAncestor( 'div' ) ).to.be.null;
 		} );
 
 		it( 'should return ancestor if matching', () => {
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'div', null, el1 );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'div', null, el1 );
 
 			expect( el1.findAncestor( 'div' ) ).to.equal( el2 );
 		} );
 
 		it( 'should return parent\'s ancestor if matching', () => {
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'div', null, el1 );
-			const el3 = new Element( 'div', { class: 'foo bar' }, el2 );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'div', null, el1 );
+			const el3 = new Element( document, 'div', { class: 'foo bar' }, el2 );
 
 			expect( el1.findAncestor( { classes: 'foo' } ) ).to.equal( el3 );
 		} );
 
 		it( 'should return null if no matches found', () => {
-			const el1 = new Element( 'p' );
-			new Element( 'div', null, el1 ); // eslint-disable-line no-new
+			const el1 = new Element( document, 'p' );
+			new Element( document, 'div', null, el1 ); // eslint-disable-line no-new
 
 			expect( el1.findAncestor( {
 				name: 'div',
@@ -952,14 +974,14 @@ describe( 'Element', () => {
 
 	describe( 'custom properties', () => {
 		it( 'should allow to set and get custom properties', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 			el._setCustomProperty( 'foo', 'bar' );
 
 			expect( el.getCustomProperty( 'foo' ) ).to.equal( 'bar' );
 		} );
 
 		it( 'should allow to add symbol property', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 			const symbol = Symbol( 'custom' );
 			el._setCustomProperty( symbol, 'bar' );
 
@@ -967,7 +989,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should allow to remove custom property', () => {
-			const el = new Element( 'foo' );
+			const el = new Element( document, 'foo' );
 			const symbol = Symbol( 'quix' );
 			el._setCustomProperty( 'bar', 'baz' );
 			el._setCustomProperty( symbol, 'test' );
@@ -983,7 +1005,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should allow to iterate over custom properties', () => {
-			const el = new Element( 'p' );
+			const el = new Element( document, 'p' );
 			el._setCustomProperty( 'foo', 1 );
 			el._setCustomProperty( 'bar', 2 );
 			el._setCustomProperty( 'baz', 3 );
@@ -1001,20 +1023,20 @@ describe( 'Element', () => {
 
 	describe( 'getIdentity()', () => {
 		it( 'should return only name if no other attributes are present', () => {
-			const el = new Element( 'foo' );
+			const el = new Element( document, 'foo' );
 
 			expect( el.getIdentity() ).to.equal( 'foo' );
 		} );
 
 		it( 'should return classes in sorted order', () => {
-			const el = new Element( 'fruit' );
+			const el = new Element( document, 'fruit' );
 			el._addClass( [ 'banana', 'lemon', 'apple' ] );
 
 			expect( el.getIdentity() ).to.equal( 'fruit class="apple,banana,lemon"' );
 		} );
 
 		it( 'should return styles in sorted order', () => {
-			const el = new Element( 'foo', {
+			const el = new Element( document, 'foo', {
 				style: 'margin-top: 2em; background-color: red'
 			} );
 
@@ -1022,7 +1044,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should return attributes in sorted order', () => {
-			const el = new Element( 'foo', {
+			const el = new Element( document, 'foo', {
 				a: 1,
 				d: 4,
 				b: 3
@@ -1032,7 +1054,7 @@ describe( 'Element', () => {
 		} );
 
 		it( 'should return classes, styles and attributes', () => {
-			const el = new Element( 'baz', {
+			const el = new Element( document, 'baz', {
 				foo: 'one',
 				bar: 'two',
 				style: 'text-align:center;border-radius:10px'

+ 9 - 6
packages/ckeditor5-engine/tests/view/emptyelement.js

@@ -5,15 +5,18 @@
 
 import EmptyElement from '../../src/view/emptyelement';
 import Element from '../../src/view/element';
+import Document from '../../src/view/document';
 
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'EmptyElement', () => {
-	let element, emptyElement;
+	let element, emptyElement, document;
 
 	beforeEach( () => {
-		element = new Element( 'b' );
-		emptyElement = new EmptyElement( 'img', {
+		document = new Document( new StylesProcessor() );
+		element = new Element( document, 'b' );
+		emptyElement = new EmptyElement( document, 'img', {
 			alt: 'alternative text',
 			style: 'margin-top: 2em;color: white;',
 			class: 'image big'
@@ -24,7 +27,7 @@ describe( 'EmptyElement', () => {
 		let el;
 
 		before( () => {
-			el = new EmptyElement( 'p' );
+			el = new EmptyElement( document, 'p' );
 		} );
 
 		it( 'should return true for emptyElement/element, also with correct name and element name', () => {
@@ -59,10 +62,10 @@ describe( 'EmptyElement', () => {
 	} );
 
 	it( 'should throw if child elements are passed to constructor', () => {
-		const el = new Element( 'i' );
+		const el = new Element( document, 'i' );
 
 		expectToThrowCKEditorError( () => {
-			new EmptyElement( 'img', null, [ el ] ); // eslint-disable-line no-new
+			new EmptyElement( document, 'img', null, [ el ] ); // eslint-disable-line no-new
 		}, 'view-emptyelement-cannot-add: Cannot add child nodes to EmptyElement instance.', el );
 	} );
 

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/clickobserver.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import DomEventObserver from '../../../src/view/observer/domeventobserver';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 
 // Disable rendering for this example, because it re-enables all observers each time view is rendered.

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/fakeselection.js

@@ -10,8 +10,9 @@ import DomEventObserver from '../../../src/view/observer/domeventobserver';
 import ViewRange from '../../../src/view/range';
 import createViewRoot from '../_utils/createroot';
 import { setData } from '../../../src/dev-utils/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 const domEditable = document.getElementById( 'editor' );
 const viewRoot = createViewRoot( viewDocument );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/focus.js

@@ -9,8 +9,9 @@ import View from '../../../src/view/view';
 import ViewPosition from '../../../src/view/position';
 import ViewRange from '../../../src/view/range';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 
 const domEditable1 = document.getElementById( 'editable1' );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/focusobserver.js

@@ -7,8 +7,9 @@
 
 import View from '../../../src/view/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 
 viewDocument.on( 'focus', ( evt, data ) => console.log( `Focus in ${ data.domTarget.id }.` ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/immutable.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import { setData } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument, 'div' );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/inline-filler.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import createViewRoot from '../_utils/createroot';
 import { setData } from '../../../src/dev-utils/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/keyobserver.js

@@ -7,8 +7,9 @@
 
 import View from '../../../src/view/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 
 viewDocument.on( 'keydown', ( evt, data ) => console.log( 'keydown', data ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/mutationobserver.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import createViewRoot from '../_utils/createroot';
 import { setData } from '../../../src/dev-utils/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/noselection-iframe.js

@@ -8,12 +8,13 @@
 import View from '../../../src/view/view';
 import { setData } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 const iframe = document.getElementById( 'iframe' );
 iframe.srcdoc = '<div contenteditable="true" id="editor"></div>';
 
 iframe.addEventListener( 'load', () => {
-	const view = new View();
+	const view = new View( new StylesProcessor() );
 	const viewDocument = view.document;
 	createViewRoot( viewDocument );
 

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/noselection.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import { setData } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/selectionobserver.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import { setData } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 2 - 1
packages/ckeditor5-engine/tests/view/manual/x-index.js

@@ -8,8 +8,9 @@
 import View from '../../../src/view/view';
 import { setData } from '../../../src/dev-utils/view';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
-const view = new View();
+const view = new View( new StylesProcessor() );
 const viewDocument = view.document;
 createViewRoot( viewDocument );
 view.attachDomRoot( document.getElementById( 'editor' ) );

+ 47 - 39
packages/ckeditor5-engine/tests/view/matcher.js

@@ -5,12 +5,20 @@
 
 import Matcher from '../../src/view/matcher';
 import Element from '../../src/view/element';
+import Document from '../../src/view/document';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Matcher', () => {
+	let document;
+
+	beforeEach( () => {
+		document = new Document( new StylesProcessor() );
+	} );
+
 	describe( 'add', () => {
 		it( 'should allow to add pattern to matcher', () => {
 			const matcher = new Matcher( 'div' );
-			const el = new Element( 'p', { title: 'foobar' } );
+			const el = new Element( document, 'p', { title: 'foobar' } );
 
 			expect( matcher.match( el ) ).to.be.null;
 			const pattern = { name: 'p', attributes: { title: 'foobar' } };
@@ -26,8 +34,8 @@ describe( 'Matcher', () => {
 
 		it( 'should allow to add more than one pattern', () => {
 			const matcher = new Matcher();
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'div' );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'div' );
 
 			matcher.add( 'p', 'div' );
 
@@ -46,8 +54,8 @@ describe( 'Matcher', () => {
 	describe( 'match', () => {
 		it( 'should match element name', () => {
 			const matcher = new Matcher( 'p' );
-			const el = new Element( 'p' );
-			const el2 = new Element( 'div' );
+			const el = new Element( document, 'p' );
+			const el2 = new Element( document, 'div' );
 
 			const result = matcher.match( el );
 
@@ -63,8 +71,8 @@ describe( 'Matcher', () => {
 		it( 'should match element name with RegExp', () => {
 			const pattern = /^text...a$/;
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'textarea' );
-			const el2 = new Element( 'div' );
+			const el1 = new Element( document, 'textarea' );
+			const el2 = new Element( document, 'div' );
 
 			const result = matcher.match( el1 );
 
@@ -82,9 +90,9 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { title: 'foobar'	} );
-			const el2 = new Element( 'p', { title: 'foobaz'	} );
-			const el3 = new Element( 'p', { name: 'foobar' } );
+			const el1 = new Element( document, 'p', { title: 'foobar'	} );
+			const el2 = new Element( document, 'p', { title: 'foobaz'	} );
+			const el3 = new Element( document, 'p', { name: 'foobar' } );
 
 			const result = matcher.match( el1 );
 
@@ -106,9 +114,9 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { title: 'foobar'	} );
-			const el2 = new Element( 'p', { title: 'foobaz'	} );
-			const el3 = new Element( 'p', { title: 'qux' } );
+			const el1 = new Element( document, 'p', { title: 'foobar'	} );
+			const el2 = new Element( document, 'p', { title: 'foobaz'	} );
+			const el3 = new Element( document, 'p', { title: 'qux' } );
 
 			let result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -133,9 +141,9 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { title: 'foobar'	} );
-			const el2 = new Element( 'p', { title: '' } );
-			const el3 = new Element( 'p' );
+			const el1 = new Element( document, 'p', { title: 'foobar'	} );
+			const el2 = new Element( document, 'p', { title: '' } );
+			const el3 = new Element( document, 'p' );
 
 			let result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -157,7 +165,7 @@ describe( 'Matcher', () => {
 		it( 'should match element class names', () => {
 			const pattern = { classes: 'foobar' };
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { class: 'foobar' } );
+			const el1 = new Element( document, 'p', { class: 'foobar' } );
 
 			const result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -171,9 +179,9 @@ describe( 'Matcher', () => {
 		it( 'should match element class names using RegExp', () => {
 			const pattern = { classes: /fooba./ };
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { class: 'foobar'	} );
-			const el2 = new Element( 'p', { class: 'foobaz'	} );
-			const el3 = new Element( 'p', { class: 'qux'	} );
+			const el1 = new Element( document, 'p', { class: 'foobar'	} );
+			const el2 = new Element( document, 'p', { class: 'foobaz'	} );
+			const el3 = new Element( document, 'p', { class: 'qux'	} );
 
 			let result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -198,8 +206,8 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { style: 'color: red' } );
-			const el2 = new Element( 'p', { style: 'position: absolute' } );
+			const el1 = new Element( document, 'p', { style: 'color: red' } );
+			const el2 = new Element( document, 'p', { style: 'position: absolute' } );
 
 			const result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -218,9 +226,9 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p', { style: 'color: blue' } );
-			const el2 = new Element( 'p', { style: 'color: darkblue' } );
-			const el3 = new Element( 'p', { style: 'color: red' } );
+			const el1 = new Element( document, 'p', { style: 'color: blue' } );
+			const el2 = new Element( document, 'p', { style: 'color: darkblue' } );
+			const el3 = new Element( document, 'p', { style: 'color: red' } );
 
 			let result = matcher.match( el1 );
 			expect( result ).to.be.an( 'object' );
@@ -248,8 +256,8 @@ describe( 'Matcher', () => {
 				return null;
 			};
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'div', null, [ el1 ] );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'div', null, [ el1 ] );
 
 			expect( matcher.match( el1 ) ).to.be.null;
 			const result = matcher.match( el2 );
@@ -262,9 +270,9 @@ describe( 'Matcher', () => {
 			const pattern = {
 				name: 'p'
 			};
-			const el1 = new Element( 'div' );
-			const el2 = new Element( 'p' );
-			const el3 = new Element( 'span' );
+			const el1 = new Element( document, 'div' );
+			const el2 = new Element( document, 'p' );
+			const el3 = new Element( document, 'span' );
 			const matcher = new Matcher( pattern );
 
 			const result = matcher.match( el1, el2, el3 );
@@ -284,7 +292,7 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el = new Element( 'a', {
+			const el = new Element( document, 'a', {
 				name: 'foo',
 				title: 'bar'
 			} );
@@ -305,7 +313,7 @@ describe( 'Matcher', () => {
 				classes: [ 'foo', 'bar' ]
 			};
 			const matcher = new Matcher( pattern );
-			const el = new Element( 'a' );
+			const el = new Element( document, 'a' );
 			el._addClass( [ 'foo', 'bar', 'baz' ] );
 
 			const result = matcher.match( el );
@@ -327,7 +335,7 @@ describe( 'Matcher', () => {
 				}
 			};
 			const matcher = new Matcher( pattern );
-			const el = new Element( 'a' );
+			const el = new Element( document, 'a' );
 			el._setStyle( {
 				color: 'red',
 				position: 'relative'
@@ -347,9 +355,9 @@ describe( 'Matcher', () => {
 	describe( 'matchAll', () => {
 		it( 'should return all matched elements with correct patterns', () => {
 			const matcher = new Matcher( 'p', 'div' );
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'div' );
-			const el3 = new Element( 'span' );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'div' );
+			const el3 = new Element( document, 'span' );
 
 			const result = matcher.matchAll( el1, el2, el3 );
 			expect( result ).to.be.an( 'array' );
@@ -372,9 +380,9 @@ describe( 'Matcher', () => {
 		it( 'should return all matched elements when using RegExp pattern', () => {
 			const pattern = { classes: /^red-.*/ };
 			const matcher = new Matcher( pattern );
-			const el1 = new Element( 'p' );
-			const el2 = new Element( 'p' );
-			const el3 = new Element( 'p' );
+			const el1 = new Element( document, 'p' );
+			const el2 = new Element( document, 'p' );
+			const el3 = new Element( document, 'p' );
 
 			el1._addClass( 'red-foreground' );
 			el2._addClass( 'red-background' );

+ 73 - 70
packages/ckeditor5-engine/tests/view/node.js

@@ -9,25 +9,28 @@ import Node from '../../src/view/node';
 import DocumentFragment from '../../src/view/documentfragment';
 import RootEditableElement from '../../src/view/rooteditableelement';
 
-import createDocumentMock from '../../tests/view/_utils/createdocumentmock';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
+import Document from '../../src/view/document';
+import { StylesProcessor } from '../../src/view/stylesmap';
 
 describe( 'Node', () => {
-	let root,
+	let root, document,
 		one, two, three,
 		charB, charA, charR, img;
 
 	before( () => {
-		charB = new Text( 'b' );
-		charA = new Text( 'a' );
-		img = new Element( 'img' );
-		charR = new Text( 'r' );
+		document = new Document( new StylesProcessor() );
 
-		one = new Element( 'one' );
-		two = new Element( 'two', null, [ charB, charA, img, charR ] );
-		three = new Element( 'three' );
+		charB = new Text( document, 'b' );
+		charA = new Text( document, 'a' );
+		img = new Element( document, 'img' );
+		charR = new Text( document, 'r' );
 
-		root = new Element( null, null, [ one, two, three ] );
+		one = new Element( document, 'one' );
+		two = new Element( document, 'two', null, [ charB, charA, img, charR ] );
+		three = new Element( document, 'three' );
+
+		root = new Element( document, null, null, [ one, two, three ] );
 	} );
 
 	describe( 'is()', () => {
@@ -125,7 +128,7 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return ancestors including DocumentFragment', () => {
-			const fragment = new DocumentFragment( root );
+			const fragment = new DocumentFragment( document, root );
 			const result = img.getAncestors();
 			root._remove();
 
@@ -146,7 +149,7 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return null for detached subtrees', () => {
-			const detached = new Element( 'foo' );
+			const detached = new Element( document, 'foo' );
 
 			expect( img.getCommonAncestor( detached ) ).to.be.null;
 			expect( detached.getCommonAncestor( img ) ).to.be.null;
@@ -176,14 +179,14 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return proper element for nodes in different branches and on different levels', () => {
-			const foo = new Text( 'foo' );
-			const bar = new Text( 'bar' );
-			const bom = new Text( 'bom' );
-			const d = new Element( 'd', null, [ bar ] );
-			const c = new Element( 'c', null, [ foo, d ] );
-			const b = new Element( 'b', null, [ c ] );
-			const e = new Element( 'e', null, [ bom ] );
-			const a = new Element( 'a', null, [ b, e ] );
+			const foo = new Text( document, 'foo' );
+			const bar = new Text( document, 'bar' );
+			const bom = new Text( document, 'bom' );
+			const d = new Element( document, 'd', null, [ bar ] );
+			const c = new Element( document, 'c', null, [ foo, d ] );
+			const b = new Element( document, 'b', null, [ c ] );
+			const e = new Element( document, 'e', null, [ bom ] );
+			const a = new Element( document, 'a', null, [ b, e ] );
 
 			// <a><b><c>foo<d>bar</d></c></b><e>bom</e></a>
 
@@ -203,9 +206,9 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return document fragment', () => {
-			const foo = new Text( 'foo' );
-			const bar = new Text( 'bar' );
-			const df = new DocumentFragment( [ foo, bar ] );
+			const foo = new Text( document, 'foo' );
+			const bar = new Text( document, 'bar' );
+			const df = new DocumentFragment( document, [ foo, bar ] );
 
 			expect( foo.getCommonAncestor( bar ) ).to.equal( df );
 		} );
@@ -228,8 +231,8 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should throw an error if parent does not contain element', () => {
-			const f = new Text( 'f' );
-			const bar = new Element( 'bar', [], [] );
+			const f = new Text( document, 'f' );
+			const bar = new Element( document, 'bar', [], [] );
 
 			f.parent = bar;
 
@@ -253,42 +256,16 @@ describe( 'Node', () => {
 		} );
 	} );
 
-	describe( 'getDocument()', () => {
-		it( 'should return null if any parent has not set Document', () => {
-			expect( charA.document ).to.be.null;
-		} );
-
-		it( 'should return Document attached to the parent element', () => {
-			const docMock = createDocumentMock();
-			const parent = new RootEditableElement( 'div' );
-			parent._document = docMock;
-			const child = new Element( 'p' );
-
-			child.parent = parent;
-
-			expect( parent.document ).to.equal( docMock );
-			expect( child.document ).to.equal( docMock );
-		} );
-
-		it( 'should return null if element is inside DocumentFragment', () => {
-			const child = new Element( 'p' );
-			new DocumentFragment( [ child ] ); // eslint-disable-line no-new
-
-			expect( child.document ).to.be.null;
-		} );
-	} );
-
 	describe( 'getRoot()', () => {
 		it( 'should return this element if it has no parent', () => {
-			const child = new Element( 'p' );
+			const child = new Element( document, 'p' );
 
 			expect( child.root ).to.equal( child );
 		} );
 
 		it( 'should return root element', () => {
-			const parent = new RootEditableElement( 'div' );
-			parent._document = createDocumentMock();
-			const child = new Element( 'p' );
+			const parent = new RootEditableElement( document, 'div' );
+			const child = new Element( document, 'p' );
 
 			child.parent = parent;
 
@@ -326,8 +303,8 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return false if elements are in different roots', () => {
-			const otherRoot = new Element( 'root' );
-			const otherElement = new Element( 'element' );
+			const otherRoot = new Element( document, 'root' );
+			const otherElement = new Element( document, 'element' );
 
 			otherRoot._appendChild( otherElement );
 
@@ -364,8 +341,8 @@ describe( 'Node', () => {
 		} );
 
 		it( 'should return false if elements are in different roots', () => {
-			const otherRoot = new Element( 'root' );
-			const otherElement = new Element( 'element' );
+			const otherRoot = new Element( document, 'root' );
+			const otherElement = new Element( document, 'element' );
 
 			otherRoot._appendChild( otherElement );
 
@@ -373,18 +350,41 @@ describe( 'Node', () => {
 		} );
 	} );
 
+	describe( 'isAttached()', () => {
+		it( 'returns false for a fresh node', () => {
+			const char = new Text( document, 'x' );
+			const el = new Element( document, 'one' );
+
+			expect( char.isAttached() ).to.equal( false );
+			expect( el.isAttached() ).to.equal( false );
+		} );
+
+		it( 'returns true for the root element', () => {
+			const root = new RootEditableElement( document, 'div' );
+
+			expect( root.isAttached() ).to.equal( true );
+		} );
+
+		it( 'returns false for a node attached to a document fragment', () => {
+			const foo = new Text( document, 'foo' );
+			new DocumentFragment( document, [ foo ] ); // eslint-disable-line no-new
+
+			expect( foo.isAttached() ).to.equal( false );
+		} );
+	} );
+
 	describe( '_remove()', () => {
 		it( 'should remove node from its parent', () => {
-			const char = new Text( 'a' );
-			const parent = new Element( 'p', null, [ char ] );
+			const char = new Text( document, 'a' );
+			const parent = new Element( document, 'p', null, [ char ] );
 			char._remove();
 
 			expect( parent.getChildIndex( char ) ).to.equal( -1 );
 		} );
 
 		it( 'uses parent._removeChildren method', () => {
-			const char = new Text( 'a' );
-			const parent = new Element( 'p', null, [ char ] );
+			const char = new Text( document, 'a' );
+			const parent = new Element( document, 'p', null, [ char ] );
 			const _removeChildrenSpy = sinon.spy( parent, '_removeChildren' );
 			const index = char.index;
 			char._remove();
@@ -396,15 +396,18 @@ describe( 'Node', () => {
 
 	describe( 'toJSON()', () => {
 		it( 'should prevent circular reference when stringifying a node', () => {
-			const char = new Text( 'a' );
-			const parent = new Element( 'p', null );
+			const char = new Text( document, 'a' );
+			const parent = new Element( document, 'p', null );
 			parent._appendChild( char );
 
+			sinon.stub( char, 'document' ).value( 'view.Document()' );
+
 			const json = JSON.stringify( char );
 			const parsed = JSON.parse( json );
 
 			expect( parsed ).to.deep.equal( {
-				_textData: 'a'
+				_textData: 'a',
+				document: 'view.Document()'
 			} );
 		} );
 	} );
@@ -417,10 +420,10 @@ describe( 'Node', () => {
 		} );
 
 		beforeEach( () => {
-			text = new Text( 'foo' );
-			img = new Element( 'img', { 'src': 'img.png' } );
+			text = new Text( document, 'foo' );
+			img = new Element( document, 'img', { 'src': 'img.png' } );
 
-			root = new Element( 'p', { renderer: { markToSync: rootChangeSpy } } );
+			root = new Element( document, 'p', { renderer: { markToSync: rootChangeSpy } } );
 			root._appendChild( [ text, img ] );
 
 			root.on( 'change:children', ( evt, node ) => rootChangeSpy( 'children', node ) );
@@ -470,7 +473,7 @@ describe( 'Node', () => {
 
 		describe( '_insertChild()', () => {
 			it( 'should fire change event', () => {
-				root._insertChild( 1, new Element( 'img' ) );
+				root._insertChild( 1, new Element( document, 'img' ) );
 
 				sinon.assert.calledOnce( rootChangeSpy );
 				sinon.assert.calledWith( rootChangeSpy, 'children', root );
@@ -479,7 +482,7 @@ describe( 'Node', () => {
 
 		describe( '_appendChild()', () => {
 			it( 'should fire change event', () => {
-				root._appendChild( new Element( 'img' ) );
+				root._appendChild( new Element( document, 'img' ) );
 
 				sinon.assert.calledOnce( rootChangeSpy );
 				sinon.assert.calledWith( rootChangeSpy, 'children', root );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/clickobserver.js

@@ -7,12 +7,13 @@
 
 import ClickObserver from '../../../src/view/observer/clickobserver';
 import View from '../../../src/view/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'ClickObserver', () => {
 	let view, viewDocument, observer;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.addObserver( ClickObserver );
 	} );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/compositionobserver.js

@@ -6,12 +6,13 @@
 /* globals document */
 import CompositionObserver from '../../../src/view/observer/compositionobserver';
 import View from '../../../src/view/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'CompositionObserver', () => {
 	let view, viewDocument, observer;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.getObserver( CompositionObserver );
 	} );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/domeventdata.js

@@ -7,12 +7,13 @@
 
 import DomEventData from '../../../src/view/observer/domeventdata';
 import View from '../../../src/view/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'DomEventData', () => {
 	let view, viewDocument, viewBody, domRoot;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 
 		domRoot = document.createElement( 'div' );

+ 3 - 2
packages/ckeditor5-engine/tests/view/observer/domeventobserver.js

@@ -10,6 +10,7 @@ import Observer from '../../../src/view/observer/observer';
 import View from '../../../src/view/view';
 import UIElement from '../../../src/view/uielement';
 import createViewRoot from '../_utils/createroot';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 class ClickObserver extends DomEventObserver {
 	constructor( view ) {
@@ -47,7 +48,7 @@ describe( 'DomEventObserver', () => {
 	let view, viewDocument;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 	} );
 
@@ -165,7 +166,7 @@ describe( 'DomEventObserver', () => {
 		let domRoot, domEvent, evtSpy, uiElement;
 
 		function createUIElement( name ) {
-			const element = new UIElement( name );
+			const element = new UIElement( viewDocument, name );
 
 			element.render = function( domDocument ) {
 				const root = this.toDomElement( domDocument );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/fakeselectionobserver.js

@@ -13,6 +13,7 @@ import createViewRoot from '../_utils/createroot';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import { setData, stringify } from '../../../src/dev-utils/view';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'FakeSelectionObserver', () => {
 	let observer, view, viewDocument, root, domRoot;
@@ -30,7 +31,7 @@ describe( 'FakeSelectionObserver', () => {
 	} );
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		root = createViewRoot( viewDocument );
 		view.attachDomRoot( domRoot );

+ 3 - 2
packages/ckeditor5-engine/tests/view/observer/focusobserver.js

@@ -8,12 +8,13 @@ import FocusObserver from '../../../src/view/observer/focusobserver';
 import View from '../../../src/view/view';
 import createViewRoot from '../_utils/createroot';
 import { setData } from '../../../src/dev-utils/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'FocusObserver', () => {
 	let view, viewDocument, observer;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.getObserver( FocusObserver );
 	} );
@@ -157,7 +158,7 @@ describe( 'FocusObserver', () => {
 			domRoot = document.createElement( 'div' );
 			document.body.appendChild( domRoot );
 
-			view = new View();
+			view = new View( new StylesProcessor() );
 			viewDocument = view.document;
 			createViewRoot( viewDocument );
 			view.attachDomRoot( domRoot );

+ 4 - 2
packages/ckeditor5-engine/tests/view/observer/inputobserver.js

@@ -6,17 +6,19 @@
 import InputObserver from '../../../src/view/observer/inputobserver';
 import View from '../../../src/view/view';
 import env from '@ckeditor/ckeditor5-utils/src/env';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'InputObserver', () => {
-	let view, viewDocument, observer;
 	const oldEnvIsAndroid = env.isAndroid;
 
+	let view, viewDocument, observer;
+
 	before( () => {
 		env.isAndroid = true;
 	} );
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.getObserver( InputObserver );
 	} );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/keyobserver.js

@@ -8,12 +8,13 @@
 import KeyObserver from '../../../src/view/observer/keyobserver';
 import View from '../../../src/view/view';
 import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'KeyObserver', () => {
 	let view, viewDocument, observer;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.getObserver( KeyObserver );
 	} );

+ 2 - 1
packages/ckeditor5-engine/tests/view/observer/mouseobserver.js

@@ -7,12 +7,13 @@
 
 import MouseObserver from '../../../src/view/observer/mouseobserver';
 import View from '../../../src/view/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'MouseObserver', () => {
 	let view, viewDocument, observer;
 
 	beforeEach( () => {
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		observer = view.addObserver( MouseObserver );
 	} );

+ 8 - 3
packages/ckeditor5-engine/tests/view/observer/mutationobserver.js

@@ -10,6 +10,7 @@ import MutationObserver from '../../../src/view/observer/mutationobserver';
 import UIElement from '../../../src/view/uielement';
 import createViewRoot from '../_utils/createroot';
 import { parse } from '../../../src/dev-utils/view';
+import { StylesProcessor } from '../../../src/view/stylesmap';
 
 describe( 'MutationObserver', () => {
 	let view, domEditor, viewDocument, viewRoot, mutationObserver, lastMutations, domRoot;
@@ -19,7 +20,7 @@ describe( 'MutationObserver', () => {
 		domRoot.innerHTML = '<div contenteditable="true" id="main"></div><div contenteditable="true" id="additional"></div>';
 		document.body.appendChild( domRoot );
 
-		view = new View();
+		view = new View( new StylesProcessor() );
 		viewDocument = view.document;
 		domEditor = document.getElementById( 'main' );
 		lastMutations = null;
@@ -225,7 +226,9 @@ describe( 'MutationObserver', () => {
 	} );
 
 	it( 'should fire children mutation if the mutation occurred in the inline filler', () => {
-		const { view: viewContainer, selection } = parse( '<container:p>foo<attribute:b>[]</attribute:b>bar</container:p>' );
+		const { view: viewContainer, selection } = parse(
+			'<container:p>foo<attribute:b>[]</attribute:b>bar</container:p>'
+		);
 
 		view.change( writer => {
 			viewRoot._appendChild( viewContainer );
@@ -243,7 +246,9 @@ describe( 'MutationObserver', () => {
 	} );
 
 	it( 'should have no inline filler in mutation', () => {
-		const { view: viewContainer, selection } = parse( '<container:p>foo<attribute:b>[]</attribute:b>bar</container:p>' );
+		const { view: viewContainer, selection } = parse(
+			'<container:p>foo<attribute:b>[]</attribute:b>bar</container:p>'
+		);
 
 		view.change( writer => {
 			viewRoot._appendChild( viewContainer );

Неке датотеке нису приказане због велике количине промена