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: https://svn.webkit.org/repository/webkit/trunk@20166
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+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.
--- /dev/null
+759805c2c075cdc457d860b20cd9eb78
\ No newline at end of file
--- /dev/null
+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
--- /dev/null
+<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.
}
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)
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) {
#include "Range.h"
#include "RenderTableCell.h"
#include "RenderTableRow.h"
+#include "visible_units.h"
using namespace std;
using namespace WTF::Unicode;
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);
// 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;
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()
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;
}
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
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;
}
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,
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;
|| 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))
return true;
}
- // Check for non-inline block
return !r->isInline() && r->isRenderBlock() && !r->isBody();
}
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.
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;
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
}
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
}
WordAwareIterator::WordAwareIterator(const Range *r)
-: m_previousText(0), m_didLookAhead(false), m_textIterator(r, RUNFINDER)
+: m_previousText(0), m_didLookAhead(false), m_textIterator(r)
{
m_didLookAhead = true; // so we consider the first chunk from the text iterator
advance(); // get in position over the first chunk of text
m_didLookAhead = false;
// Go to next non-empty chunk
- while (!m_textIterator.atEnd() && m_textIterator.length() == 0) {
+ while (!m_textIterator.atEnd() && m_textIterator.length() == 0)
m_textIterator.advance();
- }
m_range = m_textIterator.range();
if (m_textIterator.atEnd())
int TextIterator::rangeLength(const Range *r)
{
int length = 0;
- for (TextIterator it(r); !it.atEnd(); it.advance()) {
+ for (TextIterator it(r); !it.atEnd(); it.advance())
length += it.length();
- }
+
return length;
}
// at points where replaced elements break up the text flow. The text comes back in
// chunks so as to optimize for performance of the iteration.
-enum IteratorKind { CONTENT = 0, RUNFINDER = 1 };
-
class TextIterator
{
public:
TextIterator();
- explicit TextIterator(const Range *, IteratorKind kind = CONTENT );
+ explicit TextIterator(const Range *);
bool atEnd() const { return !m_positionNode; }
void advance();
private:
void exitNode();
+ bool shouldRepresentNodeOffsetZero();
+ void representNodeOffsetZero();
bool handleTextNode();
bool handleReplacedElement();
bool handleNonTextNode();
void handleTextBox();
void emitCharacter(UChar, Node *textNode, Node *offsetBaseNode, int textStartOffset, int textEndOffset);
+ void emitText(Node *textNode, int textStartOffset, int textEndOffset);
// Current position, not necessarily of the text being returned, but position
// as we walk through the DOM tree.
bool m_handledNode;
bool m_handledChildren;
- // End of the range.
+ // The range.
+ Node *m_startContainer;
+ int m_startOffset;
Node *m_endContainer;
int m_endOffset;
Node *m_pastEndNode;
// Used when text boxes are out of order (Hebrew/Arabic w/ embeded LTR text)
Vector<InlineTextBox*> m_sortedTextBoxes;
size_t m_sortedTextBoxesPosition;
+
+ // Used when deciding whether to emit a "positioning" (e.g. newline) before any other content
+ bool m_haveEmitted;
};
// Iterates through the DOM range, returning all the text, and 0-length boundaries
ExceptionCode ec = 0;
searchRange->selectNodeContents(boundary, ec);
searchRange->setStart(start.node(), start.offset(), ec);
- TextIterator it(searchRange.get(), RUNFINDER);
+ TextIterator it(searchRange.get());
DeprecatedString string;
unsigned next = 0;
bool inTextSecurityMode = start.node() && start.node()->renderer() && start.node()->renderer()->style()->textSecurity() != TSNONE;