Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for Edge not supporting custom MIME types in dataTransfer.setData #968

Merged
merged 47 commits into from
Nov 10, 2017
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bf67ea6
Switch on support for Clipboard API in Edge 15+.
Comandeer Jun 10, 2017
9d5d97f
Updated tools for clipboard unit tests.
Comandeer Jun 10, 2017
b039172
Updated tests for dataTransfer.
Comandeer Jun 10, 2017
36c4da1
Clipboard support from Edge 16.
f1ames Sep 21, 2017
f39e02d
Initial clipboard API support for Edge >= 16.
f1ames Sep 22, 2017
280d3c0
Edge workaround.
f1ames Sep 26, 2017
cf26c37
Unit tests.
f1ames Sep 26, 2017
293fcc6
Set id after setting other MIME types.
f1ames Sep 26, 2017
f8c8e02
Edge version fix in tests.
f1ames Sep 26, 2017
2427a6f
Manual test added.
f1ames Sep 26, 2017
76a1bb9
Specs corrections.
f1ames Sep 26, 2017
b431eca
Fixed failing tests.
f1ames Sep 27, 2017
704a11f
Test fixes.
f1ames Sep 27, 2017
2347c18
Fix for 'OpenClipboard Failed' error.
f1ames Sep 28, 2017
95078d2
Set native dataTransfer id only on certain events.
f1ames Oct 2, 2017
ea41a52
Tests: set native dataTransfer id only on certain events.
f1ames Oct 2, 2017
a5dba7f
Extracted all Edge logic to 'CKEDITOR.plugins.clipboard.fallbackDataT…
f1ames Oct 10, 2017
4eafeee
Introduced 'setId' method.
f1ames Oct 10, 2017
596f2b2
Docs fix.
f1ames Oct 10, 2017
d6e8fcb
Fallback data transfer improvements.
f1ames Oct 11, 2017
230e7ca
Fixed tests.
f1ames Oct 11, 2017
0404325
Handle empty 'dataTransfer' in 'isRequired' method.
f1ames Oct 11, 2017
5d3abe1
Fixed failing test.
f1ames Oct 11, 2017
514f93c
Fix for 'dataTransfer.items'.
f1ames Oct 11, 2017
cac60ad
Proper Edge workaround description.
f1ames Oct 12, 2017
6223ae2
Refactoring, simplified manual test.
f1ames Oct 13, 2017
31a1a53
Added fallbackDataTransfer unit tests.
f1ames Oct 13, 2017
89e6830
Troublesome test workaround.
f1ames Oct 13, 2017
59ab5cd
Docs adjustments. Simplified 'isRequired' method.
f1ames Oct 19, 2017
10b8bea
Use 'clearData' to remove test mime type.
f1ames Oct 19, 2017
b3b776b
Renamed 'fallbackDataTransfer' to '_fallbackDataTransfer'.
f1ames Oct 20, 2017
51c4045
Refactoring.
f1ames Oct 20, 2017
0a2ed5d
Refactor and rename 'setId' to 'storeId'.
f1ames Oct 20, 2017
4b77734
Specs adjustments.
f1ames Oct 20, 2017
767f52c
Added manual test for custom data transfer types.
mlewand Oct 24, 2017
28c4601
Simplified manual test.
mlewand Oct 24, 2017
5e52ce3
Improved manual test.
mlewand Oct 24, 2017
e359599
Shared cache added to 'fallbackDataTransfer'.
f1ames Oct 26, 2017
9b93460
Fix for 'getNative' param.
f1ames Oct 26, 2017
96bf403
Docs corrections.
f1ames Oct 26, 2017
0aac1fe
Corrected input data in manual test.
mlewand Nov 6, 2017
a94c5bd
Cache simplified and refactored.
f1ames Nov 7, 2017
9c0a25e
Tests: refactoring and adjusting to new cache structure.
f1ames Nov 7, 2017
3c621f4
Tests: corrected manual test not to leak undefined class.
mlewand Nov 8, 2017
3375736
Tests: use CKEditor dataTransfer API for getting raw HTML rather than…
mlewand Nov 8, 2017
e61ac60
Review fixes.
f1ames Nov 8, 2017
270fbda
Tests: ignore datatransfer fallback manual test for browsers that do …
mlewand Nov 10, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
414 changes: 378 additions & 36 deletions plugins/clipboard/plugin.js

Large diffs are not rendered by default.

35 changes: 29 additions & 6 deletions tests/_benderjs/ckeditor/static/tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -816,13 +816,26 @@
types: [],
files: CKEDITOR.env.ie && CKEDITOR.env.version < 10 ? undefined : [],
_data: {},
// Emulate browsers native behavior for getDeta/setData.
// Emulate browsers native behavior for getData/setData.
setData: function( type, data ) {
if ( CKEDITOR.env.ie && type != 'Text' && type != 'URL' )
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 16 && type != 'Text' && type != 'URL' ) {
throw 'Unexpected call to method or property access.';
}

