From bf67ea662b70caffed9430259a6b201db7f54b94 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Sat, 10 Jun 2017 15:54:53 +0200 Subject: [PATCH 01/47] Switch on support for Clipboard API in Edge 15+. --- plugins/clipboard/plugin.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 42191cf7079..4a8bb436495 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -1515,7 +1515,7 @@ * @readonly * @property {Boolean} */ - isCustomCopyCutSupported: !CKEDITOR.env.ie && !CKEDITOR.env.iOS, + isCustomCopyCutSupported: ( !CKEDITOR.env.ie || CKEDITOR.env.version >= 15 ) && !CKEDITOR.env.iOS, /** * True if the environment supports MIME types and custom data types in dataTransfer/cliboardData getData/setData methods. @@ -1524,7 +1524,7 @@ * @readonly * @property {Boolean} */ - isCustomDataTypesSupported: !CKEDITOR.env.ie, + isCustomDataTypesSupported: !CKEDITOR.env.ie || CKEDITOR.env.version >= 15, /** * True if the environment supports File API. @@ -1583,6 +1583,12 @@ return true; } + // Edge 15 added support for Clipboard API + // (https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6515107-clipboard-api) (#468). + if ( CKEDITOR.env.edge && CKEDITOR.env.version >= 15 ) { + return true; + } + // In older Safari and IE HTML data is not available though the Clipboard API. // In Edge things are a bit messy at the moment - // https://connect.microsoft.com/IE/feedback/details/1572456/edge-clipboard-api-text-html-content-messed-up-in-event-clipboarddata From 9d5d97fe2035c42b64a32872f81218f67bda1983 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Sat, 10 Jun 2017 15:55:24 +0200 Subject: [PATCH 02/47] Updated tools for clipboard unit tests. --- tests/_benderjs/ckeditor/static/tools.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index 0651c7a6f10..f5577cf434e 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -818,7 +818,7 @@ _data: {}, // Emulate browsers native behavior for getDeta/setData. setData: function( type, data ) { - if ( CKEDITOR.env.ie && type != 'Text' && type != 'URL' ) + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 15 && type != 'Text' && type != 'URL' ) throw 'Unexpected call to method or property access.'; if ( CKEDITOR.env.ie && CKEDITOR.env.version > 9 && type == 'URL' ) @@ -834,7 +834,7 @@ this.types.push( type ); }, getData: function( type ) { - if ( CKEDITOR.env.ie && type != 'Text' && type != 'URL' ) + if ( CKEDITOR.env.ie && CKEDITOR.env.version < 15 && type != 'Text' && type != 'URL' ) throw 'Invalid argument.'; if ( typeof this._data[ type ] === 'undefined' || this._data[ type ] === null ) @@ -884,7 +884,7 @@ return { $: { ctrlKey: true, - clipboardData: CKEDITOR.env.ie ? undefined : dataTransfer + clipboardData: ( CKEDITOR.env.ie && CKEDITOR.env.version < 15 ) ? undefined : dataTransfer }, preventDefault: function() { // noop From b03917224b59ea436114057e9b8280db9b78e330 Mon Sep 17 00:00:00 2001 From: Tomasz Jakut Date: Sat, 10 Jun 2017 15:55:43 +0200 Subject: [PATCH 03/47] Updated tests for dataTransfer. --- tests/plugins/clipboard/paste.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index 52860464307..8dd1fc4ef4c 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -49,7 +49,8 @@ } ); } - var trustySafari = CKEDITOR.env.safari && CKEDITOR.env.version >= 603 && !CKEDITOR.env.iOS; + var trustySafari = CKEDITOR.env.safari && CKEDITOR.env.version >= 603 && !CKEDITOR.env.iOS, + trustyEdge = CKEDITOR.env.edge && CKEDITOR.env.version >= 15; bender.test( { setUp: function() { @@ -1362,6 +1363,23 @@ assert.isTrue( canClipboardApiBeTrusted( dataTransfer ), 'Clipboard API should be marked as trusted.' ); }, + // #468 + 'test canClipboardApiBeTrusted in Edge 15+': function() { + if ( !trustyEdge ) { + assert.ignore(); + } + + var canClipboardApiBeTrusted = CKEDITOR.plugins.clipboard.canClipboardApiBeTrusted, + nativeData = bender.tools.mockNativeDataTransfer(); + + nativeData.setData( 'text/html', 'foo' ); + + var evt = { data: { $: { clipboardData: nativeData } } }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt ); + + assert.isTrue( canClipboardApiBeTrusted( dataTransfer ), 'Clipboard API should be marked as trusted.' ); + }, + 'test canClipboardApiBeTrusted in Android Chrome (no dataTransfer support)': function() { if ( !CKEDITOR.env.chrome ) { assert.ignore(); @@ -1425,7 +1443,7 @@ }, 'test canClipboardApiBeTrusted on other browser': function() { - if ( CKEDITOR.env.chrome || CKEDITOR.env.gecko || trustySafari ) { + if ( CKEDITOR.env.chrome || CKEDITOR.env.gecko || trustySafari || trustyEdge ) { assert.ignore(); } From 36c4da103d5ffdfe39add25ad5234749d4953785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 21 Sep 2017 15:35:05 +0200 Subject: [PATCH 04/47] Clipboard support from Edge 16. --- plugins/clipboard/plugin.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 4a8bb436495..e1bcf14aa52 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -1515,7 +1515,7 @@ * @readonly * @property {Boolean} */ - isCustomCopyCutSupported: ( !CKEDITOR.env.ie || CKEDITOR.env.version >= 15 ) && !CKEDITOR.env.iOS, + isCustomCopyCutSupported: ( !CKEDITOR.env.ie || CKEDITOR.env.version >= 16 ) && !CKEDITOR.env.iOS, /** * True if the environment supports MIME types and custom data types in dataTransfer/cliboardData getData/setData methods. @@ -1524,7 +1524,7 @@ * @readonly * @property {Boolean} */ - isCustomDataTypesSupported: !CKEDITOR.env.ie || CKEDITOR.env.version >= 15, + isCustomDataTypesSupported: !CKEDITOR.env.ie, /** * True if the environment supports File API. @@ -1584,8 +1584,9 @@ } // Edge 15 added support for Clipboard API - // (https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6515107-clipboard-api) (#468). - if ( CKEDITOR.env.edge && CKEDITOR.env.version >= 15 ) { + // (https://wpdev.uservoice.com/forums/257854-microsoft-edge-developer/suggestions/6515107-clipboard-api), however it is + // usable for our case starting from Edge 16 (#468). + if ( CKEDITOR.env.edge && CKEDITOR.env.version >= 16 ) { return true; } From f39e02d841cdb80808de1fe575c71bd316d5395a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Fri, 22 Sep 2017 13:44:02 +0200 Subject: [PATCH 05/47] Initial clipboard API support for Edge >= 16. --- plugins/clipboard/plugin.js | 45 ++++++++++++++++++++++++++++--------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index e1bcf14aa52..a5d919f167e 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -187,6 +187,7 @@ } editor.on( 'paste', function( evt ) { + // Init `dataTransfer` if `paste` event was fired without it, so it will be always available. if ( !evt.data.dataTransfer ) { evt.data.dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer(); @@ -558,7 +559,6 @@ // But don't you know any way to distinguish first two cases from last two? // Only one - special flag set in CTRL+V handler and exec method of 'paste' // command. And that's what we did using preventPasteEventNow(). - pasteDataFromClipboard( evt ); } ); @@ -662,7 +662,6 @@ this.type == 'cut' && fixCut(); var success = tryToCutCopy( this.type ); - if ( !success ) { // Show cutError or copyError. editor.showNotification( editor.lang.clipboard[ this.type + 'Error' ] ); // jshint ignore:line @@ -1526,6 +1525,15 @@ */ isCustomDataTypesSupported: !CKEDITOR.env.ie, + /** + * True if the environment supports only predefined MIME types in dataTransfer/cliboardData getData/setData methods. + * + * @since 4.8 + * @readonly + * @property {Boolean} + */ + isOnlyMimeTypeDataTypesSupported: CKEDITOR.env.edge && CKEDITOR.env.version >= 16, + /** * True if the environment supports File API. * @@ -2086,11 +2094,10 @@ */ initPasteDataTransfer: function( evt, sourceEditor ) { if ( !this.isCustomCopyCutSupported ) { - // Edge does not support custom copy/cut, but it have some useful data in the clipboardData (http://dev.ckeditor.com/ticket/13755). + // Edge < 16 does not support custom copy/cut, but it have some useful data in the clipboardData (http://dev.ckeditor.com/ticket/13755). return new this.dataTransfer( ( CKEDITOR.env.edge && evt && evt.data.$ && evt.data.$.clipboardData ) || null, sourceEditor ); } else if ( evt && evt.data && evt.data.$ ) { var dataTransfer = new this.dataTransfer( evt.data.$.clipboardData, sourceEditor ); - if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) { dataTransfer = this.copyCutData; dataTransfer.$ = evt.data.$.clipboardData; @@ -2165,7 +2172,7 @@ // If there is no ID we need to create it. Different browsers needs different ID. if ( !this.id ) { - if ( clipboardIdDataType == 'Text' ) { + if ( clipboardIdDataType == 'Text' && !CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { // For IE10+ only Text data type is supported and we have to compare dragged // and dropped text. If the ID is not set it means that empty string was dragged // (ex. image with no alt). We change null to empty string. @@ -2177,7 +2184,7 @@ } // In IE10+ we can not use any data type besides text, so we do not call setData. - if ( clipboardIdDataType != 'Text' ) { + if ( clipboardIdDataType != 'Text' || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { // Try to set ID so it will be passed from the drag to the drop event. // On some browsers with some event it is not possible to setData so we // need to catch exceptions. @@ -2350,7 +2357,7 @@ // There is "Unexpected call to method or property access." error if you try // to set data of unsupported type on IE. - if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && type != 'URL' && type != 'Text' ) { + if ( !this._isMimeTypeSupported( type ) && type != 'URL' && type != 'Text' ) { return; } @@ -2407,7 +2414,7 @@ } // Copy data. - if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { + if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { if ( this.$.types ) { for ( i = 0; i < this.$.types.length; i++ ) { getAndSetData( this.$.types[ i ] ); @@ -2495,7 +2502,7 @@ // Add native types. if ( this.$ ) { - if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { + if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { if ( this.$.types ) { for ( var i = 0; i < this.$.types.length; i++ ) { typesToCheck[ this.$.types[ i ] ] = 1; @@ -2531,7 +2538,9 @@ _getImageFromClipboard: function() { var file; - if ( this.$ && this.$.items && this.$.items[ 0 ] ) { + // The dataTransfer.items is not supported in IE/Edge. This function is used as a backup always after + // dataTransfer.files is checked so there is no need for implementing more logic than ignoring IE/Edge (#468). + if ( !CKEDITOR.env.ie && this.$ && this.$.items && this.$.items[ 0 ] ) { try { file = this.$.items[ 0 ].getAsFile(); // Duck typing @@ -2544,6 +2553,22 @@ } return undefined; + }, + + /** + * Checks if given MIME type could be used in `dataTransfer.setData`. It is important for browser not supporting custom + * MIME types in `dataTransfer.setData`. Such browsers are flagged with {@link CKEDITOR.plugins.clipboard#isOnlyMimeTypeDataTypesSupported}. + * + * @private + * @param {String} type The MIME type to check. + * @returns {Boolean} Whether the MIME type is supported by `dataTransfer.setData`. + */ + _isMimeTypeSupported: function( type ) { + // We could use static array of predefined types or try/catch to test if setting mime type throws an error. + var supportedTypes = [ 'text/plain', 'text/html' ]; + + return CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || + ( CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported && CKEDITOR.tools.indexOf( supportedTypes, type ) !== -1 ); } }; } )(); From 280d3c004e43beb9853467983e7b6eb513d2b85e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 12:39:01 +0200 Subject: [PATCH 06/47] Edge workaround. --- plugins/clipboard/plugin.js | 160 ++++++++++++++++++++++++++++-------- 1 file changed, 127 insertions(+), 33 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index a5d919f167e..494c1eb1aae 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -1523,16 +1523,7 @@ * @readonly * @property {Boolean} */ - isCustomDataTypesSupported: !CKEDITOR.env.ie, - - /** - * True if the environment supports only predefined MIME types in dataTransfer/cliboardData getData/setData methods. - * - * @since 4.8 - * @readonly - * @property {Boolean} - */ - isOnlyMimeTypeDataTypesSupported: CKEDITOR.env.edge && CKEDITOR.env.version >= 16, + isCustomDataTypesSupported: !CKEDITOR.env.ie || CKEDITOR.env.version >= 16, /** * True if the environment supports File API. @@ -1599,7 +1590,7 @@ } // In older Safari and IE HTML data is not available though the Clipboard API. - // In Edge things are a bit messy at the moment - + // In older Edge version things are also a bit messy - // https://connect.microsoft.com/IE/feedback/details/1572456/edge-clipboard-api-text-html-content-messed-up-in-event-clipboarddata // It is safer to use the paste bin in unknown cases. return false; @@ -2098,6 +2089,7 @@ return new this.dataTransfer( ( CKEDITOR.env.edge && evt && evt.data.$ && evt.data.$.clipboardData ) || null, sourceEditor ); } else if ( evt && evt.data && evt.data.$ ) { var dataTransfer = new this.dataTransfer( evt.data.$.clipboardData, sourceEditor ); + if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) { dataTransfer = this.copyCutData; dataTransfer.$ = evt.data.$.clipboardData; @@ -2146,6 +2138,8 @@ this.$ = nativeDataTransfer; } + this.customDataFallbackType = 'text/html'; + this._ = { metaRegExp: /^/i, bodyRegExp: /([\s\S]*)<\/body>/i, @@ -2172,7 +2166,7 @@ // If there is no ID we need to create it. Different browsers needs different ID. if ( !this.id ) { - if ( clipboardIdDataType == 'Text' && !CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { + if ( clipboardIdDataType == 'Text' ) { // For IE10+ only Text data type is supported and we have to compare dragged // and dropped text. If the ID is not set it means that empty string was dragged // (ex. image with no alt). We change null to empty string. @@ -2184,13 +2178,9 @@ } // In IE10+ we can not use any data type besides text, so we do not call setData. - if ( clipboardIdDataType != 'Text' || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { + if ( clipboardIdDataType != 'Text' ) { // Try to set ID so it will be passed from the drag to the drop event. - // On some browsers with some event it is not possible to setData so we - // need to catch exceptions. - try { - this.$.setData( clipboardIdDataType, this.id ); - } catch ( err ) {} + this._setCustomData( clipboardIdDataType, this.id, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); } if ( editor ) { @@ -2234,6 +2224,14 @@ * @private * @property {Object} _ */ + + /** + * The MIME type which is used to store custom data (in comment) when + * the browser does not allow to use custom MIME types in `dataTransfer.setData`. + * + * @readonly + * @property {String} customDataFallbackType + */ }; /** @@ -2302,6 +2300,7 @@ type = this._.normalizeType( type ); var data = this._.data[ type ], + content, result; if ( isEmpty( data ) ) { @@ -2310,6 +2309,21 @@ } catch ( e ) {} } + // If we are getting the same type which may store custom data we need to extract it. + // Or if we are getting different type and it is empty we need to check inside data comment. + if ( !isEmpty( data ) && type === this.customDataFallbackType ) { + content = this._extractDataComment( data ); + data = content.content; + + } else if ( isEmpty( data ) ) { + try { + content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); + if ( content.data && content.data[ type ] ) { + data = content.data[ type ]; + } + } catch ( e ) {} + } + if ( isEmpty( data ) ) { data = ''; } @@ -2357,7 +2371,7 @@ // There is "Unexpected call to method or property access." error if you try // to set data of unsupported type on IE. - if ( !this._isMimeTypeSupported( type ) && type != 'URL' && type != 'Text' ) { + if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && type != 'URL' && type != 'Text' ) { return; } @@ -2367,9 +2381,22 @@ this.id = value; } - try { - this.$.setData( type, value ); - } catch ( e ) {} + // Extract custom data if used type is the same one which is used for storing it. + // Then custom data is added to a new value so it will not be overwritten. + if ( type === this.customDataFallbackType ) { + var content = null; + try { + content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); + } catch ( e ) {} + + if ( content && content.data ) { + value = this._applyDataComment( value, content.data ); + } + this._setCustomData( type, value ); + + } else { + this._setCustomData( type, value, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); + } }, /** @@ -2414,7 +2441,7 @@ } // Copy data. - if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { + if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { if ( this.$.types ) { for ( i = 0; i < this.$.types.length; i++ ) { getAndSetData( this.$.types[ i ] ); @@ -2502,7 +2529,7 @@ // Add native types. if ( this.$ ) { - if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported ) { + if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { if ( this.$.types ) { for ( var i = 0; i < this.$.types.length; i++ ) { typesToCheck[ this.$.types[ i ] ] = 1; @@ -2556,19 +2583,86 @@ }, /** - * Checks if given MIME type could be used in `dataTransfer.setData`. It is important for browser not supporting custom - * MIME types in `dataTransfer.setData`. Such browsers are flagged with {@link CKEDITOR.plugins.clipboard#isOnlyMimeTypeDataTypesSupported}. + * Additional layer over `dataTransfer.setData` method. If used with `useFallback=true` in case of native `setData` + * throwing an error it will try to place passed value in + * {@link CKEDITOR.plugins.clipboard.dataTransfer#customDataFallbackType} type using special comment format: + * + * + * + * It is important to keep in mind that { type: value } object is stringified (using JSON.stringify) + * and encoded (using encodeURIComponent). + * + * @private + * @param {String} type + * @param {String} value + * @param {Boolean} useFallback + */ + _setCustomData: function( type, value, useFallback ) { + try { + this.$.setData( type, value ); + + } catch ( e ) { + if ( useFallback ) { + // Still in some situations some browsers may throw errors even when using predefined MIME types. + try { + // We need to get the data first to append `type` and not to overwrite whole custom data. + var content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); + + if ( !content.data ) { + content.data = {}; + } + content.data[ type ] = value; + + this.$.setData( this.customDataFallbackType, this._applyDataComment( content.content, content.data ) ); + } catch ( e ) {} + } + } + }, + + /** + * Extracts `cke-data` comment from the given content. Returns an object containing extracted data as `data` + * and content without `cke-data` comment as `content`. * * @private - * @param {String} type The MIME type to check. - * @returns {Boolean} Whether the MIME type is supported by `dataTransfer.setData`. + * @param {String} content + * @returns {Object} result + * @returns {Object} result.data + * @returns {String} result.content */ - _isMimeTypeSupported: function( type ) { - // We could use static array of predefined types or try/catch to test if setting mime type throws an error. - var supportedTypes = [ 'text/plain', 'text/html' ]; + _extractDataComment: function( content ) { + var result = { + data: null, + content: content || '' + }; + + // At least 17 characters length: . + if ( content && content.length > 16 ) { + var matcher = //g, + matches; + + matches = matcher.exec( content ); + if ( matches && matches[ 1 ] ) { + result.data = JSON.parse( decodeURIComponent( matches[ 1 ] ) ); + result.content = content.replace( matches[ 0 ], '' ); + } + } + return result; + }, - return CKEDITOR.plugins.clipboard.isCustomDataTypesSupported || - ( CKEDITOR.plugins.clipboard.isOnlyMimeTypeDataTypesSupported && CKEDITOR.tools.indexOf( supportedTypes, type ) !== -1 ); + /** + * Creates `cke-data` comment containing stringified and encoded data object which is prepended to a given content. + * + * @private + * @param {String} content + * @param {Object} data + * @returns {String} + */ + _applyDataComment: function( content, data ) { + var customData = ''; + if ( data && CKEDITOR.tools.objectKeys( data ).length ) { + customData = ''; + } + return customData + ( content && content.length ? content : '' ); } }; } )(); From cf26c3724b6745e69e2af6ad34d110d0053f8e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 12:44:49 +0200 Subject: [PATCH 07/47] Unit tests. --- tests/_benderjs/ckeditor/static/tools.js | 20 +- tests/plugins/clipboard/datatransfer.js | 13 +- .../clipboard/datatransferfallback.html | 33 +++ .../plugins/clipboard/datatransferfallback.js | 277 ++++++++++++++++++ tests/plugins/clipboard/manual/paste.html | 2 +- 5 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 tests/plugins/clipboard/datatransferfallback.html create mode 100644 tests/plugins/clipboard/datatransferfallback.js diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index f5577cf434e..1e4ba060170 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -816,13 +816,21 @@ 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 && CKEDITOR.env.version < 15 && 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; + } + + if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 16 && + CKEDITOR.tools.indexOf( [ 'Text', 'URL', 'text/plain', 'text/html' ], type ) === -1 ) { + + throw 'Element not found.'; + } if ( type == 'text/plain' || type == 'Text' ) { this._data[ 'text/plain' ] = data; @@ -834,11 +842,13 @@ this.types.push( type ); }, getData: function( type ) { - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 15 && 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 ]; } diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 4796b931739..4e914e45985 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -482,7 +482,10 @@ bender.test( { // Emulate native clipboard. nativeData = bender.tools.mockNativeDataTransfer(); - if ( isCustomDataTypesSupported ) { + // This test uses native (mocked) `setData` which does not applies fallback + // for Edge >= 16 (because it skips our wrapper) so it works + // as if `isCustomDataTypesSupported` flag was turned off for Edge. + if ( isCustomDataTypesSupported && !CKEDITOR.env.edge ) { nativeData.setData( 'text/html', 'foo' ); nativeData.setData( 'text/plain', 'bom' ); nativeData.setData( 'cke/custom', 'bar' ); @@ -502,7 +505,7 @@ bender.test( { nativeData.getData = throwPermissionDenied; // Assert - if ( isCustomDataTypesSupported ) { + 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' ) ); @@ -553,7 +556,8 @@ bender.test( { // http://dev.ckeditor.com/ticket/12961 'test file in items': function() { - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { + // DataTransfer.items is not supported in IE/EDGE so there is no reason to test it. + if ( CKEDITOR.env.ie ) { assert.ignore(); } @@ -642,7 +646,8 @@ bender.test( { // http://dev.ckeditor.com/ticket/12961 'test file in items with cache': function() { - if ( CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { + // DataTransfer.items is not supported in IE/EDGE so there is no reason to test it. + if ( CKEDITOR.env.ie ) { assert.ignore(); } diff --git a/tests/plugins/clipboard/datatransferfallback.html b/tests/plugins/clipboard/datatransferfallback.html new file mode 100644 index 00000000000..ff26cc1a1fd --- /dev/null +++ b/tests/plugins/clipboard/datatransferfallback.html @@ -0,0 +1,33 @@ +
+ +

Header1

+

Test1

+
+ +
+ +

Header1

+

Test1

+
+ +
+ + +

Header1

+

Test1

+
+ +
+ +

Header1

+

Test1

+ +
+ +
+ +
+ +
+

foobar

+
diff --git a/tests/plugins/clipboard/datatransferfallback.js b/tests/plugins/clipboard/datatransferfallback.js new file mode 100644 index 00000000000..a8833631a9f --- /dev/null +++ b/tests/plugins/clipboard/datatransferfallback.js @@ -0,0 +1,277 @@ +/* bender-tags: editor,clipboard,468 */ +/* bender-ckeditor-plugins: toolbar,clipboard */ +/* bender-include: _helpers/pasting.js */ + +'use strict'; + +bender.test( { + init: function() { + if ( !CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ) { + assert.ignore(); + } + }, + + 'test setData/getData with predefined type': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'text/plain', 'plain text' ); + dataTransfer.setData( 'text/html', '

html text

' ); + + assert.areSame( 'plain text', getDataNoCache( dataTransfer, 'text/plain' ) ); + assert.areSame( '

html text

', getDataNoCache( dataTransfer, 'text/html' ) ); + + this.assertDataTransferType( dataTransfer, 'text/plain', 'plain text' ); + this.assertDataTransferType( dataTransfer, 'text/html', '

html text

', { 'cke/id': dataTransfer.id } ); + }, + + 'test setData/getData with custom type': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'cke/custom', 'cke-custom data' ); + dataTransfer.setData( 'custom/tag', '

custom html tag

' ); + + assert.areSame( 'cke-custom data', getDataNoCache( dataTransfer, 'cke/custom' ) ); + assert.areSame( '

custom html tag

', getDataNoCache( dataTransfer, 'custom/tag' ) ); + + this.assertDataTransferType( dataTransfer, 'text/html', '', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'cke-custom data', + 'custom/tag': '

custom html tag

' + } ); + }, + + 'test setData with custom type does not affect getData( "text/html" )': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'text/html', '

Header1

' ); + + assert.areSame( getDataNoCache( dataTransfer, 'text/html' ), '

Header1

' ); + this.assertDataTransferType( dataTransfer, 'text/html', '

Header1

', { 'cke/id': dataTransfer.id } ); + + dataTransfer.setData( 'cke/custom', 'custom data' ); + + assert.areSame( getDataNoCache( dataTransfer, 'text/html' ), '

Header1

' ); + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + this.assertDataTransferType( dataTransfer, 'text/html', '

Header1

', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'custom data' + } ); + }, + + 'test setData( "text/html" ) does not overwrite custom data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'cke/custom', 'custom data' ); + + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + this.assertDataTransferType( dataTransfer, 'text/html', '', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'custom data' + } ); + + dataTransfer.setData( 'text/html', '

Header1

' ); + + assert.areSame( getDataNoCache( dataTransfer, 'text/html' ), '

Header1

' ); + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + this.assertDataTransferType( dataTransfer, 'text/html', '

Header1

', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'custom data' + } ); + }, + + 'test setData( "text/html" ) called a few times does not overwrite custom data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'cke/custom', 'custom data' ); + + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + + dataTransfer.setData( 'text/html', '

Header1

' ); + + assert.areSame( getDataNoCache( dataTransfer, 'text/html' ), '

Header1

' ); + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + + dataTransfer.setData( 'text/html', '

Header2

' ); + + assert.areSame( getDataNoCache( dataTransfer, 'text/html' ), '

Header2

' ); + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'custom data' ); + this.assertDataTransferType( dataTransfer, 'text/html', '

Header2

', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'custom data' + } ); + }, + + 'test setting same custom type overwrites the previous value and does not affect other types': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + + dataTransfer.setData( 'cke/custom', 'cke-custom data' ); + dataTransfer.setData( 'custom/tag', '

custom html tag

' ); + + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'cke-custom data' ); + assert.areSame( getDataNoCache( dataTransfer, 'custom/tag' ), '

custom html tag

' ); + this.assertDataTransferType( dataTransfer, 'text/html', '', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'cke-custom data', + 'custom/tag': '

custom html tag

' + } ); + + dataTransfer.setData( 'cke/custom', 'cke-custom' ); + + assert.areSame( getDataNoCache( dataTransfer, 'cke/custom' ), 'cke-custom' ); + assert.areSame( getDataNoCache( dataTransfer, 'custom/tag' ), '

custom html tag

' ); + this.assertDataTransferType( dataTransfer, 'text/html', '', { + 'cke/id': dataTransfer.id, + 'cke/custom': 'cke-custom', + 'custom/tag': '

custom html tag

' + } ); + }, + + 'test _applyDataComment case1': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = document.querySelector( '#case1' ).innerHTML; + + this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransfer, expected ); + }, + + 'test _applyDataComment case2': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = document.querySelector( '#case2' ).innerHTML; + + this.assertApplyDataComment( '

Header1

Test1

', { test: 1, comment: '' }, dataTransfer, expected ); + }, + + 'test _applyDataComment case3': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = CKEDITOR.tools.trim( document.querySelector( '#case3' ).innerHTML ); + + this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransfer, expected ); + }, + + 'test _applyDataComment case4': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = CKEDITOR.tools.trim( document.querySelector( '#case4' ).innerHTML ); + + this.assertApplyDataComment( '

Header1

Test1

', { test: 123 }, dataTransfer, expected ); + }, + + 'test _applyDataComment with empty content': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ); + + this.assertApplyDataComment( undefined, { test: 1 }, dataTransfer, expected ); + this.assertApplyDataComment( null, { test: 1 }, dataTransfer, expected ); + this.assertApplyDataComment( '', { test: 1 }, dataTransfer, expected ); + }, + + 'test _applyDataComment with empty data': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ); + + this.assertApplyDataComment( '

foobar

', '', dataTransfer, expected ); + this.assertApplyDataComment( '

foobar

', null, dataTransfer, expected ); + this.assertApplyDataComment( '

foobar

', {}, dataTransfer, expected ); + }, + + 'test _applyDataComment with empty content and data': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + expected = ''; + + this.assertApplyDataComment( undefined, null, dataTransfer, expected ); + this.assertApplyDataComment( null, undefined, dataTransfer, expected ); + this.assertApplyDataComment( '', {}, dataTransfer, expected ); + }, + + 'test _extractDataComment case1': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = document.querySelector( '#case1' ).innerHTML, + extracted = dataTransfer._extractDataComment( content ); + + objectAssert.areEqual( extracted.data, { test: 1 } ); + assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); + }, + + 'test _extractDataComment case2': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = document.querySelector( '#case2' ).innerHTML, + extracted = dataTransfer._extractDataComment( content ); + + objectAssert.areEqual( extracted.data, { test: 1, comment: '' } ); + assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); + }, + + 'test _extractDataComment case3': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = document.querySelector( '#case3' ).innerHTML, + extracted = dataTransfer._extractDataComment( content ); + + objectAssert.areEqual( extracted.data, { test: 1 } ); + assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); + }, + + 'test _extractDataComment case4': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = document.querySelector( '#case4' ).innerHTML, + extracted = dataTransfer._extractDataComment( content ); + + objectAssert.areEqual( extracted.data, { test: 123 } ); + assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); + }, + + 'test _extractDataComment with empty content': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ), + extracted = dataTransfer._extractDataComment( content ); + + objectAssert.areEqual( extracted.data, { test: 1 } ); + assert.areSame( '', extracted.content ); + }, + + 'test _extractDataComment with empty data': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + content = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ), + extracted = dataTransfer._extractDataComment( content ); + + assert.isNull( extracted.data ); + assert.isInnerHtmlMatching( '

foobar

', extracted.content ); + }, + + 'test _extractDataComment with empty data and content': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + extracted = dataTransfer._extractDataComment( '' ); + + assert.isNull( extracted.data ); + assert.areSame( '', extracted.content ); + }, + + 'test _extractDataComment with falsy value': function() { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ); + + assert.areSame( '', dataTransfer._extractDataComment( '' ).content ); + assert.areSame( '', dataTransfer._extractDataComment( null ).content ); + assert.areSame( '', dataTransfer._extractDataComment( undefined ).content ); + assert.areSame( '', dataTransfer._extractDataComment( false ).content ); + }, + + assertDataTransferType: function( dataTransfer, type, value, customValue ) { + if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 16 && customValue ) { + value = '' + value; + } + assert.areSame( value, dataTransfer.$.getData( type ) ); + }, + + assertApplyDataComment: function( content, data, dataTransfer, expected ) { + assert.isInnerHtmlMatching( expected.replace( /[\n\r\t]*/g, '' ), dataTransfer._applyDataComment( content, data ) ); + } +} ); + +// Gets data but clears the cache first, so data is extracted from saved types not from cache object. +function getDataNoCache( dataTransfer, type ) { + dataTransfer._.data = {}; + return dataTransfer.getData( type ); +} diff --git a/tests/plugins/clipboard/manual/paste.html b/tests/plugins/clipboard/manual/paste.html index 373a3474970..953ca59d8cd 100644 --- a/tests/plugins/clipboard/manual/paste.html +++ b/tests/plugins/clipboard/manual/paste.html @@ -196,7 +196,7 @@

Technical details

for ( var i = 0; i < editors.length; i++ ) { var editor = editors[ i ]; - if ( !CKEDITOR.env.ie ) { + if ( !CKEDITOR.env.ie || CKEDITOR.env.edge && CKEDITOR.env.version > 15 ) { editor.on( 'contentDom', function( evt ) { evt.editor.editable().on( 'paste', function( evt ) { var clipboardData = evt.data.$.clipboardData, From 293fcc6a8a7a2d960f828b7ac8301b6cb617b9ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 12:57:54 +0200 Subject: [PATCH 08/47] Set id after setting other MIME types. --- plugins/clipboard/plugin.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 494c1eb1aae..ea7fa70f5d9 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2177,12 +2177,6 @@ } } - // In IE10+ we can not use any data type besides text, so we do not call setData. - if ( clipboardIdDataType != 'Text' ) { - // Try to set ID so it will be passed from the drag to the drop event. - this._setCustomData( clipboardIdDataType, this.id, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); - } - if ( editor ) { this.sourceEditor = editor; @@ -2195,6 +2189,13 @@ } } + // Set id as a last thing to not force rereading/rewriting custom data comment. + // In IE10+ we can not use any data type besides text, so we do not call setData. + if ( clipboardIdDataType != 'Text' ) { + // Try to set ID so it will be passed from the drag to the drop event. + this._setCustomData( clipboardIdDataType, this.id, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); + } + /** * Data transfer ID used to bind all dataTransfer * objects based on the same event (e.g. in drag and drop events). @@ -2309,8 +2310,8 @@ } catch ( e ) {} } - // If we are getting the same type which may store custom data we need to extract it. - // Or if we are getting different type and it is empty we need to check inside data comment. + // If we are getting the same type which may store custom data we need to extract it (removing custom data comment). + // Or if we are getting different type and it is empty we need to check inside data comment if it is stored there. if ( !isEmpty( data ) && type === this.customDataFallbackType ) { content = this._extractDataComment( data ); data = content.content; @@ -2382,7 +2383,7 @@ } // Extract custom data if used type is the same one which is used for storing it. - // Then custom data is added to a new value so it will not be overwritten. + // Then new value is added to a custom data so it will not be overwritten entirely. if ( type === this.customDataFallbackType ) { var content = null; try { From f8c8e02da8c790ca229380dca7c1c2420ca9c1e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 13:00:59 +0200 Subject: [PATCH 09/47] Edge version fix in tests. --- tests/plugins/clipboard/manual/paste.html | 2 +- tests/plugins/clipboard/paste.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/clipboard/manual/paste.html b/tests/plugins/clipboard/manual/paste.html index 953ca59d8cd..cb4a4316332 100644 --- a/tests/plugins/clipboard/manual/paste.html +++ b/tests/plugins/clipboard/manual/paste.html @@ -196,7 +196,7 @@

Technical details

for ( var i = 0; i < editors.length; i++ ) { var editor = editors[ i ]; - if ( !CKEDITOR.env.ie || CKEDITOR.env.edge && CKEDITOR.env.version > 15 ) { + if ( !CKEDITOR.env.ie || CKEDITOR.env.version >= 16 ) { editor.on( 'contentDom', function( evt ) { evt.editor.editable().on( 'paste', function( evt ) { var clipboardData = evt.data.$.clipboardData, diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index 8dd1fc4ef4c..f81cea24c7e 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -50,7 +50,7 @@ } var trustySafari = CKEDITOR.env.safari && CKEDITOR.env.version >= 603 && !CKEDITOR.env.iOS, - trustyEdge = CKEDITOR.env.edge && CKEDITOR.env.version >= 15; + trustyEdge = CKEDITOR.env.edge && CKEDITOR.env.version >= 16; bender.test( { setUp: function() { From 2427a6fadab01e37df5177d97ea29eebc39eda32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 16:23:29 +0200 Subject: [PATCH 10/47] Manual test added. --- .../manual/datatransferfallback.html | 211 ++++++++++++++++++ .../clipboard/manual/datatransferfallback.md | 30 +++ 2 files changed, 241 insertions(+) create mode 100644 tests/plugins/clipboard/manual/datatransferfallback.html create mode 100644 tests/plugins/clipboard/manual/datatransferfallback.md diff --git a/tests/plugins/clipboard/manual/datatransferfallback.html b/tests/plugins/clipboard/manual/datatransferfallback.html new file mode 100644 index 00000000000..f644ec642ff --- /dev/null +++ b/tests/plugins/clipboard/manual/datatransferfallback.html @@ -0,0 +1,211 @@ + + + + + +
+

Helpers

+
+ +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. CKEditor logo In commodo vulputate tempor. Sed <b>at elit</b> vel ligula mollis aliquet a ac odio. +
+Aenean cursus egestas ipsum.
+			
+
+
+
+
+ +
+

Classic editor

+
+
+

Apollo 11

+ +

Apollo 11 was the spaceflight that landed the first humans, Americans Neil Armstrong and Buzz Aldrin, on the Moon on July 20, 1969, at 20:18 UTC. CKEditor logo Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.

+ +
CKEditor logo +
CKEditor
+
+ +

Armstrong spent about three and a half two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5 kg) of lunar material for return to Earth. A third member of the mission, Michael Collins, piloted the command spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.

+ +

Broadcasting and quotes

+ +

Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:

+ +
+

One small step for [a] man, one giant leap for mankind.

+
+ +

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

+ +
+

[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.

+
+ +

Technical details

+ + + + + + + + + + + + + + + + + + + + + + + +
Mission crew
PositionAstronaut
CommanderNeil A. Armstrong
Command Module PilotMichael Collins
Lunar Module PilotEdwin "Buzz" E. Aldrin, Jr.
+ +

Launched by a Saturn V rocket from Kennedy Space Center in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of NASA's Apollo program. The Apollo spacecraft had three parts:

+ +
    +
  1. Command Module with a cabin for the three astronauts which was the only part which landed back on Earth
  2. +
  3. Service Module which supported the Command Module with propulsion, electrical power, oxygen and water
  4. +
  5. Lunar Module for landing on the Moon.
  6. +
+ +

After being sent to the Moon by the Saturn V's upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the Sea of Tranquility. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the Pacific Ocean on July 24.

+ +
+

Source: Wikipedia.org

+
+
+
+ +
+

Inline editor

+
+

Apollo 11

+ +

Apollo 11 was the spaceflight that landed the first humans, Americans Neil Armstrong and Buzz Aldrin, on the Moon on July 20, 1969, at 20:18 UTC. CKEditor logo Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.

+ +
CKEditor logo +
CKEditor
+
+ +

Armstrong spent about three and a half two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5 kg) of lunar material for return to Earth. A third member of the mission, Michael Collins, piloted the command spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.

+ +

Broadcasting and quotes

+ +

Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:

+ +
+

One small step for [a] man, one giant leap for mankind.

+
+ +

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

+ +
+

[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.

+
+ +

Technical details

+ + + + + + + + + + + + + + + + + + + + + + + +
Mission crew
PositionAstronaut
CommanderNeil A. Armstrong
Command Module PilotMichael Collins
Lunar Module PilotEdwin "Buzz" E. Aldrin, Jr.
+ +

Launched by a Saturn V rocket from Kennedy Space Center in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of NASA's Apollo program. The Apollo spacecraft had three parts:

+ +
    +
  1. Command Module with a cabin for the three astronauts which was the only part which landed back on Earth
  2. +
  3. Service Module which supported the Command Module with propulsion, electrical power, oxygen and water
  4. +
  5. Lunar Module for landing on the Moon.
  6. +
+ +

After being sent to the Moon by the Saturn V's upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the Sea of Tranquility. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the Pacific Ocean on July 24.

+ +
+

Source: Wikipedia.org

+
+
+ diff --git a/tests/plugins/clipboard/manual/datatransferfallback.md b/tests/plugins/clipboard/manual/datatransferfallback.md new file mode 100644 index 00000000000..a59b6371dd4 --- /dev/null +++ b/tests/plugins/clipboard/manual/datatransferfallback.md @@ -0,0 +1,30 @@ +@bender-ui: collapsed +@bender-tags: 4.8.0, feature, 468, 962 +@bender-ckeditor-plugins: wysiwygarea, toolbar, undo, basicstyles, image2, font, stylescombo, basicstyles, format, maximize, blockquote, list, table, resize, elementspath, justify, clipboard, floatingspace, sourcearea, htmlwriter, link, placeholder + +_Open dev console as events are logged there._ + +## Test D&D and copy/paste: + * internally (same editor), + * between editors, + * externally (helpers, MS Word, etc). + +## using content like: + * simple text, + * table cell/cells, + * link, + * helpers textarea content, + * helpers html content, + * content from MS Word, + * widgets (inline and block). + +## Expected: + * Events sequence caused by one action (e.g. `drag`, `drop`, `paste` for internal drag) always have the same `DataTransfer id`. + * `Id storage` (if present) should be `text/html` for `Edge` browser and `cke/id` for other browsers. + * Check also: + * proper drop position, + * in the internal and cross editor D&D, dragged content should be removed, + * no content lost (e.g. ids of anchors), + * undo should work properly, + * widget (both block and inline) D&D works only internally in one editor, + * no crashes, nor errors. From 76a1bb9a51b24e2429c3419aa32d8a185730f5cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 26 Sep 2017 16:49:47 +0200 Subject: [PATCH 11/47] Specs corrections. --- plugins/clipboard/plugin.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index ea7fa70f5d9..6992e593adb 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2227,8 +2227,8 @@ */ /** - * The MIME type which is used to store custom data (in comment) when - * the browser does not allow to use custom MIME types in `dataTransfer.setData`. + * The MIME type which is used to store custom data (in special `` comment) + * when browser does not allow to use custom MIME types in `dataTransfer.setData`. * * @readonly * @property {String} customDataFallbackType @@ -2584,14 +2584,14 @@ }, /** - * Additional layer over `dataTransfer.setData` method. If used with `useFallback=true` in case of native `setData` + * Additional layer over `dataTransfer.setData` method. If used with `useFallback = true` in case of native `setData` * throwing an error it will try to place passed value in * {@link CKEDITOR.plugins.clipboard.dataTransfer#customDataFallbackType} type using special comment format: * - * + * * - * It is important to keep in mind that { type: value } object is stringified (using JSON.stringify) - * and encoded (using encodeURIComponent). + * It is important to keep in mind that `{ type: value }` object is stringified (using `JSON.stringify`) + * and encoded (using `encodeURIComponent`). * * @private * @param {String} type @@ -2622,13 +2622,13 @@ /** * Extracts `cke-data` comment from the given content. Returns an object containing extracted data as `data` - * and content without `cke-data` comment as `content`. + * and content (without `cke-data` comment) as `content`. * * @private * @param {String} content - * @returns {Object} result - * @returns {Object} result.data - * @returns {String} result.content + * @returns {Object} + * @returns {Object} return.data + * @returns {String} return.content */ _extractDataComment: function( content ) { var result = { From b431eca8eb69eeca417d0a2aceee6b45f7f5a9a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 27 Sep 2017 15:00:08 +0200 Subject: [PATCH 12/47] Fixed failing tests. --- tests/_benderjs/ckeditor/static/tools.js | 2 +- tests/plugins/clipboard/paste.js | 30 ++++++++++++++++++---- tests/plugins/pastefromword/parsestyles.js | 17 ++++++++++-- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index 1e4ba060170..260f46d5539 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -827,7 +827,7 @@ } if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 16 && - CKEDITOR.tools.indexOf( [ 'Text', 'URL', 'text/plain', 'text/html' ], type ) === -1 ) { + CKEDITOR.tools.indexOf( [ 'Text', 'URL', 'text/plain', 'text/html', 'application/xml' ], type ) === -1 ) { throw 'Element not found.'; } diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index f81cea24c7e..8a2da097ff2 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -1187,7 +1187,14 @@ editable.fire( 'cut', pasteEventMock ); - assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); + // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). + if ( CKEDITOR.env.edge ) { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); + assert.areSame( 'bar', dataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + } else { + assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); + } + assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'Text' ), 'Plain text' ); assert.isInnerHtmlMatching( '

x^x@

', bender.tools.selection.getWithHtml( editor ), { compareSelection: true, normalizeSelection: true }, 'Editor content' ); assert.areSame( pasteEventMock.$.clipboardData, CKEDITOR.plugins.clipboard.copyCutData.$, 'copyCutData should be initialized' ); @@ -1205,7 +1212,14 @@ editable.fire( 'copy', pasteEventMock ); - assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML data' ); + // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). + if ( CKEDITOR.env.edge ) { + var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); + assert.areSame( 'bar', dataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + } else { + assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); + } + assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'Text' ), 'Plain text data' ); assert.isInnerHtmlMatching( '

x[bar]x@

', bender.tools.selection.getWithHtml( editor ), { compareSelection: true, normalizeSelection: true }, 'Editor content' ); assert.areSame( pasteEventMock.$.clipboardData, CKEDITOR.plugins.clipboard.copyCutData.$, 'copyCutData should be initialized' ); @@ -1289,17 +1303,23 @@ if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) assert.ignore(); - var editor = this.editor; + var editor = this.editor, + // As dataTransfer mock is used in `bender.tools.emulatePaste` we need to pass type which is acceptable in Edge + // as it does not support custom types (#962). + customType = CKEDITOR.env.edge ? 'application/xml' : 'cke/custom', + initialData = {}; + + initialData[ customType ] = 'foo'; this.on( 'paste', function( evt ) { resume( function() { assert.areSame( 'paste', evt.data.method, 'Paste method.' ); - assert.areSame( 'foo', evt.data.dataTransfer.getData( 'cke/custom' ), 'cke/custom data' ); + assert.areSame( 'foo', evt.data.dataTransfer.getData( customType ), 'cke/custom data' ); assert.areSame( '', evt.data.dataValue, 'dataValue' ); } ); } ); - bender.tools.emulatePaste( editor, '', { 'cke/custom': 'foo' } ); + bender.tools.emulatePaste( editor, '', initialData ); this.wait(); }, diff --git a/tests/plugins/pastefromword/parsestyles.js b/tests/plugins/pastefromword/parsestyles.js index 8948419c363..7740ed7cbc4 100644 --- a/tests/plugins/pastefromword/parsestyles.js +++ b/tests/plugins/pastefromword/parsestyles.js @@ -12,6 +12,14 @@ bender.tools.testInputOut( name, function( styles, expected ) { var tested = CKEDITOR.plugins.pastefromword.styles.inliner.parse( styles ); + // In CKEDITOR.plugins.pastefromword.styles.inliner.parse#createIsolatedStylesheet function + // somehow Edge camelcases the selectors so we need to lowercase it. + if ( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported && CKEDITOR.env.edge ) { + CKEDITOR.tools.array.forEach( tested, function( item ) { + item.selector = item.selector.toLowerCase(); + } ); + } + assert.beautified.js( expected, JSON.stringify( tested ), name ); } ); } @@ -41,8 +49,13 @@ 'test if parseStyles parses styles correctly': function() { testStyles( 'styles1' ); - testStyles( 'styles2' ); - testStyles( 'multiple' ); + + // Edge cannot handle 'margin-bottom: 0.0001pt' as it rounds it to 0 when parsing stylesheet. + if ( !( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported && CKEDITOR.env.edge ) ) { + testStyles( 'styles2' ); + testStyles( 'multiple' ); + } + testStyles( 'empty' ); testStyles( 'font-face' ); testStyles( 'page' ); From 704a11fef10dde38afbd03d18051a906bc5ffee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 27 Sep 2017 15:15:48 +0200 Subject: [PATCH 13/47] Test fixes. --- tests/_benderjs/ckeditor/static/tools.js | 2 +- tests/plugins/clipboard/datatransfer.js | 4 ++-- tests/plugins/clipboard/datatransferfallback.js | 2 +- tests/plugins/clipboard/paste.js | 2 +- tests/plugins/pastefromword/parsestyles.html | 8 ++++---- tests/plugins/pastefromword/parsestyles.js | 11 +++-------- 6 files changed, 12 insertions(+), 17 deletions(-) diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index 260f46d5539..8a0db75c5d4 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -894,7 +894,7 @@ return { $: { ctrlKey: true, - clipboardData: ( CKEDITOR.env.ie && CKEDITOR.env.version < 15 ) ? undefined : dataTransfer + clipboardData: ( CKEDITOR.env.ie && CKEDITOR.env.version < 16 ) ? undefined : dataTransfer }, preventDefault: function() { // noop diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 4e914e45985..301322860a4 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -484,7 +484,7 @@ bender.test( { // This test uses native (mocked) `setData` which does not applies fallback // for Edge >= 16 (because it skips our wrapper) so it works - // as if `isCustomDataTypesSupported` flag was turned off for Edge. + // 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' ); @@ -504,7 +504,7 @@ bender.test( { nativeData.setData = throwPermissionDenied; nativeData.getData = throwPermissionDenied; - // Assert + // 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' ) ); diff --git a/tests/plugins/clipboard/datatransferfallback.js b/tests/plugins/clipboard/datatransferfallback.js index a8833631a9f..8b7432fb4b6 100644 --- a/tests/plugins/clipboard/datatransferfallback.js +++ b/tests/plugins/clipboard/datatransferfallback.js @@ -1,4 +1,4 @@ -/* bender-tags: editor,clipboard,468 */ +/* bender-tags: editor,clipboard,468,962 */ /* bender-ckeditor-plugins: toolbar,clipboard */ /* bender-include: _helpers/pasting.js */ diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index 8a2da097ff2..955f023f7d1 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -1384,7 +1384,7 @@ }, // #468 - 'test canClipboardApiBeTrusted in Edge 15+': function() { + 'test canClipboardApiBeTrusted in Edge 16+': function() { if ( !trustyEdge ) { assert.ignore(); } diff --git a/tests/plugins/pastefromword/parsestyles.html b/tests/plugins/pastefromword/parsestyles.html index d1a9eea6930..b44f42e5005 100644 --- a/tests/plugins/pastefromword/parsestyles.html +++ b/tests/plugins/pastefromword/parsestyles.html @@ -18,13 +18,13 @@ mso-style-qformat:yes; mso-style-parent:""; margin:0cm; - margin-bottom:.0001pt; + margin-bottom:.001pt; } => [ { "selector": "p.MsoNormal, li.MsoNormal, div.MsoNormal", "styles": { - "margin": "0cm 0cm 0.0001pt" + "margin": "0cm 0cm 0.001pt" } } ] @@ -39,7 +39,7 @@ mso-style-qformat:yes; mso-style-parent:""; margin:0cm; - margin-bottom:.0001pt; + margin-bottom:.001pt; } => [ { @@ -50,7 +50,7 @@ }, { "selector": "p.MsoNormal, li.MsoNormal, div.MsoNormal", "styles": { - "margin": "0cm 0cm 0.0001pt" + "margin": "0cm 0cm 0.001pt" } } ] diff --git a/tests/plugins/pastefromword/parsestyles.js b/tests/plugins/pastefromword/parsestyles.js index 7740ed7cbc4..6147386f97e 100644 --- a/tests/plugins/pastefromword/parsestyles.js +++ b/tests/plugins/pastefromword/parsestyles.js @@ -13,7 +13,7 @@ var tested = CKEDITOR.plugins.pastefromword.styles.inliner.parse( styles ); // In CKEDITOR.plugins.pastefromword.styles.inliner.parse#createIsolatedStylesheet function - // somehow Edge camelcases the selectors so we need to lowercase it. + // somehow Edge camelcases the selectors so we need to lowercase it (#962). if ( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported && CKEDITOR.env.edge ) { CKEDITOR.tools.array.forEach( tested, function( item ) { item.selector = item.selector.toLowerCase(); @@ -49,13 +49,8 @@ 'test if parseStyles parses styles correctly': function() { testStyles( 'styles1' ); - - // Edge cannot handle 'margin-bottom: 0.0001pt' as it rounds it to 0 when parsing stylesheet. - if ( !( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported && CKEDITOR.env.edge ) ) { - testStyles( 'styles2' ); - testStyles( 'multiple' ); - } - + testStyles( 'styles2' ); + testStyles( 'multiple' ); testStyles( 'empty' ); testStyles( 'font-face' ); testStyles( 'page' ); From 2347c18a4136ec9e9761579d5f0a626b9541d254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 28 Sep 2017 14:06:36 +0200 Subject: [PATCH 14/47] Fix for 'OpenClipboard Failed' error. --- plugins/clipboard/plugin.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 6992e593adb..bbb96a79dd4 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2088,11 +2088,12 @@ // Edge < 16 does not support custom copy/cut, but it have some useful data in the clipboardData (http://dev.ckeditor.com/ticket/13755). return new this.dataTransfer( ( CKEDITOR.env.edge && evt && evt.data.$ && evt.data.$.clipboardData ) || null, sourceEditor ); } else if ( evt && evt.data && evt.data.$ ) { - var dataTransfer = new this.dataTransfer( evt.data.$.clipboardData, sourceEditor ); + var clipboardData = evt.data.$.clipboardData, + dataTransfer = new this.dataTransfer( clipboardData, sourceEditor ); if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) { dataTransfer = this.copyCutData; - dataTransfer.$ = evt.data.$.clipboardData; + dataTransfer.$ = clipboardData; } else { this.copyCutData = dataTransfer; } From 95078d2f541317795a79cb5ab93843f695f6b781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Mon, 2 Oct 2017 17:52:24 +0200 Subject: [PATCH 15/47] Set native dataTransfer id only on certain events. --- plugins/clipboard/plugin.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index bbb96a79dd4..926aed80063 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -114,6 +114,8 @@ 'use strict'; ( function() { + var clipboardIdDataType; + // Register the plugin. CKEDITOR.plugins.add( 'clipboard', { requires: 'notification,toolbar', @@ -2019,6 +2021,11 @@ var nativeDataTransfer = evt.data.$ ? evt.data.$.dataTransfer : null, dataTransfer = new this.dataTransfer( nativeDataTransfer, sourceEditor ); + // Set dataTransfer.id only for 'dragstart' event (so for events initializing dataTransfer inside editor) (#962). + if ( evt.name === 'dragstart' && clipboardIdDataType !== 'Text' ) { + dataTransfer.setData( clipboardIdDataType, dataTransfer.id ); + } + if ( !nativeDataTransfer ) { // No native event. if ( this.dragData ) { @@ -2091,6 +2098,11 @@ var clipboardData = evt.data.$.clipboardData, dataTransfer = new this.dataTransfer( clipboardData, sourceEditor ); + // Set dataTransfer.id only for 'copy'/'cut' events (so for events initializing dataTransfer inside editor) (#962). + if ( ( evt.name === 'copy' || evt.name === 'cut' ) && clipboardIdDataType !== 'Text' ) { + dataTransfer.setData( clipboardIdDataType, dataTransfer.id ); + } + if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) { dataTransfer = this.copyCutData; dataTransfer.$ = clipboardData; @@ -2122,7 +2134,8 @@ // so we just read dragged text. // // In Chrome and Firefox we can use custom data types. - var clipboardIdDataType = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ? 'cke/id' : 'Text'; + clipboardIdDataType = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported ? 'cke/id' : 'Text'; + /** * Facade for the native `dataTransfer`/`clipboadData` object to hide all differences * between browsers. @@ -2190,13 +2203,6 @@ } } - // Set id as a last thing to not force rereading/rewriting custom data comment. - // In IE10+ we can not use any data type besides text, so we do not call setData. - if ( clipboardIdDataType != 'Text' ) { - // Try to set ID so it will be passed from the drag to the drop event. - this._setCustomData( clipboardIdDataType, this.id, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); - } - /** * Data transfer ID used to bind all dataTransfer * objects based on the same event (e.g. in drag and drop events). From ea41a522d36f1ab8154b7c422ff98bc4b7e1238d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Mon, 2 Oct 2017 17:56:23 +0200 Subject: [PATCH 16/47] Tests: set native dataTransfer id only on certain events. --- tests/plugins/clipboard/datatransfer.js | 92 +++++++++++++++++-- .../plugins/clipboard/datatransferfallback.js | 18 ++-- tests/plugins/widget/dnd.js | 2 +- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 301322860a4..590a31075f9 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -36,9 +36,19 @@ 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 ); + idMimeType = CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ? 'cke/id' : 'Text', + dataTransfer1a, dataTransfer1b, dataTransfer2; + + // 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.setData( idMimeType, dataTransfer1a.id ); + + dataTransfer1b = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 ); + dataTransfer1b.setData( idMimeType, dataTransfer1b.id ); + + dataTransfer2 = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData2 ); + dataTransfer2.setData( idMimeType, dataTransfer2.id ); assert.areSame( dataTransfer1a.id, dataTransfer1b.id, 'Ids for object based on the same event should be the same.' ); @@ -818,9 +828,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 ); @@ -897,9 +907,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 ); @@ -955,5 +965,69 @@ bender.test( { text: isCustomDataTypesSupported ? 'xfoox' : '', html: 'xfoox' }, dataTransfer ); + }, + + // (#962) + 'test if dataTransfer id is set natively 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 ), + dataTransfer3; + + CKEDITOR.plugins.clipboard.initDragDataTransfer( evt3 ); + dataTransfer3 = evt3.data.dataTransfer; + + if ( CKEDITOR.plugins.clipboard.isCustomDataTypesSupported && !CKEDITOR.env.edge ) { + assert.areSame( dataTransfer1.id, nativeData1.getData( 'cke/id' ), 'dataTransfer1 id is not empty' ); + assert.areSame( dataTransfer2.id, nativeData2.getData( 'cke/id' ), 'dataTransfer2 id is not empty' ); + assert.areSame( dataTransfer3.id, nativeData3.getData( 'cke/id' ), 'dataTransfer3 id is not empty' ); + } else { + assert.areSame( dataTransfer1.id, + dataTransfer1._extractDataComment( nativeData1.getData( dataTransfer1.customDataFallbackType ) ).data[ 'cke/id' ], + 'dataTransfer1 id is not empty' ); + + assert.areSame( dataTransfer2.id, + dataTransfer2._extractDataComment( nativeData2.getData( dataTransfer2.customDataFallbackType ) ).data[ 'cke/id' ], + 'dataTransfer2 id is not empty' ); + + assert.areSame( dataTransfer3.id, + dataTransfer3._extractDataComment( nativeData3.getData( dataTransfer3.customDataFallbackType ) ).data[ 'cke/id' ], + 'dataTransfer3 id is not empty' ); + } + }, + + // (#962) + 'test if dataTransfer id is not set natively for all paste/drop events': function() { + if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) { + assert.ignore(); + } + + var nativeData1 = bender.tools.mockNativeDataTransfer(), + nativeData2 = bender.tools.mockNativeDataTransfer(), + evt1 = { data: { $: { clipboardData: nativeData1 } }, name: 'paste' }, + evt2 = { data: { $: { dataTransfer: nativeData2 } }, name: 'drop' }, + dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ), + dataTransfer2; + + CKEDITOR.plugins.clipboard.initDragDataTransfer( evt2 ); + dataTransfer2 = evt2.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' ); + } else { + // As dataTransfer id is stored in `customDataFallbackType` ('text/html' mime type), we just check if it is empty. + assert.areSame( '', nativeData1.getData( dataTransfer1.customDataFallbackType ), 'dataTransfer1 id is empty' ); + assert.areSame( '', nativeData2.getData( dataTransfer2.customDataFallbackType ), 'dataTransfer2 id is empty' ); + } } } ); diff --git a/tests/plugins/clipboard/datatransferfallback.js b/tests/plugins/clipboard/datatransferfallback.js index 8b7432fb4b6..ba21908c14c 100644 --- a/tests/plugins/clipboard/datatransferfallback.js +++ b/tests/plugins/clipboard/datatransferfallback.js @@ -13,7 +13,8 @@ bender.test( { 'test setData/getData with predefined type': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'text/plain', 'plain text' ); dataTransfer.setData( 'text/html', '

html text

' ); @@ -27,7 +28,8 @@ bender.test( { 'test setData/getData with custom type': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'cke/custom', 'cke-custom data' ); dataTransfer.setData( 'custom/tag', '

custom html tag

' ); @@ -44,7 +46,8 @@ bender.test( { 'test setData with custom type does not affect getData( "text/html" )': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'text/html', '

Header1

' ); @@ -63,7 +66,8 @@ bender.test( { 'test setData( "text/html" ) does not overwrite custom data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'cke/custom', 'custom data' ); @@ -85,7 +89,8 @@ bender.test( { 'test setData( "text/html" ) called a few times does not overwrite custom data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'cke/custom', 'custom data' ); @@ -108,7 +113,8 @@ bender.test( { 'test setting same custom type overwrites the previous value and does not affect other types': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ); + eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, + dataTransfer = CKEDITOR.plugins.clipboard.initPasteDataTransfer( eventMock ); dataTransfer.setData( 'cke/custom', 'cke-custom data' ); dataTransfer.setData( 'custom/tag', '

custom html tag

' ); diff --git a/tests/plugins/widget/dnd.js b/tests/plugins/widget/dnd.js index ade3607f401..e99011b2ccd 100644 --- a/tests/plugins/widget/dnd.js +++ b/tests/plugins/widget/dnd.js @@ -308,7 +308,7 @@ var editor = this.editor; this.editorBot.setData( '

foo

xfoox

', function() { - var evt = { data: bender.tools.mockDropEvent() }, + var evt = { data: bender.tools.mockDropEvent(), name: 'dragstart' }, range = editor.createRange(), dropCalled = false, dropNotCancelled = false; From a5dba7f90d34f568eedf70d8419d10399682aa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 10 Oct 2017 15:51:30 +0200 Subject: [PATCH 17/47] Extracted all Edge logic to 'CKEDITOR.plugins.clipboard.fallbackDataTransfer'. --- plugins/clipboard/plugin.js | 235 +++++++++++++++++++++++++++--------- 1 file changed, 179 insertions(+), 56 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 926aed80063..3d2b55285e9 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2152,7 +2152,7 @@ this.$ = nativeDataTransfer; } - this.customDataFallbackType = 'text/html'; + this.fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( this.$ ); this._ = { metaRegExp: /^/i, @@ -2234,11 +2234,12 @@ */ /** - * The MIME type which is used to store custom data (in special `` comment) - * when browser does not allow to use custom MIME types in `dataTransfer.setData`. + * Fallback object which is used in browsers not supporting custom + * MIME types in native `dataTransfer.setData` (Edge 16+). * + * @since 4.8 * @readonly - * @property {String} customDataFallbackType + * @property {CKEDITOR.plugins.clipboard.fallbackDataTransfer} fallbackDataTransfer */ }; @@ -2308,32 +2309,22 @@ type = this._.normalizeType( type ); var data = this._.data[ type ], - content, result; if ( isEmpty( data ) ) { - try { - data = this.$.getData( type ); - } catch ( e ) {} - } - // If we are getting the same type which may store custom data we need to extract it (removing custom data comment). - // Or if we are getting different type and it is empty we need to check inside data comment if it is stored there. - if ( !isEmpty( data ) && type === this.customDataFallbackType ) { - content = this._extractDataComment( data ); - data = content.content; + // First get data using fallback object and then proceed with regular flow. + if ( this.fallbackDataTransfer.isRequired() ) { + data = this.fallbackDataTransfer.getData( type ); + } - } else if ( isEmpty( data ) ) { - try { - content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); - if ( content.data && content.data[ type ] ) { - data = content.data[ type ]; + if ( isEmpty( data ) ) { + try { + data = this.$.getData( type ) || ''; + } catch ( e ) { + data = ''; } - } catch ( e ) {} - } - - if ( isEmpty( data ) ) { - data = ''; + } } // Some browsers add at the begging of the HTML data @@ -2389,21 +2380,13 @@ this.id = value; } - // Extract custom data if used type is the same one which is used for storing it. - // Then new value is added to a custom data so it will not be overwritten entirely. - if ( type === this.customDataFallbackType ) { - var content = null; - try { - content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); - } catch ( e ) {} - - if ( content && content.data ) { - value = this._applyDataComment( value, content.data ); - } - this._setCustomData( type, value ); + if ( this.fallbackDataTransfer.isRequired() ) { + this.fallbackDataTransfer.setData( type, value ); } else { - this._setCustomData( type, value, CKEDITOR.env.ie && CKEDITOR.env.version >= 16 ); + try { + this.$.setData( type, value ); + } catch ( e ) {} } }, @@ -2588,45 +2571,184 @@ } return undefined; + } + }; + + /** + * Fallback dataTransfer object which is used together with {@link CKEDITOR.plugins.clipboard.dataTransfer} + * for browsers supporting Clipboard API, but not supporting custom MIME types (Edge 16+). + * + * @since 4.8 + * @class CKEDITOR.plugins.clipboard.fallbackDataTransfer + * @constructor Creates a class instance. + * @param {Object} [nativeDataTransfer] A native data transfer object. + * @param {String} [customDataFallbackType=text/html] A MIME type used for storing custom data types. + */ + CKEDITOR.plugins.clipboard.fallbackDataTransfer = function( nativeDataTransfer, customDataFallbackType ) { + + /** + * A native dataTransfer object. + * + * @private + * @property {Object} _nativeDataTransfer + */ + this._nativeDataTransfer = nativeDataTransfer; + + /** + * A MIME type used for storing custom MIME types. Defaults to 'text/html'. + * + * @private + * @property {String} _customDataFallbackType + */ + this._customDataFallbackType = customDataFallbackType || 'text/html'; + }; + + /** + * True if the environment supports custom MIME types in dataTransfer/cliboardData getData/setData methods. Introduced + * for Edge 16+ which supports only some whitelisted types (like 'text/html', 'application/xml'), but does not support + * custom MIME types (like `cke/id`). + * This property should not be accessed directly, use {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#isRequired} + * method instead. + * + * @private + * @static + * @property {Boolean} + */ + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; + + CKEDITOR.plugins.clipboard.fallbackDataTransfer.prototype = { + /** + * Whether {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer} should be used when operating on dataTransfer. + * If true is returned, it means custom MIME types are not supported in the current browser + * (see {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_isCustomMimeTypeSupported}). + * + * @returns {Boolean} + */ + isRequired: function() { + if ( CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported === null ) { + + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = true; + + try { + this._nativeDataTransfer.setData( 'cke/mimetypetest', 'test value' ); + this._nativeDataTransfer.setData( 'cke/mimetypetest', null ); + } catch ( e ) { + if ( this._isUnsupportedMimeTypeError( e ) ) { + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = false; + } + } + } + + return !CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported; }, /** - * Additional layer over `dataTransfer.setData` method. If used with `useFallback = true` in case of native `setData` - * throwing an error it will try to place passed value in - * {@link CKEDITOR.plugins.clipboard.dataTransfer#customDataFallbackType} type using special comment format: + * Extracts and returns data of the given MIME type if stored in + * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType} special comment or if type + * is the same as {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType}. + * + * @param {String} type + * @returns {String} + */ + getData: function( type ) { + var value = null; + + // If we are getting the same type which may store custom data we need to extract it (removing custom data comment). + if ( type === this._customDataFallbackType ) { + value = this._extractDataComment( this._getData( type ) ).content; + + // If we are getting different type we need to check inside data comment if it is stored there. + } else { + var result = this._extractDataComment( this._getData( this._customDataFallbackType ) ); + if ( result.data && result.data[ type ] ) { + value = result.data[ type ]; + } + } + + return value; + }, + + /** + * Sets given data in native dataTransfer object. If given MIME type is not supported it uses + * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType} MIME type + * to save data using special comment format: * * * * It is important to keep in mind that `{ type: value }` object is stringified (using `JSON.stringify`) * and encoded (using `encodeURIComponent`). * - * @private * @param {String} type * @param {String} value - * @param {Boolean} useFallback */ - _setCustomData: function( type, value, useFallback ) { - try { - this.$.setData( type, value ); + setData: function( type, value ) { + // Extract custom data if used type is the same one which is used for storing it. + if ( type === this._customDataFallbackType ) { + var content = this._extractDataComment( this._getData( this._customDataFallbackType ) ); + + if ( content && content.data ) { + value = this._applyDataComment( value, content.data ); + } + } + try { + this._nativeDataTransfer.setData( type, value ); } catch ( e ) { - if ( useFallback ) { - // Still in some situations some browsers may throw errors even when using predefined MIME types. - try { - // We need to get the data first to append `type` and not to overwrite whole custom data. - var content = this._extractDataComment( this.$.getData( this.customDataFallbackType ) ); + if ( this._isUnsupportedMimeTypeError( e ) ) { + // We need to get the data first to append `type` and not to overwrite whole custom data. + var current = this._extractDataComment( this._getData( this._customDataFallbackType ) ); - if ( !content.data ) { - content.data = {}; - } - content.data[ type ] = value; + if ( !current.data ) { + current.data = {}; + } + current.data[ type ] = value; - this.$.setData( this.customDataFallbackType, this._applyDataComment( content.content, content.data ) ); - } catch ( e ) {} + this._setData( this._customDataFallbackType, this._applyDataComment( current.content, current.data ) ); } } }, + /** + * Native getData wrapper. + * + * @private + * @param {String} type + * @returns {String} + */ + _getData: function( type ) { + try { + return this._nativeDataTransfer.getData( type ); + } catch ( e ) { + return null; + } + }, + + /** + * Native setData wrapper. + * + * @private + * @param {String} type + * @param {String} value + */ + _setData: function( type, value ) { + try { + this._nativeDataTransfer.setData( type, value ); + } catch ( e ) { + // Some dev logger should be added here. + } + }, + + /** + * Whether provided error means that unsupported MIME type was used when calling native `dataTransfer.setData` method. + * + * @private + * @param {Object} error + * @returns {Boolean} + */ + _isUnsupportedMimeTypeError: function( error ) { + return error.message.search( /element not found/gi ) !== -1; + }, + /** * Extracts `cke-data` comment from the given content. Returns an object containing extracted data as `data` * and content (without `cke-data` comment) as `content`. @@ -2673,6 +2795,7 @@ return customData + ( content && content.length ? content : '' ); } }; + } )(); /** From 4eafeee70b435f05577f282bb65911dd1ea9e5ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 10 Oct 2017 16:42:57 +0200 Subject: [PATCH 18/47] Introduced 'setId' method. --- plugins/clipboard/plugin.js | 20 ++++++++++++++++---- tests/plugins/clipboard/datatransfer.js | 10 +++++----- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 3d2b55285e9..e59f99785ec 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2022,8 +2022,8 @@ dataTransfer = new this.dataTransfer( nativeDataTransfer, sourceEditor ); // Set dataTransfer.id only for 'dragstart' event (so for events initializing dataTransfer inside editor) (#962). - if ( evt.name === 'dragstart' && clipboardIdDataType !== 'Text' ) { - dataTransfer.setData( clipboardIdDataType, dataTransfer.id ); + if ( evt.name === 'dragstart' ) { + dataTransfer.setId( dataTransfer.id ); } if ( !nativeDataTransfer ) { @@ -2099,8 +2099,8 @@ dataTransfer = new this.dataTransfer( clipboardData, sourceEditor ); // Set dataTransfer.id only for 'copy'/'cut' events (so for events initializing dataTransfer inside editor) (#962). - if ( ( evt.name === 'copy' || evt.name === 'cut' ) && clipboardIdDataType !== 'Text' ) { - dataTransfer.setData( clipboardIdDataType, dataTransfer.id ); + if ( evt.name === 'copy' || evt.name === 'cut' ) { + dataTransfer.setId( dataTransfer.id ); } if ( this.copyCutData && dataTransfer.id == this.copyCutData.id ) { @@ -2390,6 +2390,18 @@ } }, + /** + * Sets dataTransfer id. + * + * @since 4.8 + * @param {String} id + */ + setId: function( id ) { + if ( clipboardIdDataType !== 'Text' ) { + this.setData( clipboardIdDataType, id ); + } + }, + /** * Gets the data transfer type. * diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 590a31075f9..c671b61d5ff 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -36,25 +36,25 @@ bender.test( { 'test id': function() { var nativeData1 = bender.tools.mockNativeDataTransfer(), nativeData2 = bender.tools.mockNativeDataTransfer(), - idMimeType = CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ? 'cke/id' : 'Text', dataTransfer1a, dataTransfer1b, dataTransfer2; // 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.setData( idMimeType, dataTransfer1a.id ); + dataTransfer1a.setId( dataTransfer1a.id ); dataTransfer1b = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData1 ); - dataTransfer1b.setData( idMimeType, dataTransfer1b.id ); + dataTransfer1b.setId( dataTransfer1b.id ); dataTransfer2 = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData2 ); - dataTransfer2.setData( idMimeType, dataTransfer2.id ); + dataTransfer2.setId( dataTransfer2.id ); 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() { From 596f2b2a8e046af03382a243b9e016730b407f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 10 Oct 2017 17:01:56 +0200 Subject: [PATCH 19/47] Docs fix. --- plugins/clipboard/plugin.js | 20 ++++++++++---------- tests/_benderjs/ckeditor/static/tools.js | 3 ++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index e59f99785ec..ce9721c045d 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2616,9 +2616,9 @@ }; /** - * True if the environment supports custom MIME types in dataTransfer/cliboardData getData/setData methods. Introduced - * for Edge 16+ which supports only some whitelisted types (like 'text/html', 'application/xml'), but does not support - * custom MIME types (like `cke/id`). + * True if the environment supports custom MIME types in `dataTransfer/cliboardData` `getData/setData` methods. Introduced + * to distinguish browsers which supports only some whitelisted types (like 'text/html', 'application/xml'), but does not support + * custom MIME types (like `cke/id`) like Edge 16+. * This property should not be accessed directly, use {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#isRequired} * method instead. * @@ -2656,8 +2656,8 @@ /** * Extracts and returns data of the given MIME type if stored in - * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType} special comment or if type - * is the same as {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType}. + * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#\_customDataFallbackType} special comment or if type + * is the same as {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#\_customDataFallbackType}. * * @param {String} type * @returns {String} @@ -2681,11 +2681,11 @@ }, /** - * Sets given data in native dataTransfer object. If given MIME type is not supported it uses + * Sets given data in native `dataTransfer` object. If given MIME type is not supported it uses * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType} MIME type * to save data using special comment format: * - * + * * * It is important to keep in mind that `{ type: value }` object is stringified (using `JSON.stringify`) * and encoded (using `encodeURIComponent`). @@ -2762,12 +2762,12 @@ }, /** - * Extracts `cke-data` comment from the given content. Returns an object containing extracted data as `data` - * and content (without `cke-data` comment) as `content`. + * Extracts `cke-data` comment from the given content. * * @private * @param {String} content - * @returns {Object} + * @returns {Object} Returns an object containing extracted data as `data` + * and content (without `cke-data` comment) as `content`. * @returns {Object} return.data * @returns {String} return.content */ diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index 8a0db75c5d4..47879411edb 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -826,7 +826,8 @@ return; } - if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 16 && + // While Edge 16+ supports Clipboard API, it does not support custom mime types in setData. + if ( CKEDITOR.env.edge && CKEDITOR.env.version >= 16 && CKEDITOR.tools.indexOf( [ 'Text', 'URL', 'text/plain', 'text/html', 'application/xml' ], type ) === -1 ) { throw 'Element not found.'; From d6e8fcbceaa15a4f2d3ec86f5aa6406f84f25860 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 11 Oct 2017 13:25:35 +0200 Subject: [PATCH 20/47] Fallback data transfer improvements. --- plugins/clipboard/plugin.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index ce9721c045d..88e94506589 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2313,12 +2313,10 @@ if ( isEmpty( data ) ) { - // First get data using fallback object and then proceed with regular flow. if ( this.fallbackDataTransfer.isRequired() ) { data = this.fallbackDataTransfer.getData( type ); - } - if ( isEmpty( data ) ) { + } else { try { data = this.$.getData( type ) || ''; } catch ( e ) { @@ -2655,9 +2653,8 @@ }, /** - * Extracts and returns data of the given MIME type if stored in - * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#\_customDataFallbackType} special comment or if type - * is the same as {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#\_customDataFallbackType}. + * Returns the data of the given MIME type. Extracts data of the given MIME type if it is stored in + * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#\_customDataFallbackType} special comment. * * @param {String} type * @returns {String} @@ -2665,7 +2662,7 @@ getData: function( type ) { var value = null; - // If we are getting the same type which may store custom data we need to extract it (removing custom data comment). + // If we are getting the same type which may store custom data we need to extract content only. if ( type === this._customDataFallbackType ) { value = this._extractDataComment( this._getData( type ) ).content; @@ -2674,10 +2671,14 @@ var result = this._extractDataComment( this._getData( this._customDataFallbackType ) ); if ( result.data && result.data[ type ] ) { value = result.data[ type ]; + + // And then fallback to regular `getData`. + } else { + value = this._getData( type ); } } - return value; + return value !== null ? value : ''; }, /** @@ -2758,7 +2759,7 @@ * @returns {Boolean} */ _isUnsupportedMimeTypeError: function( error ) { - return error.message.search( /element not found/gi ) !== -1; + return error.message && error.message.search( /element not found/gi ) !== -1; }, /** From 230e7ca75d337ff5004b2563e05ec73a4ee3c9c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 11 Oct 2017 13:26:13 +0200 Subject: [PATCH 21/47] Fixed tests. --- tests/_benderjs/ckeditor/static/tools.js | 8 +- tests/plugins/clipboard/datatransfer.js | 56 ++++++++---- ...allback.html => fallbackdatatransfer.html} | 0 ...ferfallback.js => fallbackdatatransfer.js} | 86 ++++++++++--------- tests/plugins/clipboard/paste.js | 4 +- 5 files changed, 91 insertions(+), 63 deletions(-) rename tests/plugins/clipboard/{datatransferfallback.html => fallbackdatatransfer.html} (100%) rename tests/plugins/clipboard/{datatransferfallback.js => fallbackdatatransfer.js} (75%) diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index 47879411edb..df93a7dac17 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -826,11 +826,15 @@ return; } - // While Edge 16+ supports Clipboard API, it does not support custom mime types in setData. + // 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 ) { - throw 'Element not found.'; + throw { + name: 'Error', + message: 'Element not found.' + }; } if ( type == 'text/plain' || type == 'Text' ) { diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index c671b61d5ff..74b30749f74 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -492,9 +492,9 @@ bender.test( { // Emulate native clipboard. nativeData = bender.tools.mockNativeDataTransfer(); - // This test uses native (mocked) `setData` which does not applies fallback - // for Edge >= 16 (because it skips our wrapper) so it works - // as if `isCustomDataTypesSupported` flag was turned off for Edge (#962). + // 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' ); @@ -968,7 +968,7 @@ bender.test( { }, // (#962) - 'test if dataTransfer id is set natively for copy/cut/dragstart events': function() { + 'test new dataTransfer id is created for copy/cut/dragstart events': function() { if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) { assert.ignore(); } @@ -981,53 +981,73 @@ bender.test( { evt3 = { data: { $: { dataTransfer: nativeData3 } }, name: 'dragstart' }, dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ), dataTransfer2 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt2 ), - dataTransfer3; + 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' ), 'dataTransfer1 id is not empty' ); - assert.areSame( dataTransfer2.id, nativeData2.getData( 'cke/id' ), 'dataTransfer2 id is not empty' ); - assert.areSame( dataTransfer3.id, nativeData3.getData( 'cke/id' ), 'dataTransfer3 id is not empty' ); + 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, - dataTransfer1._extractDataComment( nativeData1.getData( dataTransfer1.customDataFallbackType ) ).data[ 'cke/id' ], - 'dataTransfer1 id is not empty' ); + dtFallback1._extractDataComment( nativeData1.getData( dtFallback1._customDataFallbackType ) ).data[ 'cke/id' ], + 'cke/id custom data holds dataTransfer1 id' ); assert.areSame( dataTransfer2.id, - dataTransfer2._extractDataComment( nativeData2.getData( dataTransfer2.customDataFallbackType ) ).data[ 'cke/id' ], - 'dataTransfer2 id is not empty' ); + dtFallback2._extractDataComment( nativeData2.getData( dtFallback2._customDataFallbackType ) ).data[ 'cke/id' ], + 'cke/id custom data holds dataTransfer2 id' ); assert.areSame( dataTransfer3.id, - dataTransfer3._extractDataComment( nativeData3.getData( dataTransfer3.customDataFallbackType ) ).data[ 'cke/id' ], - 'dataTransfer3 id is not empty' ); + dtFallback3._extractDataComment( nativeData3.getData( dtFallback3._customDataFallbackType ) ).data[ 'cke/id' ], + 'cke/id custom data holds dataTransfer3 id' ); } }, // (#962) - 'test if dataTransfer id is not set natively for all paste/drop events': function() { + '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; + 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.customDataFallbackType ), 'dataTransfer1 id is empty' ); - assert.areSame( '', nativeData2.getData( dataTransfer2.customDataFallbackType ), 'dataTransfer2 id 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' ); } } } ); diff --git a/tests/plugins/clipboard/datatransferfallback.html b/tests/plugins/clipboard/fallbackdatatransfer.html similarity index 100% rename from tests/plugins/clipboard/datatransferfallback.html rename to tests/plugins/clipboard/fallbackdatatransfer.html diff --git a/tests/plugins/clipboard/datatransferfallback.js b/tests/plugins/clipboard/fallbackdatatransfer.js similarity index 75% rename from tests/plugins/clipboard/datatransferfallback.js rename to tests/plugins/clipboard/fallbackdatatransfer.js index ba21908c14c..813326784e9 100644 --- a/tests/plugins/clipboard/datatransferfallback.js +++ b/tests/plugins/clipboard/fallbackdatatransfer.js @@ -139,129 +139,129 @@ bender.test( { }, 'test _applyDataComment case1': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = document.querySelector( '#case1' ).innerHTML; - this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransfer, expected ); + this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case2': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = document.querySelector( '#case2' ).innerHTML; - this.assertApplyDataComment( '

Header1

Test1

', { test: 1, comment: '' }, dataTransfer, expected ); + this.assertApplyDataComment( '

Header1

Test1

', { test: 1, comment: '' }, dataTransferFallback, expected ); }, 'test _applyDataComment case3': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = CKEDITOR.tools.trim( document.querySelector( '#case3' ).innerHTML ); - this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransfer, expected ); + this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case4': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = CKEDITOR.tools.trim( document.querySelector( '#case4' ).innerHTML ); - this.assertApplyDataComment( '

Header1

Test1

', { test: 123 }, dataTransfer, expected ); + this.assertApplyDataComment( '

Header1

Test1

', { test: 123 }, dataTransferFallback, expected ); }, 'test _applyDataComment with empty content': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ); - this.assertApplyDataComment( undefined, { test: 1 }, dataTransfer, expected ); - this.assertApplyDataComment( null, { test: 1 }, dataTransfer, expected ); - this.assertApplyDataComment( '', { test: 1 }, dataTransfer, expected ); + this.assertApplyDataComment( undefined, { test: 1 }, dataTransferFallback, expected ); + this.assertApplyDataComment( null, { test: 1 }, dataTransferFallback, expected ); + this.assertApplyDataComment( '', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment with empty data': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ); - this.assertApplyDataComment( '

foobar

', '', dataTransfer, expected ); - this.assertApplyDataComment( '

foobar

', null, dataTransfer, expected ); - this.assertApplyDataComment( '

foobar

', {}, dataTransfer, expected ); + this.assertApplyDataComment( '

foobar

', '', dataTransferFallback, expected ); + this.assertApplyDataComment( '

foobar

', null, dataTransferFallback, expected ); + this.assertApplyDataComment( '

foobar

', {}, dataTransferFallback, expected ); }, 'test _applyDataComment with empty content and data': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), expected = ''; - this.assertApplyDataComment( undefined, null, dataTransfer, expected ); - this.assertApplyDataComment( null, undefined, dataTransfer, expected ); - this.assertApplyDataComment( '', {}, dataTransfer, expected ); + this.assertApplyDataComment( undefined, null, dataTransferFallback, expected ); + this.assertApplyDataComment( null, undefined, dataTransferFallback, expected ); + this.assertApplyDataComment( '', {}, dataTransferFallback, expected ); }, 'test _extractDataComment case1': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = document.querySelector( '#case1' ).innerHTML, - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); objectAssert.areEqual( extracted.data, { test: 1 } ); assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); }, 'test _extractDataComment case2': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = document.querySelector( '#case2' ).innerHTML, - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); objectAssert.areEqual( extracted.data, { test: 1, comment: '' } ); assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); }, 'test _extractDataComment case3': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = document.querySelector( '#case3' ).innerHTML, - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); objectAssert.areEqual( extracted.data, { test: 1 } ); assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); }, 'test _extractDataComment case4': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = document.querySelector( '#case4' ).innerHTML, - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); objectAssert.areEqual( extracted.data, { test: 123 } ); assert.isInnerHtmlMatching( '

Header1

Test1

', extracted.content ); }, 'test _extractDataComment with empty content': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ), - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); objectAssert.areEqual( extracted.data, { test: 1 } ); assert.areSame( '', extracted.content ); }, 'test _extractDataComment with empty data': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ), - extracted = dataTransfer._extractDataComment( content ); + extracted = fallbackDataTransfer._extractDataComment( content ); assert.isNull( extracted.data ); assert.isInnerHtmlMatching( '

foobar

', extracted.content ); }, 'test _extractDataComment with empty data and content': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ), - extracted = dataTransfer._extractDataComment( '' ); + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + extracted = fallbackDataTransfer._extractDataComment( '' ); assert.isNull( extracted.data ); assert.areSame( '', extracted.content ); }, 'test _extractDataComment with falsy value': function() { - var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {} ); + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ); - assert.areSame( '', dataTransfer._extractDataComment( '' ).content ); - assert.areSame( '', dataTransfer._extractDataComment( null ).content ); - assert.areSame( '', dataTransfer._extractDataComment( undefined ).content ); - assert.areSame( '', dataTransfer._extractDataComment( false ).content ); + assert.areSame( '', fallbackDataTransfer._extractDataComment( '' ).content ); + assert.areSame( '', fallbackDataTransfer._extractDataComment( null ).content ); + assert.areSame( '', fallbackDataTransfer._extractDataComment( undefined ).content ); + assert.areSame( '', fallbackDataTransfer._extractDataComment( false ).content ); }, assertDataTransferType: function( dataTransfer, type, value, customValue ) { @@ -271,8 +271,8 @@ bender.test( { assert.areSame( value, dataTransfer.$.getData( type ) ); }, - assertApplyDataComment: function( content, data, dataTransfer, expected ) { - assert.isInnerHtmlMatching( expected.replace( /[\n\r\t]*/g, '' ), dataTransfer._applyDataComment( content, data ) ); + assertApplyDataComment: function( content, data, dataTransferFallback, expected ) { + assert.isInnerHtmlMatching( expected.replace( /[\n\r\t]*/g, '' ), dataTransferFallback._applyDataComment( content, data ) ); } } ); @@ -281,3 +281,7 @@ function getDataNoCache( dataTransfer, type ) { dataTransfer._.data = {}; return dataTransfer.getData( type ); } + + +// check if 'text/html' read from cache works fine +// check if 'text/html' with data '0' is not nulled diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index 955f023f7d1..278132e3479 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -1190,7 +1190,7 @@ // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). if ( CKEDITOR.env.edge ) { var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); - assert.areSame( 'bar', dataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + assert.areSame( 'bar', dataTransfer.fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); } else { assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); } @@ -1215,7 +1215,7 @@ // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). if ( CKEDITOR.env.edge ) { var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); - assert.areSame( 'bar', dataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + assert.areSame( 'bar', dataTransfer.fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); } else { assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); } From 0404325f929ca8f811d227a392ec494553bf566f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 11 Oct 2017 16:06:34 +0200 Subject: [PATCH 22/47] Handle empty 'dataTransfer' in 'isRequired' method. --- plugins/clipboard/plugin.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 88e94506589..d1a6aae31f3 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -355,10 +355,11 @@ // events chain. editor.on( 'paste', function( evt ) { var data = evt.data; - if ( data.dataValue ) { editor.insertHtml( data.dataValue, data.type, data.range ); + // debugger; + // Defer 'afterPaste' so all other listeners for 'paste' will be fired first. // Fire afterPaste only if paste inserted some HTML. setTimeout( function() { @@ -2311,6 +2312,8 @@ var data = this._.data[ type ], result; + + if ( isEmpty( data ) ) { if ( this.fallbackDataTransfer.isRequired() ) { @@ -2635,6 +2638,12 @@ * @returns {Boolean} */ isRequired: function() { + // If there is no `dataTransfer` we cannot detect if fallback is needed. + // Method returns `false` so regular flow will be applied. + if ( !this._nativeDataTransfer ) { + return false; + } + if ( CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported === null ) { CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = true; From 5d3abe15e88f7e5979e1c6f8dc14bcbfe37606d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 11 Oct 2017 17:46:47 +0200 Subject: [PATCH 23/47] Fixed failing test. --- tests/plugins/widget/acf.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/plugins/widget/acf.js b/tests/plugins/widget/acf.js index b3aa592a97b..bb2e7c355f9 100644 --- a/tests/plugins/widget/acf.js +++ b/tests/plugins/widget/acf.js @@ -97,7 +97,15 @@ var editor = bot.editor; assert.areSame( 3, obj2Array( editor.widgets.instances ).length ); - assert.areSame( '

foobarbimbom

', editor.getData() ); + + // On Edge 16+ when test is run from the main bender panel the `` tag attributes order + // is different so isInnerHtmlMatching should be used here. + assert.isInnerHtmlMatching( + '

foobarbimbom

', + editor.getData(), { + fixNbsp: false, + fixZWS: false + } ); } ); }, @@ -188,4 +196,4 @@ } ); } } ); -} )(); \ No newline at end of file +} )(); From 514f93ceda7a3feac19b66f685abbcf7f90c0ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 11 Oct 2017 18:01:48 +0200 Subject: [PATCH 24/47] Fix for 'dataTransfer.items'. --- plugins/clipboard/plugin.js | 4 +--- tests/plugins/clipboard/datatransfer.js | 10 ---------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index d1a6aae31f3..f9462d09a75 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2569,9 +2569,7 @@ _getImageFromClipboard: function() { var file; - // The dataTransfer.items is not supported in IE/Edge. This function is used as a backup always after - // dataTransfer.files is checked so there is no need for implementing more logic than ignoring IE/Edge (#468). - if ( !CKEDITOR.env.ie && this.$ && this.$.items && this.$.items[ 0 ] ) { + if ( this.$ && this.$.items && this.$.items[ 0 ] ) { try { file = this.$.items[ 0 ].getAsFile(); // Duck typing diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 74b30749f74..c4d6e61f2ce 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -566,11 +566,6 @@ bender.test( { // http://dev.ckeditor.com/ticket/12961 'test file in items': function() { - // DataTransfer.items is not supported in IE/EDGE so there is no reason to test it. - if ( CKEDITOR.env.ie ) { - assert.ignore(); - } - var nativeData = bender.tools.mockNativeDataTransfer(), dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ), file = { type: 'type' }; @@ -656,11 +651,6 @@ bender.test( { // http://dev.ckeditor.com/ticket/12961 'test file in items with cache': function() { - // DataTransfer.items is not supported in IE/EDGE so there is no reason to test it. - if ( CKEDITOR.env.ie ) { - assert.ignore(); - } - var nativeData = bender.tools.mockNativeDataTransfer(), dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData ), file = { type: 'type' }; From cac60adf6be2711b4288cce31df8c74941b2ce07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 12 Oct 2017 20:36:57 +0200 Subject: [PATCH 25/47] Proper Edge workaround description. --- tests/plugins/pastefromword/parsestyles.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/pastefromword/parsestyles.js b/tests/plugins/pastefromword/parsestyles.js index 6147386f97e..f4d9f926916 100644 --- a/tests/plugins/pastefromword/parsestyles.js +++ b/tests/plugins/pastefromword/parsestyles.js @@ -12,8 +12,8 @@ bender.tools.testInputOut( name, function( styles, expected ) { var tested = CKEDITOR.plugins.pastefromword.styles.inliner.parse( styles ); - // In CKEDITOR.plugins.pastefromword.styles.inliner.parse#createIsolatedStylesheet function - // somehow Edge camelcases the selectors so we need to lowercase it (#962). + // In `CKEDITOR.plugins.pastefromword.styles.inliner.parse#createIsolatedStylesheet` + // function Edge camelcases the selectors so we need to lowercase it (#1042). if ( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported && CKEDITOR.env.edge ) { CKEDITOR.tools.array.forEach( tested, function( item ) { item.selector = item.selector.toLowerCase(); From 6223ae2d16fd66c7766b4ce0f16864c65e2f0191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Fri, 13 Oct 2017 07:37:27 +0200 Subject: [PATCH 26/47] Refactoring, simplified manual test. --- plugins/clipboard/plugin.js | 26 +--- .../manual/datatransferfallback.html | 134 ++---------------- .../clipboard/manual/datatransferfallback.md | 34 ++--- 3 files changed, 36 insertions(+), 158 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index f9462d09a75..0db8863a9ec 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -189,7 +189,6 @@ } editor.on( 'paste', function( evt ) { - // Init `dataTransfer` if `paste` event was fired without it, so it will be always available. if ( !evt.data.dataTransfer ) { evt.data.dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer(); @@ -358,8 +357,6 @@ if ( data.dataValue ) { editor.insertHtml( data.dataValue, data.type, data.range ); - // debugger; - // Defer 'afterPaste' so all other listeners for 'paste' will be fired first. // Fire afterPaste only if paste inserted some HTML. setTimeout( function() { @@ -562,6 +559,7 @@ // But don't you know any way to distinguish first two cases from last two? // Only one - special flag set in CTRL+V handler and exec method of 'paste' // command. And that's what we did using preventPasteEventNow(). + pasteDataFromClipboard( evt ); } ); @@ -665,6 +663,7 @@ this.type == 'cut' && fixCut(); var success = tryToCutCopy( this.type ); + if ( !success ) { // Show cutError or copyError. editor.showNotification( editor.lang.clipboard[ this.type + 'Error' ] ); // jshint ignore:line @@ -2723,7 +2722,11 @@ } current.data[ type ] = value; - this._setData( this._customDataFallbackType, this._applyDataComment( current.content, current.data ) ); + try { + this._nativeDataTransfer.setData( this._customDataFallbackType, this._applyDataComment( current.content, current.data ) ); + } catch ( e ) { + // Some dev logger should be added here. + } } } }, @@ -2743,21 +2746,6 @@ } }, - /** - * Native setData wrapper. - * - * @private - * @param {String} type - * @param {String} value - */ - _setData: function( type, value ) { - try { - this._nativeDataTransfer.setData( type, value ); - } catch ( e ) { - // Some dev logger should be added here. - } - }, - /** * Whether provided error means that unsupported MIME type was used when calling native `dataTransfer.setData` method. * diff --git a/tests/plugins/clipboard/manual/datatransferfallback.html b/tests/plugins/clipboard/manual/datatransferfallback.html index f644ec642ff..46a1b9a66e6 100644 --- a/tests/plugins/clipboard/manual/datatransferfallback.html +++ b/tests/plugins/clipboard/manual/datatransferfallback.html @@ -1,94 +1,25 @@ - -
-

Helpers

-
- -
- Lorem ipsum dolor sit amet, consectetur adipiscing elit. CKEditor logo In commodo vulputate tempor. Sed <b>at elit</b> vel ligula mollis aliquet a ac odio. -
-Aenean cursus egestas ipsum.
-			
-
-
-
-
-

Classic editor

-

Apollo 11

- -

Apollo 11 was the spaceflight that landed the first humans, Americans Neil Armstrong and Buzz Aldrin, on the Moon on July 20, 1969, at 20:18 UTC. CKEditor logo Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.

- -
CKEditor logo -
CKEditor
-
- -

Armstrong spent about three and a half two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5 kg) of lunar material for return to Earth. A third member of the mission, Michael Collins, piloted the command spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.

- -

Broadcasting and quotes

- -

Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:

- -
-

One small step for [a] man, one giant leap for mankind.

-
- -

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

- -
-

[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.

-
- -

Technical details

- - - - - - - - - - - - - - - - - - - - - - - -
Mission crew
PositionAstronaut
CommanderNeil A. Armstrong
Command Module PilotMichael Collins
Lunar Module PilotEdwin "Buzz" E. Aldrin, Jr.
- -

Launched by a Saturn V rocket from Kennedy Space Center in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of NASA's Apollo program. The Apollo spacecraft had three parts:

- +

Foo Bar Baz

    -
  1. Command Module with a cabin for the three astronauts which was the only part which landed back on Earth
  2. -
  3. Service Module which supported the Command Module with propulsion, electrical power, oxygen and water
  4. -
  5. Lunar Module for landing on the Moon.
  6. +
  7. Apollo 11 was the spaceflight that landed the first humans,
  8. +
  9. Americans Neil Armstrong and Buzz Aldrin
-

After being sent to the Moon by the Saturn V's upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the Sea of Tranquility. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the Pacific Ocean on July 24.

+
CKEditor logo +
CKEditor
+
-
-

Source: Wikipedia.org

+
    +
  • CKEditor logo Armstrong became the first to step onto the lunar surface 6 hours
  • +
  • later on July 21 at 02:56 UTC. Broadcasting and quotes
  • +
@@ -96,17 +27,7 @@

Technical details

Inline editor

-

Apollo 11

- -

Apollo 11 was the spaceflight that landed the first humans, Americans Neil Armstrong and Buzz Aldrin, on the Moon on July 20, 1969, at 20:18 UTC. CKEditor logo Armstrong became the first to step onto the lunar surface 6 hours later on July 21 at 02:56 UTC.

- -
CKEditor logo -
CKEditor
-
- -

Armstrong spent about three and a half two and a half hours outside the spacecraft, Aldrin slightly less; and together they collected 47.5 pounds (21.5 kg) of lunar material for return to Earth. A third member of the mission, Michael Collins, piloted the command spacecraft alone in lunar orbit until Armstrong and Aldrin returned to it for the trip back to Earth.

- -

Broadcasting and quotes

+

Broadcasting and quotes

Broadcast on live TV to a world-wide audience, Armstrong stepped onto the lunar surface and described the event as:

@@ -114,16 +35,7 @@

Broadcasting and quotes

One small step for [a] man, one giant leap for mankind.

-

Apollo 11 effectively ended the Space Race and fulfilled a national goal proposed in 1961 by the late U.S. President John F. Kennedy in a speech before the United States Congress:

- -
-

[...] before this decade is out, of landing a man on the Moon and returning him safely to the Earth.

-
- -

Technical details

- - - +
Mission crew
@@ -141,25 +53,13 @@

Technical details

- +
Position
Lunar Module PilotEdwin "Buzz" E. Aldrin, Jr.Edwin "Buzz" E. Aldrin, Jr.
- -

Launched by a Saturn V rocket from Kennedy Space Center in Merritt Island, Florida on July 16, Apollo 11 was the fifth manned mission of NASA's Apollo program. The Apollo spacecraft had three parts:

- -
    -
  1. Command Module with a cabin for the three astronauts which was the only part which landed back on Earth
  2. -
  3. Service Module which supported the Command Module with propulsion, electrical power, oxygen and water
  4. -
  5. Lunar Module for landing on the Moon.
  6. -
- -

After being sent to the Moon by the Saturn V's upper stage, the astronauts separated the spacecraft from it and travelled for three days until they entered into lunar orbit. Armstrong and Aldrin then moved into the Lunar Module and landed in the Sea of Tranquility. They stayed a total of about 21 and a half hours on the lunar surface. After lifting off in the upper part of the Lunar Module and rejoining Collins in the Command Module, they returned to Earth and landed in the Pacific Ocean on July 24.

- -
-

Source: Wikipedia.org

+ diff --git a/tests/plugins/clipboard/manual/customtypes.md b/tests/plugins/clipboard/manual/customtypes.md new file mode 100644 index 00000000000..6ce50f133cf --- /dev/null +++ b/tests/plugins/clipboard/manual/customtypes.md @@ -0,0 +1,9 @@ +@bender-ui: collapsed +@bender-tags: 4.8.0, feature, 468 +@bender-ckeditor-plugins: wysiwygarea, toolbar, undo, basicstyles, image2, list, elementspath, clipboard, link + +## Custom types + +1. Select part of the editor. +1. Copy using `ctrl/cmd+c`. +1. Paste the clipboard into the same editable. \ No newline at end of file From 28c460128d29b2aa01b1f62f71aa1d114e9c4dd5 Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Tue, 24 Oct 2017 14:51:39 +0200 Subject: [PATCH 36/47] Simplified manual test. --- .../plugins/clipboard/manual/customtypes.html | 35 +++---------------- 1 file changed, 4 insertions(+), 31 deletions(-) diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index 1b2c6e20b18..c2dd5cfa0fe 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -31,8 +31,8 @@

Classic editor

instanceReady: function( evt ) { evt.editor.editable().on( 'copy', function( ckEvt ) { var evt = ckEvt.data.$, - nativeDt = evt.clipboardData, - ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDt ); + nativeDataTransfer = evt.clipboardData, + ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer ); ckDataTransfer.setData( 'text/plain', 'CK plain text' + Math.random() * 100 ); ckDataTransfer.setData( 'text/html', 'CK HTML text' + Math.random() * 100 ); @@ -41,38 +41,11 @@

Classic editor

evt.preventDefault(); }, null, null, 100000 ); - // evt.editor.editable().on( 'paste', function( evt ) { - // // This is native approach. - // var tbody = CKEDITOR.document.findOne( 'tbody' ), - // dt = evt.data.$.clipboardData, - // types = dt.types, - // output = ''; - - // function getRow( row1, row2, rowClass ) { - // return '' + row1 + '' + CKEDITOR.tools.htmlEncode( row2 ) + ''; - // } - - // for ( var j = 0; j < types.length; j++ ) { - // output += getRow( types[ j ], dt.getData( types[ j ] ) ); - // } - - - // if ( CKEDITOR.tools.array.indexOf( types, 'text/html' ) != -1 ) { - // output += getRow( 'Raw HTML data', dt.getData( 'text/html', true ), 'raw' ); - // } - - // if ( types.length === 0 ) { - // output += 'No items in datatransfer'; - // } - - // tbody.setHtml( output ); - // } ); - evt.editor.editable().on( 'paste', function( evt ) { // This is native approach. var tbody = CKEDITOR.document.findOne( 'tbody' ), - dt = evt.data.$.clipboardData, - ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( dt ), + nativeDataTransfer = evt.data.$.clipboardData, + ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer ), output = '', types; From 5e52ce309aacf3bf07e9a6866b82ee1d9e545bff Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Tue, 24 Oct 2017 15:29:48 +0200 Subject: [PATCH 37/47] Improved manual test. --- tests/plugins/clipboard/manual/customtypes.html | 16 ++++++++++------ tests/plugins/clipboard/manual/customtypes.md | 7 ++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index c2dd5cfa0fe..59ac0fd37b1 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -34,10 +34,12 @@

Classic editor

nativeDataTransfer = evt.clipboardData, ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer ); - ckDataTransfer.setData( 'text/plain', 'CK plain text' + Math.random() * 100 ); - ckDataTransfer.setData( 'text/html', 'CK HTML text' + Math.random() * 100 ); - ckDataTransfer.setData( 'foo-bar', 'customvalue' ); - ckDataTransfer.setData( 'baz', 'bom' ); + ckDataTransfer.setData( 'text/plain', 'plain value' ); + ckDataTransfer.setData( 'text/html', 'html value' ); + ckDataTransfer.setData( 'foo-bar', 'foo-bar value' ); + ckDataTransfer.setData( 'baz', 'baz value' ); + + // Prevent DOM event, so that clipboard gets overriden. evt.preventDefault(); }, null, null, 100000 ); @@ -52,7 +54,8 @@

Classic editor

// Force data cache so that we can acces data types from cache. ckDataTransfer.cacheData(); - types = [ 'Text', 'text/html', 'foo-bar', 'cke/id', 'baz' ]; + // Eventually these types should be dynamically fetched. + types = [ 'Text', 'text/html', 'foo-bar', 'baz' ]; function getRow( row1, row2, rowClass ) { return '' + row1 + '' + CKEDITOR.tools.htmlEncode( row2 ) + ''; @@ -62,8 +65,9 @@

Classic editor

output += getRow( types[ j ], ckDataTransfer.getData( types[ j ] ) ); } + // Extract raw HTML, see if ( CKEDITOR.tools.array.indexOf( types, 'text/html' ) != -1 ) { - output += getRow( 'Raw HTML data', ckDataTransfer.getData( 'text/html', true ), 'raw' ); + output += getRow( 'Native HTML data', ckDataTransfer.getData( 'text/html', true ), 'raw' ); } if ( types.length === 0 ) { diff --git a/tests/plugins/clipboard/manual/customtypes.md b/tests/plugins/clipboard/manual/customtypes.md index 6ce50f133cf..b4192700b03 100644 --- a/tests/plugins/clipboard/manual/customtypes.md +++ b/tests/plugins/clipboard/manual/customtypes.md @@ -6,4 +6,9 @@ 1. Select part of the editor. 1. Copy using `ctrl/cmd+c`. -1. Paste the clipboard into the same editable. \ No newline at end of file +1. Paste the clipboard into the same editable. + +### Expected + +1. Table contains 4 types: `Text`, `text/html`, `foo-bar`, `baz`. +1. Each type has a value: ` value`, e.g. `foo-bar` has `foo-bar value`. \ No newline at end of file From e3595993f9caad38e79c4299f11dfa7b0ab31eaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 26 Oct 2017 16:54:47 +0200 Subject: [PATCH 38/47] Shared cache added to 'fallbackDataTransfer'. --- plugins/clipboard/plugin.js | 230 +++++++++++++++--- tests/_benderjs/ckeditor/static/tools.js | 8 + tests/plugins/clipboard/datatransfercache.js | 103 ++++++++ .../plugins/clipboard/fallbackdatatransfer.js | 146 +++++++++-- 4 files changed, 429 insertions(+), 58 deletions(-) create mode 100644 tests/plugins/clipboard/datatransfercache.js diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index cc8df4c6d9b..3131aa07b70 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2152,14 +2152,15 @@ this.$ = nativeDataTransfer; } - this._fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( this.$ ); + this._cache = this._getCacheObject(); + this._fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( this._cache, this.$ ); + this._ = { metaRegExp: /^/i, bodyRegExp: /([\s\S]*)<\/body>/i, fragmentRegExp: //g, - data: {}, files: [], normalizeType: function( type ) { @@ -2308,7 +2309,7 @@ type = this._.normalizeType( type ); - var data = this._.data[ type ], + var data = this._cache.get( type ), result; if ( isEmpty( data ) ) { @@ -2363,7 +2364,7 @@ setData: function( type, value ) { type = this._.normalizeType( type ); - this._.data[ type ] = value; + this._cache.set( type, value ); // There is "Unexpected call to method or property access." error if you try // to set data of unsupported type on IE. @@ -2436,7 +2437,7 @@ var data = that.getData( type, true ); if ( data ) { - that._.data[ type ] = data; + that._cache.set( type, data ); } } @@ -2522,10 +2523,9 @@ return false; } - // Add custom types. - for ( type in this._.data ) { + CKEDITOR.tools.array.forEach( this._cache.types(), function( type ) { typesToCheck[ type ] = 1; - } + } ); // Add native types. if ( this.$ ) { @@ -2578,7 +2578,105 @@ } return undefined; - } + }, + + /** + * Returns new cache instance. + * + * @since 4.8.0 + * @private + * @returns {Object} Cache object. + */ + _getCacheObject: ( function() { + + /* + * Simple cache object. + * + * @class dataTransferCache + * @constructor + */ + function dataTransferCache() { + /* + * Object storing cached data. + * + * @private + * @property {Object} _storage + */ + this._storage = {}; + + /* + * Array storing custom types. + * + * @private + * @type {Array} _customTypes + */ + this._customTypes = []; + } + + dataTransferCache.prototype = { + /* + * Returns stored value for the given type. + * + * @param {String} type + * @returns {String|null} Value of the given type or null if value not set. + */ + get: function( type ) { + return this._storage[ type ] || null; + }, + + /* + * Sets value for the given type. + * + * @param {String} type + * @param {String} value + */ + set: function( type, value ) { + this._storage[ type ] = value; + }, + + /* + * Returns list of all types currently stored in cache. + * + * @returns {Array} + */ + types: function() { + return CKEDITOR.tools.objectKeys( this._storage ); + }, + + /* + * Marks given type as a custom one. + * + * @param {String} type + */ + markCustomType: function( type ) { + if ( CKEDITOR.tools.indexOf( this._customTypes, type ) === -1 ) { + this._customTypes.push( type ); + } + }, + + /* + * Returns custom data. + * + * @returns {Object} Object containing custom types in 'type : data' format. + */ + getCustomTypesData: function() { + var customData = {}; + if ( this._customTypes.length ) { + CKEDITOR.tools.array.forEach( this._customTypes, function( type ) { + // Custom type can be marked, but value for it may not be set so it needs to be checked. + if ( this._storage[ type ] ) { + customData[ type ] = this._storage[ type ]; + } + }, this ); + } + return customData; + } + }; + + return function() { + return new dataTransferCache(); + }; + } )() }; /** @@ -2589,9 +2687,18 @@ * @since 4.8.0 * @class CKEDITOR.plugins.clipboard.fallbackDataTransfer * @constructor + * @param {Object} cache Cache object used on storing and retrieving data. * @param {Object} [nativeDataTransfer] A native data transfer object. */ - CKEDITOR.plugins.clipboard.fallbackDataTransfer = function( nativeDataTransfer ) { + CKEDITOR.plugins.clipboard.fallbackDataTransfer = function( cache, nativeDataTransfer ) { + + /** + * Cache object. Shared with {@link CKEDITOR.plugins.clipboard.dataTransfer} instance. + * + * @private + * @type {Object} _cache + */ + this._cache = cache; /** * A native dataTransfer object. @@ -2666,21 +2773,24 @@ * @returns {String} */ getData: function( type ) { - var value = null; + // As cache is already checked in CKEDITOR.plugins.clipboard.dataTransfer#getData it is skipped + // here. So the assumption is the given type is not in cache. + + var dataComment = this._extractDataComment( this._getData( this._customDataFallbackType, true ) ), + value = null; // If we are getting the same type which may store custom data we need to extract content only. if ( type === this._customDataFallbackType ) { - value = this._extractDataComment( this._getData( type ) ).content; + value = dataComment.content; // If we are getting different type we need to check inside data comment if it is stored there. } else { - var result = this._extractDataComment( this._getData( this._customDataFallbackType ) ); - if ( result.data && result.data[ type ] ) { - value = result.data[ type ]; + if ( dataComment.data && dataComment.data[ type ] ) { + value = dataComment.data[ type ]; // And then fallback to regular `getData`. } else { - value = this._getData( type ); + value = this._getData( type, true ); } } @@ -2701,29 +2811,44 @@ * @param {String} value */ setData: function( type, value ) { - // Extract custom data if used type is the same one which is used for storing it. - if ( type === this._customDataFallbackType ) { - var content = this._extractDataComment( this._getData( this._customDataFallbackType ) ); + // In case of fallbackDataTransfer, cache does not reflect native data one-to-one. For example, having + // types like text/plain, text/html, cke/id will result in cache storing: + // + // { + // text/plain: value1, + // text/html: value2, + // cke/id: value3 + // } + // + // and native dataTransfer storing: + // + // { + // text/plain: value1, + // text/html: value2 + // } + // + // This way, accessing cache will always return proper value for a given type without a need for further processing. + // Cache is already set in CKEDITOR.plugins.clipboard.dataTransfer#setData so it is skipped here. - if ( content && content.data ) { - value = this._applyDataComment( value, content.data ); - } + if ( type === this._customDataFallbackType ) { + value = this._applyDataComment( value, this._getFallbackTypeData() ); } try { this._nativeDataTransfer.setData( type, value ); } catch ( e ) { if ( this._isUnsupportedMimeTypeError( e ) ) { - // We need to get the data first to append `type` and not to overwrite whole custom data. - var current = this._extractDataComment( this._getData( this._customDataFallbackType ) ); - if ( !current.data ) { - current.data = {}; - } - current.data[ type ] = value; + this._cache.markCustomType( type ); + + var fallbackTypeContent = this._getFallbackTypeContent(), + fallbackTypeData = this._getFallbackTypeData(); + + fallbackTypeData[ type ] = value; try { - this._nativeDataTransfer.setData( this._customDataFallbackType, this._applyDataComment( current.content, current.data ) ); + this._nativeDataTransfer.setData( this._customDataFallbackType, + this._applyDataComment( fallbackTypeContent, fallbackTypeData ) ); } catch ( e ) { // Some dev logger should be added here. } @@ -2736,14 +2861,51 @@ * * @private * @param {String} type + * @param {Boolean} [skipCache=false] * @returns {String|null} */ - _getData: function( type ) { - try { - return this._nativeDataTransfer.getData( type ); - } catch ( e ) { - return null; + _getData: function( type, skipCache ) { + if ( !skipCache && this._cache[ type ] ) { + return this._cache[ type ]; + } else { + try { + return this._nativeDataTransfer.getData( type ); + } catch ( e ) { + return null; + } + } + }, + + /** + * Returns content stored in {@link #_customDataFallbackType}. Content is always first retrieved + * from {@link #_cache} and then from native `dataTransfer` object. + * + * @private + * @returns {String} + */ + _getFallbackTypeContent: function() { + var fallbackTypeContent = this._cache.get( this._customDataFallbackType ); + + if ( !fallbackTypeContent ) { + fallbackTypeContent = this._extractDataComment( this._getData( this._customDataFallbackType, true ) ).content; + } + return fallbackTypeContent; + }, + + /** + * Returns custom data stored in {@link #_customDataFallbackType}. Custom data is always first retrieved + * from {@link #_cache} and then from native `dataTransfer` object. + * + * @private + * @returns {Object} + */ + _getFallbackTypeData: function() { + var fallbackTypeData = this._cache.getCustomTypesData(); + + if ( CKEDITOR.tools.objectKeys( fallbackTypeData ).length === 0 ) { + fallbackTypeData = this._extractDataComment( this._getData( this._customDataFallbackType, true ) ).data || {}; } + return fallbackTypeData; }, /** diff --git a/tests/_benderjs/ckeditor/static/tools.js b/tests/_benderjs/ckeditor/static/tools.js index df93a7dac17..f0175c67379 100644 --- a/tests/_benderjs/ckeditor/static/tools.js +++ b/tests/_benderjs/ckeditor/static/tools.js @@ -856,6 +856,14 @@ } 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 ); + } } }; }, diff --git a/tests/plugins/clipboard/datatransfercache.js b/tests/plugins/clipboard/datatransfercache.js new file mode 100644 index 00000000000..b117f94f640 --- /dev/null +++ b/tests/plugins/clipboard/datatransfercache.js @@ -0,0 +1,103 @@ +/* bender-tags: editor,clipboard */ +/* bender-ckeditor-plugins: toolbar,clipboard */ + +'use strict'; + +bender.test( { + + 'test if cache is initialized on dataTransfer creation': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + assert.isObject( cache, 'cache should be initialized' ); + assert.isFunction( cache.get, 'cache should provide get method' ); + assert.isFunction( cache.set, 'cache should provide set method' ); + assert.isFunction( cache.types, 'cache should provide types method' ); + }, + + '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' ); + }, + + 'test get method': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + cache._storage = { + 'text/plain': 'text1', + 'Text': 'text2', + 'cke/id': 12345 + }; + + assert.areEqual( 'text1', cache.get( 'text/plain' ), 'text/plain returns proper value' ); + assert.areEqual( 'text2', cache.get( 'Text' ), 'Text returns proper value' ); + assert.areEqual( 12345, cache.get( 'cke/id' ), 'cke/id returns proper value' ); + assert.areEqual( null, cache.get( 'non-existing-type' ), 'non existing type returns null' ); + + }, + + 'test set method': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + cache.set( 'text/plain', 'foo' ); + cache.set( 'text/plain', 'foo2' ); + cache.set( 'Text', 'bar' ); + cache.set( 'cke/id', 67890 ); + + objectAssert.areEqual( { + 'text/plain': 'foo2', + 'Text': 'bar', + 'cke/id': 67890 + }, cache._storage, 'all types were set properly' ); + }, + + 'test types method': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + assert.areEqual( 0, cache.types().length, 'no types in empty cache' ); + + cache.set( 'text/plain', 'foo' ); + cache.set( 'text/plain', 'foo2' ); + cache.set( 'Text', 'bar' ); + cache.set( 'cke/id', 67890 ); + + assertArraySameItems( [ 'text/plain', 'Text', 'cke/id' ], cache.types(), 'types method returns all types' ); + }, + + 'test markCustomType method': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + assert.areEqual( 0, cache._customTypes.length, 'no custom types in empty cache' ); + + cache.markCustomType( 'text/plain' ); + cache.markCustomType( 'text/plain' ); + cache.markCustomType( 'cke/id' ); + + assert.areEqual( 2, cache._customTypes.length, '2 custom types' ); + assertArraySameItems( [ 'text/plain', 'cke/id' ], cache._customTypes, 'proper custom types values are stored' ); + }, + + 'test getCustomTypesData method': function() { + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; + + cache.set( 'text/plain', 'plain' ); + cache.set( 'text/html', 'html' ); + cache.set( 'regular/type', 'foobar' ); + + cache.markCustomType( 'text/plain' ); + cache.markCustomType( 'text/plain' ); + cache.markCustomType( 'text/html' ); + cache.markCustomType( 'cke/id' ); + + assert.areEqual( 2, CKEDITOR.tools.objectKeys( cache.getCustomTypesData() ).length, 'valid custom data types returned' ); + objectAssert.areEqual( { + 'text/plain': 'plain', + 'text/html': 'html' + }, cache.getCustomTypesData(), 'valid custom data types with values returned' ); + } +} ); + +function assertArraySameItems( arr1, arr2, msg ) { + arrayAssert.itemsAreEqual( arr1.sort(), arr2.sort(), msg ); +} diff --git a/tests/plugins/clipboard/fallbackdatatransfer.js b/tests/plugins/clipboard/fallbackdatatransfer.js index 476d24d9b74..56a3d63c9c3 100644 --- a/tests/plugins/clipboard/fallbackdatatransfer.js +++ b/tests/plugins/clipboard/fallbackdatatransfer.js @@ -28,7 +28,7 @@ bender.test( { 'test setData/getData with predefined type - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'text/plain', 'plain text' ); dataTransferFallback.setData( 'text/html', '

html text

' ); @@ -57,7 +57,7 @@ bender.test( { 'test setData/getData with custom type - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'cke-custom data' ); dataTransferFallback.setData( 'custom/tag', '

custom html tag

' ); @@ -93,7 +93,7 @@ bender.test( { 'test setData with custom type does not affect getData( "text/html" ) - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'text/html', '

Header1

' ); @@ -133,7 +133,7 @@ bender.test( { 'test setData( "text/html" ) does not overwrite custom data - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'custom data' ); @@ -177,7 +177,7 @@ bender.test( { 'test setData( "text/html" ) called a few times does not overwrite custom data - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'custom data' ); @@ -226,7 +226,7 @@ bender.test( { 'test setting same custom type overwrites the previous value and does not affect other types - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( nativeData ); + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'cke-custom data' ); dataTransferFallback.setData( 'custom/tag', '

custom html tag

' ); @@ -261,35 +261,35 @@ bender.test( { }, 'test _applyDataComment case1': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = document.querySelector( '#case1' ).innerHTML; this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case2': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = document.querySelector( '#case2' ).innerHTML; this.assertApplyDataComment( '

Header1

Test1

', { test: 1, comment: '' }, dataTransferFallback, expected ); }, 'test _applyDataComment case3': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = CKEDITOR.tools.trim( document.querySelector( '#case3' ).innerHTML ); this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case4': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = CKEDITOR.tools.trim( document.querySelector( '#case4' ).innerHTML ); this.assertApplyDataComment( '

Header1

Test1

', { test: 123 }, dataTransferFallback, expected ); }, 'test _applyDataComment with empty content': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ); this.assertApplyDataComment( undefined, { test: 1 }, dataTransferFallback, expected ); @@ -298,7 +298,7 @@ bender.test( { }, 'test _applyDataComment with empty data': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ); this.assertApplyDataComment( '

foobar

', '', dataTransferFallback, expected ); @@ -307,7 +307,7 @@ bender.test( { }, 'test _applyDataComment with empty content and data': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), expected = ''; this.assertApplyDataComment( undefined, null, dataTransferFallback, expected ); @@ -316,7 +316,7 @@ bender.test( { }, 'test _extractDataComment case1': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = document.querySelector( '#case1' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -325,7 +325,7 @@ bender.test( { }, 'test _extractDataComment case2': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = document.querySelector( '#case2' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -334,7 +334,7 @@ bender.test( { }, 'test _extractDataComment case3': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = document.querySelector( '#case3' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -343,7 +343,7 @@ bender.test( { }, 'test _extractDataComment case4': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = document.querySelector( '#case4' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -352,7 +352,7 @@ bender.test( { }, 'test _extractDataComment with empty content': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ), extracted = fallbackDataTransfer._extractDataComment( content ); @@ -361,7 +361,7 @@ bender.test( { }, 'test _extractDataComment with empty data': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ), extracted = fallbackDataTransfer._extractDataComment( content ); @@ -370,7 +370,7 @@ bender.test( { }, 'test _extractDataComment with empty data and content': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), extracted = fallbackDataTransfer._extractDataComment( '' ); assert.isNull( extracted.data ); @@ -378,7 +378,7 @@ bender.test( { }, 'test _extractDataComment with falsy value': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( {} ); + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ); assert.areSame( '', fallbackDataTransfer._extractDataComment( '' ).content ); assert.areSame( '', fallbackDataTransfer._extractDataComment( null ).content ); @@ -386,6 +386,105 @@ bender.test( { assert.areSame( '', fallbackDataTransfer._extractDataComment( false ).content ); }, + 'test if isRequired sets _isCustomMimeTypeSupported flag on the first run': function() { + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, bender.tools.mockNativeDataTransfer() ); + + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; + + var isRequiredValue = fallbackDataTransfer.isRequired(), + flagValue = CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported; + + assert.isTrue( flagValue !== null, '_isCustomMimeTypeSupported should be set' ); + assert.isTrue( flagValue === false || flagValue === true, '_isCustomMimeTypeSupported should be only true or false' ); + assert.isTrue( flagValue !== isRequiredValue, 'isRequired should return value equal to !_isCustomMimeTypeSupported' ); + }, + + 'test if isRequired clears test MIME type': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, nativeData ); + + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; + + fallbackDataTransfer.isRequired(); + + assert.isTrue( nativeData.types.length === 0, 'dataTransfer.types should be empty' ); + assert.isTrue( CKEDITOR.tools.objectKeys( nativeData._data ).length === 0, 'dataTransfer should be empty' ); + }, + + 'test if isRequired does not remove other MIME types': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, nativeData ); + + CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; + + nativeData.setData( 'text/html', 'foobar' ); + + fallbackDataTransfer.isRequired(); + + assert.areSame( 1, nativeData.types.length, 'dataTransfer.types should only contain one type' ); + assert.areSame( 1, CKEDITOR.tools.objectKeys( nativeData._data ).length, 'dataTransfer should only contain one type' ); + arrayAssert.itemsAreEqual( CKEDITOR.tools.objectKeys( nativeData._data ), [ 'text/html' ], 'dataTransfer should only contain text/html' ); + }, + + 'test getFallbackTypeContent prioritize cache': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + dataTransferFallback._cache.set( dataTransferFallback._customDataFallbackType, 'cache value' ); + nativeData.setData( dataTransferFallback._customDataFallbackType, 'native value' ); + + assert.areEqual( 'cache value', dataTransferFallback._getFallbackTypeContent() ); + }, + + 'test getFallbackTypeContent fallbacks to native data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + nativeData.setData( dataTransferFallback._customDataFallbackType, 'native value' ); + + assert.areEqual( 'native value', dataTransferFallback._getFallbackTypeContent() ); + }, + + 'test getFallbackTypeContent for empty content in both cache and native data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + assert.areEqual( '', dataTransferFallback._getFallbackTypeContent() ); + }, + + 'test getFallbackTypeData prioritize cache': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + dataTransferFallback._cache.set( 'cke/id', 'cache value' ); + dataTransferFallback._cache.markCustomType( 'cke/id' ); + nativeData.setData( dataTransferFallback._customDataFallbackType, + dataTransferFallback._applyDataComment( 'html', { 'cke/id': 'native value' } ) ); + + objectAssert.areEqual( { + 'cke/id': 'cache value' + }, dataTransferFallback._getFallbackTypeData() ); + }, + + 'test getFallbackTypeData fallbacks to native data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + nativeData.setData( dataTransferFallback._customDataFallbackType, + dataTransferFallback._applyDataComment( 'html', { 'cke/id': 'native value' } ) ); + + objectAssert.areEqual( { + 'cke/id': 'native value' + }, dataTransferFallback._getFallbackTypeData() ); + }, + + 'test getFallbackTypeData for empty content in both cache and native data': function() { + var nativeData = bender.tools.mockNativeDataTransfer(), + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + + objectAssert.areEqual( {}, dataTransferFallback._getFallbackTypeData() ); + }, + assertDataTransferType: function( dataTransfer, type, value, customValue ) { if ( CKEDITOR.env.ie && CKEDITOR.env.version >= 16 && customValue ) { value = '' + value; @@ -400,8 +499,7 @@ bender.test( { } } ); -// Gets data but clears the cache first, so data is extracted from saved types not from cache object. +// Gets data with omitting the cache. function getDataNoCache( dataTransfer, type ) { - dataTransfer._.data = {}; - return dataTransfer.getData( type ); + return dataTransfer._fallbackDataTransfer.getData( type ); } From 9b9346053ce82855da08043655d4feb709202799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 26 Oct 2017 18:02:38 +0200 Subject: [PATCH 39/47] Fix for 'getNative' param. --- plugins/clipboard/plugin.js | 6 ++++- tests/plugins/clipboard/datatransfer.js | 27 +++++++++++++++++++ .../plugins/clipboard/manual/customtypes.html | 4 ++- 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 3131aa07b70..1d493a2d886 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2309,9 +2309,13 @@ type = this._.normalizeType( type ); - var data = this._cache.get( type ), + var data, result; + if ( !getNative ) { + data = this._cache.get( type ); + } + if ( isEmpty( data ) ) { if ( this._fallbackDataTransfer.isRequired() ) { diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 5168824aa68..07583b63284 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -487,6 +487,33 @@ 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 = '' + + '' + + '' + + '' + + '' + + '' + + '' + + '

Foo

' + + '

Bar

' + + '' + + '', + 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. diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index 59ac0fd37b1..1deab97e4b1 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -67,7 +67,9 @@

Classic editor

// Extract raw HTML, see if ( CKEDITOR.tools.array.indexOf( types, 'text/html' ) != -1 ) { - output += getRow( 'Native HTML data', ckDataTransfer.getData( 'text/html', true ), 'raw' ); + // Native 'getData' needs to be used here as 'ckDataTransfer.getData' + // strips special comment which we would like to show here too. + output += getRow( 'Native HTML data', ckDataTransfer.$.getData( 'text/html', true ), 'raw' ); } if ( types.length === 0 ) { From 96bf4037cb3ff812fc9b993bce4a304a884f361a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Thu, 26 Oct 2017 18:14:16 +0200 Subject: [PATCH 40/47] Docs corrections. --- plugins/clipboard/plugin.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 1d493a2d886..86441308481 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2242,6 +2242,14 @@ * @private * @property {CKEDITOR.plugins.clipboard.fallbackDataTransfer} _fallbackDataTransfer */ + + /** + * Cache object. + * + * @since 4.8.0 + * @private + * @property {Object} _cache + */ }; /** @@ -2700,7 +2708,7 @@ * Cache object. Shared with {@link CKEDITOR.plugins.clipboard.dataTransfer} instance. * * @private - * @type {Object} _cache + * @property {Object} _cache */ this._cache = cache; @@ -2803,7 +2811,7 @@ /** * Sets given data in native `dataTransfer` object. If given MIME type is not supported it uses - * {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_customDataFallbackType} MIME type + * {@link #_customDataFallbackType} MIME type * to save data using special comment format: * * @@ -2881,7 +2889,7 @@ }, /** - * Returns content stored in {@link #_customDataFallbackType}. Content is always first retrieved + * Returns content stored in {@link #\_customDataFallbackType}. Content is always first retrieved * from {@link #_cache} and then from native `dataTransfer` object. * * @private @@ -2897,7 +2905,7 @@ }, /** - * Returns custom data stored in {@link #_customDataFallbackType}. Custom data is always first retrieved + * Returns custom data stored in {@link #\_customDataFallbackType}. Custom data is always first retrieved * from {@link #_cache} and then from native `dataTransfer` object. * * @private From 0aac1feea109cb56ea4091df730ac169f96555d2 Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Mon, 6 Nov 2017 13:33:48 +0100 Subject: [PATCH 41/47] Corrected input data in manual test. Without this change values were a bit different than expected pattern. --- tests/plugins/clipboard/manual/customtypes.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index 1deab97e4b1..3b28e4b58bd 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -34,8 +34,8 @@

Classic editor

nativeDataTransfer = evt.clipboardData, ckDataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( nativeDataTransfer ); - ckDataTransfer.setData( 'text/plain', 'plain value' ); - ckDataTransfer.setData( 'text/html', 'html value' ); + ckDataTransfer.setData( 'text/plain', 'Text value' ); + ckDataTransfer.setData( 'text/html', 'text/html value' ); ckDataTransfer.setData( 'foo-bar', 'foo-bar value' ); ckDataTransfer.setData( 'baz', 'baz value' ); From a94c5bd6ed9ee9fdddc22765d56e18939d7c3994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 7 Nov 2017 11:17:11 +0100 Subject: [PATCH 42/47] Cache simplified and refactored. --- plugins/clipboard/plugin.js | 202 ++++++++++-------------------------- 1 file changed, 52 insertions(+), 150 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index 86441308481..ed03d52ebd4 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2152,9 +2152,7 @@ this.$ = nativeDataTransfer; } - this._cache = this._getCacheObject(); - this._fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( this._cache, this.$ ); - + var cache = {}; this._ = { metaRegExp: /^/i, @@ -2162,6 +2160,9 @@ fragmentRegExp: //g, files: [], + cache: cache, + + fallbackDataTransfer: new CKEDITOR.plugins.clipboard.fallbackDataTransfer( cache, this.$ ), normalizeType: function( type ) { type = type.toLowerCase(); @@ -2233,23 +2234,6 @@ * @private * @property {Object} _ */ - - /** - * Fallback object which is used in browsers not supporting custom - * MIME types in native `dataTransfer.setData`. - * - * @since 4.8.0 - * @private - * @property {CKEDITOR.plugins.clipboard.fallbackDataTransfer} _fallbackDataTransfer - */ - - /** - * Cache object. - * - * @since 4.8.0 - * @private - * @property {Object} _cache - */ }; /** @@ -2320,19 +2304,23 @@ var data, result; - if ( !getNative ) { - data = this._cache.get( type ); - } - - if ( isEmpty( data ) ) { - - if ( this._fallbackDataTransfer.isRequired() ) { - data = this._fallbackDataTransfer.getData( type ); - } else { - try { - data = this.$.getData( type ) || ''; - } catch ( e ) { - data = ''; + if ( getNative ) { + try { + data = this.$.getData( type ) || ''; + } catch ( e ) { + data = ''; + } + } else { + data = this._.cache[ type ] || null; + if ( isEmpty( data ) ) { + if ( this._.fallbackDataTransfer.isRequired() ) { + data = this._.fallbackDataTransfer.getData( type ); + } else { + try { + data = this.$.getData( type ) || ''; + } catch ( e ) { + data = ''; + } } } } @@ -2376,7 +2364,7 @@ setData: function( type, value ) { type = this._.normalizeType( type ); - this._cache.set( type, value ); + this._.cache[ type ] = value; // There is "Unexpected call to method or property access." error if you try // to set data of unsupported type on IE. @@ -2390,8 +2378,8 @@ this.id = value; } - if ( this._fallbackDataTransfer.isRequired() ) { - this._fallbackDataTransfer.setData( type, value ); + if ( this._.fallbackDataTransfer.isRequired() ) { + this._.fallbackDataTransfer.setData( type, value ); } else { try { @@ -2447,9 +2435,9 @@ function getAndSetData( type ) { type = that._.normalizeType( type ); - var data = that.getData( type, true ); + var data = that.getData( type, !that._.fallbackDataTransfer.isRequired() ); if ( data ) { - that._cache.set( type, data ); + that._.cache[ type ] = data; } } @@ -2535,7 +2523,7 @@ return false; } - CKEDITOR.tools.array.forEach( this._cache.types(), function( type ) { + CKEDITOR.tools.array.forEach( CKEDITOR.tools.objectKeys( this._.cache ), function( type ) { typesToCheck[ type ] = 1; } ); @@ -2590,105 +2578,7 @@ } return undefined; - }, - - /** - * Returns new cache instance. - * - * @since 4.8.0 - * @private - * @returns {Object} Cache object. - */ - _getCacheObject: ( function() { - - /* - * Simple cache object. - * - * @class dataTransferCache - * @constructor - */ - function dataTransferCache() { - /* - * Object storing cached data. - * - * @private - * @property {Object} _storage - */ - this._storage = {}; - - /* - * Array storing custom types. - * - * @private - * @type {Array} _customTypes - */ - this._customTypes = []; - } - - dataTransferCache.prototype = { - /* - * Returns stored value for the given type. - * - * @param {String} type - * @returns {String|null} Value of the given type or null if value not set. - */ - get: function( type ) { - return this._storage[ type ] || null; - }, - - /* - * Sets value for the given type. - * - * @param {String} type - * @param {String} value - */ - set: function( type, value ) { - this._storage[ type ] = value; - }, - - /* - * Returns list of all types currently stored in cache. - * - * @returns {Array} - */ - types: function() { - return CKEDITOR.tools.objectKeys( this._storage ); - }, - - /* - * Marks given type as a custom one. - * - * @param {String} type - */ - markCustomType: function( type ) { - if ( CKEDITOR.tools.indexOf( this._customTypes, type ) === -1 ) { - this._customTypes.push( type ); - } - }, - - /* - * Returns custom data. - * - * @returns {Object} Object containing custom types in 'type : data' format. - */ - getCustomTypesData: function() { - var customData = {}; - if ( this._customTypes.length ) { - CKEDITOR.tools.array.forEach( this._customTypes, function( type ) { - // Custom type can be marked, but value for it may not be set so it needs to be checked. - if ( this._storage[ type ] ) { - customData[ type ] = this._storage[ type ]; - } - }, this ); - } - return customData; - } - }; - - return function() { - return new dataTransferCache(); - }; - } )() + } }; /** @@ -2745,6 +2635,17 @@ */ CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; + /** + * Array containing MIME types which are not supported by native `setData`. Those types are + * recognized by error which is thrown when using native `setData` with a given type + * (see {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer#_isUnsupportedMimeTypeError}). + * + * @private + * @static + * @property {Array} + */ + CKEDITOR.plugins.clipboard.fallbackDataTransfer._customTypes = []; + CKEDITOR.plugins.clipboard.fallbackDataTransfer.prototype = { /** * Whether {@link CKEDITOR.plugins.clipboard.fallbackDataTransfer fallbackDataTransfer object} should @@ -2794,14 +2695,12 @@ // If we are getting the same type which may store custom data we need to extract content only. if ( type === this._customDataFallbackType ) { value = dataComment.content; - - // If we are getting different type we need to check inside data comment if it is stored there. } else { + // If we are getting different type we need to check inside data comment if it is stored there. if ( dataComment.data && dataComment.data[ type ] ) { value = dataComment.data[ type ]; - - // And then fallback to regular `getData`. } else { + // And then fallback to regular `getData`. value = this._getData( type, true ); } } @@ -2811,8 +2710,7 @@ /** * Sets given data in native `dataTransfer` object. If given MIME type is not supported it uses - * {@link #_customDataFallbackType} MIME type - * to save data using special comment format: + * {@link #_customDataFallbackType} MIME type to save data using special comment format: * * * @@ -2850,8 +2748,11 @@ this._nativeDataTransfer.setData( type, value ); } catch ( e ) { if ( this._isUnsupportedMimeTypeError( e ) ) { + var fallbackDataTransfer = CKEDITOR.plugins.clipboard.fallbackDataTransfer; - this._cache.markCustomType( type ); + if ( CKEDITOR.tools.indexOf( fallbackDataTransfer._customTypes, type ) === -1 ) { + fallbackDataTransfer._customTypes.push( type ); + } var fallbackTypeContent = this._getFallbackTypeContent(), fallbackTypeData = this._getFallbackTypeData(); @@ -2896,7 +2797,7 @@ * @returns {String} */ _getFallbackTypeContent: function() { - var fallbackTypeContent = this._cache.get( this._customDataFallbackType ); + var fallbackTypeContent = this._cache[ this._customDataFallbackType ]; if ( !fallbackTypeContent ) { fallbackTypeContent = this._extractDataComment( this._getData( this._customDataFallbackType, true ) ).content; @@ -2912,11 +2813,12 @@ * @returns {Object} */ _getFallbackTypeData: function() { - var fallbackTypeData = this._cache.getCustomTypesData(); - - if ( CKEDITOR.tools.objectKeys( fallbackTypeData ).length === 0 ) { + var fallbackTypes = CKEDITOR.plugins.clipboard.fallbackDataTransfer._customTypes, fallbackTypeData = this._extractDataComment( this._getData( this._customDataFallbackType, true ) ).data || {}; - } + + CKEDITOR.tools.array.forEach( fallbackTypes, function( type ) { + fallbackTypeData[ type ] = this._cache[ type ] !== undefined ? this._cache[ type ] : fallbackTypeData[ type ]; + }, this ); return fallbackTypeData; }, From 9c0a25eea79994de4d8ef93a047906d78512f33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Tue, 7 Nov 2017 11:21:04 +0100 Subject: [PATCH 43/47] Tests: refactoring and adjusting to new cache structure. --- tests/plugins/clipboard/datatransfer.js | 26 +++-- tests/plugins/clipboard/datatransfercache.js | 103 ------------------ .../plugins/clipboard/fallbackdatatransfer.js | 36 +++--- .../plugins/clipboard/manual/customtypes.html | 7 +- tests/plugins/clipboard/manual/customtypes.md | 4 +- .../manual/datatransferfallback.html | 65 ++++++----- .../clipboard/manual/datatransferfallback.md | 8 +- tests/plugins/clipboard/paste.js | 4 +- 8 files changed, 86 insertions(+), 167 deletions(-) delete mode 100644 tests/plugins/clipboard/datatransfercache.js diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 07583b63284..3d5d1bb6451 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -720,7 +720,6 @@ bender.test( { nativeData.files.push( 'foo' ); - // debugger; dataTransfer.cacheData(); assert.areSame( 1, dataTransfer.getFilesCount() ); @@ -998,14 +997,14 @@ bender.test( { evt3 = { data: { $: { dataTransfer: nativeData3 } }, name: 'dragstart' }, dataTransfer1 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt1 ), dataTransfer2 = CKEDITOR.plugins.clipboard.initPasteDataTransfer( evt2 ), - dtFallback1 = dataTransfer1._fallbackDataTransfer, - dtFallback2 = dataTransfer2._fallbackDataTransfer, + dtFallback1 = dataTransfer1._.fallbackDataTransfer, + dtFallback2 = dataTransfer2._.fallbackDataTransfer, dataTransfer3, dtFallback3; CKEDITOR.plugins.clipboard.initDragDataTransfer( evt3 ); dataTransfer3 = evt3.data.dataTransfer; - dtFallback3 = dataTransfer3._fallbackDataTransfer; + dtFallback3 = dataTransfer3._.fallbackDataTransfer; // Check if ids are not empty. assert.isTrue( dataTransfer1.id.length > 0, 'dataTransfer1 id is not empty' ); @@ -1060,11 +1059,24 @@ bender.test( { } 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' ); + nativeData1.getData( dataTransfer1._.fallbackDataTransfer._customDataFallbackType ), 'dataTransfer1 id is empty' ); assert.areSame( '', - nativeData2.getData( dataTransfer2._fallbackDataTransfer._customDataFallbackType ), 'dataTransfer2 id is empty' ); + nativeData2.getData( dataTransfer2._.fallbackDataTransfer._customDataFallbackType ), 'dataTransfer2 id is empty' ); assert.areSame( '', - nativeData2.getData( dataTransfer3._fallbackDataTransfer._customDataFallbackType ), 'dataTransfer2 id is empty' ); + 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' ); } } ); diff --git a/tests/plugins/clipboard/datatransfercache.js b/tests/plugins/clipboard/datatransfercache.js deleted file mode 100644 index b117f94f640..00000000000 --- a/tests/plugins/clipboard/datatransfercache.js +++ /dev/null @@ -1,103 +0,0 @@ -/* bender-tags: editor,clipboard */ -/* bender-ckeditor-plugins: toolbar,clipboard */ - -'use strict'; - -bender.test( { - - 'test if cache is initialized on dataTransfer creation': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - assert.isObject( cache, 'cache should be initialized' ); - assert.isFunction( cache.get, 'cache should provide get method' ); - assert.isFunction( cache.set, 'cache should provide set method' ); - assert.isFunction( cache.types, 'cache should provide types method' ); - }, - - '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' ); - }, - - 'test get method': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - cache._storage = { - 'text/plain': 'text1', - 'Text': 'text2', - 'cke/id': 12345 - }; - - assert.areEqual( 'text1', cache.get( 'text/plain' ), 'text/plain returns proper value' ); - assert.areEqual( 'text2', cache.get( 'Text' ), 'Text returns proper value' ); - assert.areEqual( 12345, cache.get( 'cke/id' ), 'cke/id returns proper value' ); - assert.areEqual( null, cache.get( 'non-existing-type' ), 'non existing type returns null' ); - - }, - - 'test set method': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - cache.set( 'text/plain', 'foo' ); - cache.set( 'text/plain', 'foo2' ); - cache.set( 'Text', 'bar' ); - cache.set( 'cke/id', 67890 ); - - objectAssert.areEqual( { - 'text/plain': 'foo2', - 'Text': 'bar', - 'cke/id': 67890 - }, cache._storage, 'all types were set properly' ); - }, - - 'test types method': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - assert.areEqual( 0, cache.types().length, 'no types in empty cache' ); - - cache.set( 'text/plain', 'foo' ); - cache.set( 'text/plain', 'foo2' ); - cache.set( 'Text', 'bar' ); - cache.set( 'cke/id', 67890 ); - - assertArraySameItems( [ 'text/plain', 'Text', 'cke/id' ], cache.types(), 'types method returns all types' ); - }, - - 'test markCustomType method': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - assert.areEqual( 0, cache._customTypes.length, 'no custom types in empty cache' ); - - cache.markCustomType( 'text/plain' ); - cache.markCustomType( 'text/plain' ); - cache.markCustomType( 'cke/id' ); - - assert.areEqual( 2, cache._customTypes.length, '2 custom types' ); - assertArraySameItems( [ 'text/plain', 'cke/id' ], cache._customTypes, 'proper custom types values are stored' ); - }, - - 'test getCustomTypesData method': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._cache; - - cache.set( 'text/plain', 'plain' ); - cache.set( 'text/html', 'html' ); - cache.set( 'regular/type', 'foobar' ); - - cache.markCustomType( 'text/plain' ); - cache.markCustomType( 'text/plain' ); - cache.markCustomType( 'text/html' ); - cache.markCustomType( 'cke/id' ); - - assert.areEqual( 2, CKEDITOR.tools.objectKeys( cache.getCustomTypesData() ).length, 'valid custom data types returned' ); - objectAssert.areEqual( { - 'text/plain': 'plain', - 'text/html': 'html' - }, cache.getCustomTypesData(), 'valid custom data types with values returned' ); - } -} ); - -function assertArraySameItems( arr1, arr2, msg ) { - arrayAssert.itemsAreEqual( arr1.sort(), arr2.sort(), msg ); -} diff --git a/tests/plugins/clipboard/fallbackdatatransfer.js b/tests/plugins/clipboard/fallbackdatatransfer.js index 56a3d63c9c3..c4d3aaf4a73 100644 --- a/tests/plugins/clipboard/fallbackdatatransfer.js +++ b/tests/plugins/clipboard/fallbackdatatransfer.js @@ -11,6 +11,10 @@ bender.test( { } }, + setUp: function() { + CKEDITOR.plugins.clipboard.fallbackDataTransfer._customTypes = []; + }, + 'test setData/getData with predefined type - dataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), eventMock = { data: { $: { clipboardData: nativeData } }, name: 'copy' }, @@ -28,7 +32,7 @@ bender.test( { 'test setData/getData with predefined type - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'text/plain', 'plain text' ); dataTransferFallback.setData( 'text/html', '

html text

' ); @@ -57,7 +61,7 @@ bender.test( { 'test setData/getData with custom type - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'cke-custom data' ); dataTransferFallback.setData( 'custom/tag', '

custom html tag

' ); @@ -93,7 +97,7 @@ bender.test( { 'test setData with custom type does not affect getData( "text/html" ) - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'text/html', '

Header1

' ); @@ -133,7 +137,7 @@ bender.test( { 'test setData( "text/html" ) does not overwrite custom data - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'custom data' ); @@ -177,7 +181,7 @@ bender.test( { 'test setData( "text/html" ) called a few times does not overwrite custom data - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'custom data' ); @@ -226,7 +230,7 @@ bender.test( { 'test setting same custom type overwrites the previous value and does not affect other types - fallbackDataTransfer': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; dataTransferFallback.setData( 'cke/custom', 'cke-custom data' ); dataTransferFallback.setData( 'custom/tag', '

custom html tag

' ); @@ -428,9 +432,9 @@ bender.test( { 'test getFallbackTypeContent prioritize cache': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; - dataTransferFallback._cache.set( dataTransferFallback._customDataFallbackType, 'cache value' ); + dataTransferFallback._cache[ dataTransferFallback._customDataFallbackType ] = 'cache value'; nativeData.setData( dataTransferFallback._customDataFallbackType, 'native value' ); assert.areEqual( 'cache value', dataTransferFallback._getFallbackTypeContent() ); @@ -438,7 +442,7 @@ bender.test( { 'test getFallbackTypeContent fallbacks to native data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; nativeData.setData( dataTransferFallback._customDataFallbackType, 'native value' ); @@ -447,17 +451,17 @@ bender.test( { 'test getFallbackTypeContent for empty content in both cache and native data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; assert.areEqual( '', dataTransferFallback._getFallbackTypeContent() ); }, 'test getFallbackTypeData prioritize cache': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; - dataTransferFallback._cache.set( 'cke/id', 'cache value' ); - dataTransferFallback._cache.markCustomType( 'cke/id' ); + dataTransferFallback._cache[ 'cke/id' ] = 'cache value'; + CKEDITOR.plugins.clipboard.fallbackDataTransfer._customTypes.push( 'cke/id' ); nativeData.setData( dataTransferFallback._customDataFallbackType, dataTransferFallback._applyDataComment( 'html', { 'cke/id': 'native value' } ) ); @@ -468,7 +472,7 @@ bender.test( { 'test getFallbackTypeData fallbacks to native data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; nativeData.setData( dataTransferFallback._customDataFallbackType, dataTransferFallback._applyDataComment( 'html', { 'cke/id': 'native value' } ) ); @@ -480,7 +484,7 @@ bender.test( { 'test getFallbackTypeData for empty content in both cache and native data': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._fallbackDataTransfer; + dataTransferFallback = new CKEDITOR.plugins.clipboard.dataTransfer( nativeData )._.fallbackDataTransfer; objectAssert.areEqual( {}, dataTransferFallback._getFallbackTypeData() ); }, @@ -501,5 +505,5 @@ bender.test( { // Gets data with omitting the cache. function getDataNoCache( dataTransfer, type ) { - return dataTransfer._fallbackDataTransfer.getData( type ); + return dataTransfer._.fallbackDataTransfer.getData( type ); } diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index 3b28e4b58bd..f2793130e38 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -29,6 +29,11 @@

Classic editor

CKEDITOR.replace( 'classic', { on: { instanceReady: function( evt ) { + if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) { + bender.ignore(); + return; + } + evt.editor.editable().on( 'copy', function( ckEvt ) { var evt = ckEvt.data.$, nativeDataTransfer = evt.clipboardData, @@ -51,7 +56,7 @@

Classic editor

output = '', types; - // Force data cache so that we can acces data types from cache. + // Force data cache so that we can access data types from cache. ckDataTransfer.cacheData(); // Eventually these types should be dynamically fetched. diff --git a/tests/plugins/clipboard/manual/customtypes.md b/tests/plugins/clipboard/manual/customtypes.md index b4192700b03..26eeb01f727 100644 --- a/tests/plugins/clipboard/manual/customtypes.md +++ b/tests/plugins/clipboard/manual/customtypes.md @@ -4,11 +4,11 @@ ## Custom types -1. Select part of the editor. +1. Select part of the editor content. 1. Copy using `ctrl/cmd+c`. 1. Paste the clipboard into the same editable. ### Expected 1. Table contains 4 types: `Text`, `text/html`, `foo-bar`, `baz`. -1. Each type has a value: ` value`, e.g. `foo-bar` has `foo-bar value`. \ No newline at end of file +1. Each type has a value: ` value`, e.g. `foo-bar` has `foo-bar value`. diff --git a/tests/plugins/clipboard/manual/datatransferfallback.html b/tests/plugins/clipboard/manual/datatransferfallback.html index 46a1b9a66e6..bada3ac4514 100644 --- a/tests/plugins/clipboard/manual/datatransferfallback.html +++ b/tests/plugins/clipboard/manual/datatransferfallback.html @@ -67,39 +67,38 @@

Inline editor

inline = CKEDITOR.inline( 'inline' ); classic.on( 'pluginsLoaded', function() { - if ( !CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) { - bender.ignore(); - } - } ); - - classic.on( 'copy', logData ); - classic.on( 'paste', logData ); - classic.on( 'dragstart', logData ); - classic.on( 'drop', logData ); - - inline.on( 'copy', logData ); - inline.on( 'paste', logData ); - inline.on( 'dragstart', logData ); - inline.on( 'drop', logData ); - - function logData( evt ) { - var idInfo = ''; - - if ( evt.data.dataTransfer.$ ) { - var ckeId = evt.data.dataTransfer.getData( 'cke/id' ), - htmlId = null; - - try { - htmlId = evt.data.dataTransfer._extractDataComment( evt.data.dataTransfer.$.getData( 'text/html' ) ).data[ 'cke/id' ]; - } catch ( ex ) {} - - if ( ckeId && !htmlId ) { - idInfo += '; Id storage: cke/id'; - } else if ( htmlId ) { - idInfo += '; Id storage: text/html'; + if ( CKEDITOR.plugins.clipboard.isCustomCopyCutSupported ) { + classic.on( 'copy', logData ); + classic.on( 'paste', logData ); + classic.on( 'dragstart', logData ); + classic.on( 'drop', logData ); + + inline.on( 'copy', logData ); + inline.on( 'paste', logData ); + inline.on( 'dragstart', logData ); + inline.on( 'drop', logData ); + + function logData( evt ) { + var idInfo = ''; + + if ( evt.data.dataTransfer.$ ) { + var ckeId = evt.data.dataTransfer.getData( 'cke/id' ), + htmlId = null; + + try { + htmlId = evt.data.dataTransfer._.fallbackDataTransfer._extractDataComment( + evt.data.dataTransfer.$.getData( 'text/html' ) ).data[ 'cke/id' ]; + } catch ( ex ) {} + + if ( ckeId && !htmlId ) { + idInfo += '; Id storage: cke/id'; + } else if ( htmlId ) { + idInfo += '; Id storage: text/html'; + } + } + + console.log( 'DataTransfer id: ' + evt.data.dataTransfer.id + '; Event: ' + evt.name + idInfo); } } - - console.log( 'DataTransfer id: ' + evt.data.dataTransfer.id + '; Event: ' + evt.name + idInfo); - } + } ); diff --git a/tests/plugins/clipboard/manual/datatransferfallback.md b/tests/plugins/clipboard/manual/datatransferfallback.md index 2bfadb9ec3d..b03c9da4438 100644 --- a/tests/plugins/clipboard/manual/datatransferfallback.md +++ b/tests/plugins/clipboard/manual/datatransferfallback.md @@ -9,17 +9,19 @@ _Open dev console as events are logged there._ * between editors, * externally (helpers, MS Word, etc). -## Expected: +## Expected (Chrome, FF, Safari Edge): * Events sequence caused by one action (e.g. `drag` -> `drop` -> `paste`) always have the same `DataTransfer id`. * `Id storage` (if present) should be `text/html` for `Edge` browser and `cke/id` for other browsers. + +## Expected (All): * No content lost (e.g. ids of anchors), no crashes, nor errors. * In the internal and cross editor D&D, dragged content should be removed. - + ## Helpers: - + Lorem ipsum dolor sit amet, consectetur adipiscing elit. CKEditor logo In commodo vulputate tempor. Sed <b>at elit</b> vel ligula mollis aliquet a ac odio. diff --git a/tests/plugins/clipboard/paste.js b/tests/plugins/clipboard/paste.js index 3bf6340d8bf..e5a185fb194 100644 --- a/tests/plugins/clipboard/paste.js +++ b/tests/plugins/clipboard/paste.js @@ -1190,7 +1190,7 @@ // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). if ( CKEDITOR.env.edge ) { var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); - assert.areSame( 'bar', dataTransfer._fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + assert.areSame( 'bar', dataTransfer._.fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); } else { assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); } @@ -1215,7 +1215,7 @@ // As Edge stores custom data in text/html it needs to be assert differently - we need to extract content part (#962). if ( CKEDITOR.env.edge ) { var dataTransfer = new CKEDITOR.plugins.clipboard.dataTransfer( {}, editor ); - assert.areSame( 'bar', dataTransfer._fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); + assert.areSame( 'bar', dataTransfer._.fallbackDataTransfer._extractDataComment( pasteEventMock.$.clipboardData.getData( 'text/html' ) ).content, 'HTML text' ); } else { assert.areSame( 'bar', pasteEventMock.$.clipboardData.getData( 'text/html' ), 'HTML text' ); } From 3c621f42b12bd5c37035349a1ed72b98ec68f659 Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Wed, 8 Nov 2017 12:37:03 +0100 Subject: [PATCH 44/47] Tests: corrected manual test not to leak undefined class. --- tests/plugins/clipboard/manual/customtypes.html | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index f2793130e38..fb925cae7eb 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -63,6 +63,7 @@

Classic editor

types = [ 'Text', 'text/html', 'foo-bar', 'baz' ]; function getRow( row1, row2, rowClass ) { + rowClass = rowClass || ''; return '' + row1 + '' + CKEDITOR.tools.htmlEncode( row2 ) + ''; } From 33757361df3480e3eb05dd526b774dd9dc9d9b05 Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Wed, 8 Nov 2017 12:57:51 +0100 Subject: [PATCH 45/47] Tests: use CKEditor dataTransfer API for getting raw HTML rather than native object. --- tests/plugins/clipboard/manual/customtypes.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins/clipboard/manual/customtypes.html b/tests/plugins/clipboard/manual/customtypes.html index fb925cae7eb..9783c2fb4d2 100644 --- a/tests/plugins/clipboard/manual/customtypes.html +++ b/tests/plugins/clipboard/manual/customtypes.html @@ -75,7 +75,7 @@

Classic editor

if ( CKEDITOR.tools.array.indexOf( types, 'text/html' ) != -1 ) { // Native 'getData' needs to be used here as 'ckDataTransfer.getData' // strips special comment which we would like to show here too. - output += getRow( 'Native HTML data', ckDataTransfer.$.getData( 'text/html', true ), 'raw' ); + output += getRow( 'Native HTML data', ckDataTransfer.getData( 'text/html', true ), 'raw' ); } if ( types.length === 0 ) { From e61ac60b4d6b3f3123943af9f4d875331432c4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Krzto=C5=84?= Date: Wed, 8 Nov 2017 16:09:50 +0100 Subject: [PATCH 46/47] Review fixes. --- plugins/clipboard/plugin.js | 29 +++++++-------- tests/plugins/clipboard/datatransfer.js | 21 +++++++---- .../plugins/clipboard/fallbackdatatransfer.js | 37 ++++++++++--------- 3 files changed, 46 insertions(+), 41 deletions(-) diff --git a/plugins/clipboard/plugin.js b/plugins/clipboard/plugin.js index ed03d52ebd4..ea1d52a9df8 100644 --- a/plugins/clipboard/plugin.js +++ b/plugins/clipboard/plugin.js @@ -2152,17 +2152,13 @@ this.$ = nativeDataTransfer; } - var cache = {}; - this._ = { metaRegExp: /^/i, bodyRegExp: /([\s\S]*)<\/body>/i, fragmentRegExp: //g, + data: {}, files: [], - cache: cache, - - fallbackDataTransfer: new CKEDITOR.plugins.clipboard.fallbackDataTransfer( cache, this.$ ), normalizeType: function( type ) { type = type.toLowerCase(); @@ -2176,6 +2172,7 @@ } } }; + this._.fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( this ); // Check if ID is already created. this.id = this.getData( clipboardIdDataType ); @@ -2311,7 +2308,7 @@ data = ''; } } else { - data = this._.cache[ type ] || null; + data = this._.data[ type ] || null; if ( isEmpty( data ) ) { if ( this._.fallbackDataTransfer.isRequired() ) { data = this._.fallbackDataTransfer.getData( type ); @@ -2364,7 +2361,7 @@ setData: function( type, value ) { type = this._.normalizeType( type ); - this._.cache[ type ] = value; + this._.data[ type ] = value; // There is "Unexpected call to method or property access." error if you try // to set data of unsupported type on IE. @@ -2437,7 +2434,7 @@ var data = that.getData( type, !that._.fallbackDataTransfer.isRequired() ); if ( data ) { - that._.cache[ type ] = data; + that._.data[ type ] = data; } } @@ -2523,7 +2520,7 @@ return false; } - CKEDITOR.tools.array.forEach( CKEDITOR.tools.objectKeys( this._.cache ), function( type ) { + CKEDITOR.tools.array.forEach( CKEDITOR.tools.objectKeys( this._.data ), function( type ) { typesToCheck[ type ] = 1; } ); @@ -2589,18 +2586,18 @@ * @since 4.8.0 * @class CKEDITOR.plugins.clipboard.fallbackDataTransfer * @constructor - * @param {Object} cache Cache object used on storing and retrieving data. - * @param {Object} [nativeDataTransfer] A native data transfer object. + * @param {CKEDITOR.plugins.clipboard.dataTransfer} dataTransfer DataTransfer + * object which internal cache and + * {@link CKEDITOR.plugins.clipboard.dataTransfer#$ data transfer} objects will be reused. */ - CKEDITOR.plugins.clipboard.fallbackDataTransfer = function( cache, nativeDataTransfer ) { - + CKEDITOR.plugins.clipboard.fallbackDataTransfer = function( dataTransfer ) { /** * Cache object. Shared with {@link CKEDITOR.plugins.clipboard.dataTransfer} instance. * * @private * @property {Object} _cache */ - this._cache = cache; + this._cache = dataTransfer._.data; /** * A native dataTransfer object. @@ -2608,7 +2605,7 @@ * @private * @property {Object} _nativeDataTransfer */ - this._nativeDataTransfer = nativeDataTransfer; + this._nativeDataTransfer = dataTransfer.$; /** * A MIME type used for storing custom MIME types. @@ -2642,7 +2639,7 @@ * * @private * @static - * @property {Array} + * @property {String[]} */ CKEDITOR.plugins.clipboard.fallbackDataTransfer._customTypes = []; diff --git a/tests/plugins/clipboard/datatransfer.js b/tests/plugins/clipboard/datatransfer.js index 3d5d1bb6451..4a7fe10af0c 100644 --- a/tests/plugins/clipboard/datatransfer.js +++ b/tests/plugins/clipboard/datatransfer.js @@ -36,7 +36,9 @@ bender.test( { 'test id': function() { var nativeData1 = bender.tools.mockNativeDataTransfer(), nativeData2 = bender.tools.mockNativeDataTransfer(), - dataTransfer1a, dataTransfer1b, dataTransfer2; + dataTransfer1a, + dataTransfer1b, + dataTransfer2; // 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). @@ -60,7 +62,8 @@ bender.test( { 'test internal drag drop': function() { var bot = this.editorBots.editor1, editor = this.editors.editor1, - nativeData, dataTransfer; + nativeData, + dataTransfer; bot.setHtmlWithSelection( '

x[xfoox]x

' ); @@ -99,7 +102,8 @@ bender.test( { 'test drop text from external source': function() { var editor = this.editors.editor1, - nativeData, dataTransfer; + nativeData, + dataTransfer; nativeData = bender.tools.mockNativeDataTransfer(); nativeData.setData( 'Text', 'xfoox' ); @@ -118,7 +122,8 @@ bender.test( { 'test drop html from external source': function() { var isCustomDataTypesSupported = CKEDITOR.plugins.clipboard.isCustomDataTypesSupported, editor = this.editors.editor1, - nativeData, dataTransfer; + nativeData, + dataTransfer; nativeData = bender.tools.mockNativeDataTransfer(); nativeData.setData( 'Text', 'bar' ); @@ -141,7 +146,8 @@ bender.test( { var bot1 = this.editorBots.editor1, editor1 = this.editors.editor1, editor2 = this.editors.editor2, - nativeData, dataTransfer; + nativeData, + dataTransfer; bot1.setHtmlWithSelection( '

x[xfoox]x

' ); @@ -1068,7 +1074,7 @@ bender.test( { }, 'test if cache is initialized on dataTransfer creation': function() { - var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._.cache; + var cache = new CKEDITOR.plugins.clipboard.dataTransfer()._.data; assert.isObject( cache, 'cache should be initialized' ); }, @@ -1077,6 +1083,7 @@ bender.test( { var dt1 = new CKEDITOR.plugins.clipboard.dataTransfer(), dt2 = new CKEDITOR.plugins.clipboard.dataTransfer(); - assert.isTrue( dt1._.cache !== dt2._.cache, 'caches should not be equal' ); + assert.isObject( dt1._.data, 'cache should be initialized' ); + assert.isTrue( dt1._.data !== dt2._.data, 'caches should not be equal' ); } } ); diff --git a/tests/plugins/clipboard/fallbackdatatransfer.js b/tests/plugins/clipboard/fallbackdatatransfer.js index c4d3aaf4a73..00b3cb78ab9 100644 --- a/tests/plugins/clipboard/fallbackdatatransfer.js +++ b/tests/plugins/clipboard/fallbackdatatransfer.js @@ -265,35 +265,35 @@ bender.test( { }, 'test _applyDataComment case1': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = document.querySelector( '#case1' ).innerHTML; this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case2': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = document.querySelector( '#case2' ).innerHTML; this.assertApplyDataComment( '

Header1

Test1

', { test: 1, comment: '' }, dataTransferFallback, expected ); }, 'test _applyDataComment case3': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = CKEDITOR.tools.trim( document.querySelector( '#case3' ).innerHTML ); this.assertApplyDataComment( '

Header1

Test1

', { test: 1 }, dataTransferFallback, expected ); }, 'test _applyDataComment case4': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = CKEDITOR.tools.trim( document.querySelector( '#case4' ).innerHTML ); this.assertApplyDataComment( '

Header1

Test1

', { test: 123 }, dataTransferFallback, expected ); }, 'test _applyDataComment with empty content': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ); this.assertApplyDataComment( undefined, { test: 1 }, dataTransferFallback, expected ); @@ -302,7 +302,7 @@ bender.test( { }, 'test _applyDataComment with empty data': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ); this.assertApplyDataComment( '

foobar

', '', dataTransferFallback, expected ); @@ -311,7 +311,7 @@ bender.test( { }, 'test _applyDataComment with empty content and data': function() { - var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var dataTransferFallback = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), expected = ''; this.assertApplyDataComment( undefined, null, dataTransferFallback, expected ); @@ -320,7 +320,7 @@ bender.test( { }, 'test _extractDataComment case1': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = document.querySelector( '#case1' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -329,7 +329,7 @@ bender.test( { }, 'test _extractDataComment case2': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = document.querySelector( '#case2' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -338,7 +338,7 @@ bender.test( { }, 'test _extractDataComment case3': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = document.querySelector( '#case3' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -347,7 +347,7 @@ bender.test( { }, 'test _extractDataComment case4': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = document.querySelector( '#case4' ).innerHTML, extracted = fallbackDataTransfer._extractDataComment( content ); @@ -356,7 +356,7 @@ bender.test( { }, 'test _extractDataComment with empty content': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-content' ).innerHTML ), extracted = fallbackDataTransfer._extractDataComment( content ); @@ -365,7 +365,7 @@ bender.test( { }, 'test _extractDataComment with empty data': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), content = CKEDITOR.tools.trim( document.querySelector( '#empty-data' ).innerHTML ), extracted = fallbackDataTransfer._extractDataComment( content ); @@ -374,7 +374,7 @@ bender.test( { }, 'test _extractDataComment with empty data and content': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ), + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ), extracted = fallbackDataTransfer._extractDataComment( '' ); assert.isNull( extracted.data ); @@ -382,7 +382,7 @@ bender.test( { }, 'test _extractDataComment with falsy value': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null ); + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { _: { data: {} } } ); assert.areSame( '', fallbackDataTransfer._extractDataComment( '' ).content ); assert.areSame( '', fallbackDataTransfer._extractDataComment( null ).content ); @@ -391,7 +391,8 @@ bender.test( { }, 'test if isRequired sets _isCustomMimeTypeSupported flag on the first run': function() { - var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, bender.tools.mockNativeDataTransfer() ); + var fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( + { $: bender.tools.mockNativeDataTransfer(), _: { data: {} } } ); CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; @@ -405,7 +406,7 @@ bender.test( { 'test if isRequired clears test MIME type': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, nativeData ); + fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { $: nativeData, _: { data: {} } } ); CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; @@ -417,7 +418,7 @@ bender.test( { 'test if isRequired does not remove other MIME types': function() { var nativeData = bender.tools.mockNativeDataTransfer(), - fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( null, nativeData ); + fallbackDataTransfer = new CKEDITOR.plugins.clipboard.fallbackDataTransfer( { $: nativeData, _: { data: {} } } ); CKEDITOR.plugins.clipboard.fallbackDataTransfer._isCustomMimeTypeSupported = null; From 270fbda7385a1d9d279be2add425ba0418255adf Mon Sep 17 00:00:00 2001 From: Marek Lewandowski Date: Fri, 10 Nov 2017 16:23:47 +0100 Subject: [PATCH 47/47] Tests: ignore datatransfer fallback manual test for browsers that do not support dataDransfer at all. --- tests/plugins/clipboard/manual/datatransferfallback.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/plugins/clipboard/manual/datatransferfallback.html b/tests/plugins/clipboard/manual/datatransferfallback.html index bada3ac4514..11067361505 100644 --- a/tests/plugins/clipboard/manual/datatransferfallback.html +++ b/tests/plugins/clipboard/manual/datatransferfallback.html @@ -61,13 +61,16 @@

Inline editor