Changeset 12358 in webkit


Ignore:
Timestamp:
Jan 25, 2006 1:05:02 PM (18 years ago)
Author:
harrison
Message:

Reviewed by Justin.

<rdar://problem/3907635> copy/paste of list item text moves list item up one <LI>
<rdar://problem/3907647> Enhance list editing: return inserts <li></li>
<rdar://problem/4060158> deleting selection within table deletes more than intended
<rdar://problem/4061232> Deleting a list can delete unselected content
<rdar://problem/4062212> after pasting in contents of web.apple.com, typing before start adds to table instead of before table
<rdar://problem/4064437> After copy/paste from bugweb cannot go back to entering text at left side of page
<rdar://problem/4259845> Table editing in design mode is broken
<rdar://problem/4287667> Insertion point goes before table instead of inside first cell
<rdar://problem/4345749> Editing HTML - Enter at end of <LI> inserts uneditable blank <LI>
<rdar://problem/4345794> HTML editing: Enter at end of last <LI> does not add new <LI>
<rdar://problem/4345825> HTML Editing: editing first <LI> element removes first <LI> and more
<rdar://problem/4345835> HTML editing: editing last <LI> removes everything within <BODY>
<rdar://problem/4345879> HTML editing: editing first <TD> moves data out of table

...also added showTree() static functions because switch to gcc 4.0 makes calling instance methods unreliable

  • numerous small changes to handle empty list items
  • fix numeric list marker updating when adding/deleting list items
  • start to decompose "special element" handling, replacing it with appropriate handling of the different kinds of special elements in various situations, rather than giving a blanket treatment. I will do more of this in subsequent checkins.
  • numerous small editing/selection changes to handle VisiblePosition at table offset childNodeCount()
  • simplify and fix equivalentRangeCompliantPosition, rename it to rangeCompliantEquivalent and make it static
  • some minor reformatting to current standards
  • the comments in the list below are for changes not covered by the above comments


  • khtml/editing/Selection.cpp: (WebCore::Selection::toRange): (WebCore::Selection::validate):
  • khtml/editing/SelectionController.cpp: (WebCore::SelectionController::modifyExtendingLeftBackward):
  • character selection backward from after table selects the table (WebCore::showTree):
  • khtml/editing/composite_edit_command.cpp: (WebCore::CompositeEditCommand::removeFullySelectedNode):
  • make sure empty cell has some height (WebCore::CompositeEditCommand::positionOutsideTabSpan):
  • check for !isTabSpanTextNode() to make calling this function easier (WebCore::CompositeEditCommand::addBlockPlaceholderIfNeeded):
  • khtml/editing/delete_selection_command.cpp: (WebCore::positionBeforePossibleContainingSpecialElement): (WebCore::positionAfterPossibleContainingSpecialElement): (WebCore::DeleteSelectionCommand::initializeStartEnd):
  • new. more consistent handling of special element boundaries. (WebCore::DeleteSelectionCommand::initializePositionData): (WebCore::DeleteSelectionCommand::handleGeneralDelete):
  • handle childless block (e.g. empty table cell)
  • allow merging across list items (WebCore::DeleteSelectionCommand::moveNodesAfterNode):
  • use new isEmpty() method for renderers, to handle empty list items
  • khtml/editing/delete_selection_command.h:
  • khtml/editing/htmlediting.cpp: (WebCore::isAtomicNode): (WebCore::editingIgnoresContent):
  • new. smarter check than isReplaced() (WebCore::rangeCompliantEquivalent): (WebCore::maxDeepOffset): (WebCore::isFirstVisiblePositionInSpecialElement): (WebCore::positionBeforeContainingSpecialElement): (WebCore::isLastVisiblePositionInSpecialElement): (WebCore::positionAfterContainingSpecialElement): (WebCore::positionOutsideContainingSpecialElement): (WebCore::positionBeforeNode): (WebCore::positionAfterNode): (WebCore::isListElement): (WebCore::isTableElement): (WebCore::isFirstVisiblePositionAfterTableElement): (WebCore::positionBeforePrecedingTableElement): (WebCore::positionAvoidingSpecialElementBoundary):
  • khtml/editing/htmlediting.h:
  • khtml/editing/insert_line_break_command.cpp: (khtml::InsertLineBreakCommand::doApply):
  • khtml/editing/insert_paragraph_separator_command.cpp: (khtml::InsertParagraphSeparatorCommand::doApply):
  • khtml/editing/insert_text_command.cpp: (khtml::InsertTextCommand::prepareForTextInsertion): (khtml::InsertTextCommand::input):
  • khtml/editing/replace_selection_command.cpp: (WebCore::isMailPasteAsQuotationNode): (WebCore::ReplacementFragment::countRenderedBlocks): (WebCore::ReplaceSelectionCommand::doApply):
  • allow for fact that fragments have no VisiblePositions
  • khtml/editing/visible_position.cpp: (khtml::VisiblePosition::isCandidate): (khtml::showTree): (khtml::makeRange): (khtml::setStart): (khtml::setEnd):
  • khtml/editing/visible_position.h:
  • khtml/editing/visible_units.cpp: (khtml::previousBoundary): (khtml::nextBoundary): (khtml::startOfLine):
  • allow for fact that renderers for list markers and other generated content have no corresponding NodeImpl. (khtml::endOfLine):
  • ditto (khtml::nextLinePosition): (khtml::startOfParagraph): (khtml::endOfParagraph):
  • khtml/xml/ContainerNodeImpl.cpp: (WebCore::ContainerNodeImpl::getUpperLeftCorner): (WebCore::ContainerNodeImpl::childNode):
  • khtml/xml/ContainerNodeImpl.h:
  • khtml/xml/NodeImpl.cpp: (WebCore::NodeImpl::childNode): (WebCore::NodeImpl::traversePreviousNode): (WebCore::NodeImpl::nextEditable): (WebCore::showTree):
  • khtml/xml/NodeImpl.h:
  • khtml/xml/dom_position.cpp: (DOM::Position::upstream): (DOM::Position::downstream): (DOM::Position::inRenderedContent): (DOM::showTree):
  • khtml/xml/dom_position.h:
  • rendering/render_canvas.cpp: (RenderCanvas::selectionRect):
  • rendering/render_list.cpp: (RenderListItem::calcListValue): (RenderListItem::isEmpty): (getParentOfFirstLineBox): (RenderListItem::resetMarkerValue): (RenderListItem::updateMarkerLocation):
  • rendering/render_list.h: (khtml::RenderListMarker::isListMarker):
  • rendering/render_object.cpp: (WebCore::RenderObject::nextRenderer): (WebCore::RenderObject::previousRenderer): (WebCore::showTree):
  • rendering/render_object.h: (WebCore::RenderObject::getBaselineOfLastLineBox): (WebCore::RenderObject::isEmpty):