if ( CKEDITOR.env.ie && CKEDITOR.env.version > 9 && type == 'URL' )
if ( CKEDITOR.env.ie && CKEDITOR.env.version > 9 && type == 'URL' ) {
return;
}

// While Edge 16+ supports Clipboard API, it does not support custom mime types
// in `setData` and throws `Element not found.` if such are used.
if ( CKEDITOR.env.edge && CKEDITOR.env.version >= 16 &&
CKEDITOR.tools.indexOf( [ 'Text', 'URL', 'text/plain', 'text/html', 'application/xml' ], type ) === -1 ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A short comment explanation, would be useful here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I believe it's valid only for Edge, isn't it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


throw {
name: 'Error',
message: 'Element not found.'
};
}

if ( type == 'text/plain' || type == 'Text' ) {
this._data[ 'text/plain' ] = data;
Expand All @@ -834,13 +847,23 @@
this.types.push( type );
},
getData: function( type ) {
if ( CKEDITOR.env.ie && type != 'Text' && type != 'URL' )
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 16 && type != 'Text' && type != 'URL' ) {
throw 'Invalid argument.';
}

if ( typeof this._data[ type ] === 'undefined' || this._data[ type ] === null )
if ( typeof this._data[ type ] === 'undefined' || this._data[ type ] === null ) {
return '';
}

return this._data[ type ];
},
clearData: function( type ) {
var index = CKEDITOR.tools.indexOf( this.types, type );

if ( index !== -1 ) {
delete this._data[ type ];
this.types.splice( index, 1 );
}
}
};
},
Expand Down Expand Up @@ -884,7 +907,7 @@
return {
$: {
ctrlKey: true,
clipboardData: CKEDITOR.env.ie ? undefined : dataTransfer
clipboardData: ( CKEDITOR.env.ie && CKEDITOR.env.version < 16 ) ? undefined : dataTransfer
},
preventDefault: function() {
// noop
Expand Down
172 changes: 150 additions & 22 deletions tests/plugins/clipboard/datatransfer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,25 @@ bender.test( {
'test id': function() {
var nativeData1 = bender.tools.mockNativeDataTransfer(),
nativeData2 = bender.tools.mockNativeDataTransfer(),
dataTransfer1a = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 ),
dataTransfer1b = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 ),
dataTransfer2 = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData2 );
dataTransfer1a, dataTransfer1b, dataTransfer2;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Vars should be defined each in a separate line.


// Setting id was moved from dataTransfer constructor to functions which initializes dataTransfer object
// only on specific events so we need to simulate these behaviour here too (#962).
dataTransfer1a = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 );
dataTransfer1a.storeId();

dataTransfer1b = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 );
dataTransfer1b.storeId();

dataTransfer2 = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData2 );
dataTransfer2.storeId();

assert.areSame( dataTransfer1a.id, dataTransfer1b.id, 'Ids for object based on the same event should be the same.' );

// In IE we can not use any data type besides text, so id is fixed.
if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported )
if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {
assert.areNotSame( dataTransfer1a.id, dataTransfer2.id, 'Ids for object based on different events should be different.' );
}
},

'test internal drag drop': function() {
Expand Down Expand Up @@ -477,12 +487,42 @@ bender.test( {
assert.areSame( html, dataTransfer.getData( 'text/html', true ) );
},

'test getData with getNative flag if cache differs from native data': function() {
if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) {
return assert.ignore();
}

var html = '<html>' +
'<head>' +
'<meta charset="UTF-8">' +
'<meta name="foo" content=bar>' +
'<STYLE>h1 { color: red; }</style>' +
'</head>' +
'<BODY>' +
'<!--StartFragment--><p>Foo</p>' +
'<p>Bar</p><!--EndFragment-->' +
'</body>' +
'</html>',
newHtml = html.replace( 'Bar', 'Baz' ),
nativeData = bender.tools.mockNativeDataTransfer(),
dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData );

nativeData.setData( 'text/html', html );
dataTransfer.cacheData();
nativeData.setData( 'text/html', newHtml );

assert.areSame( newHtml, dataTransfer.getData( 'text/html', true ) );
},

