| 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();
|
| + 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:
|
| + 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();
|
|
|