Chromium Code Reviews| Index: Source/core/dom/Position.cpp |
| diff --git a/Source/core/dom/Position.cpp b/Source/core/dom/Position.cpp |
| index 8d52a1e59c15ea0bf34ca0181b834fa116332a62..c554fb2eacaf3f444da4d3c2efdf67b25056665a 100644 |
| --- a/Source/core/dom/Position.cpp |
| +++ b/Source/core/dom/Position.cpp |
| @@ -44,7 +44,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" |
| @@ -453,16 +453,47 @@ bool Position::atEndOfTree() const |
| return !deprecatedNode()->parentNode() && m_offset >= lastOffsetForEditing(deprecatedNode()); |
| } |
| -int Position::renderedOffset() const |
| +RenderObject* Position::rendererOfAnchorNode() const |
| { |
| - if (!deprecatedNode()->isTextNode()) |
| - return m_offset; |
| + if (!m_anchorNode) |
| + return 0; |
| + RenderObject* renderer = m_anchorNode->renderer(); |
| + if (!renderer || !renderer->isText() || !toRenderText(renderer)->isTextFragment()) |
| + return renderer; |
| + if (m_offset >= static_cast<int>(toRenderTextFragment(renderer)->textStartOffset())) |
| + return renderer; |
| + return toRenderTextFragment(renderer)->firstRenderTextInFirstLetter(); |
| +} |
| + |
| +// Position::deprecatedOffsetInRendererOfAnchorNode() is replacement of usage of |
| +// deprecatedEditingOffset() in FrameSelection::updateAppearance(). |
| +int Position::deprecatedOffsetInRendererOfAnchorNode() const |
| +{ |
| + switch (m_isLegacyEditingPosition ? PositionIsOffsetInAnchor : m_anchorType) { |
| + case PositionIsBeforeChildren: |
| + case PositionIsBeforeAnchor: |
| + case PositionIsOffsetInAnchor: { |
| + RenderObject* renderer = rendererOfAnchorNode(); |
| + 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 = rendererOfAnchorNode(); |
| + 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(); |
| @@ -592,14 +623,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 = rendererOfAnchorNode(); |
| 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) { |
| @@ -608,6 +642,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 |
| @@ -616,8 +651,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) { |
| @@ -642,18 +676,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()) { |
| @@ -715,14 +743,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 = rendererOfAnchorNode(); |
| 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) { |
| @@ -732,6 +763,7 @@ Position Position::downstream(EditingBoundaryCrossingRule rule) const |
| } |
| lastNode = currentNode; |
| + lastRenderer = currentRenderer; |
| } |
| // stop before going above the body, up into the head |
| @@ -749,8 +781,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) { |
| @@ -764,20 +795,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()) { |
| @@ -838,9 +867,21 @@ bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* render |
| return false; |
| } |
| +static bool rendererIsUserSelectNone(RenderObject* renderer) |
| +{ |
| + return renderer && renderer->style()->userSelect() == SELECT_NONE && renderer->style()->userModify() == READ_ONLY; |
| +} |
| + |
| +// 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()->isSelectable(); |
| + return node && rendererIsUserSelectNone(node->renderer()); |
| +} |
| + |
| +ContainerNode* Position::findParent(const Node* node) |
|
ojan
2013/09/23 23:01:45
This looks unused.
|
| +{ |
| + return node->parentNode(); |
| } |
| bool Position::nodeIsUserSelectAll(const Node* node) |
| @@ -872,10 +913,7 @@ Node* Position::rootUserSelectAllForNode(Node* node) |
| bool Position::isCandidate() const |
| { |
| - if (isNull()) |
| - return false; |
| - |
| - RenderObject* renderer = deprecatedNode()->renderer(); |
| + RenderObject* renderer = rendererOfAnchorNode(); |
| if (!renderer) |
| return false; |
| @@ -887,7 +925,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()); |
| @@ -912,24 +950,23 @@ bool Position::isCandidate() const |
| bool Position::inRenderedText() const |
| { |
| - if (isNull() || !deprecatedNode()->isTextNode()) |
| + RenderObject* renderer = rendererOfAnchorNode(); |
| + 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; |
| @@ -937,22 +974,20 @@ bool Position::inRenderedText() const |
| bool Position::isRenderedCharacter() const |
| { |
| - if (isNull() || !deprecatedNode()->isTextNode()) |
| - return false; |
| - |
| - RenderObject* renderer = deprecatedNode()->renderer(); |
| - if (!renderer) |
| + RenderObject* renderer = rendererOfAnchorNode(); |
| + 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; |
| } |
| @@ -961,14 +996,11 @@ bool Position::isRenderedCharacter() const |
| bool Position::rendersInDifferentPosition(const Position &pos) const |
| { |
| - if (isNull() || pos.isNull()) |
| - return false; |
| - |
| - RenderObject* renderer = deprecatedNode()->renderer(); |
| + RenderObject* renderer = rendererOfAnchorNode(); |
| if (!renderer) |
| return false; |
| - RenderObject* posRenderer = pos.deprecatedNode()->renderer(); |
| + RenderObject* posRenderer = pos.rendererOfAnchorNode(); |
| if (!posRenderer) |
| return false; |
| @@ -1149,7 +1181,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 = rendererOfAnchorNode(); |
| if (!renderer->isText()) { |
| inlineBox = 0; |
| @@ -1178,6 +1210,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(); |