Ticket #6946: 6946_4.patch

File 6946_4.patch, 9.5 KB (added by Garry Yao, 8 years ago)
  • _source/core/htmlparser/fragment.js

     
    3737
    3838(function()
    3939{
    40         // Elements which the end tag is marked as optional in the HTML 4.01 DTD
    41         // (expect empty elements).
    42         var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
    43 
    4440        // Block-level elements whose internal structure should be respected during
    4541        // parser fixing.
    46         var nonBreakingBlocks = CKEDITOR.tools.extend(
    47                         {table:1,ul:1,ol:1,dl:1},
    48                         CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl ),
    49                 listBlocks = CKEDITOR.dtd.$list, listItems = CKEDITOR.dtd.$listItem;
     42        var nonBreakingBlocks = CKEDITOR.tools.extend( { table:1,ul:1,ol:1,dl:1 }, CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl );
    5043
     44        var listBlocks = { ol:1, ul:1 };
     45
     46        // Dtd of the fragment element, basically it accept anything except for intermediate structure, e.g. orphan <li>.
     47        var rootDtd = CKEDITOR.tools.extend( {}, { html: 1 }, CKEDITOR.dtd.html, CKEDITOR.dtd.body, CKEDITOR.dtd.head, { style:1,script:1 } );
     48
    5149        /**
    5250         * Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
    5351         * @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
     
    6159        CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, fixForBody )
    6260        {
    6361                var parser = new CKEDITOR.htmlParser(),
    64                         html = [],
    6562                        fragment = new CKEDITOR.htmlParser.fragment(),
    6663                        pendingInline = [],
    6764                        pendingBRs = [],
    6865                        currentNode = fragment,
    6966                    // Indicate we're inside a <pre> element, spaces should be touched differently.
    70                         inPre = false,
    71                         returnPoint;
     67                        inPre = false;
    7268
    7369                function checkPending( newTagName )
    7470                {
     
    114110                                currentNode.add( pendingBRs.shift() );
    115111                }
    116112
    117                 function addElement( element, target, enforceCurrent )
     113                /*
     114                * Beside of simply append specified element to target, it also takes
     115                * care of other dirty lifts like forcing block in body, trimming spaces at
     116                * the block boundaries etc.
     117                *
     118                * Note: This function should NOT change the "currentNode" global unless
     119                * there's a return point node specified on the element.
     120                 */
     121                function addElement( element, target )
    118122                {
     123                        // Ignore any element that has already been added.
     124                        if ( element.previous !== undefined )
     125                                return;
     126
    119127                        target = target || currentNode || fragment;
    120128
     129                        // Current element might be mangled by fix body below,
     130                        // save it for restore later.
     131                        var savedCurrent = currentNode;
     132
    121133                        // If the target is the fragment and this inline element can't go inside
    122134                        // body (if fixForBody).
    123                         if ( fixForBody && !target.type )
     135                        if ( fixForBody && ( !target.type || target.name == 'body' ) )
    124136                        {
    125137                                var elementName, realElementName;
    126138                                if ( element.attributes
     
    130142                                else
    131143                                        elementName =  element.name;
    132144
    133                                 if ( elementName && elementName in CKEDITOR.dtd.$inline )
     145                                if ( elementName && !( elementName in CKEDITOR.dtd.$body || elementName == 'body' || element.isOrphan ) )
    134146                                {
    135                                         var savedCurrent = currentNode;
    136 
    137147                                        // Create a <p> in the fragment.
    138148                                        currentNode = target;
    139149                                        parser.onTagOpen( fixForBody, {} );
    140150
    141151                                        // The new target now is the <p>.
    142152                                        target = currentNode;
    143 
    144                                         if ( enforceCurrent )
    145                                                 currentNode = savedCurrent;
    146153                                }
    147154                        }
    148155
     
    170177                                currentNode = element.returnPoint;
    171178                                delete element.returnPoint;
    172179                        }
     180                        else
     181                                currentNode = savedCurrent;
    173182                }
    174183
    175184                parser.onTagOpen = function( tagName, attributes, selfClosing )
     
    201210                                return;
    202211                        }
    203212
    204                         var currentName = currentNode.name;
     213                        while( 1 )
     214                        {
     215                                var currentName = currentNode.name;
    205216
    206                         var currentDtd = currentName
    207                                 && ( CKEDITOR.dtd[ currentName ]
    208                                         || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
     217                                var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ]
     218                                                || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) )
     219                                                : rootDtd;
    209220
    210                         // If the element cannot be child of the current element.
    211                         if ( currentDtd   // Fragment could receive any elements.
    212                                  && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
    213                         {
    214 
    215                                 var reApply = false,
    216                                         addPoint;   // New position to start adding nodes.
    217 
    218                                 // Fixing malformed nested lists by moving it into a previous list item. (#3828)
    219                                 if ( tagName in listBlocks
    220                                         && currentName in listBlocks )
    221                                 {
    222                                         var children = currentNode.children,
    223                                                 lastChild = children[ children.length - 1 ];
     221                                // If the element cannot be child of the current element.
     222                                if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
     223                                {
     224                                        // Fixing malformed nested lists by moving it into a previous list item. (#3828)
     225                                        if ( tagName in listBlocks
     226                                                && currentName in listBlocks )
     227                                        {
     228                                                var children = currentNode.children,
     229                                                        lastChild = children[ children.length - 1 ];
    224230
    225                                         // Establish the list item if it's not existed.
    226                                         if ( !( lastChild && lastChild.name in listItems ) )
    227                                                 addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
     231                                                // Establish the list item if it's not existed.
     232                                                if ( !( lastChild && lastChild.name == 'li' ) )
     233                                                        addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
    228234
    229                                         returnPoint = currentNode, addPoint = lastChild;
    230                                 }
    231                                 // If the element name is the same as the current element name,
    232                                 // then just close the current one and append the new one to the
    233                                 // parent. This situation usually happens with <p>, <li>, <dt> and
    234                                 // <dd>, specially in IE. Do not enter in this if block in this case.
    235                                 else if ( tagName == currentName )
    236                                 {
    237                                         addElement( currentNode, currentNode.parent );
    238                                 }
    239                                 else if ( tagName in CKEDITOR.dtd.$listItem )
    240                                 {
    241                                         parser.onTagOpen( 'ul', {} );
    242                                         addPoint = currentNode;
    243                                         reApply = true;
    244                                 }
    245                                 else
    246                                 {
    247                                         if ( nonBreakingBlocks[ currentName ] )
    248                                         {
    249                                                 if ( !returnPoint )
    250                                                         returnPoint = currentNode;
    251                                         }
    252                                         else
    253                                         {
    254                                                 addElement( currentNode, currentNode.parent, true );
    255 
    256                                                 if ( !optionalClose[ currentName ] )
    257                                                 {
    258                                                         // The current element is an inline element, which
    259                                                         // cannot hold the new one. Put it in the pending list,
    260                                                         // and try adding the new one after it.
    261                                                         pendingInline.unshift( currentNode );
     235                                                currentNode = lastChild;
     236                                        }
     237                                        // Establish new list root for orphan list items.
     238                                        else if ( tagName in CKEDITOR.dtd.$listItem && currentName != tagName )
     239                                                parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {} );
     240                                        // We're inside a structural block like table and list, AND the incoming element
     241                                        // is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,
     242                                        // and most importantly, return back to here once this element is added,
     243                                        // e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>
     244                                        else if ( currentName in nonBreakingBlocks && currentName != tagName )
     245                                        {
     246                                                !element.returnPoint && ( element.returnPoint = currentNode );
     247                                                currentNode = currentNode.parent;
     248                                        }
     249                                        else
     250                                        {
     251                                                // The current element is an inline element, which
     252                                                // need to be continued even after the close, so put
     253                                                // it in the pending list.
     254                                                if ( currentName in CKEDITOR.dtd.$inline )
     255                                                        pendingInline.unshift( currentNode );
     256
     257                                                // The most common case where we just need to close the
     258                                                // current one and append the new one to the parent.
     259                                                if ( currentNode.parent )
     260                                                {
     261                                                        addElement( currentNode, currentNode.parent );
     262                                                        currentNode = currentNode.parent;
     263                                                }
     264                                                // We've tried our best to fix the embarrassment here, while
     265                                                // this element still doesn't find it's parent, mark it as
     266                                                // orphan and show our tolerance to it.
     267                                                else
     268                                                {
     269                                                        element.isOrphan = 1;
     270                                                        break;
    262271                                                }
    263272                                        }
    264 
    265                                         reApply = true;
    266273                                }
    267 
    268                                 if ( addPoint )
    269                                         currentNode = addPoint;
    270                                 // Try adding it to the return point, or the parent element.
    271274                                else
    272                                         currentNode = currentNode.returnPoint || currentNode.parent;
    273 
    274                                 if ( reApply )
    275                                 {
    276                                         parser.onTagOpen.apply( this, arguments );
    277                                         return;
    278                                 }
    279                         }
     275                                        break;
     276                        }
    280277
    281278                        checkPending( tagName );
    282279                        sendPendingBRs();
    283280
    284281                        element.parent = currentNode;
    285                         element.returnPoint = returnPoint;
    286                         returnPoint = 0;
    287282
    288283                        if ( element.isEmpty )
    289284                                addElement( element );
     
    403398                sendPendingBRs( !CKEDITOR.env.ie && 1 );
    404399
    405400                // Close all pending nodes.
    406                 while ( currentNode.type )
     401                while ( currentNode && currentNode.type )
    407402                {
    408                         var parent = currentNode.parent,
    409                                 node = currentNode;
    410 
    411                         if ( fixForBody
    412                                  && ( !parent.type || parent.name == 'body' )
    413                                  && !CKEDITOR.dtd.$body[ node.name ] )
    414                         {
    415                                 currentNode = parent;
    416                                 parser.onTagOpen( fixForBody, {} );
    417                                 parent = currentNode;
    418                         }
     403                        addElement( currentNode, currentNode.parent );
     404                        currentNode = currentNode.parent;
     405                }
    419406
    420                         parent.add( node );
    421                         currentNode = parent;
    422                 }
    423 
    424407                return fragment;
    425408        };
    426409
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy