#include "config.h"
#include "core/dom/Position.h"
-#include <stdio.h>
-#include "HTMLNames.h"
+#include "core/HTMLNames.h"
#include "core/css/CSSComputedStyleDeclaration.h"
#include "core/dom/PositionIterator.h"
#include "core/dom/Text.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleUnits.h"
#include "core/editing/htmlediting.h"
-#include "core/frame/Frame.h"
+#include "core/frame/LocalFrame.h"
#include "core/frame/Settings.h"
-#include "platform/Logging.h"
+#include "core/html/HTMLTableElement.h"
#include "core/rendering/InlineIterator.h"
#include "core/rendering/InlineTextBox.h"
#include "core/rendering/RenderBlock.h"
#include "core/rendering/RenderInline.h"
#include "core/rendering/RenderText.h"
+#include "platform/Logging.h"
#include "wtf/text/CString.h"
#include "wtf/unicode/CharacterNames.h"
+#include <stdio.h>
-namespace WebCore {
+namespace blink {
using namespace HTMLNames;
static Node* nextRenderedEditable(Node* node)
{
- while ((node = node->nextLeafNode())) {
+ for (node = node->nextLeafNode(); node; node = node->nextLeafNode()) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
- if (!node->rendererIsEditable())
+ if (!node->hasEditableStyle())
continue;
if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
return node;
static Node* previousRenderedEditable(Node* node)
{
- while ((node = node->previousLeafNode())) {
+ for (node = node->previousLeafNode(); node; node = node->previousLeafNode()) {
RenderObject* renderer = node->renderer();
if (!renderer)
continue;
- if (!node->rendererIsEditable())
+ if (!node->hasEditableStyle())
continue;
if ((renderer->isBox() && toRenderBox(renderer)->inlineBoxWrapper()) || (renderer->isText() && toRenderText(renderer)->firstTextBox()))
return node;
return 0;
}
-Position::Position(PassRefPtr<Node> anchorNode, LegacyEditingOffset offset)
+Position::Position(PassRefPtrWillBeRawPtr<Node> anchorNode, LegacyEditingOffset offset)
: m_anchorNode(anchorNode)
, m_offset(offset.value())
, m_anchorType(anchorTypeForLegacyEditingPosition(m_anchorNode.get(), m_offset))
ASSERT(!m_anchorNode || !m_anchorNode->isPseudoElement());
}
-Position::Position(PassRefPtr<Node> anchorNode, AnchorType anchorType)
+Position::Position(PassRefPtrWillBeRawPtr<Node> anchorNode, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(0)
, m_anchorType(anchorType)
&& (m_anchorNode->isTextNode() || editingIgnoresContent(m_anchorNode.get()))));
}
-Position::Position(PassRefPtr<Node> anchorNode, int offset, AnchorType anchorType)
+Position::Position(PassRefPtrWillBeRawPtr<Node> anchorNode, int offset, AnchorType anchorType)
: m_anchorNode(anchorNode)
, m_offset(offset)
, m_anchorType(anchorType)
ASSERT(anchorType == PositionIsOffsetInAnchor);
}
-Position::Position(PassRefPtr<Text> textNode, unsigned offset)
+Position::Position(PassRefPtrWillBeRawPtr<Text> textNode, unsigned offset)
: m_anchorNode(textNode)
, m_offset(static_cast<int>(offset))
, m_anchorType(PositionIsOffsetInAnchor)
ASSERT(m_anchorNode);
}
-void Position::moveToPosition(PassRefPtr<Node> node, int offset)
+void Position::moveToPosition(PassRefPtrWillBeRawPtr<Node> node, int offset)
{
ASSERT(!editingIgnoresContent(node.get()));
ASSERT(anchorType() == PositionIsOffsetInAnchor || m_isLegacyEditingPosition);
// FIXME: This should only be necessary for legacy positions, but is also needed for positions before and after Tables
if (m_offset <= 0 && (m_anchorType != PositionIsAfterAnchor && m_anchorType != PositionIsAfterChildren)) {
- if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isRenderedTableElement(m_anchorNode.get())))
- return positionInParentBeforeNode(m_anchorNode.get());
+ if (m_anchorNode->parentNode() && (editingIgnoresContent(m_anchorNode.get()) || isRenderedHTMLTableElement(m_anchorNode.get())))
+ return positionInParentBeforeNode(*m_anchorNode);
return Position(m_anchorNode.get(), 0, PositionIsOffsetInAnchor);
}
if (!m_anchorNode->offsetInCharacters()
- && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->childNodeCount())
- && (editingIgnoresContent(m_anchorNode.get()) || isRenderedTableElement(m_anchorNode.get()))
+ && (m_anchorType == PositionIsAfterAnchor || m_anchorType == PositionIsAfterChildren || static_cast<unsigned>(m_offset) == m_anchorNode->countChildren())
+ && (editingIgnoresContent(m_anchorNode.get()) || isRenderedHTMLTableElement(m_anchorNode.get()))
&& containerNode()) {
- return positionInParentAfterNode(m_anchorNode.get());
+ return positionInParentAfterNode(*m_anchorNode);
}
return Position(containerNode(), computeOffsetInContainerNode(), PositionIsOffsetInAnchor);
{
if (!m_anchorNode)
return 0;
-
switch (anchorType()) {
case PositionIsBeforeChildren:
return 0;
case PositionIsAfterChildren:
return m_anchorNode->lastChild();
case PositionIsOffsetInAnchor:
- return m_anchorNode->childNode(m_offset - 1); // -1 converts to childNode((unsigned)-1) and returns null.
+ return m_offset ? NodeTraversal::childAt(*m_anchorNode, m_offset - 1) : 0;
case PositionIsBeforeAnchor:
return m_anchorNode->previousSibling();
case PositionIsAfterAnchor:
case PositionIsAfterChildren:
return 0;
case PositionIsOffsetInAnchor:
- return m_anchorNode->childNode(m_offset);
+ return NodeTraversal::childAt(*m_anchorNode, m_offset);
case PositionIsBeforeAnchor:
return m_anchorNode.get();
case PositionIsAfterAnchor:
return toElement(n);
}
-PassRefPtr<CSSComputedStyleDeclaration> Position::computedStyle() const
+PassRefPtrWillBeRawPtr<CSSComputedStyleDeclaration> Position::computedStyle() const
{
Element* elem = element();
if (!elem)
- return 0;
+ return nullptr;
return CSSComputedStyleDeclaration::create(elem);
}
ASSERT(offset >= 0);
if (offset > 0) {
- if (Node* child = node->childNode(offset - 1))
+ if (Node* child = NodeTraversal::childAt(*node, offset - 1))
return lastPositionInOrAfterNode(child);
// There are two reasons child might be 0:
// FIXME: Negative offsets shouldn't be allowed. We should catch this earlier.
ASSERT(offset >= 0);
- if (Node* child = node->childNode(offset))
+ if (Node* child = NodeTraversal::childAt(*node, offset))
return firstPositionInOrBeforeNode(child);
- if (!node->hasChildNodes() && offset < lastOffsetForEditing(node)) {
+ if (!node->hasChildren() && offset < lastOffsetForEditing(node)) {
// There are two reasons child might be 0:
// 1) The node is node like a text node that is not an element, and therefore has no children.
// Going forward one character at a time is correct.
bool Position::atEditingBoundary() const
{
Position nextPosition = downstream(CanCrossEditingBoundary);
- if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable())
+ if (atFirstEditingPositionForNode() && nextPosition.isNotNull() && !nextPosition.deprecatedNode()->hasEditableStyle())
return true;
Position prevPosition = upstream(CanCrossEditingBoundary);
- if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable())
+ if (atLastEditingPositionForNode() && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->hasEditableStyle())
return true;
- return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->rendererIsEditable()
- && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->rendererIsEditable();
+ return nextPosition.isNotNull() && !nextPosition.deprecatedNode()->hasEditableStyle()
+ && prevPosition.isNotNull() && !prevPosition.deprecatedNode()->hasEditableStyle();
}
Node* Position::parentEditingBoundary() const
return 0;
Node* boundary = m_anchorNode.get();
- while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->rendererIsEditable() == boundary->parentNode()->rendererIsEditable())
+ while (boundary != documentElement && boundary->nonShadowBoundaryParentNode() && m_anchorNode->hasEditableStyle() == boundary->parentNode()->hasEditableStyle())
boundary = boundary->nonShadowBoundaryParentNode();
return boundary;
return result;
}
-// return first preceding DOM position rendered at a different location, or "this"
-Position Position::previousCharacterPosition(EAffinity affinity) const
-{
- if (isNull())
- return Position();
-
- Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
-
- bool atStartOfLine = isStartOfLine(VisiblePosition(*this, affinity));
- bool rendered = isCandidate();
-
- Position currentPos = *this;
- while (!currentPos.atStartOfTree()) {
- currentPos = currentPos.previous();
-
- if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
- return *this;
-
- if (atStartOfLine || !rendered) {
- if (currentPos.isCandidate())
- return currentPos;
- } else if (rendersInDifferentPosition(currentPos))
- return currentPos;
- }
-
- return *this;
-}
-
-// return first following position rendered at a different location, or "this"
-Position Position::nextCharacterPosition(EAffinity affinity) const
-{
- if (isNull())
- return Position();
-
- Node* fromRootEditableElement = deprecatedNode()->rootEditableElement();
-
- bool atEndOfLine = isEndOfLine(VisiblePosition(*this, affinity));
- bool rendered = isCandidate();
-
- Position currentPos = *this;
- while (!currentPos.atEndOfTree()) {
- currentPos = currentPos.next();
-
- if (currentPos.deprecatedNode()->rootEditableElement() != fromRootEditableElement)
- return *this;
-
- if (atEndOfLine || !rendered) {
- if (currentPos.isCandidate())
- return currentPos;
- } else if (rendersInDifferentPosition(currentPos))
- return currentPos;
- }
-
- return *this;
-}
-
// Whether or not [node, 0] and [node, lastOffsetForEditing(node)] are their own VisiblePositions.
// If true, adjacent candidates are visually distinct.
// FIXME: Disregard nodes with renderers that have no height, as we do in isCandidate.
return true;
// Don't include inline tables.
- if (node->hasTagName(tableTag))
+ if (isHTMLTableElement(*node))
return false;
+ // A Marquee elements are moving so we should assume their ends are always
+ // visibily distinct.
+ if (isHTMLMarqueeElement(*node))
+ return true;
+
// There is a VisiblePosition inside an empty inline-block container.
- return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->firstChild();
+ return node->renderer()->isReplaced() && canHaveChildrenForEditing(node) && toRenderBox(node->renderer())->height() != 0 && !node->hasChildren();
}
static Node* enclosingVisualBoundary(Node* node)
// FIXME: PositionIterator should respect Before and After positions.
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
PositionIterator currentPos = lastVisible;
- bool startEditable = startNode->rendererIsEditable();
+ bool startEditable = startNode->hasEditableStyle();
Node* lastNode = startNode;
bool boundaryCrossed = false;
for (; !currentPos.atStart(); currentPos.decrement()) {
Node* currentNode = currentPos.node();
// Don't check for an editability change if we haven't moved to a different node,
- // to avoid the expense of computing rendererIsEditable().
+ // to avoid the expense of computing hasEditableStyle().
if (currentNode != lastNode) {
// Don't change editability.
- bool currentEditable = currentNode->rendererIsEditable();
+ bool currentEditable = currentNode->hasEditableStyle();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
return lastVisible;
// Return position after tables and nodes which have content that can be ignored.
- if (editingIgnoresContent(currentNode) || isRenderedTableElement(currentNode)) {
+ if (editingIgnoresContent(currentNode) || isRenderedHTMLTableElement(currentNode)) {
if (currentPos.atEndOfNode())
return positionAfterNode(currentNode);
continue;
// FIXME: PositionIterator should respect Before and After positions.
PositionIterator lastVisible = m_anchorType == PositionIsAfterAnchor ? createLegacyEditingPosition(m_anchorNode.get(), caretMaxOffset(m_anchorNode.get())) : *this;
PositionIterator currentPos = lastVisible;
- bool startEditable = startNode->rendererIsEditable();
+ bool startEditable = startNode->hasEditableStyle();
Node* lastNode = startNode;
bool boundaryCrossed = false;
for (; !currentPos.atEnd(); currentPos.increment()) {
Node* currentNode = currentPos.node();
// Don't check for an editability change if we haven't moved to a different node,
- // to avoid the expense of computing rendererIsEditable().
+ // to avoid the expense of computing hasEditableStyle().
if (currentNode != lastNode) {
// Don't change editability.
- bool currentEditable = currentNode->rendererIsEditable();
+ bool currentEditable = currentNode->hasEditableStyle();
if (startEditable != currentEditable) {
if (rule == CannotCrossEditingBoundary)
break;
// stop before going above the body, up into the head
// return the last visible streamer position
- if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode())
+ if (isHTMLBodyElement(*currentNode) && currentPos.atEndOfNode())
break;
// Do not move to a visually distinct position.
lastVisible = currentPos;
// Return position before tables and nodes which have content that can be ignored.
- if (editingIgnoresContent(currentNode) || isRenderedTableElement(currentNode)) {
+ if (editingIgnoresContent(currentNode) || isRenderedHTMLTableElement(currentNode)) {
if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset())
return createLegacyEditingPosition(currentNode, renderer->caretMinOffset());
continue;
bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* renderer)
{
RenderObject* stop = renderer->nextInPreOrderAfterChildren();
- for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder())
+ for (RenderObject *o = renderer->slowFirstChild(); o && o != stop; o = o->nextInPreOrder())
if (o->nonPseudoNode()) {
if ((o->isText() && boundingBoxLogicalHeight(o, toRenderText(o)->linesBoundingBox()))
|| (o->isBox() && toRenderBox(o)->pixelSnappedLogicalHeight())
if (renderer->isText())
return !nodeIsUserSelectNone(deprecatedNode()) && inRenderedText();
- if (isRenderedTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode()))
+ if (renderer->isSVG()) {
+ // We don't consider SVG elements are contenteditable except for
+ // associated renderer returns isText() true, e.g. RenderSVGInlineText.
+ return false;
+ }
+
+ if (isRenderedHTMLTableElement(deprecatedNode()) || editingIgnoresContent(deprecatedNode()))
return (atFirstEditingPositionForNode() || atLastEditingPositionForNode()) && !nodeIsUserSelectNone(deprecatedNode()->parentNode());
- if (m_anchorNode->hasTagName(htmlTag))
+ if (isHTMLHtmlElement(*m_anchorNode))
return false;
if (renderer->isRenderBlockFlow()) {
- if (toRenderBlock(renderer)->logicalHeight() || m_anchorNode->hasTagName(bodyTag)) {
+ if (toRenderBlock(renderer)->logicalHeight() || isHTMLBodyElement(*m_anchorNode)) {
if (!Position::hasRenderedNonAnonymousDescendantsWithHeight(renderer))
return atFirstEditingPositionForNode() && !Position::nodeIsUserSelectNone(deprecatedNode());
- return m_anchorNode->rendererIsEditable() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
+ return m_anchorNode->hasEditableStyle() && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
} else {
- Frame* frame = m_anchorNode->document().frame();
+ LocalFrame* frame = m_anchorNode->document().frame();
bool caretBrowsing = frame->settings() && frame->settings()->caretBrowsingEnabled();
- return (caretBrowsing || m_anchorNode->rendererIsEditable()) && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
+ return (caretBrowsing || m_anchorNode->hasEditableStyle()) && !Position::nodeIsUserSelectNone(deprecatedNode()) && atEditingBoundary();
}
return false;
return false;
if (deprecatedNode() == pos.deprecatedNode()) {
- if (deprecatedNode()->hasTagName(brTag))
+ if (isHTMLBRElement(*deprecatedNode()))
return false;
if (m_offset == pos.deprecatedEditingOffset())
}
}
- if (deprecatedNode()->hasTagName(brTag) && pos.isCandidate())
+ if (isHTMLBRElement(*deprecatedNode()) && pos.isCandidate())
return true;
- if (pos.deprecatedNode()->hasTagName(brTag) && isCandidate())
+ if (isHTMLBRElement(*pos.deprecatedNode()) && isCandidate())
return true;
- if (deprecatedNode()->enclosingBlockFlowElement() != pos.deprecatedNode()->enclosingBlockFlowElement())
+ if (!inSameContainingBlockFlowElement(deprecatedNode(), pos.deprecatedNode()))
return true;
if (deprecatedNode()->isTextNode() && !inRenderedText())
return true;
}
-// This assumes that it starts in editable content.
-Position Position::leadingWhitespacePosition(EAffinity affinity, bool considerNonCollapsibleWhitespace) const
-{
- ASSERT(isEditablePosition(*this, ContentIsEditable, DoNotUpdateStyle));
- if (isNull())
- return Position();
-
- if (upstream().deprecatedNode()->hasTagName(brTag))
- return Position();
-
- Position prev = previousCharacterPosition(affinity);
- if (prev != *this && prev.deprecatedNode()->inSameContainingBlockFlowElement(deprecatedNode()) && prev.deprecatedNode()->isTextNode()) {
- String string = toText(prev.deprecatedNode())->data();
- UChar c = string[prev.deprecatedEditingOffset()];
- if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
- if (isEditablePosition(prev))
- return prev;
- }
-
- return Position();
-}
-
-// This assumes that it starts in editable content.
-Position Position::trailingWhitespacePosition(EAffinity, bool considerNonCollapsibleWhitespace) const
-{
- ASSERT(isEditablePosition(*this, ContentIsEditable, DoNotUpdateStyle));
- if (isNull())
- return Position();
-
- VisiblePosition v(*this);
- UChar c = v.characterAfter();
- // The space must not be in another paragraph and it must be editable.
- if (!isEndOfParagraph(v) && v.next(CannotCrossEditingBoundary).isNotNull())
- if (considerNonCollapsibleWhitespace ? (isSpaceOrNewline(c) || c == noBreakSpace) : isCollapsibleWhitespace(c))
- return *this;
-
- return Position();
-}
-
void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const
{
getInlineBoxAndOffset(affinity, primaryDirection(), inlineBox, caretOffset);
static bool isNonTextLeafChild(RenderObject* object)
{
- if (object->firstChild())
+ if (object->slowFirstChild())
return false;
if (object->isText())
return false;
static InlineTextBox* searchAheadForBetterMatch(RenderObject* renderer)
{
RenderBlock* container = renderer->containingBlock();
- RenderObject* next = renderer;
- while ((next = next->nextInPreOrder(container))) {
+ for (RenderObject* next = renderer->nextInPreOrder(container); next; next = next->nextInPreOrder(container)) {
if (next->isRenderBlock())
return 0;
if (next->isBR())
break;
inlineBox = prevBox;
}
- caretOffset = inlineBox->caretLeftmostOffset();
+ if (m_anchorNode->selfOrAncestorHasDirAutoAttribute())
+ caretOffset = inlineBox->bidiLevel() < level ? inlineBox->caretLeftmostOffset() : inlineBox->caretRightmostOffset();
+ else
+ caretOffset = inlineBox->caretLeftmostOffset();
} else if (nextBox->bidiLevel() > level) {
// Left edge of a "tertiary" run. Set to the right edge of that run.
while (InlineBox* tertiaryBox = inlineBox->nextLeafChildIgnoringLineBreak()) {
return primaryDirection;
}
+void Position::trace(Visitor* visitor)
+{
+ visitor->trace(m_anchorNode);
+}
void Position::debugPosition(const char* msg) const
{
-} // namespace WebCore
+} // namespace blink
#ifndef NDEBUG
-void showTree(const WebCore::Position& pos)
+void showTree(const blink::Position& pos)
{
pos.showTreeForThis();
}
-void showTree(const WebCore::Position* pos)
+void showTree(const blink::Position* pos)
{
if (pos)
pos->showTreeForThis();