Ticket #6946: 6946_5.patch

File 6946_5.patch, 9.6 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.
     
    6765                        pendingBRs = [],
    6866                        currentNode = fragment,
    6967                    // Indicate we're inside a <pre> element, spaces should be touched differently.
    70                         inPre = false,
    71                         returnPoint;
     68                        inPre = false;
    7269
    7370                function checkPending( newTagName )
    7471                {
     
    114111                                currentNode.add( pendingBRs.shift() );
    115112                }
    116113
    117                 function addElement( element, target, enforceCurrent )
     114                /*
     115                * Beside of simply append specified element to target, it also takes
     116                * care of other dirty lifts like forcing block in body, trimming spaces at
     117                * the block boundaries etc.
     118                *
     119                * Note: This function should NOT change the "currentNode" global unless
     120                * there's a return point node specified on the element.
     121                 */
     122                function addElement( element, target )
    118123                {
     124                        // Ignore any element that has already been added.
     125                        if ( element.previous !== undefined )
     126                                return;
     127
    119128                        target = target || currentNode || fragment;
    120129
     130                        // Current element might be mangled by fix body below,
     131                        // save it for restore later.
     132                        var savedCurrent = currentNode;
     133
    121134                        // If the target is the fragment and this inline element can't go inside
    122135                        // body (if fixForBody).
    123                         if ( fixForBody && !target.type )
     136                        if ( fixForBody && ( !target.type || target.name == 'body' ) )
    124137                        {
    125138                                var elementName, realElementName;
    126139                                if ( element.attributes
     
    130143                                else
    131144                                        elementName =  element.name;
    132145
    133                                 if ( elementName && elementName in CKEDITOR.dtd.$inline )
     146                                if ( elementName && !( elementName in CKEDITOR.dtd.$body || elementName == 'body' || element.isOrphan ) )
    134147                                {
    135                                         var savedCurrent = currentNode;
    136 
    137148                                        // Create a <p> in the fragment.
    138149                                        currentNode = target;
    139150                                        parser.onTagOpen( fixForBody, {} );
    140151
    141152                                        // The new target now is the <p>.
    142153                                        target = currentNode;
    143 
    144                                         if ( enforceCurrent )
    145                                                 currentNode = savedCurrent;
    146154                                }
    147155                        }
    148156
     
    170178                                currentNode = element.returnPoint;
    171179                                delete element.returnPoint;
    172180                        }
     181                        else
     182                                currentNode = savedCurrent;
    173183                }
    174184
    175185                parser.onTagOpen = function( tagName, attributes, selfClosing )
     
    201211                                return;
    202212                        }
    203213
    204                         var currentName = currentNode.name;
     214                        while( 1 )
     215                        {
     216                                var currentName = currentNode.name;
    205217
    206                         var currentDtd = currentName
    207                                 && ( CKEDITOR.dtd[ currentName ]
    208                                         || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
     218                                var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ]
     219                                                || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) )
     220                                                : rootDtd;
    209221
    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 ];
     222                                // If the element cannot be child of the current element.
     223                                if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
     224                                {
     225                                        // Fixing malformed nested lists by moving it into a previous list item. (#3828)
     226                                        if ( tagName in listBlocks
     227                                                && currentName in listBlocks )
     228                                        {
     229                                                var children = currentNode.children,
     230                                                        lastChild = children[ children.length - 1 ];
    224231
    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 );
     232                                                // Establish the list item if it's not existed.
     233                                                if ( !( lastChild && lastChild.name == 'li' ) )
     234                                                        addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
    228235
    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 );
     236                                                currentNode = lastChild;
     237                                        }
     238                                        // Establish new list root for orphan list items.
     239                                        else if ( tagName in CKEDITOR.dtd.$listItem && currentName != tagName )
     240                                                parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {} );
     241                                        // We're inside a structural block like table and list, AND the incoming element
     242                                        // is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,
     243                                        // and most importantly, return back to here once this element is added,
     244                                        // e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>
     245                                        else if ( currentName in nonBreakingBlocks && currentName != tagName )
     246                                        {
     247                                                !element.returnPoint && ( element.returnPoint = currentNode );
     248                                                currentNode = currentNode.parent;
     249                                        }
     250                                        else
     251                                        {
     252                                                // The current element is an inline element, which
     253                                                // need to be continued even after the close, so put
     254                                                // it in the pending list.
     255                                                if ( currentName in CKEDITOR.dtd.$inline )
     256                                                        pendingInline.unshift( currentNode );
     257
     258                                                // The most common case where we just need to close the
     259                                                // current one and append the new one to the parent.
     260                                                if ( currentNode.parent )
     261                                                {
     262                                                        addElement( currentNode, currentNode.parent );
     263                                                        currentNode = currentNode.parent;
     264                                                }
     265                                                // We've tried our best to fix the embarrassment here, while
     266                                                // this element still doesn't find it's parent, mark it as
     267                                                // orphan and show our tolerance to it.
     268                                                else
     269                                                {
     270                                                        element.isOrphan = 1;
     271                                                        break;
    262272                                                }
    263273                                        }
    264 
    265                                         reApply = true;
    266274                                }
    267 
    268                                 if ( addPoint )
    269                                         currentNode = addPoint;
    270                                 // Try adding it to the return point, or the parent element.
    271275                                else
    272                                         currentNode = currentNode.returnPoint || currentNode.parent;
    273 
    274                                 if ( reApply )
    275                                 {
    276                                         parser.onTagOpen.apply( this, arguments );
    277                                         return;
    278                                 }
    279                         }
     276                                        break;
     277                        }
    280278
    281279                        checkPending( tagName );
    282280                        sendPendingBRs();
    283281
    284282                        element.parent = currentNode;
    285                         element.returnPoint = returnPoint;
    286                         returnPoint = 0;
    287283
    288284                        if ( element.isEmpty )
    289285                                addElement( element );
     
    321317                                // one of the nodes. So, for now, we just cache it.
    322318                                pendingAdd.push( candidate );
    323319
    324                                 candidate = candidate.parent;
     320                                // Make sure return point is properly restored.
     321                                candidate = candidate.returnPoint || candidate.parent;
    325322                        }
    326323
    327324                        if ( candidate != fragment )
     
    405402                // Close all pending nodes.
    406403                while ( currentNode != fragment )
    407404                {
    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                         }
     405                        // Make sure return point is properly restored.
     406                        var returnPoint = currentNode.returnPoint;
     407                        addElement( currentNode, currentNode.parent );
     408                        currentNode = returnPoint || currentNode.parent;
     409                }
    419410
    420                         parent.add( node );
    421                         currentNode = parent;
    422                 }
    423 
    424411                return fragment;
    425412        };
    426413
© 2003 – 2019 CKSource – Frederico Knabben. All rights reserved. | Terms of use | Privacy policy