'test cacheData': function() {
var isCustomDataTypesSupported = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported,
// Emulate native clipboard.
nativeData = bender.tools.mockNativeDataTransfer();

if ( isCustomDataTypesSupported ) {
// This test uses mocked `setData` which does not applies fallback
// for Edge >= 16 (because it skips `CKEDITOR.plugins.clipboard.dataTransfer` wrapper)
// so it works as if `isCustomDataTypesSupported` flag was turned off for Edge (#962).
if ( isCustomDataTypesSupported && !CKEDITOR.env.edge ) {
nativeData.setData( 'text/html', 'foo' );
nativeData.setData( 'text/plain', 'bom' );
nativeData.setData( 'cke/custom', 'bar' );
Expand All @@ -501,8 +541,8 @@ bender.test( {
nativeData.setData = throwPermissionDenied;
nativeData.getData = throwPermissionDenied;

// Assert
if ( isCustomDataTypesSupported ) {
// Assert. Edge browser case same as above (#962).
if ( isCustomDataTypesSupported && !CKEDITOR.env.edge ) {
assert.areSame( 'foo', dataTransfer.getData( 'text/html' ) );
assert.areSame( 'bom', dataTransfer.getData( 'text/plain' ) );
assert.areSame( 'bar', dataTransfer.getData( 'cke/custom' ) );
Expand Down Expand Up @@ -553,10 +593,6 @@ bender.test( {

// http://dev.ckeditor.com/ticket/12961
'test file in items': function() {
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {
assert.ignore();
}

var nativeData = bender.tools.mockNativeDataTransfer(),
dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ),
file = { type: 'type' };
Expand Down Expand Up @@ -642,10 +678,6 @@ bender.test( {

// http://dev.ckeditor.com/ticket/12961
'test file in items with cache': function() {
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) {
assert.ignore();
}

var nativeData = bender.tools.mockNativeDataTransfer(),
dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ),
file = { type: 'type' };
Expand Down Expand Up @@ -688,7 +720,6 @@ bender.test( {

nativeData.files.push( 'foo' );

// debugger;
dataTransfer.cacheData();

assert.areSame( 1, dataTransfer.getFilesCount() );
Expand Down Expand Up @@ -813,9 +844,9 @@ bender.test( {
'test initDragDataTransfer binding': function() {
var nativeData1 = bender.tools.mockNativeDataTransfer(),
nativeData2 = bender.tools.mockNativeDataTransfer(),
evt1a = { data: { $: { dataTransfer: nativeData1 } } },
evt1b = { data: { $: { dataTransfer: nativeData1 } } },
evt2 = { data: { $: { dataTransfer: nativeData2 } } };
evt1a = { data: { $: { dataTransfer: nativeData1 } }, name: 'dragstart' },
evt1b = { data: { $: { dataTransfer: nativeData1 } }, name: 'dragstart' },
evt2 = { data: { $: { dataTransfer: nativeData2 } }, name: 'dragstart' };

CKEDITOR.plugins.clipboard.initDragDataTransfer( evt1a );
CKEDITOR.plugins.clipboard.initDragDataTransfer( evt1b );
Expand Down Expand Up @@ -892,9 +923,9 @@ bender.test( {

var nativeData1 = bender.tools.mockNativeDataTransfer(),
nativeData2 = bender.tools.mockNativeDataTransfer(),
evt1 = { data: { $: { clipboardData: nativeData1 } } },
evt2 = { data: { $: { clipboardData: nativeData1 } } },
evt3 = { data: { $: { clipboardData: nativeData2 } } },
evt1 = { data: { $: { clipboardData: nativeData1 } }, name: 'copy' },
evt2 = { data: { $: { clipboardData: nativeData1 } }, name: 'copy' },
evt3 = { data: { $: { clipboardData: nativeData2 } }, name: 'copy' },
dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ),
dataTransfer2 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt2 ),
dataTransfer3 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt3 );
Expand Down Expand Up @@ -950,5 +981,102 @@ bender.test( {
text: isCustomDataTypesSupported ? 'xfoox' : '',
html: 'x<b>foo</b>x' },
dataTransfer );
},

// (#962)
'test new dataTransfer id is created for copy/cut/dragstart events': function() {
if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) {
assert.ignore();
}

var nativeData1 = bender.tools.mockNativeDataTransfer(),
nativeData2 = bender.tools.mockNativeDataTransfer(),
nativeData3 = bender.tools.mockNativeDataTransfer(),
evt1 = { data: { $: { clipboardData: nativeData1 } }, name: 'copy' },
evt2 = { data: { $: { clipboardData: nativeData2 } }, name: 'cut' },
evt3 = { data: { $: { dataTransfer: nativeData3 } }, name: 'dragstart' },
dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ),
dataTransfer2 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt2 ),
dtFallback1 = dataTransfer1._.fallbackDataTransfer,
dtFallback2 = dataTransfer2._.fallbackDataTransfer,
dataTransfer3,
dtFallback3;

CKEDITOR.plugins.clipboard.initDragDataTransfer( evt3 );
dataTransfer3 = evt3.data.dataTransfer;
dtFallback3 = dataTransfer3._.fallbackDataTransfer;

// Check if ids are not empty.
assert.isTrue( dataTransfer1.id.length > 0, 'dataTransfer1 id is not empty' );
assert.isTrue( dataTransfer2.id.length > 0, 'dataTransfer2 id is not empty' );
assert.isTrue( dataTransfer3.id.length > 0, 'dataTransfer3 id is not empty' );

if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && !CKEDITOR.env.edge ) {
assert.areSame( dataTransfer1.id, nativeData1.getData( 'cke/id' ), 'cke/id type holds dataTransfer1 id' );
assert.areSame( dataTransfer2.id, nativeData2.getData( 'cke/id' ), 'cke/id type holds dataTransfer2 id' );
assert.areSame( dataTransfer3.id, nativeData3.getData( 'cke/id' ), 'cke/id type holds dataTransfer3 id' );
} else {
assert.areSame( dataTransfer1.id,
dtFallback1._extractDataComment( nativeData1.getData( dtFallback1._customDataFallbackType ) ).data[ 'cke/id' ],
'cke/id custom data holds dataTransfer1 id' );

assert.areSame( dataTransfer2.id,
dtFallback2._extractDataComment( nativeData2.getData( dtFallback2._customDataFallbackType ) ).data[ 'cke/id' ],
'cke/id custom data holds dataTransfer2 id' );

assert.areSame( dataTransfer3.id,
dtFallback3._extractDataComment( nativeData3.getData( dtFallback3._customDataFallbackType ) ).data[ 'cke/id' ],
'cke/id custom data holds dataTransfer3 id' );
}
},

// (#962)
'test no new dataTransfer id is created for paste/drop/dragend events': function() {
if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) {
assert.ignore();
}

var nativeData1 = bender.tools.mockNativeDataTransfer(),
nativeData2 = bender.tools.mockNativeDataTransfer(),
nativeData3 = bender.tools.mockNativeDataTransfer(),
evt1 = { data: { $: { clipboardData: nativeData1 } }, name: 'paste' },
evt2 = { data: { $: { dataTransfer: nativeData2 } }, name: 'drop' },
evt3 = { data: { $: { dataTransfer: nativeData3 } }, name: 'dragend' },
dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ),
dataTransfer2,
dataTransfer3;

CKEDITOR.plugins.clipboard.initDragDataTransfer( evt2 );
dataTransfer2 = evt2.data.dataTransfer;

CKEDITOR.plugins.clipboard.initDragDataTransfer( evt3 );
dataTransfer3 = evt3.data.dataTransfer;

if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && !CKEDITOR.env.edge ) {
assert.areSame( '', nativeData1.getData( 'cke/id' ), 'dataTransfer1 id is empty' );
assert.areSame( '', nativeData2.getData( 'cke/id' ), 'dataTransfer2 id is empty' );
assert.areSame( '', nativeData3.getData( 'cke/id' ), 'dataTransfer2 id is empty' );
} else {
// As dataTransfer id is stored in `customDataFallbackType` ('text/html' mime type), we just check if it is empty.
assert.areSame( '',
nativeData1.getData( dataTransfer1._.fallbackDataTransfer._customDataFallbackType ), 'dataTransfer1 id is empty' );
assert.areSame( '',
nativeData2.getData( dataTransfer2._.fallbackDataTransfer._customDataFallbackType ), 'dataTransfer2 id is empty' );
assert.areSame( '',
nativeData2.getData( dataTransfer3._.fallbackDataTransfer._customDataFallbackType ), 'dataTransfer2 id is empty' );
}
},

'test if cache is initialized on dataTransfer creation': function() {
var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._.cache;

assert.isObject( cache, 'cache should be initialized' );
},

'test if different dataTransfer objects has different caches': function() {
var dt1 = new CKEDITOR.plugins.clipboard.dataTransfer(),
dt2 = new CKEDITOR.plugins.clipboard.dataTransfer();

assert.isTrue( dt1._.cache !== dt2._.cache, 'caches should not be equal' );
}
} );
33 changes: 33 additions & 0 deletions tests/plugins/clipboard/fallbackdatatransfer.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<div id="case1">
<!--cke-data:%7B%22test%22%3A1%7D-->
<h1>Header1</h1>
<p>Test1</p>
</div>

<div id="case2">
<!--cke-data:%7B%22test%22%3A1%2C%22comment%22%3A%22%3C!--%20comment%20--%3E%22%7D-->
<h1>Header1</h1>
<p>Test1</p>
</div>

<div id="case3">
<!--cke-data:%7B%22test%22%3A1%7D-->
<!-- Start Comment -->
<h1>Header1</h1>
<p>Test1</p>
</div>

<div id="case4">
<!--cke-data:%7B%22test%22%3A123%7D-->
<h1>Header1</h1>
<p>Test1</p>
<!-- End Comment -->
</div>

<div id="empty-content">
<!--cke-data:%7B%22test%22%3A1%7D-->
</div>

<div id="empty-data">
<p>foobar</p>
</div>
Loading