diff --git a/packages/ckeditor5-clipboard/src/clipboard.js b/packages/ckeditor5-clipboard/src/clipboard.js
index 5d612ffbc7c..8f3b9984422 100644
--- a/packages/ckeditor5-clipboard/src/clipboard.js
+++ b/packages/ckeditor5-clipboard/src/clipboard.js
@@ -103,6 +103,15 @@ export default class Clipboard extends Plugin {
return;
}
+ // While pasting plain text, apply selection attributes on the text.
+ if ( isPlainText( modelFragment ) ) {
+ const node = modelFragment.getChild( 0 );
+
+ model.change( writer => {
+ writer.setAttributes( modelDocument.selection.getAttributes(), node );
+ } );
+ }
+
model.insertContent( modelFragment );
evt.stop();
}
@@ -198,3 +207,17 @@ export default class Clipboard extends Plugin {
*
* @member {'copy'|'cut'} module:clipboard/clipboard~ClipboardOutputEventData#method
*/
+
+// Returns true if specified `documentFragment` represents a plain text.
+//
+// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
+// @returns {Boolean}
+function isPlainText( documentFragment ) {
+ if ( documentFragment.childCount > 1 ) {
+ return false;
+ }
+
+ const child = documentFragment.getChild( 0 );
+
+ return [ ...child.getAttributeKeys() ].length == 0;
+}
diff --git a/packages/ckeditor5-clipboard/tests/clipboard.js b/packages/ckeditor5-clipboard/tests/clipboard.js
index 84f4fde509f..3ecc18eee7a 100644
--- a/packages/ckeditor5-clipboard/tests/clipboard.js
+++ b/packages/ckeditor5-clipboard/tests/clipboard.js
@@ -343,6 +343,116 @@ describe( 'Clipboard feature', () => {
expect( spy.callCount ).to.equal( 1 );
} );
+ // https://github.com/ckeditor/ckeditor5/issues/1006
+ describe( 'pasting plain text', () => {
+ let model;
+
+ beforeEach( () => {
+ model = editor.model;
+
+ model.schema.extend( '$text', { allowAttributes: 'bold' } );
+ } );
+
+ it( 'should inherit selection attributes (collapsed selection)', () => {
+ const insertContent = model.insertContent.bind( model );
+ let insertedNode;
+
+ sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
+ insertedNode = documentFragment.getChild( 0 );
+
+ return insertContent( documentFragment );
+ } );
+
+ setModelData( model, '<$text bold="true">Bolded []text.$text>' );
+
+ const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );
+
+ viewDocument.fire( 'paste', {
+ dataTransfer: dataTransferMock,
+ stopPropagation() {},
+ preventDefault() {}
+ } );
+
+ expect( getModelData( model ) ).to.equal( '<$text bold="true">Bolded foo[]text.$text>' );
+ expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
+ } );
+
+ it( 'should inherit selection attributes (non-collapsed selection)', () => {
+ const insertContent = model.insertContent.bind( model );
+ let insertedNode;
+
+ sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
+ insertedNode = documentFragment.getChild( 0 );
+
+ return insertContent( documentFragment );
+ } );
+
+ setModelData( model, '<$text bold="true">Bolded [text.]$text>' );
+
+ const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );
+
+ viewDocument.fire( 'paste', {
+ dataTransfer: dataTransferMock,
+ stopPropagation() {},
+ preventDefault() {}
+ } );
+
+ expect( getModelData( model ) ).to.equal( '<$text bold="true">Bolded foo[]$text>' );
+ expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
+ } );
+
+ it( 'should inherit selection attributes while pasting a plain text as text/html', () => {
+ setModelData( model, '<$text bold="true">Bolded []text.$text>' );
+
+ const dataTransferMock = createDataTransfer( {
+ 'text/html': 'foo',
+ 'text/plain': 'foo'
+ } );
+
+ viewDocument.fire( 'paste', {
+ dataTransfer: dataTransferMock,
+ stopPropagation() {},
+ preventDefault() {}
+ } );
+
+ expect( getModelData( model ) ).to.equal( '<$text bold="true">Bolded foo[]text.$text>' );
+ } );
+
+ it( 'should inherit selection attributes while pasting a plain text as text/html (Chrome style)', () => {
+ setModelData( model, '<$text bold="true">Bolded []text.$text>' );
+
+ const dataTransferMock = createDataTransfer( {
+ 'text/html': 'foo',
+ 'text/plain': 'foo'
+ } );
+
+ viewDocument.fire( 'paste', {
+ dataTransfer: dataTransferMock,
+ stopPropagation() {},
+ preventDefault() {}
+ } );
+
+ expect( getModelData( model ) ).to.equal( '<$text bold="true">Bolded foo[]text.$text>' );
+ } );
+
+ it( 'should inherit selection attributes while pasting HTML with unsupported attributes', () => {
+ setModelData( model, '<$text bold="true">Bolded []text.$text>' );
+
+ const dataTransferMock = createDataTransfer( {
+ 'text/html': 'foo',
+ 'text/plain': 'foo'
+ } );
+
+ viewDocument.fire( 'paste', {
+ dataTransfer: dataTransferMock,
+ stopPropagation() {},
+ preventDefault() {}
+ } );
+
+ expect( getModelData( model ) ).to.equal( '<$text bold="true">Bolded foo[]text.$text>' );
+ } );
+ } );
+
function createDataTransfer( data ) {
return {
getData( type ) {