Index: Source/core/dom/Position.cpp |
diff --git a/Source/core/dom/Position.cpp b/Source/core/dom/Position.cpp |
index c917fb5bf97a64c57766a55d202d19a370c2d1c6..7aaad8f3763b03fcd163760beb468c559717f566 100644 |
--- a/Source/core/dom/Position.cpp |
+++ b/Source/core/dom/Position.cpp |
@@ -42,7 +42,7 @@ |
#include "core/rendering/InlineTextBox.h" |
#include "core/rendering/RenderBlock.h" |
#include "core/rendering/RenderInline.h" |
-#include "core/rendering/RenderText.h" |
+#include "core/rendering/RenderTextFragment.h" |
#include "wtf/text/CString.h" |
#include "wtf/unicode/CharacterNames.h" |
@@ -457,16 +457,45 @@ bool Position::atEndOfTree() const |
return !findParent(deprecatedNode()) && m_offset >= lastOffsetForEditing(deprecatedNode()); |
} |
-int Position::renderedOffset() const |
+RenderObject* Position::renderer() const |
{ |
- if (!deprecatedNode()->isTextNode()) |
- return m_offset; |
+ if (!m_anchorNode) |
+ return 0; |
+ RenderObject* renderer = m_anchorNode->renderer(); |
eseidel
2013/09/09 20:28:27
Positions can be relative to anchor nodes other th
yosin_UTC9
2013/09/20 09:30:26
This function is replacement of Position.anchorNod
|
+ if (!renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment()) |
+ return renderer; |
+ if (m_offset >= static_cast<int>(toRenderTextFragment(renderer)->textStartOffset())) |
+ return renderer; |
+ return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter(); |
+} |
+ |
+int Position::offsetInRenderer() const |
+{ |
+ switch (m_isLegacyEditingPosition ? PositionIsOffsetInAnchor : m_anchorType) { |
+ case PositionIsBeforeChildren: |
+ case PositionIsBeforeAnchor: |
eseidel
2013/09/09 20:28:27
Wouldn't this possibly have a different renderer d
yosin_UTC9
2013/09/20 09:30:26
This function does as same as deprecatedEditingOff
|
+ case PositionIsOffsetInAnchor: { |
+ RenderObject* renderer = this->renderer(); |
+ if (!renderer || !renderer->isText()) |
+ return m_offset; |
+ return m_offset - toRenderText(renderer)->textStartOffset(); |
+ } |
+ case PositionIsAfterChildren: |
+ case PositionIsAfterAnchor: |
+ return offsetForPositionAfterAnchor(); |
+ } |
+ ASSERT_NOT_REACHED(); |
+ return m_offset; |
+} |
- if (!deprecatedNode()->renderer()) |
+int Position::renderedOffset() const |
+{ |
+ RenderObject* renderer = this->renderer(); |
+ if (!renderer || !renderer->isText()) |
return m_offset; |
- int result = 0; |
- RenderText* textRenderer = toRenderText(deprecatedNode()->renderer()); |
+ RenderText* textRenderer = toRenderText(renderer); |
+ int result = textRenderer->textStartOffset(); |
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
int start = box->start(); |
int end = box->start() + box->len(); |
@@ -596,14 +625,17 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const |
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; |
PositionIterator currentPos = lastVisible; |
bool startEditable = startNode->rendererIsEditable(); |
+ RenderObject* startRenderer = renderer(); |
Node* lastNode = startNode; |
+ RenderObject* lastRenderer = startRenderer; |
bool boundaryCrossed = false; |
for (; !currentPos.atStart(); currentPos.decrement()) { |
Node* currentNode = currentPos.node(); |
+ RenderObject* currentRenderer = currentPos.renderer(); |
// Don't check for an editability change if we haven't moved to a different node, |
// to avoid the expense of computing rendererIsEditable(). |
- if (currentNode != lastNode) { |
+ if (currentRenderer != lastRenderer) { |
// Don't change editability. |
bool currentEditable = currentNode->rendererIsEditable(); |
if (startEditable != currentEditable) { |
@@ -612,6 +644,7 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const |
boundaryCrossed = true; |
} |
lastNode = currentNode; |
+ lastRenderer = currentPos.renderer(); |
} |
// If we've moved to a position that is visually distinct, return the last saved position. There |
@@ -620,8 +653,7 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const |
return lastVisible; |
// skip position in unrendered or invisible node |
- RenderObject* renderer = currentNode->renderer(); |
- if (!renderer || renderer->style()->visibility() != VISIBLE) |
+ if (!currentRenderer || currentRenderer->style()->visibility() != VISIBLE) |
continue; |
if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
@@ -646,18 +678,12 @@ Position Position::upstream(EditingBoundaryCrossingRule rule) const |
} |
// return current position if it is in rendered text |
- if (renderer->isText() && toRenderText(renderer)->firstTextBox()) { |
- if (currentNode != startNode) { |
- // This assertion fires in layout tests in the case-transform.html test because |
- // of a mix-up between offsets in the text in the DOM tree with text in the |
- // render tree which can have a different length due to case transformation. |
- // Until we resolve that, disable this so we can run the layout tests! |
- //ASSERT(currentOffset >= renderer->caretMaxOffset()); |
- return createLegacyEditingPosition(currentNode, renderer->caretMaxOffset()); |
- } |
+ if (currentRenderer->isText() && toRenderText(currentRenderer)->firstTextBox()) { |
+ RenderText* textRenderer = toRenderText(currentRenderer); |
+ if (currentRenderer != startRenderer) |
+ return createLegacyEditingPosition(currentNode, textRenderer->caretMaxOffset() + textRenderer->textStartOffset()); |
- unsigned textOffset = currentPos.offsetInLeafNode(); |
- RenderText* textRenderer = toRenderText(renderer); |
+ unsigned textOffset = currentPos.offsetInLeafNode() - textRenderer->textStartOffset(); |
InlineTextBox* lastTextBox = textRenderer->lastTextBox(); |
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
if (textOffset <= box->start() + box->len()) { |
@@ -719,14 +745,17 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const |
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this; |
PositionIterator currentPos = lastVisible; |
bool startEditable = startNode->rendererIsEditable(); |
+ RenderObject* startRenderer = renderer(); |
Node* lastNode = startNode; |
+ RenderObject* lastRenderer = startRenderer; |
bool boundaryCrossed = false; |
for (; !currentPos.atEnd(); currentPos.increment()) { |
Node* currentNode = currentPos.node(); |
+ RenderObject* currentRenderer = currentPos.renderer(); |
// Don't check for an editability change if we haven't moved to a different node, |
// to avoid the expense of computing rendererIsEditable(). |
- if (currentNode != lastNode) { |
+ if (currentRenderer!= lastRenderer) { |
// Don't change editability. |
bool currentEditable = currentNode->rendererIsEditable(); |
if (startEditable != currentEditable) { |
@@ -736,6 +765,7 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const |
} |
lastNode = currentNode; |
+ lastRenderer = currentRenderer; |
} |
// stop before going above the body, up into the head |
@@ -753,8 +783,7 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const |
return lastVisible; |
// skip position in unrendered or invisible node |
- RenderObject* renderer = currentNode->renderer(); |
- if (!renderer || renderer->style()->visibility() != VISIBLE) |
+ if (!currentRenderer || currentRenderer->style()->visibility() != VISIBLE) |
continue; |
if (rule == CanCrossEditingBoundary && boundaryCrossed) { |
@@ -768,20 +797,18 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const |
// Return position before tables and nodes which have content that can be ignored. |
if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { |
- if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset()) |
- return createLegacyEditingPosition(currentNode, renderer->caretMinOffset()); |
+ if (currentPos.offsetInLeafNode() <= currentRenderer->caretMinOffset()) |
+ return createLegacyEditingPosition(currentNode, currentRenderer->caretMinOffset()); |
continue; |
} |
// return current position if it is in rendered text |
- if (renderer->isText() && toRenderText(renderer)->firstTextBox()) { |
- if (currentNode != startNode) { |
- ASSERT(currentPos.atStartOfNode()); |
- return createLegacyEditingPosition(currentNode, renderer->caretMinOffset()); |
- } |
+ if (currentRenderer->isText() && toRenderText(currentRenderer)->firstTextBox()) { |
+ RenderText* textRenderer = toRenderText(currentRenderer); |
+ if (currentRenderer != startRenderer) |
+ return createLegacyEditingPosition(currentNode, textRenderer->caretMinOffset() + textRenderer->textStartOffset()); |
- unsigned textOffset = currentPos.offsetInLeafNode(); |
- RenderText* textRenderer = toRenderText(renderer); |
+ unsigned textOffset = currentPos.offsetInLeafNode() - textRenderer->textStartOffset(); |
InlineTextBox* lastTextBox = textRenderer->lastTextBox(); |
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
if (textOffset <= box->end()) { |
@@ -842,9 +869,16 @@ bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* render |
return false; |
} |
+// FIXME: Caller of nodeIsUserSelectNode() should consider first letter and |
+// remaining text have different user-select CSS property. |
bool Position::nodeIsUserSelectNone(Node* node) |
{ |
- return node && node->renderer() && node->renderer()->style()->userSelect() == SELECT_NONE && node->renderer()->style()->userModify() == READ_ONLY; |
+ return node && rendererIsUserSelectNone(node->renderer()); |
+} |
+ |
+bool Position::rendererIsUserSelectNone(RenderObject* renderer) |
+{ |
+ return renderer && renderer->style()->userSelect() == SELECT_NONE && renderer->style()->userModify() == READ_ONLY; |
} |
ContainerNode* Position::findParent(const Node* node) |
@@ -883,10 +917,7 @@ Node* Position::rootUserSelectAllForNode(Node* node) |
bool Position::isCandidate() const |
{ |
- if (isNull()) |
- return false; |
- |
- RenderObject* renderer = deprecatedNode()->renderer(); |
+ RenderObject* renderer = this->renderer(); |
if (!renderer) |
return false; |
@@ -898,7 +929,7 @@ bool Position::isCandidate() const |
return !m_offset && m_anchorType != PositionIsAfterAnchor && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); |
if (renderer->isText()) |
- return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText(); |
+ return !rendererIsUserSelectNone(renderer) && inRenderedText(); |
if (isTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode())) |
return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode()); |
@@ -920,24 +951,23 @@ bool Position::isCandidate() const |
bool Position::inRenderedText() const |
{ |
- if (isNull() || !deprecatedNode()->isTextNode()) |
+ RenderObject* renderer = this->renderer(); |
+ if (!renderer || !renderer->isText()) |
return false; |
- RenderObject* renderer = deprecatedNode()->renderer(); |
- if (!renderer) |
- return false; |
- |
- RenderText *textRenderer = toRenderText(renderer); |
+ RenderText* textRenderer = toRenderText(renderer); |
+ int offset = m_offset - textRenderer->textStartOffset(); |
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
- if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { |
+ if (offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { |
// The offset we're looking for is before this node |
// this means the offset must be in content that is |
// not rendered. Return false. |
return false; |
} |
- if (box->containsCaretOffset(m_offset)) |
+ if (box->containsCaretOffset(offset)) { |
// Return false for offsets inside composed characters. |
- return m_offset == 0 || m_offset == textRenderer->nextOffset(textRenderer->previousOffset(m_offset)); |
+ return !offset || offset == textRenderer->nextOffset(textRenderer->previousOffset(offset)); |
+ } |
} |
return false; |
@@ -945,22 +975,20 @@ bool Position::inRenderedText() const |
bool Position::isRenderedCharacter() const |
{ |
- if (isNull() || !deprecatedNode()->isTextNode()) |
- return false; |
- |
- RenderObject* renderer = deprecatedNode()->renderer(); |
- if (!renderer) |
+ RenderObject* renderer = this->renderer(); |
+ if (!renderer || !renderer->isText()) |
return false; |
RenderText* textRenderer = toRenderText(renderer); |
+ int offset = m_offset - textRenderer->textStartOffset(); |
for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
- if (m_offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { |
+ if (offset < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { |
// The offset we're looking for is before this node |
// this means the offset must be in content that is |
// not rendered. Return false. |
return false; |
} |
- if (m_offset >= static_cast<int>(box->start()) && m_offset < static_cast<int>(box->start() + box->len())) |
+ if (offset >= static_cast<int>(box->start()) && offset < static_cast<int>(box->start() + box->len())) |
return true; |
} |
@@ -969,14 +997,11 @@ bool Position::isRenderedCharacter() const |
bool Position::rendersInDifferentPosition(const Position &pos) const |
{ |
- if (isNull() || pos.isNull()) |
- return false; |
- |
- RenderObject* renderer = deprecatedNode()->renderer(); |
+ RenderObject* renderer = this->renderer(); |
if (!renderer) |
return false; |
- RenderObject* posRenderer = pos.deprecatedNode()->renderer(); |
+ RenderObject* posRenderer = pos.renderer(); |
if (!posRenderer) |
return false; |
@@ -1157,7 +1182,7 @@ static Position upstreamIgnoringEditingBoundaries(Position position) |
void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const |
{ |
caretOffset = deprecatedEditingOffset(); |
- RenderObject* renderer = deprecatedNode()->renderer(); |
+ RenderObject* renderer = this->renderer(); |
if (!renderer->isText()) { |
inlineBox = 0; |
@@ -1186,6 +1211,16 @@ void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDi |
InlineTextBox* box; |
InlineTextBox* candidate = 0; |
+ if (textRenderer->isTextFragment() && toRenderTextFragment(textRenderer)->firstLetter()) { |
+ if (caretOffset) { |
+ caretOffset -= textRenderer->textStartOffset(); |
+ } else { |
+ textRenderer = toRenderTextFragment(textRenderer)->firstRenderTextInFirstLetter(); |
+ if (!textRenderer) |
+ return; |
+ } |
+ } |
+ |
for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { |
int caretMinOffset = box->caretMinOffset(); |
int caretMaxOffset = box->caretMaxOffset(); |