diff --git a/src/model/documentselection.js b/src/model/documentselection.js index 02b06b22d..a9e9d358c 100644 --- a/src/model/documentselection.js +++ b/src/model/documentselection.js @@ -391,6 +391,17 @@ export default class DocumentSelection extends Selection { return storePrefix + key; } + /** + * Checks whether the given attribute key is an attribute stored on an element. + * + * @protected + * @param {String} key + * @returns {Boolean} + */ + static _isStoreAttributeKey( key ) { + return key.startsWith( storePrefix ); + } + /** * Internal method for setting `DocumentSelection` attribute. Supports attribute priorities (through `directChange` * parameter). diff --git a/src/model/schema.js b/src/model/schema.js index 5f927c3a9..eefd0f189 100644 --- a/src/model/schema.js +++ b/src/model/schema.js @@ -10,6 +10,7 @@ import Position from './position'; import Element from './element'; import Range from './range'; +import DocumentSelection from './documentselection'; import clone from '@ckeditor/ckeditor5-utils/src/lib/lodash/clone'; import isArray from '@ckeditor/ckeditor5-utils/src/lib/lodash/isArray'; import isString from '@ckeditor/ckeditor5-utils/src/lib/lodash/isString'; @@ -217,6 +218,13 @@ export default class Schema { // Keep in mind that if the query has no attributes, query.attribute was converted to an array // with a single `undefined` value. This fits the algorithm well. for ( const attribute of query.attributes ) { + // Skip all attributes that are stored in elements. + // This isn't perfect solution but we have to deal with it for now. + // `attribute` may have `undefined` value. + if ( attribute && DocumentSelection._isStoreAttributeKey( attribute ) ) { + continue; + } + let matched = false; for ( const schemaItem of schemaItems ) { diff --git a/tests/model/documentselection.js b/tests/model/documentselection.js index 8f782d991..5ae1a0543 100644 --- a/tests/model/documentselection.js +++ b/tests/model/documentselection.js @@ -386,6 +386,16 @@ describe( 'DocumentSelection', () => { } ); } ); + describe( '_isStoreAttributeKey', () => { + it( 'should return true if given key is a key of an attribute stored in element by DocumentSelection', () => { + expect( DocumentSelection._isStoreAttributeKey( fooStoreAttrKey ) ).to.be.true; + } ); + + it( 'should return false if given key is not a key of an attribute stored in element by DocumentSelection', () => { + expect( DocumentSelection._isStoreAttributeKey( 'foo' ) ).to.be.false; + } ); + } ); + // DocumentSelection uses LiveRanges so here are only simple test to see if integration is // working well, without getting into complicated corner cases. describe( 'after applying an operation should get updated and fire events', () => { diff --git a/tests/model/schema/schema.js b/tests/model/schema/schema.js index 656c77a2f..d60ab29fb 100644 --- a/tests/model/schema/schema.js +++ b/tests/model/schema/schema.js @@ -242,6 +242,16 @@ describe( 'Schema', () => { it( 'should omit path elements that are added to schema', () => { expect( schema.check( { name: '$inline', inside: '$block new $block' } ) ).to.be.true; } ); + + it( 'should ignore attributes stored in elements by document selection', () => { + expect( schema.check( { name: '$block', attributes: 'selection:foo', inside: '$root' } ) ).to.be.true; + } ); + + it( 'should disallow attribute stored in an element if that attribute was explicitly disallowed', () => { + schema.disallow( { name: '$block', attributes: [ 'selection:foo' ], inside: '$root' } ); + + expect( schema.check( { name: '$block', attributes: [ 'selection:foo' ], inside: '$root' } ) ).to.be.false; + } ); } ); describe( 'array of elements as inside', () => {