Diff
Copied: branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash-expected.txt (from rev 93347, trunk/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash-expected.txt) (0 => 93499)
--- branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash-expected.txt (rev 0)
+++ branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash-expected.txt 2011-08-22 09:07:56 UTC (rev 93499)
@@ -0,0 +1 @@
+PASS if WebKit did not hit assertions
Copied: branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash.html (from rev 93347, trunk/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash.html) (0 => 93499)
--- branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash.html (rev 0)
+++ branches/chromium/835/LayoutTests/editing/text-iterator/backward-textiterator-first-letter-crash.html 2011-08-22 09:07:56 UTC (rev 93499)
@@ -0,0 +1,15 @@
+<style>
+div:first-letter { margin-top: 0em; }
+</style>
+<script>
+if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+ layoutTestController.waitUntilDone();
+}
+function done() {
+ document.body.innerHTML = 'PASS if WebKit did not hit assertions';
+ if (window.layoutTestController)
+ layoutTestController.notifyDone();
+}
+</script>
+<body _onload_="setTimeout(done, 100);"><div>AB<select autofocus contenteditable>
Modified: branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary-expected.txt (93498 => 93499)
--- branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary-expected.txt 2011-08-22 09:04:29 UTC (rev 93498)
+++ branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary-expected.txt 2011-08-22 09:07:56 UTC (rev 93499)
@@ -2,9 +2,9 @@
hello world'
white-space: normal;
-FAIL: moving forward by word put caret at offset 4 but expected 6
-PASS: moving backward by word put caret at offset 0
+FAIL: moving forward by word from offset 4 put caret at offset 10 but expected 6
+PASS: moving backward by word from offset 4 put caret at offset 1
white-space: pre;
-FAIL: moving forward by word put caret at offset 4 but expected 6
-PASS: moving backward by word put caret at offset 0
+FAIL: moving forward by word from offset 4 put caret at offset 10 but expected 6
+PASS: moving backward by word from offset 4 put caret at offset 1
Modified: branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary.html (93498 => 93499)
--- branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary.html 2011-08-22 09:04:29 UTC (rev 93498)
+++ branches/chromium/835/LayoutTests/editing/text-iterator/first-letter-word-boundary.html 2011-08-22 09:07:56 UTC (rev 93499)
@@ -20,7 +20,8 @@
layoutTestController.dumpAsText();
function runTest(actor, expectedOffset) {
- var action = "" + ' put caret at offset ';
+ window.getSelection().setPosition(test.firstChild, 4);
+ var action = "" + ' from offset ' + 4 + ' put caret at offset ';
var startOffset = window.getSelection().getRangeAt(0).startOffset;
action += startOffset;
if (startOffset == expectedOffset)
@@ -31,16 +32,15 @@
var test = document.getElementById('test');
var console = document.getElementById('console');
-window.getSelection().setPosition(test, 0);
console.innerHTML += 'white-space: normal;\n';
runTest(function () {window.getSelection().modify('move', 'forward', 'word'); return 'moving forward by word';}, 6);
-runTest(function () {window.getSelection().modify('move', 'backward', 'word'); return 'moving backward by word';}, 0);
+runTest(function () {window.getSelection().modify('move', 'backward', 'word'); return 'moving backward by word';}, 1);
console.innerHTML += 'white-space: pre;\n';
test.style.whiteSpace = 'pre';
runTest(function () {window.getSelection().modify('move', 'forward', 'word'); return 'moving forward by word';}, 6);
-runTest(function () {window.getSelection().modify('move', 'backward', 'word'); return 'moving backward by word';}, 0);
+runTest(function () {window.getSelection().modify('move', 'backward', 'word'); return 'moving backward by word';}, 1);
</script>
</body>
Modified: branches/chromium/835/Source/WebCore/editing/TextIterator.cpp (93498 => 93499)
--- branches/chromium/835/Source/WebCore/editing/TextIterator.cpp 2011-08-22 09:04:29 UTC (rev 93498)
+++ branches/chromium/835/Source/WebCore/editing/TextIterator.cpp 2011-08-22 09:07:56 UTC (rev 93499)
@@ -603,21 +603,30 @@
}
}
+static inline RenderText* firstRenderTextInFirstLetter(RenderObject* firstLetter)
+{
+ if (!firstLetter)
+ return 0;
+
+ // FIXME: Should this check descendent objects?
+ for (RenderObject* current = firstLetter->firstChild(); current; current = current->nextSibling()) {
+ if (current->isText())
+ return toRenderText(current);
+ }
+ return 0;
+}
+
void TextIterator::handleTextNodeFirstLetter(RenderTextFragment* renderer)
{
if (renderer->firstLetter()) {
RenderObject* r = renderer->firstLetter();
if (r->style()->visibility() != VISIBLE && !m_ignoresStyleVisibility)
return;
- for (RenderObject *currChild = r->firstChild(); currChild; currChild->nextSibling()) {
- if (currChild->isText()) {
- RenderText* firstLetter = toRenderText(currChild);
- m_handledFirstLetter = true;
- m_remainingTextBox = m_textBox;
- m_textBox = firstLetter->firstTextBox();
- m_firstLetterText = firstLetter;
- return;
- }
+ if (RenderText* firstLetter = firstRenderTextInFirstLetter(r)) {
+ m_handledFirstLetter = true;
+ m_remainingTextBox = m_textBox;
+ m_textBox = firstLetter->firstTextBox();
+ m_firstLetterText = firstLetter;
}
}
m_handledFirstLetter = true;
@@ -1043,14 +1052,46 @@
SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator()
: m_behavior(TextIteratorDefaultBehavior)
, m_node(0)
+ , m_offset(0)
+ , m_handledNode(false)
+ , m_handledChildren(false)
+ , m_startNode(0)
+ , m_startOffset(0)
+ , m_endNode(0)
+ , m_endOffset(0)
, m_positionNode(0)
+ , m_positionStartOffset(0)
+ , m_positionEndOffset(0)
+ , m_textCharacters(0)
+ , m_textLength(0)
+ , m_lastTextNode(0)
+ , m_lastCharacter(0)
+ , m_singleCharacterBuffer(0)
+ , m_havePassedStartNode(false)
+ , m_shouldHandleFirstLetter(false)
{
}
SimplifiedBackwardsTextIterator::SimplifiedBackwardsTextIterator(const Range* r, TextIteratorBehavior behavior)
: m_behavior(behavior)
, m_node(0)
+ , m_offset(0)
+ , m_handledNode(false)
+ , m_handledChildren(false)
+ , m_startNode(0)
+ , m_startOffset(0)
+ , m_endNode(0)
+ , m_endOffset(0)
, m_positionNode(0)
+ , m_positionStartOffset(0)
+ , m_positionEndOffset(0)
+ , m_textCharacters(0)
+ , m_textLength(0)
+ , m_lastTextNode(0)
+ , m_lastCharacter(0)
+ , m_singleCharacterBuffer(0)
+ , m_havePassedStartNode(false)
+ , m_shouldHandleFirstLetter(false)
{
ASSERT(m_behavior == TextIteratorDefaultBehavior);
@@ -1178,25 +1219,64 @@
{
m_lastTextNode = m_node;
- RenderText* renderer = toRenderText(m_node->renderer());
- String str = renderer->text();
+ int startOffset;
+ int offsetInNode;
+ RenderText* renderer = handleFirstLetter(startOffset, offsetInNode);
+ if (!renderer)
+ return true;
- if (!renderer->firstTextBox() && str.length() > 0)
+ String text = renderer->text();
+ if (!renderer->firstTextBox() && text.length() > 0)
return true;
m_positionEndOffset = m_offset;
-
- m_offset = (m_node == m_startNode) ? m_startOffset : 0;
+ m_offset = startOffset + offsetInNode;
m_positionNode = m_node;
m_positionStartOffset = m_offset;
+
+ ASSERT(0 <= m_positionStartOffset - offsetInNode && m_positionStartOffset - offsetInNode <= static_cast<int>(text.length()));
+ ASSERT(1 <= m_positionEndOffset - offsetInNode && m_positionEndOffset - offsetInNode <= static_cast<int>(text.length()));
+ ASSERT(m_positionStartOffset <= m_positionEndOffset);
+
m_textLength = m_positionEndOffset - m_positionStartOffset;
- m_textCharacters = str.characters() + m_positionStartOffset;
+ m_textCharacters = text.characters() + (m_positionStartOffset - offsetInNode);
+ ASSERT(m_textCharacters >= text.characters());
+ ASSERT(m_textCharacters + m_textLength <= text.characters() + static_cast<int>(text.length()));
- m_lastCharacter = str[m_positionEndOffset - 1];
+ m_lastCharacter = text[m_positionEndOffset - 1];
- return true;
+ return !m_shouldHandleFirstLetter;
}
+RenderText* SimplifiedBackwardsTextIterator::handleFirstLetter(int& startOffset, int& offsetInNode)
+{
+ RenderText* renderer = toRenderText(m_node->renderer());
+ startOffset = (m_node == m_startNode) ? m_startOffset : 0;
+
+ if (!renderer->isTextFragment()) {
+ offsetInNode = 0;
+ return renderer;
+ }
+
+ RenderTextFragment* fragment = toRenderTextFragment(renderer);
+ int offsetAfterFirstLetter = fragment->start();
+ if (startOffset >= offsetAfterFirstLetter) {
+ ASSERT(!m_shouldHandleFirstLetter);
+ offsetInNode = offsetAfterFirstLetter;
+ return renderer;
+ }
+
+ if (!m_shouldHandleFirstLetter && offsetAfterFirstLetter < m_offset) {
+ m_shouldHandleFirstLetter = true;
+ offsetInNode = offsetAfterFirstLetter;
+ return renderer;
+ }
+
+ m_shouldHandleFirstLetter = false;
+ offsetInNode = 0;
+ return firstRenderTextInFirstLetter(fragment->firstLetter());
+}
+
bool SimplifiedBackwardsTextIterator::handleReplacedElement()
{
unsigned index = m_node->nodeIndex();
Modified: branches/chromium/835/Source/WebCore/editing/TextIterator.h (93498 => 93499)
--- branches/chromium/835/Source/WebCore/editing/TextIterator.h 2011-08-22 09:04:29 UTC (rev 93498)
+++ branches/chromium/835/Source/WebCore/editing/TextIterator.h 2011-08-22 09:07:56 UTC (rev 93499)
@@ -203,6 +203,7 @@
private:
void exitNode();
bool handleTextNode();
+ RenderText* handleFirstLetter(int& startOffset, int& offsetInNode);
bool handleReplacedElement();
bool handleNonTextNode();
void emitCharacter(UChar, Node*, int startOffset, int endOffset);
@@ -240,6 +241,9 @@
// Whether m_node has advanced beyond the iteration range (i.e. m_startNode).
bool m_havePassedStartNode;
+
+ // Should handle first-letter renderer in the next call to handleTextNode.
+ bool m_shouldHandleFirstLetter;
};
// Builds on the text iterator, adding a character position so we can walk one