diff --git a/packages/ckeditor5-image/src/image/utils.js b/packages/ckeditor5-image/src/image/utils.js
index 12584bb1db9..e430ac0c455 100644
--- a/packages/ckeditor5-image/src/image/utils.js
+++ b/packages/ckeditor5-image/src/image/utils.js
@@ -54,18 +54,20 @@ export function createBlockImageViewElement( writer ) {
* @returns {module:engine/view/matcher~MatcherPattern}
*/
export function getImgViewElementMatcher( editor, matchImageType ) {
- if ( editor.plugins.has( 'ImageInlineEditing' ) !== editor.plugins.has( 'ImageBlockEditing' ) ) {
- return { name: 'img' };
- }
-
const imageUtils = editor.plugins.get( 'ImageUtils' );
+ const areBothImagePluginsLoaded = editor.plugins.has( 'ImageInlineEditing' ) && editor.plugins.has( 'ImageBlockEditing' );
return element => {
- // Check if view element is an `img`.
+ // Check if the matched view element is an
.
if ( !imageUtils.isInlineImageView( element ) ) {
return null;
}
+ // If just one of the plugins is loaded (block or inline), it will match all kinds of images.
+ if ( !areBothImagePluginsLoaded ) {
+ return getPositiveMatchPattern( element );
+ }
+
// The
can be standalone, wrapped in ... (ImageBlock plugin) or
// wrapped in ... (LinkImage plugin).
const imageType = element.findAncestor( imageUtils.isBlockImageView ) ? 'imageBlock' : 'imageInline';
@@ -74,8 +76,21 @@ export function getImgViewElementMatcher( editor, matchImageType ) {
return null;
}
- return { name: true };
+ return getPositiveMatchPattern( element );
};
+
+ function getPositiveMatchPattern( element ) {
+ const pattern = {
+ name: true
+ };
+
+ // This will trigger src consumption (See https://github.com/ckeditor/ckeditor5/issues/11530).
+ if ( element.hasAttribute( 'src' ) ) {
+ pattern.attributes = [ 'src' ];
+ }
+
+ return pattern;
+ }
}
/**
diff --git a/packages/ckeditor5-image/tests/image/imageblockediting.js b/packages/ckeditor5-image/tests/image/imageblockediting.js
index f39916ef6ea..1f1d3f700af 100644
--- a/packages/ckeditor5-image/tests/image/imageblockediting.js
+++ b/packages/ckeditor5-image/tests/image/imageblockediting.js
@@ -290,6 +290,14 @@ describe( 'ImageBlockEditing', () => {
.to.equal( '' );
} );
+ it( 'should consume the src attribute on
', () => {
+ editor.data.upcastDispatcher.on( 'element:img', ( evt, data, conversionApi ) => {
+ expect( conversionApi.consumable.test( data.viewItem, { attributes: 'src' } ) ).to.be.false;
+ }, { priority: 'low' } );
+
+ editor.setData( '
' );
+ } );
+
it( 'should dispatch conversion for nested elements', () => {
const conversionSpy = sinon.spy();
editor.data.upcastDispatcher.on( 'element:figcaption', conversionSpy );
diff --git a/packages/ckeditor5-image/tests/image/imageinlineediting.js b/packages/ckeditor5-image/tests/image/imageinlineediting.js
index 5afa15366b4..a0414b66d1e 100644
--- a/packages/ckeditor5-image/tests/image/imageinlineediting.js
+++ b/packages/ckeditor5-image/tests/image/imageinlineediting.js
@@ -278,6 +278,14 @@ describe( 'ImageInlineEditing', () => {
.to.equal( '' );
} );
+ it( 'should consume the src attribute on
', () => {
+ editor.data.upcastDispatcher.on( 'element:img', ( evt, data, conversionApi ) => {
+ expect( conversionApi.consumable.test( data.viewItem, { attributes: 'src' } ) ).to.be.false;
+ }, { priority: 'low' } );
+
+ editor.setData( '

' );
+ } );
+
it( 'should dispatch conversion for nested elements', () => {
const conversionSpy = sinon.spy();
editor.data.upcastDispatcher.on( 'element:img', conversionSpy );
diff --git a/packages/ckeditor5-image/tests/image/utils.js b/packages/ckeditor5-image/tests/image/utils.js
index 49914381b80..fac5c51285f 100644
--- a/packages/ckeditor5-image/tests/image/utils.js
+++ b/packages/ckeditor5-image/tests/image/utils.js
@@ -128,42 +128,75 @@ describe( 'image utils', () => {
} );
describe( 'getImgViewElementMatcher()', () => {
- let editor;
+ describe( 'when one of the image editing plugins is not loaded', () => {
+ let editor;
- beforeEach( async () => {
- editor = await VirtualTestEditor.create( {
- plugins: [ ImageUtils, ImageEditing ]
- } );
+ beforeEach( async () => {
+ editor = await VirtualTestEditor.create( {
+ plugins: [ ImageUtils, ImageEditing ]
+ } );
- imageUtils = editor.plugins.get( 'ImageUtils' );
- } );
+ imageUtils = editor.plugins.get( 'ImageUtils' );
- afterEach( async () => {
- editor.destroy();
- } );
+ writer = new UpcastWriter( editor.editing.view.document );
+ } );
- describe( 'when one of the image editing plugins is not loaded', () => {
- const returnValue = {
- name: 'img'
- };
+ afterEach( async () => {
+ editor.destroy();
+ } );
it( 'should return a matcher pattern for an img element if ImageBlockEditing plugin is not loaded', () => {
sinon.stub( editor.plugins, 'has' ).callsFake( pluginName => pluginName !== 'ImageBlockEditing' );
- expect( getImgViewElementMatcher( editor, 'imageBlock' ) ).to.eql( returnValue );
- expect( getImgViewElementMatcher( editor, 'imageInline' ) ).to.eql( returnValue );
+ element = writer.createElement( 'img', { src: 'sample.jpg' } );
+ writer.appendChild( element, writer.createElement( 'figure', { class: 'image' } ) );
+
+ expect( getImgViewElementMatcher( editor, 'imageBlock' )( element ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
+
+ expect( getImgViewElementMatcher( editor, 'imageInline' )( element ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
+ } );
+
+ it( 'should return a matcher pattern for an img element if ImageInlineEditing plugin is not loaded', () => {
+ sinon.stub( editor.plugins, 'has' ).callsFake( pluginName => pluginName !== 'ImageInlineEditing' );
+
+ element = writer.createElement( 'img', { src: 'sample.jpg' } );
+ writer.appendChild( element, writer.createElement( 'figure', { class: 'image' } ) );
+
+ expect( getImgViewElementMatcher( editor, 'imageBlock' )( element ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
+
+ expect( getImgViewElementMatcher( editor, 'imageInline' )( element ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
} );
- it( 'should return a matcher patter for an img element if ImageInlineEditing plugin is not loaded', () => {
+ it( 'should not include "src" in the matcher pattern if the image has no "src"', () => {
sinon.stub( editor.plugins, 'has' ).callsFake( pluginName => pluginName !== 'ImageInlineEditing' );
- expect( getImgViewElementMatcher( editor, 'imageBlock', editor ) ).to.eql( returnValue );
- expect( getImgViewElementMatcher( editor, 'imageInline' ) ).to.eql( returnValue );
+ element = writer.createElement( 'img' );
+ writer.appendChild( element, writer.createElement( 'figure', { class: 'image' } ) );
+
+ expect( getImgViewElementMatcher( editor, 'imageBlock' )( element ) ).to.deep.equal( {
+ name: true
+ } );
+
+ expect( getImgViewElementMatcher( editor, 'imageInline' )( element ) ).to.deep.equal( {
+ name: true
+ } );
} );
} );
describe( 'when both image editing plugins are loaded', () => {
- let matcherPattern, editorElement;
+ let editor, matcherPattern, editorElement;
beforeEach( async () => {
editorElement = document.createElement( 'div' );
@@ -184,7 +217,7 @@ describe( 'image utils', () => {
} );
describe( 'the returned matcherPattern function', () => {
- describe( 'for the "image" type requested', () => {
+ describe( 'for the "imageBlock" type requested', () => {
beforeEach( () => {
matcherPattern = getImgViewElementMatcher( editor, 'imageBlock' );
} );
@@ -225,6 +258,16 @@ describe( 'image utils', () => {
element = writer.createElement( 'img', { src: 'sample.jpg' } );
writer.appendChild( element, writer.createElement( 'figure', { class: 'image' } ) );
+ expect( matcherPattern( element ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
+ } );
+
+ it( 'should not include "src" in the matcher pattern if the image has no "src"', () => {
+ element = writer.createElement( 'img' );
+ writer.appendChild( element, writer.createElement( 'figure', { class: 'image' } ) );
+
expect( matcherPattern( element ) ).to.deep.equal( {
name: true
} );
@@ -261,7 +304,8 @@ describe( 'image utils', () => {
element = writer.createElement( 'img', { src: 'sample.jpg' } );
expect( matcherPattern( element ) ).to.deep.equal( {
- name: true
+ name: true,
+ attributes: [ 'src' ]
} );
} );
@@ -273,6 +317,15 @@ describe( 'image utils', () => {
);
expect( matcherPattern( fragment.selection.getSelectedElement() ) ).to.deep.equal( {
+ name: true,
+ attributes: [ 'src' ]
+ } );
+ } );
+
+ it( 'should not include "src" in the matcher pattern if the image has no "src"', () => {
+ element = writer.createElement( 'img' );
+
+ expect( matcherPattern( element ) ).to.deep.equal( {
name: true
} );
} );