|
198 | 198 | var toBeRemoved = [],
|
199 | 199 | rules = this._.rules,
|
200 | 200 | transformations = this._.transformations,
|
201 |
| - filterFn = getFilterFunction( this ); |
| 201 | + filterFn = getFilterFunction( this ), |
| 202 | + protectedRegexs = this.editor && this.editor.config.protectedSource; |
202 | 203 |
|
203 | 204 | // Filter all children, skip root (fragment or editable-like wrapper used by data processor).
|
204 | 205 | fragment.forEach( function( el ) {
|
205 |
| - filterFn( el, rules, transformations, toBeRemoved, toHtml ); |
206 |
| - }, CKEDITOR.NODE_ELEMENT, true ); |
| 206 | + if ( el.type == CKEDITOR.NODE_ELEMENT ) |
| 207 | + filterFn( el, rules, transformations, toBeRemoved, toHtml ); |
| 208 | + else if ( el.type == CKEDITOR.NODE_COMMENT && el.value.match( /^{cke_protected}(?!{C})/ ) ) { |
| 209 | + if ( !filterProtectedElement( el, protectedRegexs, filterFn, rules, transformations, toHtml ) ) |
| 210 | + toBeRemoved.push( el ); |
| 211 | + } |
| 212 | + }, null, true ); |
207 | 213 |
|
208 |
| - var element, check, |
| 214 | + var node, element, check, |
209 | 215 | toBeChecked = [],
|
210 | 216 | enterTag = [ 'p', 'br', 'div' ][ this.enterMode - 1 ];
|
211 | 217 |
|
212 | 218 | // Remove elements in reverse order - from leaves to root, to avoid conflicts.
|
213 |
| - while ( ( element = toBeRemoved.pop() ) ) |
214 |
| - removeElement( element, enterTag, toBeChecked ); |
| 219 | + while ( ( node = toBeRemoved.pop() ) ) { |
| 220 | + if ( node.type == CKEDITOR.NODE_ELEMENT ) |
| 221 | + removeElement( node, enterTag, toBeChecked ); |
| 222 | + // This is a comment securing rejected element - remove it completely. |
| 223 | + else |
| 224 | + node.remove(); |
| 225 | + } |
215 | 226 |
|
216 | 227 | // Check elements that have been marked as possibly invalid.
|
217 | 228 | while ( ( check = toBeChecked.pop() ) ) {
|
|
661 | 672 | return props;
|
662 | 673 | }
|
663 | 674 |
|
| 675 | + // Filter element protected with a comment. |
| 676 | + // Returns true if protected content is ok, false otherwise. |
| 677 | + function filterProtectedElement( comment, protectedRegexs, filterFn, rules, transformations, toHtml ) { |
| 678 | + var source = decodeURIComponent( comment.value.replace( /^{cke_protected}/, '' ) ), |
| 679 | + protectedFrag, |
| 680 | + toBeRemoved = [], |
| 681 | + node, i, match; |
| 682 | + |
| 683 | + // Protected element's and protected source's comments look exactly the same. |
| 684 | + // Check if what we have isn't a protected source instead of protected script/noscript. |
| 685 | + if ( protectedRegexs ) { |
| 686 | + for ( i = 0; i < protectedRegexs.length; ++i ) { |
| 687 | + if ( ( match = source.match( protectedRegexs[ i ] ) ) && |
| 688 | + match[ 0 ].length == source.length // Check whether this pattern matches entire source |
| 689 | + // to avoid '<script>alert("<? 1 ?>")</script>' matching |
| 690 | + // the PHP's protectedSource regexp. |
| 691 | + ) |
| 692 | + return true; |
| 693 | + } |
| 694 | + } |
| 695 | + |
| 696 | + protectedFrag = CKEDITOR.htmlParser.fragment.fromHtml( source ); |
| 697 | + |
| 698 | + if ( protectedFrag.children.length == 1 && ( node = protectedFrag.children[ 0 ] ).type == CKEDITOR.NODE_ELEMENT ) |
| 699 | + filterFn( node, rules, transformations, toBeRemoved, toHtml ); |
| 700 | + |
| 701 | + // If protected element has been marked to be removed, return 'false' - comment was rejected. |
| 702 | + return !toBeRemoved.length; |
| 703 | + } |
| 704 | + |
664 | 705 | // Returns function that accepts {@link CKEDITOR.htmlParser.element}
|
665 | 706 | // and filters it basing on allowed content rules registered by
|
666 | 707 | // {@link #allow} method.
|
|
1197 | 1238 |
|
1198 | 1239 | element.remove();
|
1199 | 1240 | }
|
1200 |
| - } else if ( DTD.$block[ name ] || name == 'tr' ) |
| 1241 | + } else if ( DTD.$block[ name ] || name == 'tr' ) { |
1201 | 1242 | if ( enterTag == 'br' )
|
1202 | 1243 | stripBlockBr( element, toBeChecked );
|
1203 | 1244 | else
|
1204 | 1245 | stripBlock( element, enterTag, toBeChecked );
|
| 1246 | + } |
| 1247 | + // Special case - elements that may contain CDATA |
| 1248 | + // should be removed completely. <script> is handled |
| 1249 | + // by filterProtectedElement(). |
| 1250 | + else if ( name == 'style' ) |
| 1251 | + element.remove(); |
| 1252 | + // The rest of inline elements. May also be the last resort |
| 1253 | + // for some special elements. |
1205 | 1254 | else {
|
1206 | 1255 | // Parent might become an empty inline specified in $removeEmpty or empty a[href].
|
1207 | 1256 | if ( element.parent )
|
|
0 commit comments