Commit 8b1ae58a authored by harrison's avatar harrison
Browse files

LayoutTests:

        Reviewed by Justin.

        <rdar://problem/5031181> cntl-k at end of paragraph adds nothing to the kill ring
        <rdar://problem/5031189> REGRESSION: cntl-y yanks only the most recently killed content

        * editing/pasteboard/emacs-ctrl-k-y-001-expected.checksum: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001-expected.png: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001-expected.txt: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001.html: Added.

WebCore:

        Reviewed by Justin.

        <rdar://problem/5031181> cntl-k at end of paragraph adds nothing to the kill ring
        <rdar://problem/5031189> REGRESSION: cntl-y yanks only the most recently killed content

        For rdar://5031181, properly extend the selection before the killring handling, and
        make sure plainText of that selection returns a linefeed.
        
        For rdar://5031189, restore Editor::deleteRange() code that continued current killring,
        even though the range deletion implicitly stopped it via changing the selection.
        
        A byproduct of this change is the elimination of RUNDFINDER vs CONTENT TextIterator. The
        only difference between the two was whether to emit a newline when the range started
        with a blockflow element. No callers actually need that any more.

        Tests added:
        * editing/pasteboard/emacs-ctrl-k-y-001-expected.checksum: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001-expected.png: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001-expected.txt: Added.
        * editing/pasteboard/emacs-ctrl-k-y-001.html: Added.

        * editing/Editor.cpp:
        (WebCore::Editor::deleteRange):
        Clear the "start new kill ring sequence" setting, because it was set to true
        when the selection was updated by deleting the range.
        
        (WebCore::Editor::deleteWithDirection):
        If extending the selection to the end of paragraph resulted in a caret selection,
        extend by character, to handle the case when the selection started as a caret at
        the end of paragraph.
        
        * editing/TextIterator.cpp:
        (WebCore::TextIterator::TextIterator):
        Initialize new member variables for tracking handling of the beginning of the range.
        
        (WebCore::TextIterator::advance):
        Call representNodeOffsetZero on the m_endContainer.
        Move visibility checks into handleTextNode and handleReplacedElement.
        
        (WebCore::TextIterator::handleTextNode):
        (WebCore::TextIterator::handleTextBox):
        Call emitText.
        
        (WebCore::TextIterator::handleReplacedElement):
        Moved visibility check into here.
        
        (WebCore::shouldEmitNewlinesBeforeAndAfterNode):
        
        (WebCore::TextIterator::shouldRepresentNodeOffsetZero):
        (WebCore::TextIterator::representNodeOffsetZero):
        New. Emits proper sequence when encountering offset 0 of a node, including the
        m_endContainer. Started with code from handleNonTextNode.
        
        (WebCore::TextIterator::handleNonTextNode):
        Call representNodeOffsetZero.
        
        (WebCore::TextIterator::exitNode):
        Similar to shouldRepresentNodeOffsetZero, do not emit the newline if the node
        was collapsed, and before any other emitted content.
        
        (WebCore::TextIterator::emitCharacter):
        
        (WebCore::TextIterator::emitText):
        New. Consolidates code used by handleText and handleTextBox.
        
        (WebCore::CharacterIterator::CharacterIterator):
        Removed RUNFINDER.

        (WebCore::WordAwareIterator::WordAwareIterator):
        Removed RUNFINDER.
        
        (WebCore::WordAwareIterator::advance):
        Formatting.

        (WebCore::TextIterator::rangeLength):
        Formatting.
        
        * editing/TextIterator.h:
        Added member variables for tracking handling of the beginning of the range.
        Eliminated concept of RUNDFINDER vs CONTENT TextIterator.
        
        * editing/visible_units.cpp:
        (WebCore::nextBoundary):
        Eliminated concept of RUNDFINDER vs CONTENT TextIterator.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@20166 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent bbd5c413
2007-03-13 David Harrison <harrison@apple.com>
Reviewed by Justin.
<rdar://problem/5031181> cntl-k at end of paragraph adds nothing to the kill ring
<rdar://problem/5031189> REGRESSION: cntl-y yanks only the most recently killed content
* editing/pasteboard/emacs-ctrl-k-y-001-expected.checksum: Added.
* editing/pasteboard/emacs-ctrl-k-y-001-expected.png: Added.
* editing/pasteboard/emacs-ctrl-k-y-001-expected.txt: Added.
* editing/pasteboard/emacs-ctrl-k-y-001.html: Added.
2007-03-13 Antti Koivisto <antti@apple.com>
 