Location:
trunk/WebCore
Files:
27 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r12354 r12358  
     12006-01-25  David Harrison  <harrison@apple.com>
     2
     3        Reviewed by Justin.
     4
     5        <rdar://problem/3907635> copy/paste of list item text moves list item up one <LI>
     6        <rdar://problem/3907647> Enhance list editing: return inserts <li></li>
     7        <rdar://problem/4060158> deleting selection within table deletes more than intended
     8        <rdar://problem/4061232> Deleting a list can delete unselected content
     9        <rdar://problem/4062212> after pasting in contents of web.apple.com, typing before start adds to table instead of before table
     10        <rdar://problem/4064437> After copy/paste from bugweb cannot go back to entering text at left side of page
     11        <rdar://problem/4259845> Table editing in design mode is broken
     12        <rdar://problem/4287667> Insertion point goes before table instead of inside first cell
     13        <rdar://problem/4345749> Editing HTML - Enter at end of <LI> inserts uneditable blank <LI>
     14        <rdar://problem/4345794> HTML editing: Enter at end of last <LI> does not add new <LI>
     15        <rdar://problem/4345825> HTML Editing: editing first <LI> element removes first <LI> and more
     16        <rdar://problem/4345835> HTML editing: editing last <LI> removes everything within <BODY>
     17        <rdar://problem/4345879> HTML editing: editing first <TD> moves data out of table
     18
     19        ...also added showTree() static functions because switch to gcc 4.0 makes calling instance methods unreliable
     20
     21        - numerous small changes to handle empty list items
     22        - fix numeric list marker updating when adding/deleting list items
     23        - start to decompose "special element" handling, replacing it with appropriate handling of the different
     24          kinds of special elements in various situations, rather than giving a blanket treatment.  I will do more
     25          of this in subsequent checkins.
     26        - numerous small editing/selection changes to handle VisiblePosition at table offset childNodeCount()
     27        - simplify and fix equivalentRangeCompliantPosition, rename it to rangeCompliantEquivalent and make it static
     28        - some minor reformatting to current standards
     29        - the comments in the list below are for changes not covered by the above comments
     30       
     31        * khtml/editing/Selection.cpp:
     32        (WebCore::Selection::toRange):
     33        (WebCore::Selection::validate):
     34        * khtml/editing/SelectionController.cpp:
     35        (WebCore::SelectionController::modifyExtendingLeftBackward):
     36        - character selection backward from after table selects the table
     37        (WebCore::showTree):
     38        * khtml/editing/composite_edit_command.cpp:
     39        (WebCore::CompositeEditCommand::removeFullySelectedNode):
     40        - make sure empty cell has some height
     41        (WebCore::CompositeEditCommand::positionOutsideTabSpan):
     42        - check for !isTabSpanTextNode() to make calling this function easier
     43        (WebCore::CompositeEditCommand::addBlockPlaceholderIfNeeded):
     44        * khtml/editing/delete_selection_command.cpp:
     45        (WebCore::positionBeforePossibleContainingSpecialElement):
     46        (WebCore::positionAfterPossibleContainingSpecialElement):
     47        (WebCore::DeleteSelectionCommand::initializeStartEnd):
     48        - new.  more consistent handling of special element boundaries.
     49        (WebCore::DeleteSelectionCommand::initializePositionData):
     50        (WebCore::DeleteSelectionCommand::handleGeneralDelete):
     51        - handle childless block (e.g. empty table cell)
     52        - allow merging across list items
     53        (WebCore::DeleteSelectionCommand::moveNodesAfterNode):
     54        - use new isEmpty() method for renderers, to handle empty list items
     55        * khtml/editing/delete_selection_command.h:
     56        * khtml/editing/htmlediting.cpp:
     57        (WebCore::isAtomicNode):
     58        (WebCore::editingIgnoresContent):
     59        - new.  smarter check than isReplaced()
     60        (WebCore::rangeCompliantEquivalent):
     61        (WebCore::maxDeepOffset):
     62        (WebCore::isFirstVisiblePositionInSpecialElement):
     63        (WebCore::positionBeforeContainingSpecialElement):
     64        (WebCore::isLastVisiblePositionInSpecialElement):
     65        (WebCore::positionAfterContainingSpecialElement):
     66        (WebCore::positionOutsideContainingSpecialElement):
     67        (WebCore::positionBeforeNode):
     68        (WebCore::positionAfterNode):
     69        (WebCore::isListElement):
     70        (WebCore::isTableElement):
     71        (WebCore::isFirstVisiblePositionAfterTableElement):
     72        (WebCore::positionBeforePrecedingTableElement):
     73        (WebCore::positionAvoidingSpecialElementBoundary):
     74        * khtml/editing/htmlediting.h:
     75        * khtml/editing/insert_line_break_command.cpp:
     76        (khtml::InsertLineBreakCommand::doApply):
     77        * khtml/editing/insert_paragraph_separator_command.cpp:
     78        (khtml::InsertParagraphSeparatorCommand::doApply):
     79        * khtml/editing/insert_text_command.cpp:
     80        (khtml::InsertTextCommand::prepareForTextInsertion):
     81        (khtml::InsertTextCommand::input):
     82        * khtml/editing/replace_selection_command.cpp:
     83        (WebCore::isMailPasteAsQuotationNode):
     84        (WebCore::ReplacementFragment::countRenderedBlocks):
     85        (WebCore::ReplaceSelectionCommand::doApply):
     86        - allow for fact that fragments have no VisiblePositions
     87        * khtml/editing/visible_position.cpp:
     88        (khtml::VisiblePosition::isCandidate):
     89        (khtml::showTree):
     90        (khtml::makeRange):
     91        (khtml::setStart):
     92        (khtml::setEnd):
     93        * khtml/editing/visible_position.h:
     94        * khtml/editing/visible_units.cpp:
     95        (khtml::previousBoundary):
     96        (khtml::nextBoundary):
     97        (khtml::startOfLine):
     98        - allow for fact that renderers for list markers and other generated content
     99          have no corresponding NodeImpl.
     100        (khtml::endOfLine):
     101        - ditto
     102        (khtml::nextLinePosition):
     103        (khtml::startOfParagraph):
     104        (khtml::endOfParagraph):
     105        * khtml/xml/ContainerNodeImpl.cpp:
     106        (WebCore::ContainerNodeImpl::getUpperLeftCorner):
     107        (WebCore::ContainerNodeImpl::childNode):
     108        * khtml/xml/ContainerNodeImpl.h:
     109        * khtml/xml/NodeImpl.cpp:
     110        (WebCore::NodeImpl::childNode):
     111        (WebCore::NodeImpl::traversePreviousNode):
     112        (WebCore::NodeImpl::nextEditable):
     113        (WebCore::showTree):
     114        * khtml/xml/NodeImpl.h:
     115        * khtml/xml/dom_position.cpp:
     116        (DOM::Position::upstream):
     117        (DOM::Position::downstream):
     118        (DOM::Position::inRenderedContent):
     119        (DOM::showTree):
     120        * khtml/xml/dom_position.h:
     121        * rendering/render_canvas.cpp:
     122        (RenderCanvas::selectionRect):
     123        * rendering/render_list.cpp:
     124        (RenderListItem::calcListValue):
     125        (RenderListItem::isEmpty):
     126        (getParentOfFirstLineBox):
     127        (RenderListItem::resetMarkerValue):
     128        (RenderListItem::updateMarkerLocation):
     129        * rendering/render_list.h:
     130        (khtml::RenderListMarker::isListMarker):
     131        * rendering/render_object.cpp:
     132        (WebCore::RenderObject::nextRenderer):
     133        (WebCore::RenderObject::previousRenderer):
     134        (WebCore::showTree):
     135        * rendering/render_object.h:
     136        (WebCore::RenderObject::getBaselineOfLastLineBox):
     137        (WebCore::RenderObject::isEmpty):
     138
    11392006-01-25  Timothy Hatcher  <timothy@apple.com>
    2140
     
    7145        (QLineEdit::sizeForCharacterWidth):
    8146        (QLineEdit::baselinePosition):
     147
    9148
    101492006-01-24  Darin Adler  <darin@apple.com>
  • trunk/WebCore/khtml/editing/Selection.cpp

    r12152 r12358  
    8080        // the conventions of text editors tested, which make style determinations based
    8181        // on the character before the caret, if any.
    82         s = m_start.upstream().equivalentRangeCompliantPosition();
     82        s = rangeCompliantEquivalent(m_start.upstream());
    8383        e = s;
    84     }
    85     else {
     84    } else {
    8685        // If the selection is a range, select the minimum range that encompasses the selection.
    8786        // Again, this is to match the conventions of text editors tested, which make style
     
    105104            e = tmp;
    106105        }
    107         s = s.equivalentRangeCompliantPosition();
    108         e = e.equivalentRangeCompliantPosition();
     106        s = rangeCompliantEquivalent(s);
     107        e = rangeCompliantEquivalent(e);
    109108    }
    110109
     
    221220            break;
    222221        }
    223         case LINE_BOUNDARY:
     222        case LINE_BOUNDARY: {
    224223            m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
    225224            m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent();
    226225            break;
     226        }
    227227        case PARAGRAPH: {
    228228            VisiblePosition pos(m_start, m_affinity);
     
    233233            // Include the space after the end of the paragraph in the selection.
    234234            VisiblePosition startOfNextParagraph = visibleParagraphEnd.next();
    235             m_end = startOfNextParagraph.isNotNull() ? startOfNextParagraph.deepEquivalent() : visibleParagraphEnd.deepEquivalent();
     235            if (startOfNextParagraph.isNull())
     236                m_end = visibleParagraphEnd.deepEquivalent();
     237            else {
     238                m_end = startOfNextParagraph.deepEquivalent();
     239                // Stay within enclosing node, e.g. do not span end of table.
     240                if (visibleParagraphEnd.deepEquivalent().node()->isAncestor(m_end.node()))
     241                    m_end = visibleParagraphEnd.deepEquivalent();
     242            }
    236243            break;
    237244        }
  • trunk/WebCore/khtml/editing/SelectionController.cpp

    r12230 r12358  
    268268{
    269269    VisiblePosition pos(m_sel.extent(), m_sel.affinity());
     270       
    270271    switch (granularity) {
    271272        case CHARACTER:
    272             pos = pos.previous();
     273            // Extending a selection backward by character from just after a table selects
     274            // the table.  This "makes sense" from the user perspective, esp. when deleting.
     275            // It was done here instead of in VisiblePosition because we want VPs to iterate
     276            // over everything.
     277            if (isFirstVisiblePositionAfterTableElement(pos.deepEquivalent()))
     278                pos = VisiblePosition(positionBeforePrecedingTableElement(pos.deepEquivalent()), VP_DEFAULT_AFFINITY);
     279            else
     280                pos = pos.previous();
    273281            break;
    274282        case WORD:
     
    844852        m_sel.start().node()->showTreeAndMark(m_sel.start().node(), "S", m_sel.end().node(), "E");
    845853}
     854
     855void showTree(const SelectionController &sel)
     856{
     857    sel.showTree();
     858}
     859
     860void showTree(const SelectionController *sel)
     861{
     862    if (sel)
     863        sel->showTree();
     864}
    846865#endif
    847866
  • trunk/WebCore/khtml/editing/composite_edit_command.cpp

    r12233 r12358  
    181181            removeFullySelectedNode(remove);
    182182        }
    183     }
    184     else {
    185         removeNode(node);
    186     }
     183       
     184        // make sure empty cell has some height
     185        updateLayout();
     186        RenderObject *r = node->renderer();
     187        if (r && r->isTableCell() && r->contentHeight() <= 0)
     188            insertBlockPlaceholder(Position(node,0));
     189        return;
     190    }
     191   
     192    removeNode(node);
    187193}
    188194
     
    276282Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
    277283{
    278     ASSERT(isTabSpanTextNode(pos.node()));
     284    if (!isTabSpanTextNode(pos.node()))
     285        return pos;
    279286   
    280287    NodeImpl *tabSpan = tabSpanNode(pos.node());
     
    487494    // append the placeholder to make sure it follows
    488495    // any unrendered blocks
     496    // FIXME: use contentHeight() instead so to not be confused by bordere, e.g.
    489497    if (renderer->height() == 0) {
    490498        return appendBlockPlaceholder(node);
  • trunk/WebCore/khtml/editing/delete_selection_command.cpp

    r12152 r12358  
    4747using namespace HTMLNames;
    4848
    49 static bool isListStructureNode(const NodeImpl *node)
    50 {
    51     // FIXME: Irritating that we can get away with just going at the render tree for isTableStructureNode,
    52     // but here we also have to peek at the type of DOM node?
    53     RenderObject *r = node->renderer();
    54     return (r && r->isListItem())
    55         || node->hasTagName(olTag)
    56         || node->hasTagName(ulTag)
    57         || node->hasTagName(ddTag)
    58         || node->hasTagName(dtTag)
    59         || node->hasTagName(dirTag)
    60         || node->hasTagName(menuTag);
    61 }
    62 
    6349static void debugPosition(const char *prefix, const Position &pos)
    6450{
     
    8167}
    8268
    83 static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
    84 {
    85     if (isFirstVisiblePositionInSpecialElement(pos)) {
    86         return positionBeforeContainingSpecialElement(pos);
    87     }
    88 
    89     return pos;
    90 }
    91 
    92 static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
    93 {
    94     if (isLastVisiblePositionInSpecialElement(pos)) {
    95         return positionAfterContainingSpecialElement(pos);
    96     }
    97 
    98     return pos;
    99 }
     69#if 1
     70static Position positionBeforePossibleContainingSpecialElement(const Position &pos, NodeImpl **containingSpecialElement)
     71 {
     72    if (isFirstVisiblePositionInSpecialElement(pos))
     73        return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
     74 
     75     return pos;
     76 }
     77 
     78static Position positionAfterPossibleContainingSpecialElement(const Position &pos, NodeImpl **containingSpecialElement)
     79 {
     80    if (isLastVisiblePositionInSpecialElement(pos))
     81        return positionAfterContainingSpecialElement(pos, containingSpecialElement);
     82 
     83     return pos;
     84 }
     85#endif
    10086
    10187DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete)
     
    126112}
    127113
     114void DeleteSelectionCommand::initializeStartEnd()
     115 {
     116#if 0
     117    Position start = m_selectionToDelete.start();
     118    Position end = m_selectionToDelete.end();
     119    m_upstreamStart = start.upstream();
     120    m_downstreamStart = start.downstream();
     121    m_upstreamEnd = end.upstream();
     122    m_downstreamEnd = end.downstream();
     123#else
     124    NodeImpl *startSpecialContainer = 0;
     125    NodeImpl *endSpecialContainer = 0;
     126   
     127    Position start = positionOutsideContainingSpecialElement(m_selectionToDelete.start(), &startSpecialContainer);
     128    Position end   = positionOutsideContainingSpecialElement(m_selectionToDelete.end(), &endSpecialContainer);
     129   
     130    m_upstreamStart = positionBeforePossibleContainingSpecialElement(start.upstream(), &startSpecialContainer);
     131    m_downstreamStart = positionBeforePossibleContainingSpecialElement(start.downstream(), 0);
     132    m_upstreamEnd = positionAfterPossibleContainingSpecialElement(end.upstream(), 0);
     133    m_downstreamEnd = positionAfterPossibleContainingSpecialElement(end.downstream(), &endSpecialContainer);
     134 
     135    // do not adjust start/end if one of them did not adjust, and the selection is entirely within the special element
     136    if (m_upstreamStart == m_selectionToDelete.start().upstream() || m_downstreamEnd == m_selectionToDelete.end().downstream()) {
     137        if (m_downstreamEnd.node()->isAncestor(startSpecialContainer) || m_upstreamStart.node()->isAncestor(endSpecialContainer)) {
     138            start = m_selectionToDelete.start();
     139            end = m_selectionToDelete.end();
     140            m_upstreamStart = start.upstream();
     141            m_downstreamStart = start.downstream();
     142            m_upstreamEnd = end.upstream();
     143            m_downstreamEnd = end.downstream();
     144        }
     145    }
     146#endif
     147}
     148
    128149void DeleteSelectionCommand::initializePositionData()
    129150{
     
    131152    // Handle setting some basic positions
    132153    //
    133     Position start = m_selectionToDelete.start();
    134     start = positionOutsideContainingSpecialElement(start);
    135     Position end = m_selectionToDelete.end();
    136     end = positionOutsideContainingSpecialElement(end);
    137 
    138     m_upstreamStart = positionBeforePossibleContainingSpecialElement(start.upstream());
    139     m_downstreamStart = positionBeforePossibleContainingSpecialElement(start.downstream());
    140     m_upstreamEnd = positionAfterPossibleContainingSpecialElement(end.upstream());
    141     m_downstreamEnd = positionAfterPossibleContainingSpecialElement(end.downstream());
    142 
     154    initializeStartEnd();
     155   
    143156    //
    144157    // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection
     
    158171        bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull();
    159172        if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) {
    160             VisiblePosition visiblePos = VisiblePosition(start, m_selectionToDelete.affinity()).previous();
     173            VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous();
    161174            pos = visiblePos.deepEquivalent();
    162175            // Expand out one character upstream for smart delete and recalculate
     
    172185            // Expand out one character downstream for smart delete and recalculate
    173186            // positions based on this change.
    174             pos = VisiblePosition(end, m_selectionToDelete.affinity()).next().deepEquivalent();
     187            pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent();
    175188            m_upstreamEnd = pos.upstream();
    176189            m_downstreamEnd = pos.downstream();
     
    192205    // This is one of the tests that determines if block merging of content needs to be done.
    193206    //
    194     VisiblePosition visibleEnd(end, m_selectionToDelete.affinity());
     207    VisiblePosition visibleEnd(m_downstreamEnd, VP_DEFAULT_AFFINITY);
    195208    if (isEndOfParagraph(visibleEnd)) {
    196209        Position previousLineStart = previousLinePosition(visibleEnd, 0).deepEquivalent();
     
    310323        if (!m_startBlock->isAncestor(m_endBlock.get()) && !isStartOfBlock(visibleEnd) && endAtEndOfBlock) {
    311324            // Delete all the children of the block, but not the block itself.
    312             m_startNode = m_startBlock->firstChild();
     325            // Use traverseNextNode in case the block has no children (e.g. is an empty table cell)
     326            m_startNode = m_startBlock->traverseNextNode();
    313327            startOffset = 0;
    314328        }
    315     }
    316     else if (startOffset >= m_startNode->caretMaxOffset() &&
    317              (isAtomicNode(m_startNode.get()) || startOffset == 0)) {
     329    }  else if (startOffset >= m_startNode->caretMaxOffset() && (isAtomicNode(m_startNode.get()) || startOffset == 0)) {
    318330        // Move the start node to the next node in the tree since the startOffset is equal to
    319331        // or beyond the start node's caretMaxOffset This means there is nothing visible to delete.
     
    500512
    501513    NodeImpl *startBlock = startNode->enclosingBlockFlowElement();
    502     if (isTableStructureNode(startBlock) || isListStructureNode(startBlock))
    503         // Do not move content between parts of a table or list.
    504         return;
    505 
     514    if (isTableStructureNode(startBlock)) {
     515        // Do not move content between parts of a table.
     516        return;
     517    }
     518   
    506519    // Now that we are about to add content, check to see if a placeholder element
    507520    // can be removed.
     
    511524    NodeImpl *node = startNode->enclosingInlineElement();
    512525
    513     // Insert after the subtree containing destNode
     526    // Insert after the subtree containing dstNode
    514527    NodeImpl *refNode = dstNode->enclosingInlineElement();
    515528
     
    553566    // This may not be ideal, but it is better than nothing.
    554567    updateLayout();
    555     if (!startBlock->renderer() || !startBlock->renderer()->firstChild()) {
     568    if (!startBlock->renderer() || startBlock->renderer()->isEmpty()) {
    556569        removeNode(startBlock);
    557570        updateLayout();
  • trunk/WebCore/khtml/editing/delete_selection_command.h

    r12152 r12358  
    4343    virtual bool preservesTypingStyle() const;
    4444
     45    void initializeStartEnd();
    4546    void initializePositionData();
    4647    void saveTypingStyleState();
  • trunk/WebCore/khtml/editing/htmlediting.cpp

    r12233 r12358  
    3030#include "dom2_range.h"
    3131#include "dom_textimpl.h"
     32#include "HTMLElementImpl.h"
    3233#include "html_interchange.h"
    3334#include "htmlnames.h"
     
    4445bool isAtomicNode(const NodeImpl *node)
    4546{
    46     return node && (!node->hasChildNodes() || node->renderer() && node->renderer()->isWidget());
     47    return node && (!node->hasChildNodes() || editingIgnoresContent(node));
     48}
     49
     50bool editingIgnoresContent(const NodeImpl *node)
     51{
     52    if (!node || !node->isHTMLElement())
     53        return false;
     54   
     55    if (node->renderer()) {
     56        if (node->renderer()->isWidget())
     57            return true;
     58        if (node->renderer()->isImage())
     59            return true;
     60    } else {
     61        // widgets
     62        if (static_cast<const HTMLElementImpl *>(node)->isGenericFormElement())
     63            return true;
     64        if (node->hasTagName(appletTag))
     65            return true;
     66        if (node->hasTagName(embedTag))
     67            return true;
     68        if (node->hasTagName(iframeTag))
     69            return true;
     70
     71        // images
     72        if (node->hasTagName(imgTag))
     73            return true;
     74    }
     75   
     76    return false;
     77}
     78
     79// antidote for maxDeepOffset()
     80Position rangeCompliantEquivalent(const Position& pos)
     81{
     82    if (pos.isNull())
     83        return Position();
     84
     85    NodeImpl *node = pos.node();
     86   
     87    if (pos.offset() <= 0) {
     88        // FIXME: createMarkup has a problem with BR 0 as the starting position
     89        // so workaround until I can come back and fix createMarkup.  The problem
     90        // is that createMarkup fails to include the initial BR in the markup.
     91        if (node->parentNode() && node->hasTagName(brTag))
     92            return positionBeforeNode(node);
     93        return Position(node, 0);
     94    }
     95   
     96    if (offsetInCharacters(node->nodeType()))
     97        return Position(node, kMin(node->maxOffset(), pos.offset()));
     98   
     99    int maxCompliantOffset = node->childNodeCount();
     100    if (pos.offset() > maxCompliantOffset) {
     101        if (node->parentNode())
     102            return positionAfterNode(node);
     103       
     104        // there is no other option at this point than to
     105        // use the highest allowed position in the node
     106        return Position(node, maxCompliantOffset);
     107    }
     108   
     109    // "select" nodes, e.g., are ignored by editing but can have children.
     110    // For us, a range inside of that node is tough to deal with, so use
     111    // a more generic position.
     112    if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
     113        // ... but we should not have generated any such positions
     114        ASSERT_NOT_REACHED();
     115        return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
     116    }
     117   
     118    return Position(pos);
     119}
     120
     121Position rangeCompliantEquivalent(const VisiblePosition& vpos)
     122{
     123    return rangeCompliantEquivalent(vpos.deepEquivalent());
    47124}
    48125
    49126// This method is used to create positions in the DOM. It returns the maximum valid offset
    50 // in a node.  It returns 1 for <br>s and replaced elements, which creates
    51 // technically invalid DOM Positions.  Be sure to call equivalentRangeCompliantPosition
     127// in a node.  It returns 1 for some elements even though they do not have children, which
     128// creates technically invalid DOM Positions.  Be sure to call rangeCompliantEquivalent
    52129// on a Position before using it to create a DOM Range, or an exception will be thrown.
    53130int maxDeepOffset(const NodeImpl *node)
    54131{
    55     if (node->isTextNode())
    56         return static_cast<const TextImpl*>(node)->length();
     132    if (offsetInCharacters(node->nodeType()))
     133        return node->maxOffset();
    57134       
    58135    if (node->hasChildNodes())
    59136        return node->childNodeCount();
    60137   
    61     RenderObject *renderer = node->renderer();
    62     if (node->hasTagName(brTag) || (renderer && renderer->isReplaced()))
     138    // NOTE: This should preempt the childNodeCount for, e.g., select nodes
     139    if (node->hasTagName(brTag) || editingIgnoresContent(node))
    63140        return 1;
    64141
     
    122199}
    123200
     201// FIXME: Why use this instead of maxDeepOffset???
    124202static int maxRangeOffset(NodeImpl *n)
    125203{
     
    133211}
    134212
     213#if 1
     214// FIXME: need to dump this
    135215bool isSpecialElement(const NodeImpl *n)
    136216{
     
    168248
    169249    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
    170         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
     250        VisiblePosition checkVP = VisiblePosition(n, 0, DOWNSTREAM);
     251        if (checkVP != vPos) {
     252            if (isTableElement(n) && checkVP.next() == vPos)
     253                return true;
    171254            return false;
     255        }
    172256        if (n->rootEditableElement() == NULL)
    173257            return false;
     
    179263}
    180264
    181 Position positionBeforeNode(const NodeImpl *node)
    182 {
    183     return Position(node->parentNode(), node->nodeIndex());
    184 }
    185 
    186 Position positionBeforeContainingSpecialElement(const Position& pos)
     265Position positionBeforeContainingSpecialElement(const Position& pos, NodeImpl **containingSpecialElement)
    187266{
    188267    ASSERT(isFirstVisiblePositionInSpecialElement(pos));
     
    193272
    194273    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
    195         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
     274        VisiblePosition checkVP = VisiblePosition(n, 0, DOWNSTREAM);
     275        if (checkVP != vPos) {
     276            if (isTableElement(n) && checkVP.next() == vPos)
     277                outermostSpecialElement = n;
    196278            break;
     279        }
    197280        if (n->rootEditableElement() == NULL)
    198281            break;
     
    202285   
    203286    ASSERT(outermostSpecialElement);
    204 
     287    if (containingSpecialElement)
     288        *containingSpecialElement = outermostSpecialElement;
     289       
    205290    Position result = positionBeforeNode(outermostSpecialElement);
    206291    if (result.isNull() || !result.node()->rootEditableElement())
     
    214299    // make sure to get a range-compliant version of the position
    215300    // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
    216     Position rangePos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent().equivalentRangeCompliantPosition();
     301    Position rangePos = rangeCompliantEquivalent(VisiblePosition(pos, DOWNSTREAM));
    217302
    218303    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
    219304
    220305    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
    221         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
    222             return false;
     306        VisiblePosition checkVP = VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM);
     307        if (checkVP != vPos)
     308            return (isTableElement(n) && checkVP.previous() == vPos);
    223309        if (n->rootEditableElement() == NULL)
    224310            return false;
     
    230316}
    231317
    232 Position positionAfterNode(const NodeImpl *node)
    233 {
    234     return Position(node->parentNode(), node->nodeIndex() + 1);
    235 }
    236 
    237 Position positionAfterContainingSpecialElement(const Position& pos)
     318Position positionAfterContainingSpecialElement(const Position& pos, NodeImpl **containingSpecialElement)
    238319{
    239320    ASSERT(isLastVisiblePositionInSpecialElement(pos));
     
    241322    // make sure to get a range-compliant version of the position
    242323    // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
    243     Position rangePos = VisiblePosition(pos, DOWNSTREAM).deepEquivalent().equivalentRangeCompliantPosition();
    244 
     324    Position rangePos = rangeCompliantEquivalent(VisiblePosition(pos, DOWNSTREAM));
    245325    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
    246326
     
    248328
    249329    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
    250         if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
     330        VisiblePosition checkVP = VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM);
     331        if (checkVP != vPos) {
     332            if (isTableElement(n) && checkVP.previous() == vPos)
     333                outermostSpecialElement = n;
    251334            break;
     335        }
    252336        if (n->rootEditableElement() == NULL)
    253337            break;
     
    257341   
    258342    ASSERT(outermostSpecialElement);
     343    if (containingSpecialElement)
     344        *containingSpecialElement = outermostSpecialElement;
    259345
    260346    Position result = positionAfterNode(outermostSpecialElement);
     
    265351}
    266352
    267 Position positionOutsideContainingSpecialElement(const Position &pos)
    268 {
    269     if (isFirstVisiblePositionInSpecialElement(pos)) {
    270         return positionBeforeContainingSpecialElement(pos);
    271     } else if (isLastVisiblePositionInSpecialElement(pos)) {
    272         return positionAfterContainingSpecialElement(pos);
    273     }
     353Position positionOutsideContainingSpecialElement(const Position &pos, NodeImpl **containingSpecialElement)
     354{
     355    if (isFirstVisiblePositionInSpecialElement(pos))
     356        return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
     357   
     358    if (isLastVisiblePositionInSpecialElement(pos))
     359        return positionAfterContainingSpecialElement(pos, containingSpecialElement);
    274360
    275361    return pos;
     362}
     363#endif
     364
     365Position positionBeforeNode(const NodeImpl *node)
     366{
     367    return Position(node->parentNode(), node->nodeIndex());
     368}
     369
     370Position positionAfterNode(const NodeImpl *node)
     371{
     372    return Position(node->parentNode(), node->nodeIndex() + 1);
     373}
     374
     375bool isListElement(NodeImpl *n)
     376{
     377    return (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag));
     378}
     379
     380// FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
     381bool isTableElement(NodeImpl *n)
     382{
     383    RenderObject *renderer = n->renderer();
     384    // all editing tests pass with this, but I don't want to commit it until I check more
     385//  ASSERT(renderer != 0);
     386    return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
     387}
     388
     389bool isFirstVisiblePositionAfterTableElement(const Position& pos)
     390{
     391    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM).previous();
     392    // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
     393    Position rangePos = rangeCompliantEquivalent(vPos.deepEquivalent().downstream());
     394    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
     395        // FIXME: can we not create a VP every time thru this loop?
     396        VisiblePosition checkVP = VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM);
     397        if (checkVP != vPos) {
     398            if (isTableElement(n) && checkVP.previous() == vPos)
     399                return true;
     400            return false;
     401        }
     402        if (n->rootEditableElement() == NULL)
     403            return false;
     404        if (isTableElement(n))
     405            return true;
     406    }
     407
     408    return false;
     409}
     410
     411Position positionBeforePrecedingTableElement(const Position& pos)
     412{
     413    ASSERT(isFirstVisiblePositionAfterTableElement(pos));
     414    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM).previous();
     415    // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
     416    Position rangePos = rangeCompliantEquivalent(vPos);
     417    NodeImpl *outermostTableElement = NULL;
     418
     419    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
     420        // FIXME: can we not create a VP every time thru this loop?
     421        VisiblePosition checkVP = VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM);
     422        if (checkVP != vPos) {
     423            if (isTableElement(n) && checkVP.previous() == vPos)
     424                outermostTableElement = n;
     425            break;
     426        }
     427        if (n->rootEditableElement() == NULL)
     428            break;
     429        if (isTableElement(n))
     430            outermostTableElement = n;
     431    }
     432   
     433    ASSERT(outermostTableElement);       
     434    Position result = positionBeforeNode(outermostTableElement);
     435   
     436    if (result.isNull() || !result.node()->rootEditableElement())
     437        return pos;
     438    return result;
     439}
     440
     441// This function is necessary because a VisiblePosition is allowed
     442// to be at the start or end of elements where we do not want to
     443// add content directly.  For example, clicking at the end of a hyperlink,
     444// then typing, needs to add the text after the link.  Also, table
     445// offset 0 and table offset childNodeCount are valid VisiblePostions,
     446// but we can not add more content right there... it needs to go before
     447// or after the table.
     448// FIXME: Consider editable/non-editable boundaries?
     449Position positionAvoidingSpecialElementBoundary(const Position &pos)
     450{
     451    NodeImpl *compNode = pos.node();
     452    if (!compNode)
     453        return pos;
     454   
     455    if (compNode->parentNode() && compNode->parentNode()->isLink())
     456        compNode = compNode->parentNode();
     457    else if (!isTableElement(compNode))
     458        return pos;
     459   
     460    // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
     461    Position rangePos = rangeCompliantEquivalent(VisiblePosition(pos, DOWNSTREAM));
     462    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
     463
     464    Position result;
     465    if (VisiblePosition(compNode, maxRangeOffset(compNode), DOWNSTREAM) == vPos)
     466        result = positionAfterNode(compNode);
     467    else if (VisiblePosition(compNode, 0, DOWNSTREAM) == vPos)
     468        result = positionBeforeNode(compNode);
     469    else
     470        return pos;
     471       
     472    if (result.isNull() || !result.node()->rootEditableElement())
     473        result = pos;
     474   
     475    return result;
    276476}
    277477
  • trunk/WebCore/khtml/editing/htmlediting.h

    r12233 r12358  
    3838class NodeImpl;
    3939class Position;
     40class VisiblePosition;
    4041
    4142const unsigned short NON_BREAKING_SPACE = 0xa0;
    4243
    43 int maxDeepOffset(const NodeImpl*);
    44 bool isAtomicNode(const NodeImpl*);
     44Position rangeCompliantEquivalent(const Position& pos);
     45Position rangeCompliantEquivalent(const VisiblePosition& vpos);
     46int maxDeepOffset(const NodeImpl* node);
     47bool isAtomicNode(const NodeImpl* node);
     48bool editingIgnoresContent(const NodeImpl* node);
    4549
    4650void rebalanceWhitespaceInTextNode(NodeImpl*, unsigned start, unsigned length);
     
    6771bool isNodeRendered(const NodeImpl*);
    6872bool isMailBlockquote(const NodeImpl*);
    69 NodeImpl *nearestMailBlockquote(const NodeImpl*);
     73NodeImpl* nearestMailBlockquote(const NodeImpl*);
    7074
    7175//------------------------------------------------------------------------------------------
     
    7478ElementImpl *createBlockPlaceholderElement(DocumentImpl*);
    7579
    76 bool isFirstVisiblePositionInSpecialElement(const Position&);
    77 Position positionBeforeContainingSpecialElement(const Position&);
    78 bool isLastVisiblePositionInSpecialElement(const Position&);
    79 Position positionAfterContainingSpecialElement(const Position&);
    80 Position positionOutsideContainingSpecialElement(const Position&);
     80bool isFirstVisiblePositionInSpecialElement(const Position& pos);
     81Position positionBeforeContainingSpecialElement(const Position& pos, NodeImpl** containingSpecialElement=0);
     82bool isLastVisiblePositionInSpecialElement(const Position& pos);
     83Position positionAfterContainingSpecialElement(const Position& pos, NodeImpl** containingSpecialElement=0);
     84Position positionOutsideContainingSpecialElement(const Position &pos, NodeImpl** containingSpecialElement=0);
     85
     86bool isListElement(NodeImpl* n);
     87bool isTableElement(NodeImpl* n);
     88bool isFirstVisiblePositionAfterTableElement(const Position &pos);
     89Position positionBeforePrecedingTableElement(const Position &pos);
     90Position positionAvoidingSpecialElementBoundary(const Position &pos);
    8191
    8292}
  • trunk/WebCore/khtml/editing/insert_line_break_command.cpp

    r12152 r12358  
    9898    Position pos(selection.start().upstream());
    9999
    100     pos = positionOutsideContainingSpecialElement(pos);
     100    pos = positionAvoidingSpecialElementBoundary(pos);
    101101
    102102    if (isTabSpanTextNode(pos.node())) {
  • trunk/WebCore/khtml/editing/insert_paragraph_separator_command.cpp

    r12313 r12358  
    118118    }
    119119
    120     pos = positionOutsideContainingSpecialElement(pos);
     120    pos = positionAvoidingSpecialElementBoundary(pos);
    121121
    122122    calculateStyleBeforeInsertion(pos);
     
    202202    LOG(Editing, "insert paragraph separator: general case");
    203203
    204     // Check if pos.node() is a <br>. If it is, and the document is in quirks mode,
    205     // then this <br> will collapse away when we add a block after it. Add an extra <br>.
     204    // If pos.node() is a <br> and the document is in quirks mode, this <br>
     205    // will collapse away when we add a block after it. Add an extra <br>.
    206206    if (!document()->inStrictMode()) {
    207207        Position upstreamPos = pos.upstream();
  • trunk/WebCore/khtml/editing/insert_text_command.cpp

    r12152 r12358  
    6262    // Prepare for text input by looking at the specified position.
    6363    // It may be necessary to insert a text node to receive characters.
     64    // FIXME: What is the rootEditable() check about?  Seems like it
     65    // assumes that the content before (or after) pos.node() is editable
     66    // (i.e. pos is at an editable/non-editable boundary).  That seems
     67    // like a bad assumption.
    6468    if (!pos.node()->isTextNode()) {
    6569        NodeImpl *textNode = document()->createEditingTextNode("");
     
    6872        // Now insert the node in the right place
    6973        if (pos.node()->rootEditableElement() != NULL) {
    70             LOG(Editing, "prepareForTextInsertion case 1");
    7174            insertNodeAt(nodeToInsert, pos.node(), pos.offset());
    72         }
    73         else if (pos.node()->caretMinOffset() == pos.offset()) {
    74             LOG(Editing, "prepareForTextInsertion case 2");
     75        } else if (pos.node()->caretMinOffset() == pos.offset()) {
    7576            insertNodeBefore(nodeToInsert, pos.node());
    76         }
    77         else if (pos.node()->caretMaxOffset() == pos.offset()) {
    78             LOG(Editing, "prepareForTextInsertion case 3");
     77        } else if (pos.node()->caretMaxOffset() == pos.offset()) {
    7978            insertNodeAfter(nodeToInsert, pos.node());
    80         }
    81         else
     79        } else
    8280            ASSERT_NOT_REACHED();
    8381       
     
    122120    else
    123121        startPosition = startPosition.upstream();
    124     startPosition = positionOutsideContainingSpecialElement(startPosition);
     122    startPosition = positionAvoidingSpecialElementBoundary(startPosition);
    125123   
    126124    if (text == "\t") {
  • trunk/WebCore/khtml/editing/replace_selection_command.cpp

    r12243 r12358  
    155155static bool isMailPasteAsQuotationNode(const NodeImpl *node)
    156156{
    157     if (!node)
    158         return false;
    159        
    160     return static_cast<const ElementImpl *>(node)->getAttribute("class") == ApplePasteAsQuotation;
     157    return node && static_cast<const ElementImpl *>(node)->getAttribute("class") == ApplePasteAsQuotation;
    161158}
    162159
     
    388385                prev = node;
    389386            }
    390         }
    391         else {
     387        } else {
    392388            NodeImpl *block = node->enclosingBlockFlowElement();
    393389            if (block != prev) {
     
    459455}
    460456
    461 static int maxRangeOffset(NodeImpl *n)
    462 {
    463     if (DOM::offsetInCharacters(n->nodeType()))
    464         return n->maxOffset();
    465 
    466     if (n->isElementNode())
    467         return n->childNodeCount();
    468 
    469     return 1;
    470 }
    471 
    472 // This version of the function is meant to be called on positions in a document fragment,
    473 // so it does not check for a root editable element, it is assumed these nodes will be put
    474 // somewhere editable in the future
    475 bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
    476 {
    477     VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
    478 
    479     for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
    480         if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
    481             return false;
    482         if (isSpecialElement(n))
    483             return true;
    484     }
    485 
    486     return false;
    487 }
    488 
    489457void ReplaceSelectionCommand::doApply()
    490458{
     
    509477        // empty editable subtree, need to mergeStart so that fragment ends up
    510478        // inside the editable subtree rather than just before it
     479        // FIXME: Reconcile comment versus mergeStart = false
    511480        mergeStart = false;
    512481    } else {
    513482        // merge if current selection starts inside a paragraph, or there is only one block and no interchange newline to add
    514483        mergeStart = !m_fragment.hasInterchangeNewlineAtStart() &&
    515             (!isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewlineAtEnd() && !m_fragment.hasMoreThanOneBlock())) &&
    516             !isLastVisiblePositionInSpecialElement(selection.start());
     484            (!isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewlineAtEnd() && !m_fragment.hasMoreThanOneBlock()));
    517485       
    518486        // This is a workaround for this bug:
    519         // <rdar://problem/4013642> REGRESSION (Mail): Copied quoted word does not paste as a quote if pasted at the start of a line
     487        // <rdar://problem/4013642> Copied quoted word does not paste as a quote if pasted at the start of a line
    520488        // We need more powerful logic in this whole mergeStart code for this case to come out right without
    521489        // breaking other cases.
    522490        if (isStartOfParagraph(visibleStart) && isMailBlockquote(m_fragment.firstChild()))
    523491            mergeStart = false;
     492       
     493        // prevent first list item from getting merged into target, thereby pulled out of list
     494        // NOTE: ideally, we'd check for "first visible position in list" here,
     495        // but we cannot.  Fragments do not have any visible positions.  Instead, we
     496        // assume that the mergeStartNode() contains the first visible content to paste.
     497        // Any better ideas?
     498        if (mergeStart) {
     499            for (NodeImpl *n = m_fragment.mergeStartNode(); n; n = n->parentNode()) {
     500                if (isListElement(n)) {
     501                    mergeStart = false;
     502                    break;
     503                }
     504            }
     505        }
    524506    }
    525507   
     
    537519    // delete the current range selection, or insert paragraph for caret selection, as needed
    538520    if (selection.isRange()) {
    539         deleteSelection(false, !(m_fragment.hasInterchangeNewlineAtStart() || m_fragment.hasInterchangeNewlineAtEnd() || m_fragment.hasMoreThanOneBlock()));
     521        bool mergeBlocksAfterDelete = !(m_fragment.hasInterchangeNewlineAtStart() || m_fragment.hasInterchangeNewlineAtEnd() || m_fragment.hasMoreThanOneBlock());
     522        deleteSelection(false, mergeBlocksAfterDelete);
    540523        updateLayout();
    541524        visibleStart = VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY);
     
    544527                if (!isEndOfDocument(visibleStart))
    545528                    setEndingSelection(visibleStart.next());
    546             }
    547             else {
     529            } else {
    548530                insertParagraphSeparator();
    549531                setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY));
     
    558540                if (!isEndOfDocument(visibleStart))
    559541                    setEndingSelection(visibleStart.next());
    560             }
    561             else {
     542            } else {
    562543                insertParagraphSeparator();
    563544                setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY));
     
    577558        startPos = Position(startBlock, 0);
    578559
    579     if (isTabSpanTextNode(startPos.node()))
    580         startPos = positionOutsideTabSpan(startPos);
    581     else
    582         startPos = positionOutsideContainingSpecialElement(startPos);
     560    // paste into run of tabs splits the tab span
     561    startPos = positionOutsideTabSpan(startPos);
     562   
     563    // paste at start or end of link goes outside of link
     564    startPos = positionAvoidingSpecialElementBoundary(startPos);
    583565
    584566    Frame *frame = document()->frame();
     
    598580    if (!linePlaceholder) {
    599581        Position downstream = startPos.downstream();
    600         downstream = positionOutsideContainingSpecialElement(downstream);
     582        // NOTE: the check for brTag offset 0 could be false negative after
     583        // positionAvoidingSpecialElementBoundary() because "downstream" is
     584        // now a "second deepest position"
     585        downstream = positionAvoidingSpecialElementBoundary(downstream);
    601586        if (downstream.node()->hasTagName(brTag) && downstream.offset() == 0 &&
    602587            m_fragment.hasInterchangeNewlineAtEnd() &&
     
    636621    Position insertionPos = startPos;
    637622
    638     // step 1: merge content into the start block, if that is needed
    639     if (mergeStart && !isFirstVisiblePositionInSpecialElementInFragment(Position(m_fragment.mergeStartNode(), 0))) {
     623    // step 1: merge content into the start block
     624    if (mergeStart) {
    640625        NodeImpl *refNode = m_fragment.mergeStartNode();
    641626        if (refNode) {
     
    763748            }
    764749        }
    765     }
    766     else {
     750    } else {
    767751        if (m_lastNodeInserted && m_lastNodeInserted->hasTagName(brTag) && !document()->inStrictMode()) {
    768752            updateLayout();
     
    778762        }
    779763
    780         if (moveNodesAfterEnd && !isLastVisiblePositionInSpecialElement(Position(m_lastNodeInserted.get(), maxRangeOffset(m_lastNodeInserted.get())))) {
     764        if (moveNodesAfterEnd) {
    781765            updateLayout();
    782766            QValueList<NodeDesiredStyle> styles;
    783767            QPtrList<NodeImpl> blocks;
    784             NodeImpl *node = beyondEndNode;
     768            NodeImpl *node = beyondEndNode->enclosingInlineElement();
    785769            NodeImpl *refNode = m_lastNodeInserted.get();
    786770            while (node) {
  • trunk/WebCore/khtml/editing/visible_position.cpp

    r12163 r12358  
    208208        return false;
    209209
    210     if (renderer->isReplaced())
     210    if (isTableElement(pos.node()) || editingIgnoresContent(pos.node()))
    211211        return pos.offset() == 0 || pos.offset() == maxDeepOffset(pos.node());
    212212
    213     if (renderer->isBR()) {
    214         if (pos.offset() == 0) {
    215             InlineBox* box = static_cast<RenderText*>(renderer)->firstTextBox();
    216             if (box) {
    217                 // return true for offset 0 into BR element on a line by itself
    218                 RootInlineBox* root = box->root();
    219                 if (root)
    220                     return root->firstLeafChild() == box && root->lastLeafChild() == box;
    221             }
    222         }   
    223         return false;
    224     }
     213    if (renderer->isBR())
     214        return pos.offset() == 0;
    225215   
    226216    // True if at a rendered offset inside a text node
     
    330320        m_deepPosition.node()->showTreeAndMark(m_deepPosition.node(), "*", NULL, NULL);
    331321}
     322
     323void showTree(const VisiblePosition *vpos)
     324{
     325    if (vpos)
     326        vpos->showTree();
     327}
     328
     329void showTree(const VisiblePosition &vpos)
     330{
     331    vpos.showTree();
     332}
     333
    332334#endif
    333335
    334336PassRefPtr<RangeImpl> makeRange(const VisiblePosition &start, const VisiblePosition &end)
    335337{
    336     Position s = start.deepEquivalent().equivalentRangeCompliantPosition();
    337     Position e = end.deepEquivalent().equivalentRangeCompliantPosition();
     338    Position s = rangeCompliantEquivalent(start);
     339    Position e = rangeCompliantEquivalent(end);
    338340    return new RangeImpl(s.node()->getDocument(), s.node(), s.offset(), e.node(), e.offset());
    339341}
     
    355357    if (!r)
    356358        return false;
    357     Position p = visiblePosition.deepEquivalent().equivalentRangeCompliantPosition();
     359    Position p = rangeCompliantEquivalent(visiblePosition);
    358360    int code = 0;
    359361    r->setStart(p.node(), p.offset(), code);
     
    365367    if (!r)
    366368        return false;
    367     Position p = visiblePosition.deepEquivalent().equivalentRangeCompliantPosition();
     369    Position p = rangeCompliantEquivalent(visiblePosition);
    368370    int code = 0;
    369371    r->setEnd(p.node(), p.offset(), code);
  • trunk/WebCore/khtml/editing/visible_position.h

    r12101 r12358  
    133133bool isFirstVisiblePositionInNode(const VisiblePosition &, const DOM::NodeImpl *);
    134134bool isLastVisiblePositionInNode(const VisiblePosition &, const DOM::NodeImpl *);
    135 
     135#ifndef NDEBUG
     136void showTree(const VisiblePosition *vpos);
     137void showTree(const VisiblePosition &vpos);
     138#endif
    136139} // namespace khtml
    137140
  • trunk/WebCore/khtml/editing/visible_units.cpp

    r12263 r12358  
    3030
    3131#include "htmlnames.h"
     32#include "htmlediting.h"
    3233#include "helper.h"
    3334#include "InlineTextBox.h"
     
    7172    int exception = 0;
    7273    searchRange->setStartBefore(boundary, exception);
    73     Position end(pos.equivalentRangeCompliantPosition());
     74    Position end(rangeCompliantEquivalent(pos));
    7475    searchRange->setEnd(end.node(), end.offset(), exception);
    7576    SimplifiedBackwardsTextIterator it(searchRange.get());
     
    146147
    147148    RefPtr<RangeImpl> searchRange(d->createRange());
    148     Position start(pos.equivalentRangeCompliantPosition());
     149    Position start(rangeCompliantEquivalent(pos));
    149150    int exception = 0;
    150151    searchRange->setStart(start.node(), start.offset(), exception);
     
    295296        return VisiblePosition();
    296297   
     298    // Generated content (e.g. list markers and CSS :before and :after
     299    // pseudoelements) have no corresponding DOM element, and so cannot be
     300    // represented by a VisiblePosition.  Use whatever follows instead.
    297301    InlineBox *startBox = rootBox->firstLeafChild();
    298     if (!startBox)
    299         return VisiblePosition();
    300 
    301     RenderObject *startRenderer = startBox->object();
    302     if (!startRenderer)
    303         return VisiblePosition();
    304 
    305     NodeImpl *startNode = startRenderer->element();
    306     if (!startNode)
    307         return VisiblePosition();
    308 
     302    NodeImpl *startNode;
     303    while (1) {
     304        if (!startBox)
     305            return VisiblePosition();
     306
     307        RenderObject *startRenderer = startBox->object();
     308        if (!startRenderer)
     309            return VisiblePosition();
     310
     311        startNode = startRenderer->element();
     312        if (startNode)
     313            break;
     314       
     315        startBox = startBox->nextLeafChild();
     316    }
     317   
    309318    int startOffset = 0;
    310319    if (startBox->isInlineTextBox()) {
     
    322331        return VisiblePosition();
    323332   
     333    // Generated content (e.g. list markers and CSS :before and :after
     334    // pseudoelements) have no corresponding DOM element, and so cannot be
     335    // represented by a VisiblePosition.  Use whatever precedes instead.
     336    NodeImpl *endNode;
    324337    InlineBox *endBox = rootBox->lastLeafChild();
    325     if (!endBox)
    326         return VisiblePosition();
    327 
    328     RenderObject *endRenderer = endBox->object();
    329     if (!endRenderer)
    330         return VisiblePosition();
    331 
    332     NodeImpl *endNode = endRenderer->element();
    333     if (!endNode)
    334         return VisiblePosition();
    335 
     338    while (1) {
     339        if (!endBox)
     340            return VisiblePosition();
     341
     342        RenderObject *endRenderer = endBox->object();
     343        if (!endRenderer)
     344            return VisiblePosition();
     345
     346        endNode = endRenderer->element();
     347        if (endNode)
     348            break;
     349       
     350        endBox = endBox->prevLeafChild();
     351    }
     352   
    336353    int endOffset = 1;
    337354    if (endNode->hasTagName(brTag)) {
     
    450467        // block and find the first root line box in that block.
    451468        NodeImpl *startBlock = node->enclosingBlockFlowElement();
    452         NodeImpl *n = node->nextEditable();
     469        NodeImpl *n = node->nextEditable(p.offset());
    453470        while (n && startBlock == n->enclosingBlockFlowElement())
    454471            n = n->nextEditable();
     
    545562    int offset = p.offset();
    546563
    547     for (NodeImpl *n = startNode; n; n = n->traversePreviousNodePostOrder(startBlock)) {
     564    NodeImpl *n = startNode;
     565    while (n) {
    548566        RenderObject *r = n->renderer();
    549         if (!r)
     567        if (!r) {
     568            n = n->traversePreviousNodePostOrder(startBlock);
    550569            continue;
     570        }
    551571        RenderStyle *style = r->style();
    552         if (style->visibility() != VISIBLE)
     572        if (style->visibility() != VISIBLE) {
     573            n = n->traversePreviousNodePostOrder(startBlock);
    553574            continue;
    554         if (r->isBR() || r->isBlockFlow())
     575        }
     576        // FIXME: isBlockFlow should not exclude non-inline tables
     577        if (r->isBR() || r->isBlockFlow() || (r->isTable() && !r->isInline()))
    555578            break;
    556579        if (r->isText()) {
     
    568591            node = n;
    569592            offset = 0;
    570         } else if (r->isReplaced()) {
     593            n = n->traversePreviousNodePostOrder(startBlock);
     594        } else if (editingIgnoresContent(n) || isTableElement(n)) {
    571595            node = n;
    572596            offset = 0;
    573         }
     597            n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock);
     598        } else
     599            n = n->traversePreviousNodePostOrder(startBlock);
    574600    }
    575601
     
    590616    int offset = p.offset();
    591617
    592     for (NodeImpl *n = startNode; n; n = n->traverseNextNode(stayInsideBlock)) {
     618    NodeImpl *n = startNode;
     619    while (n) {
    593620        if (n->isContentEditable() != startNode->isContentEditable())
    594621            break;
    595622        RenderObject *r = n->renderer();
    596         if (!r)
     623        if (!r) {
     624            n = n->traverseNextNode(stayInsideBlock);
    597625            continue;
     626        }
    598627        RenderStyle *style = r->style();
    599         if (style->visibility() != VISIBLE)
     628        if (style->visibility() != VISIBLE) {
     629            n = n->traverseNextNode(stayInsideBlock);
    600630            continue;
    601            
    602         if (r->isBR() || r->isBlockFlow())
     631        }
     632       
     633        // FIXME: isBlockFlow should not exclude non-inline tables
     634        if (r->isBR() || r->isBlockFlow() || (r->isTable() && !r->isInline()))
    603635            break;
    604636           
     
    617649            node = n;
    618650            offset = r->caretMaxOffset();
    619         } else if (r->isReplaced()) {
     651            n = n->traverseNextNode(stayInsideBlock);
     652        } else if (editingIgnoresContent(n) || isTableElement(n)) {
    620653            node = n;
    621             offset = 1;
    622         }
     654            offset = maxDeepOffset(n);
     655            n = n->traverseNextSibling(stayInsideBlock);
     656        } else
     657            n = n->traverseNextNode(stayInsideBlock);
    623658    }
    624659
  • trunk/WebCore/khtml/xml/ContainerNodeImpl.cpp

    r12343 r12358  
    622622    while(o) {
    623623        p = o;
    624         if(o->firstChild())
     624        if (o->firstChild())
    625625            o = o->firstChild();
    626626        else if(o->nextSibling())
     
    641641        if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) {
    642642                // do nothing - skip unrendered whitespace that is a child or next sibling of the anchor
    643         }
    644         else if((o->isText() && !o->isBR()) || o->isReplaced()) {
     643        } else if ((o->isText() && !o->isBR()) || o->isReplaced()) {
    645644            o->container()->absolutePosition( xPos, yPos );
    646645            if (o->isText() && static_cast<RenderText *>(o)->firstTextBox()) {
    647646                xPos += static_cast<RenderText *>(o)->minXPos();
    648647                yPos += static_cast<RenderText *>(o)->firstTextBox()->root()->topOverflow();
    649             }
    650             else {
     648            } else {
    651649                xPos += o->xPos();
    652650                yPos += o->yPos();
     
    809807}
    810808
    811 NodeImpl *ContainerNodeImpl::childNode(unsigned index)
     809NodeImpl *ContainerNodeImpl::childNode(unsigned index) const
    812810{
    813811    unsigned i;
  • trunk/WebCore/khtml/xml/ContainerNodeImpl.h

    r12338 r12358  
    5353    virtual void setHovered(bool = true);
    5454    virtual unsigned childNodeCount() const;
    55     virtual NodeImpl* childNode(unsigned index);
     55    virtual NodeImpl* childNode(unsigned index) const;
     56
    5657    virtual void insertedIntoDocument();
    5758    virtual void removedFromDocument();
  • trunk/WebCore/khtml/xml/NodeImpl.cpp

    r12321 r12358  
    941941}
    942942
    943 NodeImpl *NodeImpl::childNode(unsigned /*index*/)
     943NodeImpl *NodeImpl::childNode(unsigned /*index*/) const
    944944{
    945945    return 0;
     
    994994        return n;
    995995    }
    996     else if (parentNode()) {
    997         return parentNode();
    998     }
    999     else {
    1000         return 0;
    1001     }
     996   
     997    return parentNode();
    1002998}
    1003999
     
    12911287            return node;
    12921288        node = node->previousLeafNode();
     1289    }
     1290    return 0;
     1291}
     1292
     1293// Offset specifies the child node to start at.  If it is past
     1294// the last child, it specifies to start at next sibling.
     1295NodeImpl *NodeImpl::nextEditable(int offset) const
     1296{
     1297    assert(offset>=0);
     1298    NodeImpl *node;
     1299    if (hasChildNodes())
     1300        node = (offset >= (int)childNodeCount()) ? nextSibling() : childNode(offset)->nextLeafNode();
     1301    else
     1302        node = nextLeafNode();
     1303    while (node) {
     1304        if (node->isContentEditable())
     1305            return node;
     1306        node = node->nextLeafNode();
    12931307    }
    12941308    return 0;
     
    19431957}
    19441958
     1959void showTree(const NodeImpl *node)
     1960{
     1961    if (node)
     1962        node->showTree();
     1963}
     1964
    19451965void NodeImpl::showTreeAndMark(NodeImpl * markedNode1, const char * markedLabel1, NodeImpl * markedNode2, const char * markedLabel2) const
    19461966{
  • trunk/WebCore/khtml/xml/NodeImpl.h

    r12329 r12358  
    303303    virtual bool childTypeAllowed(unsigned short /*type*/) { return false; }
    304304    virtual unsigned childNodeCount() const;
    305     virtual NodeImpl* childNode(unsigned index);
     305    virtual NodeImpl* childNode(unsigned index) const;
    306306
    307307    /**
     
    331331    NodeImpl* traversePreviousNodePostOrder(const NodeImpl *stayWithin = 0) const;
    332332
     333    /**
     334     * Finds previous or next editable leaf node.
     335     */
    333336    NodeImpl* previousEditable() const;
    334337    NodeImpl* nextEditable() const;
     338    NodeImpl* nextEditable(int offset) const;
    335339
    336340    RenderObject* renderer() const { return m_renderer; }
     
    506510#ifndef NDEBUG
    507511
     512void showTree(const NodeImpl *node);
     513
    508514extern int gEventDispatchForbidden;
    509515inline void forbidEventDispatch() { ++gEventDispatchForbidden; }
  • trunk/WebCore/khtml/xml/dom_position.cpp

    r12263 r12358  
    385385        // NOTE: caretMaxOffset() can be less than childNodeCount()!!
    386386        // e.g. SELECT and APPLET nodes
    387         if (renderer->isReplaced() || renderer->isBR()) {
    388             if (currentOffset >= renderer->caretMaxOffset())
    389                 return Position(currentNode, renderer->caretMaxOffset());
     387        if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) {
     388            int maxOffset = maxDeepOffset(currentNode);
     389            if (currentOffset >= maxOffset)
     390                return Position(currentNode, maxOffset);
    390391            continue;
    391392        }
     
    498499
    499500        // return position before replaced or BR elements
    500         if (renderer->isReplaced() || renderer->isBR()) {
     501        if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) {
    501502            if (currentOffset <= renderer->caretMinOffset())
    502503                return Position(currentNode, renderer->caretMinOffset());
     
    533534}
    534535
    535 Position Position::equivalentRangeCompliantPosition() const
    536 {
    537     if (isNull())
    538         return Position();
    539 
    540     // Make sure that 0 <= constrainedOffset <= num kids, otherwise using this Position
    541     // in DOM calls can result in exceptions.
    542     int maxOffset = node()->isTextNode() ? static_cast<TextImpl *>(node())->length(): node()->childNodeCount();
    543     int constrainedOffset = offset() <= 0 ? 0 : kMin(maxOffset, offset());
    544 
    545     if (!node()->parentNode())
    546         return Position(node(), constrainedOffset);
    547 
    548     RenderObject *renderer = node()->renderer();
    549     if (!renderer)
    550         return Position(node(), constrainedOffset);
    551        
    552     if (!renderer->isReplaced() && !renderer->isBR())
    553         return Position(node(), constrainedOffset);
    554    
    555     int o = offset();
    556     const NodeImpl *n = node();
    557     while ((n = n->previousSibling()))
    558         o++;
    559    
    560     // Make sure that 0 <= constrainedOffset <= num kids, as above.
    561     NodeImpl *parent = node()->parentNode();
    562     maxOffset = parent->isTextNode() ? static_cast<TextImpl *>(parent)->length(): parent->childNodeCount();
    563     constrainedOffset = o <= 0 ? 0 : kMin(maxOffset, o);
    564     return Position(parent, constrainedOffset);
    565 }
    566 
    567536bool Position::inRenderedContent() const
    568537{
     
    578547
    579548    // FIXME: This check returns false for a <br> at the end of a line!
    580     if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
     549    if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox())
    581550        return offset() == 0;
    582     }
    583     else if (renderer->isText()) {
     551
     552    if (renderer->isText()) {
    584553        RenderText *textRenderer = static_cast<RenderText *>(renderer);
    585554        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
    586             if (offset() >= box->m_start && offset() <= box->m_start + box->m_len) {
     555            if (offset() >= box->m_start && offset() <= box->m_start + box->m_len)
    587556                return true;
    588             }
    589             else if (offset() < box->m_start) {
     557            if (offset() < box->m_start) {
    590558                // The offset we're looking for is before this node
    591559                // this means the offset must be in content that is
     
    594562            }
    595563        }
    596     }
    597     else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) {
     564    } else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) {
    598565        // return true for replaced elements, for inline flows if they have a line box
    599566        // and for blocks if they are empty
     
    823790        m_node->showTree();
    824791}
     792
     793void showTree(const Position &pos)
     794{
     795    pos.showTree();
     796}
     797
     798void showTree(const Position *pos)
     799{
     800    if (pos)
     801        pos->showTree();
     802}
    825803#endif
    826804
  • trunk/WebCore/khtml/xml/dom_position.h

    r12005 r12358  
    120120Position endPosition(const RangeImpl *);
    121121
     122#ifndef NDEBUG
     123void showTree(const Position &pos);
     124void showTree(const Position *pos);
     125#endif
     126
    122127} // namespace DOM
    123128
  • trunk/WebCore/rendering/RenderContainer.cpp

    r12345 r12358  
    3030#include "render_image.h"
    3131#include "render_canvas.h"
     32#include "render_list.h"
    3233#include "DocumentImpl.h"
    3334#include "xml/dom_position.h"
     
    7980}
    8081
     82static void updateListMarkerNumbers(RenderObject *child)
     83{
     84    for (RenderObject *r = child; r && r->isListItem(); r = r->nextSibling())
     85        static_cast<RenderListItem *>(r)->resetMarkerValue();
     86}
     87
    8188void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild)
    8289{
     
    9097    if(!newChild->isText() && !newChild->isReplaced()) {
    9198        switch(newChild->style()->display()) {
     99        case LIST_ITEM:
     100            updateListMarkerNumbers(beforeChild);
     101            break;
    92102        case INLINE:
    93103        case BLOCK:
    94104        case INLINE_BLOCK:
    95         case LIST_ITEM:
    96105        case RUN_IN:
    97106        case COMPACT:
     
    180189        if (oldChild->isSelectionBorder())
    181190            canvas()->clearSelection();
     191
     192        // renumber ordered lists
     193        if (oldChild->isListItem())
     194            updateListMarkerNumbers(oldChild->nextSibling());
    182195    }
    183196   
  • trunk/WebCore/rendering/render_canvas.cpp

    r12329 r12358  
    329329        if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
    330330            // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well.
    331             assert(!selectedObjects.get(os));
     331//          assert(!selectedObjects.get(os));
    332332            selectedObjects.set(os, new SelectionInfo(os));
    333333            RenderBlock* cb = os->containingBlock();
  • trunk/WebCore/rendering/render_list.cpp

    r12340 r12358  
    182182    KHTMLAssert(m_marker);
    183183
    184     if(predefVal != -1)
     184    if (predefVal != -1)
    185185        m_marker->m_value = predefVal;
    186     else if(!previousSibling())
     186    else if (!previousSibling())
    187187        m_marker->m_value = 1;
    188188    else {
    189189        RenderObject *o = previousSibling();
    190         while ( o && (!o->isListItem() || o->style()->listStyleType() == LNONE) )
     190        while (o && (!o->isListItem() || o->style()->listStyleType() == LNONE))
    191191            o = o->previousSibling();
    192         if( o && o->isListItem() && o->style()->listStyleType() != LNONE ) {
     192        if (o && o->isListItem() && o->style()->listStyleType() != LNONE) {
    193193            RenderListItem *item = static_cast<RenderListItem *>(o);
    194194            m_marker->m_value = item->value() + 1;
    195         }
    196         else
     195        } else
    197196            m_marker->m_value = 1;
    198197    }
     198}
     199
     200bool RenderListItem::isEmpty() const
     201{
     202    return lastChild() == m_marker;
    199203}
    200204
     
    205209        return 0;
    206210       
    207     for (RenderObject* currChild = firstChild;
    208          currChild; currChild = currChild->nextSibling()) {
     211    for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
    209212        if (currChild == marker)
    210213            continue;
     
    231234}
    232235
     236void RenderListItem::resetMarkerValue()
     237{
     238    m_marker->m_value = -1;
     239    m_marker->setNeedsLayoutAndMinMaxRecalc();
     240}
     241
    233242void RenderListItem::updateMarkerLocation()
    234243{
     
    247256                lineBoxParent = this;
    248257        }
    249         if (markerPar != lineBoxParent)
    250         {
     258       
     259        if (markerPar != lineBoxParent || !m_marker->minMaxKnown()) {
    251260            if (markerPar)
    252261                markerPar->removeChild(m_marker);
  • trunk/WebCore/rendering/render_list.h

    r12263 r12358  
    6161    virtual short lineHeight(bool b, bool isRootLineBox=false) const;
    6262    virtual short baselinePosition(bool b, bool isRootLineBox=false) const;
     63
     64    virtual bool isListMarker() const { return true; }
    6365   
    64     virtual bool isListMarker() const { return true; }
    65 
    6666    CachedImage* listImage() const { return m_listImage; }
    6767   
     
    108108    void calcListValue();
    109109   
     110    virtual bool isEmpty() const;
    110111    virtual void paint(PaintInfo& i, int xoff, int yoff);
    111112
     
    120121    bool notInList() const { return _notInList; }
    121122
     123    void resetMarkerValue();
    122124    QString markerStringValue() { if (m_marker) return m_marker->m_item; return ""; }
    123125
  • trunk/WebCore/rendering/render_object.cpp

    r12340 r12358  
    258258    if (firstChild())
    259259        return firstChild();
    260     else if (nextSibling())
     260
     261    if (nextSibling())
    261262        return nextSibling();
    262     else {
    263         const RenderObject *r = this;
    264         while (r && !r->nextSibling())
    265             r = r->parent();
    266         if (r)
    267             return r->nextSibling();
    268     }
     263
     264    const RenderObject *r = this;
     265    while (r && !r->nextSibling())
     266        r = r->parent();
     267    if (r)
     268        return r->nextSibling();
     269
    269270    return 0;
    270271}
     
    278279        return r;
    279280    }
    280     else if (parent()) {
    281         return parent();
    282     }
    283     else {
    284         return 0;
    285     }
     281
     282    return parent();
    286283}
    287284
     
    16231620        element()->showTree();
    16241621}
     1622
     1623void showTree(const RenderObject *ro)
     1624{
     1625    if (ro)
     1626        ro->showTree();
     1627}
    16251628#endif
    16261629
  • trunk/WebCore/rendering/render_object.h

    r12340 r12358  
    171171   
    172172    virtual int getBaselineOfFirstLineBox() const { return -1; }
    173     virtual int getBaselineOfLastLineBox() const { return -1; }
     173    virtual int getBaselineOfLastLineBox() const { return -1; }
     174    virtual bool isEmpty() const { return firstChild() == 0; }
    174175   
    175176    // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline
     
    225226    virtual void dump(QTextStream *stream, QString ind = "") const;
    226227    void showTree() const;
     228    static void showTree(const RenderObject *ro);
    227229#endif
    228230
Note: See TracChangeset for help on using the changeset viewer.