htmldataprocessor.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. /**
  2. * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  4. */
  5. /* globals setTimeout, window */
  6. import HtmlDataProcessor from '../../src/dataprocessor/htmldataprocessor';
  7. import xssTemplates from '../../tests/dataprocessor/_utils/xsstemplates';
  8. import ViewDocumentFragment from '../../src/view/documentfragment';
  9. import { stringify, parse } from '../../src/dev-utils/view';
  10. import { StylesProcessor } from '../../src/view/stylesmap';
  11. import ViewDocument from '../../src/view/document';
  12. describe( 'HtmlDataProcessor', () => {
  13. let dataProcessor, viewDocument;
  14. beforeEach( () => {
  15. viewDocument = new ViewDocument( new StylesProcessor() );
  16. dataProcessor = new HtmlDataProcessor( viewDocument );
  17. } );
  18. describe( 'toView()', () => {
  19. it( 'should return empty DocumentFragment when empty string is passed', () => {
  20. const fragment = dataProcessor.toView( '' );
  21. expect( fragment ).to.be.an.instanceOf( ViewDocumentFragment );
  22. expect( fragment.childCount ).to.equal( 0 );
  23. } );
  24. it( 'should convert HTML to DocumentFragment with single text node', () => {
  25. const fragment = dataProcessor.toView( 'foo bar' );
  26. expect( stringify( fragment ) ).to.equal( 'foo bar' );
  27. } );
  28. it( 'should convert HTML to DocumentFragment with multiple child nodes', () => {
  29. const fragment = dataProcessor.toView( '<p>foo</p><p>bar</p>' );
  30. expect( stringify( fragment ) ).to.equal( '<p>foo</p><p>bar</p>' );
  31. } );
  32. it( 'should return only elements inside body tag', () => {
  33. const fragment = dataProcessor.toView( '<html><head></head><body><p>foo</p></body></html>' );
  34. expect( stringify( fragment ) ).to.equal( '<p>foo</p>' );
  35. } );
  36. it( 'should not add any additional nodes', () => {
  37. const fragment = dataProcessor.toView( 'foo <b>bar</b> text' );
  38. expect( stringify( fragment ) ).to.equal( 'foo <b>bar</b> text' );
  39. } );
  40. // Test against XSS attacks.
  41. for ( const name in xssTemplates ) {
  42. const input = xssTemplates[ name ].replace( /%xss%/g, 'testXss()' );
  43. it( 'should prevent XSS attacks: ' + name, done => {
  44. window.testXss = sinon.spy();
  45. dataProcessor.toView( input );
  46. setTimeout( () => {
  47. sinon.assert.notCalled( window.testXss );
  48. done();
  49. }, 10 );
  50. } );
  51. }
  52. describe( 'https://github.com/ckeditor/ckeditor5-clipboard/issues/2#issuecomment-310417731 + #404', () => {
  53. it( 'does not lose whitespaces in Chrome\'s paste-like content', () => {
  54. const fragment = dataProcessor.toView(
  55. '<meta charset=\'utf-8\'>' +
  56. '<span>This is the<span>\u00a0</span></span>' +
  57. '<a href="url">third developer preview</a>' +
  58. '<span><span>\u00a0</span>of<span>\u00a0</span></span>' +
  59. '<strong>CKEditor\u00a05</strong>' +
  60. '<span>.</span>'
  61. );
  62. expect( stringify( fragment ) ).to.equal(
  63. '<span>This is the<span>\u00a0</span></span>' +
  64. '<a href="url">third developer preview</a>' +
  65. '<span><span>\u00a0</span>of<span>\u00a0</span></span>' +
  66. '<strong>CKEditor\u00a05</strong>' +
  67. '<span>.</span>'
  68. );
  69. // Just to be sure... stringify() uses conversion and the browser extensively,
  70. // so it's not entirely safe.
  71. expect( fragment.getChild( 0 ).getChild( 1 ).getChild( 0 ).data ).to.equal( '\u00a0' );
  72. expect( fragment.getChild( 2 ).getChild( 0 ).getChild( 0 ).data ).to.equal( '\u00a0' );
  73. expect( fragment.getChild( 2 ).getChild( 2 ).getChild( 0 ).data ).to.equal( '\u00a0' );
  74. } );
  75. } );
  76. } );
  77. describe( 'toData()', () => {
  78. it( 'should return empty string when empty DocumentFragment is passed', () => {
  79. const fragment = new ViewDocumentFragment( viewDocument );
  80. expect( dataProcessor.toData( fragment ) ).to.equal( '' );
  81. } );
  82. it( 'should return text if document fragment with single text node is passed', () => {
  83. const fragment = new ViewDocumentFragment( viewDocument );
  84. fragment._appendChild( parse( 'foo bar' ) );
  85. expect( dataProcessor.toData( fragment ) ).to.equal( 'foo bar' );
  86. } );
  87. it( 'should convert HTML to DocumentFragment with multiple child nodes', () => {
  88. const fragment = parse( '<p>foo</p><p>bar</p>' );
  89. expect( dataProcessor.toData( fragment ) ).to.equal( '<p>foo</p><p>bar</p>' );
  90. } );
  91. } );
  92. } );