Reviewed by Darin.
759805c2c075cdc457d860b20cd9eb78
\ No newline at end of file
EDITING DELEGATE: shouldBeginEditingInDOMRange:range from 0 of DIV > BODY > HTML > #document to 3 of DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldDeleteDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 14 of #text > DIV > DIV > BODY > HTML > #document
EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document toDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldDeleteDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldDeleteDOMRange:range from 0 of #text > DIV > DIV > BODY > HTML > #document to 13 of #text > DIV > DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldDeleteDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldDeleteDOMRange:range from 0 of #text > DIV > DIV > BODY > HTML > #document to 16 of #text > DIV > DIV > BODY > HTML > #document
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: shouldInsertText: three
four five six
seven eight nine replacingDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document givenAction:WebViewInsertActionTyped
EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 8 of #text > DIV > DIV > BODY > HTML > #document to 8 of #text > DIV > DIV > BODY > HTML > #document toDOMRange:range from 14 of #text > DIV > DIV > BODY > HTML > #document to 14 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 14 of #text > DIV > DIV > BODY > HTML > #document to 14 of #text > DIV > DIV > BODY > HTML > #document toDOMRange:range from 0 of DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 13 of #text > DIV > DIV > BODY > HTML > #document to 13 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 13 of #text > DIV > DIV > BODY > HTML > #document to 13 of #text > DIV > DIV > BODY > HTML > #document toDOMRange:range from 0 of DIV > DIV > BODY > HTML > #document to 0 of DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: shouldChangeSelectedDOMRange:(null) toDOMRange:range from 16 of #text > DIV > DIV > BODY > HTML > #document to 16 of #text > DIV > DIV > BODY > HTML > #document affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
layer at (0,0) size 800x600
RenderView at (0,0) size 800x600
layer at (0,0) size 800x600
RenderBlock {HTML} at (0,0) size 800x600
RenderBody {BODY} at (8,8) size 784x584
RenderBlock {DIV} at (0,0) size 784x212 [border: (2px solid #0000FF)]
RenderBlock {DIV} at (14,14) size 756x56
RenderText {#text} at (0,0) size 67x28
text run at (0,0) width 67: "Tests: "
RenderBR {BR} at (0,0) size 0x0
RenderText {#text} at (0,28) size 348x28
text run at (0,28) width 348: "Multiple ctrl-k presses then a ctrl-y."
RenderBlock {DIV} at (14,86) size 756x112
RenderBlock (anonymous) at (0,0) size 756x28
RenderText {#text} at (0,0) size 189x28
text run at (0,0) width 189: "Expected Results: "
RenderBR {BR} at (189,22) size 0x0
RenderBlock {DIV} at (0,28) size 756x84
RenderBlock (anonymous) at (0,0) size 756x28
RenderText {#text} at (0,0) size 132x28
text run at (0,0) width 132: "one two three"
RenderBlock {DIV} at (0,28) size 756x28
RenderText {#text} at (0,0) size 118x28
text run at (0,0) width 118: "four five six"
RenderBlock {DIV} at (0,56) size 756x28
RenderText {#text} at (0,0) size 158x28
text run at (0,0) width 158: "seven eight nine"
RenderBlock {DIV} at (0,236) size 784x96
RenderBlock {DIV} at (0,0) size 784x32 [border: (2px solid #FF0000)]
RenderText {#text} at (2,2) size 132x28
text run at (2,2) width 132: "one two three"
RenderBlock {DIV} at (0,32) size 784x32 [border: (2px solid #FF0000)]
RenderText {#text} at (2,2) size 118x28
text run at (2,2) width 118: "four five six"
RenderBlock {DIV} at (0,64) size 784x32 [border: (2px solid #FF0000)]
RenderText {#text} at (2,2) size 158x28
text run at (2,2) width 158: "seven eight nine"
caret: position 16 of child 0 {#text} of child 3 {DIV} of child 3 {DIV} of child 1 {BODY} of child 0 {HTML} of document
<html>
<head>
<style>
.editing {
border: 2px solid red;
font-size: 24px;
}
.explanation {
border: 2px solid blue;
padding: 12px;
font-size: 24px;
margin-bottom: 24px;
}
.scenario { margin-bottom: 16px;}
.scenario:first-line { font-weight: bold; margin-bottom: 16px;}
.expected-results:first-line { font-weight: bold }
</style>
<script src='../editing.js'></script>
<script>
function editingTest()
{
moveSelectionForwardByWordCommand(); // cursor after "one"
moveSelectionForwardByWordCommand(); // cursor after "two"
if (window.eventSender) {
eventSender.clearKillRing();
eventSender.keyDown("k", ["ctrlKey"]); // two three in the kill ring
eventSender.keyDown("k", ["ctrlKey"]); // newline added to killring
eventSender.keyDown("k", ["ctrlKey"]); // four five six added to kill ring
eventSender.keyDown("k", ["ctrlKey"]); // newline added to killring
eventSender.keyDown("k", ["ctrlKey"]); // seven eight nine added to kill ring
eventSender.keyDown("y", ["ctrlKey"]);
}
}
</script>
<title>Editing Test for ctrl-k and ctrl-y</title>
</head>
<body>
<div class="explanation">
<div class="scenario">
Tests:
<br>
Multiple ctrl-k presses then a ctrl-y.
</div>
<div class="expected-results">
Expected Results:
<br>
<div>
one two three
<div>four five six</div>
<div>seven eight nine</div>
</div>
</div>
</div>
<div contenteditable id="root" style="word-wrap: break-word; -khtml-nbsp-mode: space; -khtml-line-break: after-white-space;">
<div id="test" class="editing">
one two three
<div>four five six</div>
<div>seven eight nine</div>
</div>
</div>
<script>
runEditingTest();
</script>
</body>
</html>
\ No newline at end of file
2007-03-13 David Harrison <harrison@apple.com>
Reviewed by Justin.
<rdar://problem/5031181> cntl-k at end of paragraph adds nothing to the kill ring
<rdar://problem/5031189> REGRESSION: cntl-y yanks only the most recently killed content
For rdar://5031181, properly extend the selection before the killring handling, and
make sure plainText of that selection returns a linefeed.
For rdar://5031189, restore Editor::deleteRange() code that continued current killring,
even though the range deletion implicitly stopped it via changing the selection.
A byproduct of this change is the elimination of RUNDFINDER vs CONTENT TextIterator. The
only difference between the two was whether to emit a newline when the range started
with a blockflow element. No callers actually need that any more.
Tests added:
* editing/pasteboard/emacs-ctrl-k-y-001-expected.checksum: Added.
* editing/pasteboard/emacs-ctrl-k-y-001-expected.png: Added.
* editing/pasteboard/emacs-ctrl-k-y-001-expected.txt: Added.
* editing/pasteboard/emacs-ctrl-k-y-001.html: Added.
* editing/Editor.cpp:
(WebCore::Editor::deleteRange):
Clear the "start new kill ring sequence" setting, because it was set to true
when the selection was updated by deleting the range.
(WebCore::Editor::deleteWithDirection):
If extending the selection to the end of paragraph resulted in a caret selection,
extend by character, to handle the case when the selection started as a caret at
the end of paragraph.
* editing/TextIterator.cpp:
(WebCore::TextIterator::TextIterator):
Initialize new member variables for tracking handling of the beginning of the range.
(WebCore::TextIterator::advance):
Call representNodeOffsetZero on the m_endContainer.
Move visibility checks into handleTextNode and handleReplacedElement.
(WebCore::TextIterator::handleTextNode):
(WebCore::TextIterator::handleTextBox):
Call emitText.
(WebCore::TextIterator::handleReplacedElement):
Moved visibility check into here.
(WebCore::shouldEmitNewlinesBeforeAndAfterNode):
(WebCore::TextIterator::shouldRepresentNodeOffsetZero):
(WebCore::TextIterator::representNodeOffsetZero):
New. Emits proper sequence when encountering offset 0 of a node, including the
m_endContainer. Started with code from handleNonTextNode.
(WebCore::TextIterator::handleNonTextNode):
Call representNodeOffsetZero.
(WebCore::TextIterator::exitNode):
Similar to shouldRepresentNodeOffsetZero, do not emit the newline if the node
was collapsed, and before any other emitted content.
(WebCore::TextIterator::emitCharacter):
(WebCore::TextIterator::emitText):
New. Consolidates code used by handleText and handleTextBox.
(WebCore::CharacterIterator::CharacterIterator):
Removed RUNFINDER.
(WebCore::WordAwareIterator::WordAwareIterator):
Removed RUNFINDER.
(WebCore::WordAwareIterator::advance):
Formatting.
(WebCore::TextIterator::rangeLength):
Formatting.
* editing/TextIterator.h:
Added member variables for tracking handling of the beginning of the range.
Eliminated concept of RUNDFINDER vs CONTENT TextIterator.
* editing/visible_units.cpp:
(WebCore::nextBoundary):
Eliminated concept of RUNDFINDER vs CONTENT TextIterator.
2007-03-13 David Hyatt <hyatt@apple.com>
 
Clean up the null image case in CachedImage::data to make sure the size totals will stay accurate.
......
......@@ -237,6 +237,11 @@ void Editor::deleteRange(Range* range, bool killRing, bool prepend, bool smartDe
}
break;
}
// clear the "start new kill ring sequence" setting, because it was set to true
// when the selection was updated by deleting the range
if (killRing)
setStartNewKillRingSequence(false);
}
bool Editor::deleteWithDirection(SelectionController::EDirection direction, TextGranularity granularity, bool killRing, bool isTypingAction)
......@@ -261,6 +266,9 @@ bool Editor::deleteWithDirection(SelectionController::EDirection direction, Text
SelectionController selectionController;
selectionController.setSelection(m_frame->selectionController()->selection());
selectionController.modify(SelectionController::EXTEND, direction, granularity);
if (killRing && selectionController.isCaret() && granularity != CharacterGranularity)
selectionController.modify(SelectionController::EXTEND, direction, CharacterGranularity);
range = selectionController.toRange();
switch (direction) {
......
......@@ -37,6 +37,7 @@
#include "Range.h"
#include "RenderTableCell.h"
#include "RenderTableRow.h"
#include "visible_units.h"
using namespace std;
using namespace WTF::Unicode;
......@@ -73,17 +74,17 @@ private:
CircularSearchBuffer &operator=(const CircularSearchBuffer&);
};
TextIterator::TextIterator() : m_endContainer(0), m_endOffset(0), m_positionNode(0), m_lastCharacter(0)
TextIterator::TextIterator() : m_startContainer(0), m_startOffset(0), m_endContainer(0), m_endOffset(0), m_positionNode(0), m_lastCharacter(0)
{
}
TextIterator::TextIterator(const Range *r, IteratorKind kind) : m_endContainer(0), m_endOffset(0), m_positionNode(0)
TextIterator::TextIterator(const Range *r) : m_startContainer(0), m_startOffset(0), m_endContainer(0), m_endOffset(0), m_positionNode(0)
{
if (!r)
return;
ExceptionCode ec = 0;
// get and validate the range endpoints
Node *startContainer = r->startContainer(ec);
int startOffset = r->startOffset(ec);
......@@ -96,15 +97,17 @@ TextIterator::TextIterator(const Range *r, IteratorKind kind) : m_endContainer(0
// the case, we could consider changing this assertion to an early return.
ASSERT(r->boundaryPointsValid());
// remember ending place - this does not change
// remember range - this does not change
m_startContainer = startContainer;
m_startOffset = startOffset;
m_endContainer = endContainer;
m_endOffset = endOffset;
// set up the current node for processing
m_node = r->startNode();
if (m_node == 0)
return;
m_offset = m_node == startContainer ? startOffset : 0;
m_offset = m_node == m_startContainer ? m_startOffset : 0;
m_handledNode = false;
m_handledChildren = false;
......@@ -116,12 +119,10 @@ TextIterator::TextIterator(const Range *r, IteratorKind kind) : m_endContainer(0
m_textBox = 0;
// initialize record of previous node processing
m_haveEmitted = false;
m_lastTextNode = 0;
m_lastTextNodeEndedWithCollapsedSpace = false;
if (kind == RUNFINDER)
m_lastCharacter = 0;
else
m_lastCharacter = '\n';
m_lastCharacter = 0;
#ifndef NDEBUG
// need this just because of the assert in advance()
......@@ -153,19 +154,27 @@ void TextIterator::advance()
return;
}
while (m_node && m_node != m_pastEndNode && !(m_node == m_endContainer && m_endOffset == 0)) {
while (m_node && m_node != m_pastEndNode) {
// if the range ends at offset 0 of an element, represent the
// position, but not the content, of that element e.g. if the
// node is a blockflow element, emit a newline that
// precedes the element
if (m_node == m_endContainer && m_endOffset == 0) {
representNodeOffsetZero();
m_node = 0;
return;
}
// handle current node according to its type
if (!m_handledNode) {
RenderObject *renderer = m_node->renderer();
if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) {
// FIXME: What about CDATA_SECTION_NODE?
if (renderer->style()->visibility() == VISIBLE)
m_handledNode = handleTextNode();
} else if (renderer && (renderer->isImage() || renderer->isWidget() || (renderer->element() && renderer->element()->isControl()))) {
if (renderer->style()->visibility() == VISIBLE)
m_handledNode = handleReplacedElement();
} else
if (renderer && renderer->isText() && m_node->nodeType() == Node::TEXT_NODE) // FIXME: What about CDATA_SECTION_NODE?
m_handledNode = handleTextNode();
else if (renderer && (renderer->isImage() || renderer->isWidget() || (renderer->element() && renderer->element()->isControl())))
m_handledNode = handleReplacedElement();
else
m_handledNode = handleNonTextNode();
if (m_positionNode)
return;
}
......@@ -211,9 +220,11 @@ static inline bool compareBoxStart(const InlineTextBox *first, const InlineTextB
bool TextIterator::handleTextNode()
{
m_lastTextNode = m_node;
RenderText* renderer = static_cast<RenderText*>(m_node->renderer());
if (renderer->style()->visibility() != VISIBLE)
return false;
m_lastTextNode = m_node;
String str = renderer->text();
// handle pre-formatted text
......@@ -230,15 +241,7 @@ bool TextIterator::handleTextNode()
if (runStart >= runEnd)
return true;
m_positionNode = m_node;
m_positionOffsetBaseNode = 0;
m_positionStartOffset = runStart;
m_positionEndOffset = runEnd;
m_textCharacters = str.characters() + runStart;
m_textLength = runEnd - runStart;
m_lastCharacter = str[runEnd - 1];
emitText(m_node, runStart, runEnd);
return true;
}
......@@ -302,18 +305,9 @@ void TextIterator::handleTextBox()
int subrunEnd = str.find('\n', runStart);
if (subrunEnd == -1 || subrunEnd > runEnd)
subrunEnd = runEnd;
m_offset = subrunEnd;
m_positionNode = m_node;
m_positionOffsetBaseNode = 0;
m_positionStartOffset = runStart;
m_positionEndOffset = subrunEnd;
m_textCharacters = str.characters() + runStart;
m_textLength = subrunEnd - runStart;
m_lastTextNodeEndedWithCollapsedSpace = false;
m_lastCharacter = str[subrunEnd - 1];
emitText(m_node, runStart, subrunEnd);
}
// If we are doing a subrun that doesn't go to the end of the text box,
......@@ -339,11 +333,16 @@ void TextIterator::handleTextBox()
bool TextIterator::handleReplacedElement()
{
if (m_node->renderer()->style()->visibility() != VISIBLE)
return false;
if (m_lastTextNodeEndedWithCollapsedSpace) {
emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1);
return false;
}
m_haveEmitted = true;
m_positionNode = m_node->parentNode();
m_positionOffsetBaseNode = m_node;
m_positionStartOffset = 0;
......@@ -423,7 +422,7 @@ static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node)
|| node->hasTagName(trTag)
|| node->hasTagName(ulTag));
}
// Need to make an exception for table cells, because they are blocks, but we
// want them tab-delimited rather than having newlines before and after.
if (isTableCell(node))
......@@ -437,7 +436,6 @@ static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node)
return true;
}
// Check for non-inline block
return !r->isInline() && r->isRenderBlock() && !r->isBody();
}
......@@ -483,25 +481,70 @@ static bool shouldEmitExtraNewlineForNode(Node* node)
return false;
}
bool TextIterator::shouldRepresentNodeOffsetZero()
{
// Can't represent the position without m_lastTextNode
if (!m_lastTextNode)
return false;
// Leave element positioned flush with start of a paragraph
// (e.g. do not insert tab before a table cell at the start of a paragraph)
if (m_lastCharacter == '\n')
return false;
// Otherwise, show the position if we have emitted any characters
if (m_haveEmitted)
return true;
// We've not emitted anything yet. Generally, there is no need for any positioning then.
// The only exception is when the element is visually not in the same position as
// the start of the range (e.g. the range starts at the end of the previous paragraph).
// NOTE: Creating VisiblePositions and comparing them is relatively expensive, so we
// make quicker checks to possibly avoid that. Another check that we could make is
// is whether the inline vs block flow changed since the previous visible element.
// I think we're already in a special enough case that that won't be needed, tho.
if (m_node == m_startContainer)
return false;
if (!m_node->isDescendantOf(m_startContainer))
return true;
VisiblePosition startPos = VisiblePosition(m_startContainer, m_startOffset, DOWNSTREAM);
VisiblePosition currPos = VisiblePosition(m_node, 0, DOWNSTREAM);
return startPos != currPos;
}
void TextIterator::representNodeOffsetZero()
{
// emit a character to show the positioning of m_node
if (!shouldRepresentNodeOffsetZero())
return;
if (shouldEmitTabBeforeNode(m_node))
emitCharacter('\t', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
else if (shouldEmitNewlineBeforeNode(m_node))
emitCharacter('\n', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
else if (shouldEmitSpaceBeforeAndAfterNode(m_node))
emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
}
bool TextIterator::handleNonTextNode()
{
if (shouldEmitNewlineForNode(m_node)) {
if (shouldEmitNewlineForNode(m_node))
emitCharacter('\n', m_node->parentNode(), m_node, 0, 1);
} else if (m_lastCharacter != '\n' && m_lastTextNode) {
// only add the tab or newline if not at the start of a line
if (shouldEmitTabBeforeNode(m_node))
emitCharacter('\t', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
else if (shouldEmitNewlineBeforeNode(m_node))
emitCharacter('\n', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
else if (shouldEmitSpaceBeforeAndAfterNode(m_node))
emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 0, 1);
}
else
representNodeOffsetZero();
return true;
}
void TextIterator::exitNode()
{
// prevent emitting a newline when exiting a collapsed block at beginning of the range
if (!m_haveEmitted)
return;
// Emit with a position *inside* m_node, after m_node's contents, in
// case it is a block, because the run should start where the
// emitted character is positioned visually.
......@@ -527,6 +570,8 @@ void TextIterator::exitNode()
void TextIterator::emitCharacter(UChar c, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset)
{
m_haveEmitted = true;
// remember information with which to construct the TextIterator::range()
// NOTE: textNode is often not a text node, so the range will specify child nodes of positionNode
m_positionNode = textNode;
......@@ -544,6 +589,23 @@ void TextIterator::emitCharacter(UChar c, Node *textNode, Node *offsetBaseNode,
m_lastCharacter = c;
}
void TextIterator::emitText(Node *textNode, int textStartOffset, int textEndOffset)
{
RenderText* renderer = static_cast<RenderText*>(m_node->renderer());
String str = renderer->text();
m_positionNode = textNode;
m_positionOffsetBaseNode = 0;
m_positionStartOffset = textStartOffset;
m_positionEndOffset = textEndOffset;
m_textCharacters = str.characters() + textStartOffset;
m_textLength = textEndOffset - textStartOffset;
m_lastCharacter = str[textEndOffset - 1];
m_lastTextNodeEndedWithCollapsedSpace = false;
m_haveEmitted = true;
}
PassRefPtr<Range> TextIterator::range() const
{
// use the current run information, if we have it
......@@ -792,11 +854,10 @@ CharacterIterator::CharacterIterator()
}
CharacterIterator::CharacterIterator(const Range *r)
: m_offset(0), m_runOffset(0), m_atBreak(true), m_textIterator(r, RUNFINDER)
: m_offset(0), m_runOffset(0), m_atBreak(true), m_textIterator(r)
{
while (!atEnd() && m_textIterator.length() == 0) {
while (!atEnd() && m_textIterator.length() == 0)
m_textIterator.advance();
}
}
PassRefPtr<Range> CharacterIterator::range() const
......@@ -881,7 +942,7 @@ WordAwareIterator::WordAwareIterator()
}