Changeset 69354 in webkit
- Timestamp:
- Oct 7, 2010 4:55:00 PM (14 years ago)
- Location:
- trunk
- Files:
-
- 4 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r69353 r69354 1 2010-10-07 Ryosuke Niwa <rniwa@webkit.org> 2 3 Reviewed by Tony Chang. 4 5 REGRESSION: Indenting pre duplicates content 6 https://bugs.webkit.org/show_bug.cgi?id=47233 7 8 Added tests to ensure WebKit indents texts inside pre correctly. 9 10 * editing/execCommand/indent-pre-expected.txt: This test passes if WebKit does not crash. 11 * editing/execCommand/indent-pre-list-expected.txt: Added. 12 * editing/execCommand/indent-pre-list.html: Added. 13 * editing/execCommand/indent-pre-paragraphs-expected.txt: Added. 14 * editing/execCommand/indent-pre-paragraphs.html: Added. 15 1 16 2010-10-07 Albert J. Wong <ajwong@chromium.org> 2 17 -
trunk/LayoutTests/editing/execCommand/indent-pre-expected.txt
r64432 r69354 1 1 CONSOLE MESSAGE: line 44: Wrong node selected. 2 2 CONSOLE MESSAGE: line 46: Wrong anchor offset: 8 instead of 0 3 CONSOLE MESSAGE: line 41: Wrong end node type: [object HTMLBRElement] 4 CONSOLE MESSAGE: line 44: Wrong node selected. 3 5 | <html> 4 6 | <head> … … 18 20 | <pre> 19 21 | id="pre-basic" 20 | "line one 21 " 22 | "line one" 22 23 | <pre> 23 24 | id="pre-basic" … … 28 29 | style="margin: 0 0 0 40px; border: none; padding: 0px;" 29 30 | <pre> 30 | "line three 31 " 31 | "line three" 32 32 | <pre> 33 33 | "line four" … … 44 44 | class="webkit-indent-blockquote" 45 45 | style="margin: 0 0 0 40px; border: none; padding: 0px;" 46 | "list two 47 " 48 | "list three 49 " 50 | "list four 51 " 46 | "list two" 47 | <br> 48 | "list three" 49 | <br> 50 | "list four" 52 51 | " 53 52 … … 67 66 | style="margin: 0 0 0 40px; border: none; padding: 0px;" 68 67 | <pre> 69 | "table two 70 " 68 | "table two" 71 69 | <pre> 72 70 | "table three<#selection-focus>" -
trunk/WebCore/ChangeLog
r69349 r69354 1 2010-10-07 Ryosuke Niwa <rniwa@webkit.org> 2 3 Reviewed by Tony Chang. 4 5 REGRESSION: Indenting pre duplicates content 6 https://bugs.webkit.org/show_bug.cgi?id=47233 7 8 The bug was caused by our not splitting text nodes properly. 9 10 In new approach, we split text nodes in each iteration. Added rangeForParagraphSplitingTextNodesIfNeeded 11 to split text nodes at the start and at the end of paragraph, which also adjusts start and end positions 12 for moveParagraphWithClones. Added endOfNextParagrahSplittingTextNodesIfNeeded to adjust endOfNextParagraph, 13 start, and end to work-around moveParagraphWithClones's removing a line feed. 14 15 Tests: editing/execCommand/indent-pre-list.html 16 editing/execCommand/indent-pre-paragraphs.html 17 18 * editing/ApplyBlockElementCommand.cpp: 19 (WebCore::ApplyBlockElementCommand::formatSelection): See above. 20 (WebCore::isNewLineAtPosition): 21 (WebCore::renderStyleOfEnclosingTextNode): Added. 22 (WebCore::ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded): Added. 23 (WebCore::ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded): Added. 24 * editing/ApplyBlockElementCommand.h: 25 * editing/FormatBlockCommand.cpp: 26 (WebCore::FormatBlockCommand::formatRange): Takes two Positions instead of one VisiblePosition. 27 * editing/FormatBlockCommand.h: 28 * editing/IndentOutdentCommand.cpp: 29 (WebCore::IndentOutdentCommand::tryIndentingAsListItem): Ditto. 30 (WebCore::IndentOutdentCommand::indentIntoBlockquote): Ditto. 31 (WebCore::IndentOutdentCommand::formatRange): Ditto. 32 * editing/IndentOutdentCommand.h: 33 1 34 2010-10-07 Jian Li <jianli@chromium.org> 2 35 -
trunk/WebCore/editing/ApplyBlockElementCommand.cpp
r68972 r69354 39 39 40 40 using namespace HTMLNames; 41 42 // This function can return -1 if we are unable to count the paragraphs between |start| and |end|.43 static int countParagraphs(const VisiblePosition& endOfFirstParagraph, const VisiblePosition& endOfLastParagraph)44 {45 int count = 0;46 VisiblePosition cur = endOfFirstParagraph;47 while (cur != endOfLastParagraph) {48 ++count;49 cur = endOfParagraph(cur.next());50 // If start is before a table and end is inside a table, we will never hit end because the51 // whole table is considered a single paragraph.52 if (cur.isNull())53 return -1;54 }55 return count;56 }57 41 58 42 ApplyBlockElementCommand::ApplyBlockElementCommand(Document* document, const QualifiedName& tagName, const AtomicString& className, const AtomicString& inlineStyle) … … 126 110 VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); 127 111 VisiblePosition endAfterSelection = endOfParagraph(endOfParagraph(endOfSelection).next()); 128 int endOfCurrentParagraphIndex = indexForVisiblePosition(endOfCurrentParagraph); 129 int endAfterSelectionIndex = indexForVisiblePosition(endAfterSelection); 130 131 // When indenting within a <pre> tag, we need to split each paragraph into a separate node for moveParagraphWithClones to work. 132 // However, splitting text nodes can cause endOfCurrentParagraph and endAfterSelection to point to an invalid position if we 133 // changed the text node it was pointing at. So we have to reset these positions. 134 int numParagraphs = countParagraphs(endOfCurrentParagraph, endAfterSelection); 135 if (splitTextNodes(startOfParagraph(startOfSelection), numParagraphs + 1)) { 136 RefPtr<Range> endOfCurrentParagraphRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endOfCurrentParagraphIndex, 0, true); 137 RefPtr<Range> endAfterSelectionRange = TextIterator::rangeFromLocationAndLength(document()->documentElement(), endAfterSelectionIndex, 0, true); 138 if (!endOfCurrentParagraphRange.get() || !endAfterSelectionRange.get()) { 139 ASSERT_NOT_REACHED(); 140 return; 141 } 142 endOfCurrentParagraph = VisiblePosition(endOfCurrentParagraphRange->startPosition(), DOWNSTREAM); 143 endAfterSelection = VisiblePosition(endAfterSelectionRange->startPosition(), DOWNSTREAM); 144 } 145 146 while (endOfCurrentParagraph != endAfterSelection) { 147 // Iterate across the selected paragraphs... 148 VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); 112 VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); 113 114 bool atEnd = false; 115 while (endOfCurrentParagraph != endAfterSelection && !atEnd) { 116 Position start; 117 Position end; 118 119 if (endOfCurrentParagraph == endOfLastParagraph) 120 atEnd = true; 121 122 rangeForParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end); 123 endOfCurrentParagraph = end; 124 125 Position afterEnd = end.next(); 149 126 Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); 150 151 formatParagraph(endOfCurrentParagraph, blockquoteForNextIndent); 127 VisiblePosition endOfNextParagraph = endOfNextParagrahSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, end); 128 129 formatRange(start, end, blockquoteForNextIndent); 152 130 153 131 // Don't put the next paragraph in the blockquote we just created for this paragraph unless … … 171 149 } 172 150 151 static bool isNewLineAtPosition(const Position& position) 152 { 153 if (position.anchorType() != Position::PositionIsOffsetInAnchor) 154 return false; 155 156 Node* textNode = position.containerNode(); 157 int offset = position.offsetInContainerNode(); 158 if (!textNode || !textNode->isTextNode() || offset < 0 || offset >= textNode->maxCharacterOffset()) 159 return false; 160 161 ExceptionCode ec = 0; 162 String textAtPosition = static_cast<Text*>(textNode)->substringData(offset, 1, ec); 163 if (ec) 164 return false; 165 166 return textAtPosition[0] == '\n'; 167 } 168 169 static RenderStyle* renderStyleOfEnclosingTextNode(const Position& position) 170 { 171 if (position.anchorType() != Position::PositionIsOffsetInAnchor 172 || !position.containerNode() 173 || !position.containerNode()->isTextNode() 174 || !position.containerNode()->renderer()) 175 return 0; 176 return position.containerNode()->renderer()->style(); 177 } 178 179 void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded(const VisiblePosition& endOfCurrentParagraph, Position& start, Position& end) 180 { 181 start = startOfParagraph(endOfCurrentParagraph).deepEquivalent(); 182 end = endOfCurrentParagraph.deepEquivalent(); 183 184 RenderStyle* startStyle = renderStyleOfEnclosingTextNode(start); 185 if (startStyle) { 186 // Avoid obtanining the start of next paragraph for start 187 if (startStyle->preserveNewline() && isNewLineAtPosition(start) && !isNewLineAtPosition(start.previous()) && start.offsetInContainerNode() > 0) 188 start = startOfParagraph(end.previous()).deepEquivalent(); 189 190 // If start is in the middle of a text node, split. 191 if (!startStyle->collapseWhiteSpace() && start.offsetInContainerNode() > 0) { 192 int startOffset = start.offsetInContainerNode(); 193 splitTextNode(static_cast<Text*>(start.node()), startOffset); 194 start = positionBeforeNode(start.node()); 195 if (start.node() == end.node()) { 196 ASSERT(end.offsetInContainerNode() >= startOffset); 197 end = Position(end.node(), end.offsetInContainerNode() - startOffset, Position::PositionIsOffsetInAnchor); 198 } 199 } 200 } 201 202 RenderStyle* endStyle = renderStyleOfEnclosingTextNode(end); 203 if (endStyle) { 204 // Include \n at the end of line if we're at an empty paragraph 205 if (endStyle->preserveNewline() && start == end 206 && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) { 207 int endOffset = end.offsetInContainerNode(); 208 if (!isNewLineAtPosition(end.previous()) && isNewLineAtPosition(end)) 209 end = Position(end.node(), endOffset + 1, Position::PositionIsOffsetInAnchor); 210 } 211 212 // If end is in the middle of a text node, split. 213 if (!endStyle->collapseWhiteSpace() && end.offsetInContainerNode() 214 && end.offsetInContainerNode() < end.containerNode()->maxCharacterOffset()) { 215 splitTextNode(static_cast<Text*>(end.node()), end.offsetInContainerNode()); 216 if (start.node() == end.node()) 217 start = positionBeforeNode(end.node()->previousSibling()); 218 end = lastPositionInNode(end.node()->previousSibling()); 219 } 220 } 221 } 222 223 VisiblePosition ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition& endOfCurrentParagraph, Position& start, Position& end) 224 { 225 VisiblePosition endOfNextParagraph = endOfParagraph(endOfCurrentParagraph.next()); 226 Position position = endOfNextParagraph.deepEquivalent(); 227 RenderStyle* style = renderStyleOfEnclosingTextNode(position); 228 if (!style) 229 return endOfNextParagraph; 230 231 RefPtr<Node> containerNode = position.containerNode(); 232 if (!style->preserveNewline() || !position.offsetInContainerNode() 233 || !isNewLineAtPosition(Position(containerNode.get(), 0, Position::PositionIsOffsetInAnchor))) 234 return endOfNextParagraph; 235 236 // \n at the beginning of the text node immediately following the current paragraph is trimmed by moveParagraphWithClones. 237 // If endOfNextParagraph was pointing at this same text node, endOfNextParagraph will be shifted by one paragraph. 238 // Avoid this by splitting "\n" 239 splitTextNode(static_cast<Text*>(containerNode.get()), 1); 240 241 if (start.anchorType() == Position::PositionIsOffsetInAnchor && containerNode.get() == start.containerNode()) { 242 ASSERT(start.offsetInContainerNode() < position.offsetInContainerNode()); 243 start = Position(containerNode->previousSibling(), start.offsetInContainerNode(), Position::PositionIsOffsetInAnchor); 244 } 245 if (end.anchorType() == Position::PositionIsOffsetInAnchor && containerNode.get() == end.containerNode()) { 246 ASSERT(end.offsetInContainerNode() < position.offsetInContainerNode()); 247 end = Position(containerNode->previousSibling(), end.offsetInContainerNode(), Position::PositionIsOffsetInAnchor); 248 } 249 250 return Position(containerNode.get(), position.offsetInContainerNode() - 1, Position::PositionIsOffsetInAnchor); 251 } 252 173 253 PassRefPtr<Element> ApplyBlockElementCommand::createBlockElement() const 174 254 { … … 181 261 } 182 262 183 // Returns true if at least one text node was split. 184 bool ApplyBlockElementCommand::splitTextNodes(const VisiblePosition& start, int numParagraphs) 185 { 186 VisiblePosition currentParagraphStart = start; 187 bool hasSplit = false; 188 int paragraphCount; 189 for (paragraphCount = 0; paragraphCount < numParagraphs; ++paragraphCount) { 190 // If there are multiple paragraphs in a single text node, we split the text node into a separate node for each paragraph. 191 if (currentParagraphStart.deepEquivalent().node()->isTextNode() && currentParagraphStart.deepEquivalent().node() == startOfParagraph(currentParagraphStart.previous()).deepEquivalent().node()) { 192 Text* textNode = static_cast<Text *>(currentParagraphStart.deepEquivalent().node()); 193 int offset = currentParagraphStart.deepEquivalent().offsetInContainerNode(); 194 splitTextNode(textNode, offset); 195 currentParagraphStart = VisiblePosition(textNode, 0, VP_DEFAULT_AFFINITY); 196 hasSplit = true; 197 } 198 VisiblePosition nextParagraph = startOfParagraph(endOfParagraph(currentParagraphStart).next()); 199 if (nextParagraph.isNull()) 200 break; 201 currentParagraphStart = nextParagraph; 202 } 203 return hasSplit; 204 } 205 206 } 263 } -
trunk/WebCore/editing/ApplyBlockElementCommand.h
r68972 r69354 47 47 private: 48 48 virtual void doApply(); 49 virtual void formatParagraph(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>&) = 0; 50 bool splitTextNodes(const VisiblePosition& start, int numParagraphs); 49 virtual void formatRange(const Position& start, const Position&, RefPtr<Element>&) = 0; 50 void rangeForParagraphSplittingTextNodesIfNeeded(const VisiblePosition&, Position&, Position&); 51 VisiblePosition endOfNextParagrahSplittingTextNodesIfNeeded(VisiblePosition&, Position&, Position&); 51 52 52 53 QualifiedName m_tagName; -
trunk/WebCore/editing/FormatBlockCommand.cpp
r68972 r69354 42 42 } 43 43 44 void FormatBlockCommand::format Paragraph(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>&)44 void FormatBlockCommand::formatRange(const Position&, const Position& end, RefPtr<Element>&) 45 45 { 46 setEndingSelection(endOfCurrentParagraph); 46 setEndingSelection(VisiblePosition(end)); 47 47 48 Node* refNode = enclosingBlockFlowElement(endingSelection().visibleStart()); 48 49 if (refNode->hasTagName(tagName())) … … 50 51 return; 51 52 52 VisiblePosition paragraphStart = startOfParagraph(end ingSelection().visibleStart());53 VisiblePosition paragraphEnd = endOfParagraph(end ingSelection().visibleStart());53 VisiblePosition paragraphStart = startOfParagraph(end); 54 VisiblePosition paragraphEnd = endOfParagraph(end); 54 55 VisiblePosition blockStart = startOfBlock(endingSelection().visibleStart()); 55 56 VisiblePosition blockEnd = endOfBlock(endingSelection().visibleStart()); -
trunk/WebCore/editing/FormatBlockCommand.h
r68972 r69354 42 42 FormatBlockCommand(Document*, const QualifiedName& tagName); 43 43 44 virtual void format Paragraph(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>&);44 virtual void formatRange(const Position&, const Position&, RefPtr<Element>&); 45 45 virtual EditAction editingAction() const { return EditActionFormatBlock; } 46 46 }; -
trunk/WebCore/editing/IndentOutdentCommand.cpp
r68972 r69354 57 57 } 58 58 59 bool IndentOutdentCommand::tryIndentingAsListItem(const VisiblePosition& endOfCurrentParagraph)59 bool IndentOutdentCommand::tryIndentingAsListItem(const Position& start, const Position& end) 60 60 { 61 61 // If our selection is not inside a list, bail out. 62 Node* lastNodeInSelectedParagraph = endOfCurrentParagraph.deepEquivalent().node();62 Node* lastNodeInSelectedParagraph = start.node(); 63 63 RefPtr<Element> listNode = enclosingList(lastNodeInSelectedParagraph); 64 64 if (!listNode) … … 79 79 insertNodeBefore(newList, selectedListItem); 80 80 81 moveParagraphWithClones(start OfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, newList.get(), selectedListItem);81 moveParagraphWithClones(start, end, newList.get(), selectedListItem); 82 82 83 83 if (canMergeLists(previousList, newList.get())) … … 88 88 return true; 89 89 } 90 91 void IndentOutdentCommand::indentIntoBlockquote(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>& targetBlockquote) 92 { 93 Position start = startOfParagraph(endOfCurrentParagraph).deepEquivalent(); 90 91 void IndentOutdentCommand::indentIntoBlockquote(const Position& start, const Position& end, RefPtr<Element>& targetBlockquote) 92 { 94 93 Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); 95 94 Node* nodeToSplitTo; … … 110 109 } 111 110 112 moveParagraphWithClones(start OfParagraph(endOfCurrentParagraph), endOfCurrentParagraph, targetBlockquote.get(), outerBlock.get());111 moveParagraphWithClones(start, end, targetBlockquote.get(), outerBlock.get()); 113 112 } 114 113 … … 221 220 } 222 221 223 void IndentOutdentCommand::format Paragraph(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>& blockquoteForNextIndent)224 { 225 if (tryIndentingAsListItem( endOfCurrentParagraph))222 void IndentOutdentCommand::formatRange(const Position& start, const Position& end, RefPtr<Element>& blockquoteForNextIndent) 223 { 224 if (tryIndentingAsListItem(start, end)) 226 225 blockquoteForNextIndent = 0; 227 226 else 228 indentIntoBlockquote( endOfCurrentParagraph, blockquoteForNextIndent);229 } 230 231 } 227 indentIntoBlockquote(start, end, blockquoteForNextIndent); 228 } 229 230 } -
trunk/WebCore/editing/IndentOutdentCommand.h
r68972 r69354 50 50 void outdentRegion(const VisiblePosition&, const VisiblePosition&); 51 51 void outdentParagraph(); 52 bool tryIndentingAsListItem(const VisiblePosition&);53 void indentIntoBlockquote(const VisiblePosition&, RefPtr<Element>&);52 bool tryIndentingAsListItem(const Position&, const Position&); 53 void indentIntoBlockquote(const Position&, const Position&, RefPtr<Element>&); 54 54 55 55 void formatSelection(const VisiblePosition& startOfSelection, const VisiblePosition& endOfSelection); 56 void format Paragraph(const VisiblePosition& endOfCurrentParagraph, RefPtr<Element>& blockquoteForNextIndent);56 void formatRange(const Position&, const Position&, RefPtr<Element>& blockquoteForNextIndent); 57 57 58 58 EIndentType m_typeOfAction;
Note: See TracChangeset
for help on using the changeset viewer.