Changeset 12358 in webkit
- Timestamp:
- Jan 25, 2006, 1:05:02 PM (19 years ago)
- Location:
- trunk/WebCore
- Files:
-
- 27 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog
r12354 r12358 1 2006-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 1 139 2006-01-25 Timothy Hatcher <timothy@apple.com> 2 140 … … 7 145 (QLineEdit::sizeForCharacterWidth): 8 146 (QLineEdit::baselinePosition): 147 9 148 10 149 2006-01-24 Darin Adler <darin@apple.com> -
trunk/WebCore/khtml/editing/Selection.cpp
r12152 r12358 80 80 // the conventions of text editors tested, which make style determinations based 81 81 // on the character before the caret, if any. 82 s = m_start.upstream().equivalentRangeCompliantPosition();82 s = rangeCompliantEquivalent(m_start.upstream()); 83 83 e = s; 84 } 85 else { 84 } else { 86 85 // If the selection is a range, select the minimum range that encompasses the selection. 87 86 // Again, this is to match the conventions of text editors tested, which make style … … 105 104 e = tmp; 106 105 } 107 s = s.equivalentRangeCompliantPosition();108 e = e.equivalentRangeCompliantPosition();106 s = rangeCompliantEquivalent(s); 107 e = rangeCompliantEquivalent(e); 109 108 } 110 109 … … 221 220 break; 222 221 } 223 case LINE_BOUNDARY: 222 case LINE_BOUNDARY: { 224 223 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); 225 224 m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); 226 225 break; 226 } 227 227 case PARAGRAPH: { 228 228 VisiblePosition pos(m_start, m_affinity); … … 233 233 // Include the space after the end of the paragraph in the selection. 234 234 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 } 236 243 break; 237 244 } -
trunk/WebCore/khtml/editing/SelectionController.cpp
r12230 r12358 268 268 { 269 269 VisiblePosition pos(m_sel.extent(), m_sel.affinity()); 270 270 271 switch (granularity) { 271 272 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(); 273 281 break; 274 282 case WORD: … … 844 852 m_sel.start().node()->showTreeAndMark(m_sel.start().node(), "S", m_sel.end().node(), "E"); 845 853 } 854 855 void showTree(const SelectionController &sel) 856 { 857 sel.showTree(); 858 } 859 860 void showTree(const SelectionController *sel) 861 { 862 if (sel) 863 sel->showTree(); 864 } 846 865 #endif 847 866 -
trunk/WebCore/khtml/editing/composite_edit_command.cpp
r12233 r12358 181 181 removeFullySelectedNode(remove); 182 182 } 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); 187 193 } 188 194 … … 276 282 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos) 277 283 { 278 ASSERT(isTabSpanTextNode(pos.node())); 284 if (!isTabSpanTextNode(pos.node())) 285 return pos; 279 286 280 287 NodeImpl *tabSpan = tabSpanNode(pos.node()); … … 487 494 // append the placeholder to make sure it follows 488 495 // any unrendered blocks 496 // FIXME: use contentHeight() instead so to not be confused by bordere, e.g. 489 497 if (renderer->height() == 0) { 490 498 return appendBlockPlaceholder(node); -
trunk/WebCore/khtml/editing/delete_selection_command.cpp
r12152 r12358 47 47 using namespace HTMLNames; 48 48 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 63 49 static void debugPosition(const char *prefix, const Position &pos) 64 50 { … … 81 67 } 82 68 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 70 static Position positionBeforePossibleContainingSpecialElement(const Position &pos, NodeImpl **containingSpecialElement) 71 { 72 if (isFirstVisiblePositionInSpecialElement(pos)) 73 return positionBeforeContainingSpecialElement(pos, containingSpecialElement); 74 75 return pos; 76 } 77 78 static Position positionAfterPossibleContainingSpecialElement(const Position &pos, NodeImpl **containingSpecialElement) 79 { 80 if (isLastVisiblePositionInSpecialElement(pos)) 81 return positionAfterContainingSpecialElement(pos, containingSpecialElement); 82 83 return pos; 84 } 85 #endif 100 86 101 87 DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete) … … 126 112 } 127 113 114 void 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 128 149 void DeleteSelectionCommand::initializePositionData() 129 150 { … … 131 152 // Handle setting some basic positions 132 153 // 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 143 156 // 144 157 // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection … … 158 171 bool hasLeadingWhitespaceBeforeAdjustment = m_upstreamStart.leadingWhitespacePosition(m_selectionToDelete.affinity(), true).isNotNull(); 159 172 if (!skipSmartDelete && hasLeadingWhitespaceBeforeAdjustment) { 160 VisiblePosition visiblePos = VisiblePosition( start, m_selectionToDelete.affinity()).previous();173 VisiblePosition visiblePos = VisiblePosition(m_upstreamStart, VP_DEFAULT_AFFINITY).previous(); 161 174 pos = visiblePos.deepEquivalent(); 162 175 // Expand out one character upstream for smart delete and recalculate … … 172 185 // Expand out one character downstream for smart delete and recalculate 173 186 // positions based on this change. 174 pos = VisiblePosition( end, m_selectionToDelete.affinity()).next().deepEquivalent();187 pos = VisiblePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY).next().deepEquivalent(); 175 188 m_upstreamEnd = pos.upstream(); 176 189 m_downstreamEnd = pos.downstream(); … … 192 205 // This is one of the tests that determines if block merging of content needs to be done. 193 206 // 194 VisiblePosition visibleEnd( end, m_selectionToDelete.affinity());207 VisiblePosition visibleEnd(m_downstreamEnd, VP_DEFAULT_AFFINITY); 195 208 if (isEndOfParagraph(visibleEnd)) { 196 209 Position previousLineStart = previousLinePosition(visibleEnd, 0).deepEquivalent(); … … 310 323 if (!m_startBlock->isAncestor(m_endBlock.get()) && !isStartOfBlock(visibleEnd) && endAtEndOfBlock) { 311 324 // 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(); 313 327 startOffset = 0; 314 328 } 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)) { 318 330 // Move the start node to the next node in the tree since the startOffset is equal to 319 331 // or beyond the start node's caretMaxOffset This means there is nothing visible to delete. … … 500 512 501 513 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 506 519 // Now that we are about to add content, check to see if a placeholder element 507 520 // can be removed. … … 511 524 NodeImpl *node = startNode->enclosingInlineElement(); 512 525 513 // Insert after the subtree containing d estNode526 // Insert after the subtree containing dstNode 514 527 NodeImpl *refNode = dstNode->enclosingInlineElement(); 515 528 … … 553 566 // This may not be ideal, but it is better than nothing. 554 567 updateLayout(); 555 if (!startBlock->renderer() || !startBlock->renderer()->firstChild()) {568 if (!startBlock->renderer() || startBlock->renderer()->isEmpty()) { 556 569 removeNode(startBlock); 557 570 updateLayout(); -
trunk/WebCore/khtml/editing/delete_selection_command.h
r12152 r12358 43 43 virtual bool preservesTypingStyle() const; 44 44 45 void initializeStartEnd(); 45 46 void initializePositionData(); 46 47 void saveTypingStyleState(); -
trunk/WebCore/khtml/editing/htmlediting.cpp
r12233 r12358 30 30 #include "dom2_range.h" 31 31 #include "dom_textimpl.h" 32 #include "HTMLElementImpl.h" 32 33 #include "html_interchange.h" 33 34 #include "htmlnames.h" … … 44 45 bool isAtomicNode(const NodeImpl *node) 45 46 { 46 return node && (!node->hasChildNodes() || node->renderer() && node->renderer()->isWidget()); 47 return node && (!node->hasChildNodes() || editingIgnoresContent(node)); 48 } 49 50 bool 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() 80 Position 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 121 Position rangeCompliantEquivalent(const VisiblePosition& vpos) 122 { 123 return rangeCompliantEquivalent(vpos.deepEquivalent()); 47 124 } 48 125 49 126 // 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 creates51 // technically invalid DOM Positions. Be sure to call equivalentRangeCompliantPosition127 // 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 52 129 // on a Position before using it to create a DOM Range, or an exception will be thrown. 53 130 int maxDeepOffset(const NodeImpl *node) 54 131 { 55 if ( node->isTextNode())56 return static_cast<const TextImpl*>(node)->length();132 if (offsetInCharacters(node->nodeType())) 133 return node->maxOffset(); 57 134 58 135 if (node->hasChildNodes()) 59 136 return node->childNodeCount(); 60 137 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)) 63 140 return 1; 64 141 … … 122 199 } 123 200 201 // FIXME: Why use this instead of maxDeepOffset??? 124 202 static int maxRangeOffset(NodeImpl *n) 125 203 { … … 133 211 } 134 212 213 #if 1 214 // FIXME: need to dump this 135 215 bool isSpecialElement(const NodeImpl *n) 136 216 { … … 168 248 169 249 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; 171 254 return false; 255 } 172 256 if (n->rootEditableElement() == NULL) 173 257 return false; … … 179 263 } 180 264 181 Position positionBeforeNode(const NodeImpl *node) 182 { 183 return Position(node->parentNode(), node->nodeIndex()); 184 } 185 186 Position positionBeforeContainingSpecialElement(const Position& pos) 265 Position positionBeforeContainingSpecialElement(const Position& pos, NodeImpl **containingSpecialElement) 187 266 { 188 267 ASSERT(isFirstVisiblePositionInSpecialElement(pos)); … … 193 272 194 273 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; 196 278 break; 279 } 197 280 if (n->rootEditableElement() == NULL) 198 281 break; … … 202 285 203 286 ASSERT(outermostSpecialElement); 204 287 if (containingSpecialElement) 288 *containingSpecialElement = outermostSpecialElement; 289 205 290 Position result = positionBeforeNode(outermostSpecialElement); 206 291 if (result.isNull() || !result.node()->rootEditableElement()) … … 214 299 // make sure to get a range-compliant version of the position 215 300 // 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)); 217 302 218 303 VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM); 219 304 220 305 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); 223 309 if (n->rootEditableElement() == NULL) 224 310 return false; … … 230 316 } 231 317 232 Position positionAfterNode(const NodeImpl *node) 233 { 234 return Position(node->parentNode(), node->nodeIndex() + 1); 235 } 236 237 Position positionAfterContainingSpecialElement(const Position& pos) 318 Position positionAfterContainingSpecialElement(const Position& pos, NodeImpl **containingSpecialElement) 238 319 { 239 320 ASSERT(isLastVisiblePositionInSpecialElement(pos)); … … 241 322 // make sure to get a range-compliant version of the position 242 323 // 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)); 245 325 VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM); 246 326 … … 248 328 249 329 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; 251 334 break; 335 } 252 336 if (n->rootEditableElement() == NULL) 253 337 break; … … 257 341 258 342 ASSERT(outermostSpecialElement); 343 if (containingSpecialElement) 344 *containingSpecialElement = outermostSpecialElement; 259 345 260 346 Position result = positionAfterNode(outermostSpecialElement); … … 265 351 } 266 352 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 }353 Position 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); 274 360 275 361 return pos; 362 } 363 #endif 364 365 Position positionBeforeNode(const NodeImpl *node) 366 { 367 return Position(node->parentNode(), node->nodeIndex()); 368 } 369 370 Position positionAfterNode(const NodeImpl *node) 371 { 372 return Position(node->parentNode(), node->nodeIndex() + 1); 373 } 374 375 bool 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() 381 bool 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 389 bool 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 411 Position 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? 449 Position 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; 276 476 } 277 477 -
trunk/WebCore/khtml/editing/htmlediting.h
r12233 r12358 38 38 class NodeImpl; 39 39 class Position; 40 class VisiblePosition; 40 41 41 42 const unsigned short NON_BREAKING_SPACE = 0xa0; 42 43 43 int maxDeepOffset(const NodeImpl*); 44 bool isAtomicNode(const NodeImpl*); 44 Position rangeCompliantEquivalent(const Position& pos); 45 Position rangeCompliantEquivalent(const VisiblePosition& vpos); 46 int maxDeepOffset(const NodeImpl* node); 47 bool isAtomicNode(const NodeImpl* node); 48 bool editingIgnoresContent(const NodeImpl* node); 45 49 46 50 void rebalanceWhitespaceInTextNode(NodeImpl*, unsigned start, unsigned length); … … 67 71 bool isNodeRendered(const NodeImpl*); 68 72 bool isMailBlockquote(const NodeImpl*); 69 NodeImpl *nearestMailBlockquote(const NodeImpl*);73 NodeImpl* nearestMailBlockquote(const NodeImpl*); 70 74 71 75 //------------------------------------------------------------------------------------------ … … 74 78 ElementImpl *createBlockPlaceholderElement(DocumentImpl*); 75 79 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&); 80 bool isFirstVisiblePositionInSpecialElement(const Position& pos); 81 Position positionBeforeContainingSpecialElement(const Position& pos, NodeImpl** containingSpecialElement=0); 82 bool isLastVisiblePositionInSpecialElement(const Position& pos); 83 Position positionAfterContainingSpecialElement(const Position& pos, NodeImpl** containingSpecialElement=0); 84 Position positionOutsideContainingSpecialElement(const Position &pos, NodeImpl** containingSpecialElement=0); 85 86 bool isListElement(NodeImpl* n); 87 bool isTableElement(NodeImpl* n); 88 bool isFirstVisiblePositionAfterTableElement(const Position &pos); 89 Position positionBeforePrecedingTableElement(const Position &pos); 90 Position positionAvoidingSpecialElementBoundary(const Position &pos); 81 91 82 92 } -
trunk/WebCore/khtml/editing/insert_line_break_command.cpp
r12152 r12358 98 98 Position pos(selection.start().upstream()); 99 99 100 pos = position OutsideContainingSpecialElement(pos);100 pos = positionAvoidingSpecialElementBoundary(pos); 101 101 102 102 if (isTabSpanTextNode(pos.node())) { -
trunk/WebCore/khtml/editing/insert_paragraph_separator_command.cpp
r12313 r12358 118 118 } 119 119 120 pos = position OutsideContainingSpecialElement(pos);120 pos = positionAvoidingSpecialElementBoundary(pos); 121 121 122 122 calculateStyleBeforeInsertion(pos); … … 202 202 LOG(Editing, "insert paragraph separator: general case"); 203 203 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>. 206 206 if (!document()->inStrictMode()) { 207 207 Position upstreamPos = pos.upstream(); -
trunk/WebCore/khtml/editing/insert_text_command.cpp
r12152 r12358 62 62 // Prepare for text input by looking at the specified position. 63 63 // 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. 64 68 if (!pos.node()->isTextNode()) { 65 69 NodeImpl *textNode = document()->createEditingTextNode(""); … … 68 72 // Now insert the node in the right place 69 73 if (pos.node()->rootEditableElement() != NULL) { 70 LOG(Editing, "prepareForTextInsertion case 1");71 74 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()) { 75 76 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()) { 79 78 insertNodeAfter(nodeToInsert, pos.node()); 80 } 81 else 79 } else 82 80 ASSERT_NOT_REACHED(); 83 81 … … 122 120 else 123 121 startPosition = startPosition.upstream(); 124 startPosition = position OutsideContainingSpecialElement(startPosition);122 startPosition = positionAvoidingSpecialElementBoundary(startPosition); 125 123 126 124 if (text == "\t") { -
trunk/WebCore/khtml/editing/replace_selection_command.cpp
r12243 r12358 155 155 static bool isMailPasteAsQuotationNode(const NodeImpl *node) 156 156 { 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; 161 158 } 162 159 … … 388 385 prev = node; 389 386 } 390 } 391 else { 387 } else { 392 388 NodeImpl *block = node->enclosingBlockFlowElement(); 393 389 if (block != prev) { … … 459 455 } 460 456 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 put474 // somewhere editable in the future475 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 489 457 void ReplaceSelectionCommand::doApply() 490 458 { … … 509 477 // empty editable subtree, need to mergeStart so that fragment ends up 510 478 // inside the editable subtree rather than just before it 479 // FIXME: Reconcile comment versus mergeStart = false 511 480 mergeStart = false; 512 481 } else { 513 482 // merge if current selection starts inside a paragraph, or there is only one block and no interchange newline to add 514 483 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())); 517 485 518 486 // 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 line487 // <rdar://problem/4013642> Copied quoted word does not paste as a quote if pasted at the start of a line 520 488 // We need more powerful logic in this whole mergeStart code for this case to come out right without 521 489 // breaking other cases. 522 490 if (isStartOfParagraph(visibleStart) && isMailBlockquote(m_fragment.firstChild())) 523 491 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 } 524 506 } 525 507 … … 537 519 // delete the current range selection, or insert paragraph for caret selection, as needed 538 520 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); 540 523 updateLayout(); 541 524 visibleStart = VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY); … … 544 527 if (!isEndOfDocument(visibleStart)) 545 528 setEndingSelection(visibleStart.next()); 546 } 547 else { 529 } else { 548 530 insertParagraphSeparator(); 549 531 setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY)); … … 558 540 if (!isEndOfDocument(visibleStart)) 559 541 setEndingSelection(visibleStart.next()); 560 } 561 else { 542 } else { 562 543 insertParagraphSeparator(); 563 544 setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY)); … … 577 558 startPos = Position(startBlock, 0); 578 559 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); 583 565 584 566 Frame *frame = document()->frame(); … … 598 580 if (!linePlaceholder) { 599 581 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); 601 586 if (downstream.node()->hasTagName(brTag) && downstream.offset() == 0 && 602 587 m_fragment.hasInterchangeNewlineAtEnd() && … … 636 621 Position insertionPos = startPos; 637 622 638 // step 1: merge content into the start block , if that is needed639 if (mergeStart && !isFirstVisiblePositionInSpecialElementInFragment(Position(m_fragment.mergeStartNode(), 0))) {623 // step 1: merge content into the start block 624 if (mergeStart) { 640 625 NodeImpl *refNode = m_fragment.mergeStartNode(); 641 626 if (refNode) { … … 763 748 } 764 749 } 765 } 766 else { 750 } else { 767 751 if (m_lastNodeInserted && m_lastNodeInserted->hasTagName(brTag) && !document()->inStrictMode()) { 768 752 updateLayout(); … … 778 762 } 779 763 780 if (moveNodesAfterEnd && !isLastVisiblePositionInSpecialElement(Position(m_lastNodeInserted.get(), maxRangeOffset(m_lastNodeInserted.get())))) {764 if (moveNodesAfterEnd) { 781 765 updateLayout(); 782 766 QValueList<NodeDesiredStyle> styles; 783 767 QPtrList<NodeImpl> blocks; 784 NodeImpl *node = beyondEndNode ;768 NodeImpl *node = beyondEndNode->enclosingInlineElement(); 785 769 NodeImpl *refNode = m_lastNodeInserted.get(); 786 770 while (node) { -
trunk/WebCore/khtml/editing/visible_position.cpp
r12163 r12358 208 208 return false; 209 209 210 if ( renderer->isReplaced())210 if (isTableElement(pos.node()) || editingIgnoresContent(pos.node())) 211 211 return pos.offset() == 0 || pos.offset() == maxDeepOffset(pos.node()); 212 212 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; 225 215 226 216 // True if at a rendered offset inside a text node … … 330 320 m_deepPosition.node()->showTreeAndMark(m_deepPosition.node(), "*", NULL, NULL); 331 321 } 322 323 void showTree(const VisiblePosition *vpos) 324 { 325 if (vpos) 326 vpos->showTree(); 327 } 328 329 void showTree(const VisiblePosition &vpos) 330 { 331 vpos.showTree(); 332 } 333 332 334 #endif 333 335 334 336 PassRefPtr<RangeImpl> makeRange(const VisiblePosition &start, const VisiblePosition &end) 335 337 { 336 Position s = start.deepEquivalent().equivalentRangeCompliantPosition();337 Position e = end.deepEquivalent().equivalentRangeCompliantPosition();338 Position s = rangeCompliantEquivalent(start); 339 Position e = rangeCompliantEquivalent(end); 338 340 return new RangeImpl(s.node()->getDocument(), s.node(), s.offset(), e.node(), e.offset()); 339 341 } … … 355 357 if (!r) 356 358 return false; 357 Position p = visiblePosition.deepEquivalent().equivalentRangeCompliantPosition();359 Position p = rangeCompliantEquivalent(visiblePosition); 358 360 int code = 0; 359 361 r->setStart(p.node(), p.offset(), code); … … 365 367 if (!r) 366 368 return false; 367 Position p = visiblePosition.deepEquivalent().equivalentRangeCompliantPosition();369 Position p = rangeCompliantEquivalent(visiblePosition); 368 370 int code = 0; 369 371 r->setEnd(p.node(), p.offset(), code); -
trunk/WebCore/khtml/editing/visible_position.h
r12101 r12358 133 133 bool isFirstVisiblePositionInNode(const VisiblePosition &, const DOM::NodeImpl *); 134 134 bool isLastVisiblePositionInNode(const VisiblePosition &, const DOM::NodeImpl *); 135 135 #ifndef NDEBUG 136 void showTree(const VisiblePosition *vpos); 137 void showTree(const VisiblePosition &vpos); 138 #endif 136 139 } // namespace khtml 137 140 -
trunk/WebCore/khtml/editing/visible_units.cpp
r12263 r12358 30 30 31 31 #include "htmlnames.h" 32 #include "htmlediting.h" 32 33 #include "helper.h" 33 34 #include "InlineTextBox.h" … … 71 72 int exception = 0; 72 73 searchRange->setStartBefore(boundary, exception); 73 Position end( pos.equivalentRangeCompliantPosition());74 Position end(rangeCompliantEquivalent(pos)); 74 75 searchRange->setEnd(end.node(), end.offset(), exception); 75 76 SimplifiedBackwardsTextIterator it(searchRange.get()); … … 146 147 147 148 RefPtr<RangeImpl> searchRange(d->createRange()); 148 Position start( pos.equivalentRangeCompliantPosition());149 Position start(rangeCompliantEquivalent(pos)); 149 150 int exception = 0; 150 151 searchRange->setStart(start.node(), start.offset(), exception); … … 295 296 return VisiblePosition(); 296 297 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. 297 301 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 309 318 int startOffset = 0; 310 319 if (startBox->isInlineTextBox()) { … … 322 331 return VisiblePosition(); 323 332 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; 324 337 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 336 353 int endOffset = 1; 337 354 if (endNode->hasTagName(brTag)) { … … 450 467 // block and find the first root line box in that block. 451 468 NodeImpl *startBlock = node->enclosingBlockFlowElement(); 452 NodeImpl *n = node->nextEditable( );469 NodeImpl *n = node->nextEditable(p.offset()); 453 470 while (n && startBlock == n->enclosingBlockFlowElement()) 454 471 n = n->nextEditable(); … … 545 562 int offset = p.offset(); 546 563 547 for (NodeImpl *n = startNode; n; n = n->traversePreviousNodePostOrder(startBlock)) { 564 NodeImpl *n = startNode; 565 while (n) { 548 566 RenderObject *r = n->renderer(); 549 if (!r) 567 if (!r) { 568 n = n->traversePreviousNodePostOrder(startBlock); 550 569 continue; 570 } 551 571 RenderStyle *style = r->style(); 552 if (style->visibility() != VISIBLE) 572 if (style->visibility() != VISIBLE) { 573 n = n->traversePreviousNodePostOrder(startBlock); 553 574 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())) 555 578 break; 556 579 if (r->isText()) { … … 568 591 node = n; 569 592 offset = 0; 570 } else if (r->isReplaced()) { 593 n = n->traversePreviousNodePostOrder(startBlock); 594 } else if (editingIgnoresContent(n) || isTableElement(n)) { 571 595 node = n; 572 596 offset = 0; 573 } 597 n = n->previousSibling() ? n->previousSibling() : n->traversePreviousNodePostOrder(startBlock); 598 } else 599 n = n->traversePreviousNodePostOrder(startBlock); 574 600 } 575 601 … … 590 616 int offset = p.offset(); 591 617 592 for (NodeImpl *n = startNode; n; n = n->traverseNextNode(stayInsideBlock)) { 618 NodeImpl *n = startNode; 619 while (n) { 593 620 if (n->isContentEditable() != startNode->isContentEditable()) 594 621 break; 595 622 RenderObject *r = n->renderer(); 596 if (!r) 623 if (!r) { 624 n = n->traverseNextNode(stayInsideBlock); 597 625 continue; 626 } 598 627 RenderStyle *style = r->style(); 599 if (style->visibility() != VISIBLE) 628 if (style->visibility() != VISIBLE) { 629 n = n->traverseNextNode(stayInsideBlock); 600 630 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())) 603 635 break; 604 636 … … 617 649 node = n; 618 650 offset = r->caretMaxOffset(); 619 } else if (r->isReplaced()) { 651 n = n->traverseNextNode(stayInsideBlock); 652 } else if (editingIgnoresContent(n) || isTableElement(n)) { 620 653 node = n; 621 offset = 1; 622 } 654 offset = maxDeepOffset(n); 655 n = n->traverseNextSibling(stayInsideBlock); 656 } else 657 n = n->traverseNextNode(stayInsideBlock); 623 658 } 624 659 -
trunk/WebCore/khtml/xml/ContainerNodeImpl.cpp
r12343 r12358 622 622 while(o) { 623 623 p = o; 624 if (o->firstChild())624 if (o->firstChild()) 625 625 o = o->firstChild(); 626 626 else if(o->nextSibling()) … … 641 641 if (p->element() && p->element() == this && o->isText() && !o->isBR() && !static_cast<RenderText*>(o)->firstTextBox()) { 642 642 // 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()) { 645 644 o->container()->absolutePosition( xPos, yPos ); 646 645 if (o->isText() && static_cast<RenderText *>(o)->firstTextBox()) { 647 646 xPos += static_cast<RenderText *>(o)->minXPos(); 648 647 yPos += static_cast<RenderText *>(o)->firstTextBox()->root()->topOverflow(); 649 } 650 else { 648 } else { 651 649 xPos += o->xPos(); 652 650 yPos += o->yPos(); … … 809 807 } 810 808 811 NodeImpl *ContainerNodeImpl::childNode(unsigned index) 809 NodeImpl *ContainerNodeImpl::childNode(unsigned index) const 812 810 { 813 811 unsigned i; -
trunk/WebCore/khtml/xml/ContainerNodeImpl.h
r12338 r12358 53 53 virtual void setHovered(bool = true); 54 54 virtual unsigned childNodeCount() const; 55 virtual NodeImpl* childNode(unsigned index); 55 virtual NodeImpl* childNode(unsigned index) const; 56 56 57 virtual void insertedIntoDocument(); 57 58 virtual void removedFromDocument(); -
trunk/WebCore/khtml/xml/NodeImpl.cpp
r12321 r12358 941 941 } 942 942 943 NodeImpl *NodeImpl::childNode(unsigned /*index*/) 943 NodeImpl *NodeImpl::childNode(unsigned /*index*/) const 944 944 { 945 945 return 0; … … 994 994 return n; 995 995 } 996 else if (parentNode()) { 997 return parentNode(); 998 } 999 else { 1000 return 0; 1001 } 996 997 return parentNode(); 1002 998 } 1003 999 … … 1291 1287 return node; 1292 1288 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. 1295 NodeImpl *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(); 1293 1307 } 1294 1308 return 0; … … 1943 1957 } 1944 1958 1959 void showTree(const NodeImpl *node) 1960 { 1961 if (node) 1962 node->showTree(); 1963 } 1964 1945 1965 void NodeImpl::showTreeAndMark(NodeImpl * markedNode1, const char * markedLabel1, NodeImpl * markedNode2, const char * markedLabel2) const 1946 1966 { -
trunk/WebCore/khtml/xml/NodeImpl.h
r12329 r12358 303 303 virtual bool childTypeAllowed(unsigned short /*type*/) { return false; } 304 304 virtual unsigned childNodeCount() const; 305 virtual NodeImpl* childNode(unsigned index) ;305 virtual NodeImpl* childNode(unsigned index) const; 306 306 307 307 /** … … 331 331 NodeImpl* traversePreviousNodePostOrder(const NodeImpl *stayWithin = 0) const; 332 332 333 /** 334 * Finds previous or next editable leaf node. 335 */ 333 336 NodeImpl* previousEditable() const; 334 337 NodeImpl* nextEditable() const; 338 NodeImpl* nextEditable(int offset) const; 335 339 336 340 RenderObject* renderer() const { return m_renderer; } … … 506 510 #ifndef NDEBUG 507 511 512 void showTree(const NodeImpl *node); 513 508 514 extern int gEventDispatchForbidden; 509 515 inline void forbidEventDispatch() { ++gEventDispatchForbidden; } -
trunk/WebCore/khtml/xml/dom_position.cpp
r12263 r12358 385 385 // NOTE: caretMaxOffset() can be less than childNodeCount()!! 386 386 // 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); 390 391 continue; 391 392 } … … 498 499 499 500 // return position before replaced or BR elements 500 if ( renderer->isReplaced() || renderer->isBR()) {501 if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) { 501 502 if (currentOffset <= renderer->caretMinOffset()) 502 503 return Position(currentNode, renderer->caretMinOffset()); … … 533 534 } 534 535 535 Position Position::equivalentRangeCompliantPosition() const536 {537 if (isNull())538 return Position();539 540 // Make sure that 0 <= constrainedOffset <= num kids, otherwise using this Position541 // 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 567 536 bool Position::inRenderedContent() const 568 537 { … … 578 547 579 548 // 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()) 581 550 return offset() == 0; 582 } 583 elseif (renderer->isText()) {551 552 if (renderer->isText()) { 584 553 RenderText *textRenderer = static_cast<RenderText *>(renderer); 585 554 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) 587 556 return true; 588 } 589 else if (offset() < box->m_start) { 557 if (offset() < box->m_start) { 590 558 // The offset we're looking for is before this node 591 559 // this means the offset must be in content that is … … 594 562 } 595 563 } 596 } 597 else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) { 564 } else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) { 598 565 // return true for replaced elements, for inline flows if they have a line box 599 566 // and for blocks if they are empty … … 823 790 m_node->showTree(); 824 791 } 792 793 void showTree(const Position &pos) 794 { 795 pos.showTree(); 796 } 797 798 void showTree(const Position *pos) 799 { 800 if (pos) 801 pos->showTree(); 802 } 825 803 #endif 826 804 -
trunk/WebCore/khtml/xml/dom_position.h
r12005 r12358 120 120 Position endPosition(const RangeImpl *); 121 121 122 #ifndef NDEBUG 123 void showTree(const Position &pos); 124 void showTree(const Position *pos); 125 #endif 126 122 127 } // namespace DOM 123 128 -
trunk/WebCore/rendering/RenderContainer.cpp
r12345 r12358 30 30 #include "render_image.h" 31 31 #include "render_canvas.h" 32 #include "render_list.h" 32 33 #include "DocumentImpl.h" 33 34 #include "xml/dom_position.h" … … 79 80 } 80 81 82 static void updateListMarkerNumbers(RenderObject *child) 83 { 84 for (RenderObject *r = child; r && r->isListItem(); r = r->nextSibling()) 85 static_cast<RenderListItem *>(r)->resetMarkerValue(); 86 } 87 81 88 void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild) 82 89 { … … 90 97 if(!newChild->isText() && !newChild->isReplaced()) { 91 98 switch(newChild->style()->display()) { 99 case LIST_ITEM: 100 updateListMarkerNumbers(beforeChild); 101 break; 92 102 case INLINE: 93 103 case BLOCK: 94 104 case INLINE_BLOCK: 95 case LIST_ITEM:96 105 case RUN_IN: 97 106 case COMPACT: … … 180 189 if (oldChild->isSelectionBorder()) 181 190 canvas()->clearSelection(); 191 192 // renumber ordered lists 193 if (oldChild->isListItem()) 194 updateListMarkerNumbers(oldChild->nextSibling()); 182 195 } 183 196 -
trunk/WebCore/rendering/render_canvas.cpp
r12329 r12358 329 329 if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { 330 330 // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. 331 331 // assert(!selectedObjects.get(os)); 332 332 selectedObjects.set(os, new SelectionInfo(os)); 333 333 RenderBlock* cb = os->containingBlock(); -
trunk/WebCore/rendering/render_list.cpp
r12340 r12358 182 182 KHTMLAssert(m_marker); 183 183 184 if (predefVal != -1)184 if (predefVal != -1) 185 185 m_marker->m_value = predefVal; 186 else if (!previousSibling())186 else if (!previousSibling()) 187 187 m_marker->m_value = 1; 188 188 else { 189 189 RenderObject *o = previousSibling(); 190 while ( o && (!o->isListItem() || o->style()->listStyleType() == LNONE))190 while (o && (!o->isListItem() || o->style()->listStyleType() == LNONE)) 191 191 o = o->previousSibling(); 192 if ( o && o->isListItem() && o->style()->listStyleType() != LNONE) {192 if (o && o->isListItem() && o->style()->listStyleType() != LNONE) { 193 193 RenderListItem *item = static_cast<RenderListItem *>(o); 194 194 m_marker->m_value = item->value() + 1; 195 } 196 else 195 } else 197 196 m_marker->m_value = 1; 198 197 } 198 } 199 200 bool RenderListItem::isEmpty() const 201 { 202 return lastChild() == m_marker; 199 203 } 200 204 … … 205 209 return 0; 206 210 207 for (RenderObject* currChild = firstChild; 208 currChild; currChild = currChild->nextSibling()) { 211 for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { 209 212 if (currChild == marker) 210 213 continue; … … 231 234 } 232 235 236 void RenderListItem::resetMarkerValue() 237 { 238 m_marker->m_value = -1; 239 m_marker->setNeedsLayoutAndMinMaxRecalc(); 240 } 241 233 242 void RenderListItem::updateMarkerLocation() 234 243 { … … 247 256 lineBoxParent = this; 248 257 } 249 if (markerPar != lineBoxParent)250 {258 259 if (markerPar != lineBoxParent || !m_marker->minMaxKnown()) { 251 260 if (markerPar) 252 261 markerPar->removeChild(m_marker); -
trunk/WebCore/rendering/render_list.h
r12263 r12358 61 61 virtual short lineHeight(bool b, bool isRootLineBox=false) const; 62 62 virtual short baselinePosition(bool b, bool isRootLineBox=false) const; 63 64 virtual bool isListMarker() const { return true; } 63 65 64 virtual bool isListMarker() const { return true; }65 66 66 CachedImage* listImage() const { return m_listImage; } 67 67 … … 108 108 void calcListValue(); 109 109 110 virtual bool isEmpty() const; 110 111 virtual void paint(PaintInfo& i, int xoff, int yoff); 111 112 … … 120 121 bool notInList() const { return _notInList; } 121 122 123 void resetMarkerValue(); 122 124 QString markerStringValue() { if (m_marker) return m_marker->m_item; return ""; } 123 125 -
trunk/WebCore/rendering/render_object.cpp
r12340 r12358 258 258 if (firstChild()) 259 259 return firstChild(); 260 else if (nextSibling()) 260 261 if (nextSibling()) 261 262 return nextSibling(); 262 else { 263 264 265 266 267 268 } 263 264 const RenderObject *r = this; 265 while (r && !r->nextSibling()) 266 r = r->parent(); 267 if (r) 268 return r->nextSibling(); 269 269 270 return 0; 270 271 } … … 278 279 return r; 279 280 } 280 else if (parent()) { 281 return parent(); 282 } 283 else { 284 return 0; 285 } 281 282 return parent(); 286 283 } 287 284 … … 1623 1620 element()->showTree(); 1624 1621 } 1622 1623 void showTree(const RenderObject *ro) 1624 { 1625 if (ro) 1626 ro->showTree(); 1627 } 1625 1628 #endif 1626 1629 -
trunk/WebCore/rendering/render_object.h
r12340 r12358 171 171 172 172 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; } 174 175 175 176 // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline … … 225 226 virtual void dump(QTextStream *stream, QString ind = "") const; 226 227 void showTree() const; 228 static void showTree(const RenderObject *ro); 227 229 #endif 228 230
Note:
See TracChangeset
for help on using the changeset viewer.