This source file includes following definitions.
- isAtomicNode
- comparePositions
- comparePositions
- comparePositions
- highestEditableRoot
- lowestEditableAncestor
- isEditablePosition
- isAtUnsplittableElement
- isRichlyEditablePosition
- editableRootForPosition
- unsplittableElementForPosition
- nextCandidate
- nextVisuallyDistinctCandidate
- previousCandidate
- previousVisuallyDistinctCandidate
- firstEditablePositionAfterPositionInRoot
- lastEditablePositionBeforePositionInRoot
- isBlock
- isInline
- enclosingBlock
- directionOfEnclosingBlock
- lastOffsetForEditing
- stringWithRebalancedWhitespace
- isTableStructureNode
- nonBreakingSpaceString
- isSpecialElement
- firstInSpecialElement
- lastInSpecialElement
- positionBeforeContainingSpecialElement
- positionAfterContainingSpecialElement
- isFirstPositionAfterTable
- isLastPositionBeforeTable
- visiblePositionBeforeNode
- visiblePositionAfterNode
- createRange
- isListElement
- isListItem
- enclosingNodeWithTag
- enclosingNodeOfType
- highestEnclosingNodeOfType
- hasARenderedDescendant
- highestNodeToRemoveInPruning
- enclosingTableCell
- enclosingAnchorElement
- enclosingList
- enclosingListChild
- enclosingEmptyListItem
- outermostEnclosingList
- canMergeLists
- isRenderedTableElement
- isRenderedTable
- isTableCell
- isEmptyTableCell
- createDefaultParagraphElement
- createBreakElement
- createOrderedListElement
- createUnorderedListElement
- createListItemElement
- createHTMLElement
- createHTMLElement
- isTabSpanNode
- isTabSpanTextNode
- tabSpanNode
- createTabSpanElement
- createTabSpanElement
- createTabSpanElement
- isNodeRendered
- numEnclosingMailBlockquotes
- updatePositionForNodeRemoval
- isMailBlockquote
- caretMinOffset
- caretMaxOffset
- lineBreakExistsAtVisiblePosition
- lineBreakExistsAtPosition
- selectionForParagraphIteration
- indexForVisiblePosition
- visiblePositionForIndex
- isVisiblyAdjacent
- isNodeVisiblyContainedWithin
- isRenderedAsNonInlineTableImageOrHR
- areIdenticalElements
- isNonTableCellHTMLBlockElement
- adjustedSelectionStartForStyleComputation
#include "config.h"
#include "core/editing/htmlediting.h"
#include "HTMLElementFactory.h"
#include "HTMLNames.h"
#include "bindings/v8/ExceptionState.h"
#include "bindings/v8/ExceptionStatePlaceholder.h"
#include "core/dom/Document.h"
#include "core/dom/NodeTraversal.h"
#include "core/dom/PositionIterator.h"
#include "core/dom/Range.h"
#include "core/dom/Text.h"
#include "core/dom/shadow/ShadowRoot.h"
#include "core/editing/Editor.h"
#include "core/editing/HTMLInterchange.h"
#include "core/editing/PlainTextRange.h"
#include "core/editing/TextIterator.h"
#include "core/editing/VisiblePosition.h"
#include "core/editing/VisibleSelection.h"
#include "core/editing/VisibleUnits.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLBRElement.h"
#include "core/html/HTMLDivElement.h"
#include "core/html/HTMLLIElement.h"
#include "core/html/HTMLOListElement.h"
#include "core/html/HTMLParagraphElement.h"
#include "core/html/HTMLTableCellElement.h"
#include "core/html/HTMLUListElement.h"
#include "core/rendering/RenderObject.h"
#include "wtf/Assertions.h"
#include "wtf/StdLibExtras.h"
#include "wtf/text/StringBuilder.h"
using namespace std;
namespace WebCore {
using namespace HTMLNames;
bool isAtomicNode(const Node *node)
{
return node && (!node->hasChildren() || editingIgnoresContent(node));
}
int comparePositions(const Position& a, const Position& b)
{
ASSERT(a.isNotNull());
ASSERT(b.isNotNull());
TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
ASSERT(commonScope);
if (!commonScope)
return 0;
Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
ASSERT(nodeA);
bool hasDescendentA = nodeA != a.containerNode();
int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
ASSERT(nodeB);
bool hasDescendentB = nodeB != b.containerNode();
int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
int bias = 0;
if (nodeA == nodeB) {
if (hasDescendentA)
bias = -1;
else if (hasDescendentB)
bias = 1;
}
int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
return result ? result : bias;
}
int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b)
{
return comparePositions(a.position(), b.position());
}
int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
{
return comparePositions(a.deepEquivalent(), b.deepEquivalent());
}
Node* highestEditableRoot(const Position& position, EditableType editableType)
{
Node* node = position.deprecatedNode();
if (!node)
return 0;
Node* highestRoot = editableRootForPosition(position, editableType);
if (!highestRoot)
return 0;
if (isHTMLBodyElement(*highestRoot))
return highestRoot;
node = highestRoot->parentNode();
while (node) {
if (node->rendererIsEditable(editableType))
highestRoot = node;
if (isHTMLBodyElement(*node))
break;
node = node->parentNode();
}
return highestRoot;
}
Node* lowestEditableAncestor(Node* node)
{
while (node) {
if (node->rendererIsEditable())
return node->rootEditableElement();
if (isHTMLBodyElement(*node))
break;
node = node->parentNode();
}
return 0;
}
bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
{
Node* node = p.deprecatedNode();
if (!node)
return false;
if (updateStyle == UpdateStyle)
node->document().updateLayoutIgnorePendingStylesheets();
else
ASSERT(updateStyle == DoNotUpdateStyle);
if (isRenderedTableElement(node))
node = node->parentNode();
return node->rendererIsEditable(editableType);
}
bool isAtUnsplittableElement(const Position& pos)
{
Node* node = pos.deprecatedNode();
return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
}
bool isRichlyEditablePosition(const Position& p, EditableType editableType)
{
Node* node = p.deprecatedNode();
if (!node)
return false;
if (isRenderedTableElement(node))
node = node->parentNode();
return node->rendererIsRichlyEditable(editableType);
}
Element* editableRootForPosition(const Position& p, EditableType editableType)
{
Node* node = p.containerNode();
if (!node)
return 0;
if (isRenderedTableElement(node))
node = node->parentNode();
return node->rootEditableElement(editableType);
}
Element* unsplittableElementForPosition(const Position& p)
{
Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
if (enclosingCell)
return enclosingCell;
return editableRootForPosition(p);
}
Position nextCandidate(const Position& position)
{
PositionIterator p = position;
while (!p.atEnd()) {
p.increment();
if (p.isCandidate())
return p;
}
return Position();
}
Position nextVisuallyDistinctCandidate(const Position& position)
{
Position p = position;
Position downstreamStart = p.downstream();
while (!p.atEndOfTree()) {
p = p.next(Character);
if (p.isCandidate() && p.downstream() != downstreamStart)
return p;
}
return Position();
}
Position previousCandidate(const Position& position)
{
PositionIterator p = position;
while (!p.atStart()) {
p.decrement();
if (p.isCandidate())
return p;
}
return Position();
}
Position previousVisuallyDistinctCandidate(const Position& position)
{
Position p = position;
Position downstreamStart = p.downstream();
while (!p.atStartOfTree()) {
p = p.previous(Character);
if (p.isCandidate() && p.downstream() != downstreamStart)
return p;
}
return Position();
}
VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
{
if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->rendererIsEditable())
return VisiblePosition(firstPositionInNode(highestRoot));
Position p = position;
if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
if (!shadowAncestor)
return VisiblePosition();
p = positionAfterNode(shadowAncestor);
}
while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(*p.deprecatedNode()) : nextVisuallyDistinctCandidate(p);
if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
return VisiblePosition();
return VisiblePosition(p);
}
VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
{
if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
return VisiblePosition(lastPositionInNode(highestRoot));
Position p = position;
if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
if (!shadowAncestor)
return VisiblePosition();
p = firstPositionInOrBeforeNode(shadowAncestor);
}
while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(*p.deprecatedNode()) : previousVisuallyDistinctCandidate(p);
if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
return VisiblePosition();
return VisiblePosition(p);
}
bool isBlock(const Node* node)
{
return node && node->renderer() && !node->renderer()->isInline() && !node->renderer()->isRubyText();
}
bool isInline(const Node* node)
{
return node && node->renderer() && node->renderer()->isInline();
}
Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
{
Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0;
}
TextDirection directionOfEnclosingBlock(const Position& position)
{
Node* enclosingBlockNode = enclosingBlock(position.containerNode());
if (!enclosingBlockNode)
return LTR;
RenderObject* renderer = enclosingBlockNode->renderer();
return renderer ? renderer->style()->direction() : LTR;
}
int lastOffsetForEditing(const Node* node)
{
ASSERT(node);
if (!node)
return 0;
if (node->offsetInCharacters())
return node->maxCharacterOffset();
if (node->hasChildren())
return node->countChildren();
if (editingIgnoresContent(node))
return 1;
return 0;
}
String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
{
unsigned length = string.length();
StringBuilder rebalancedString;
rebalancedString.reserveCapacity(length);
bool previousCharacterWasSpace = false;
for (size_t i = 0; i < length; i++) {
UChar c = string[i];
if (!isWhitespace(c)) {
rebalancedString.append(c);
previousCharacterWasSpace = false;
continue;
}
if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) {
rebalancedString.append(noBreakSpace);
previousCharacterWasSpace = false;
} else {
rebalancedString.append(' ');
previousCharacterWasSpace = true;
}
}
ASSERT(rebalancedString.length() == length);
return rebalancedString.toString();
}
bool isTableStructureNode(const Node *node)
{
RenderObject* renderer = node->renderer();
return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
}
const String& nonBreakingSpaceString()
{
DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
return nonBreakingSpaceString;
}
bool isSpecialElement(const Node *n)
{
if (!n)
return false;
if (!n->isHTMLElement())
return false;
if (n->isLink())
return true;
RenderObject* renderer = n->renderer();
if (!renderer)
return false;
if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
return true;
if (renderer->style()->isFloating())
return true;
return false;
}
static Node* firstInSpecialElement(const Position& pos)
{
Node* rootEditableElement = pos.containerNode()->rootEditableElement();
for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
if (isSpecialElement(n)) {
VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
if (isRenderedTable(n) && vPos == firstInElement.next())
return n;
if (vPos == firstInElement)
return n;
}
return 0;
}
static Node* lastInSpecialElement(const Position& pos)
{
Node* rootEditableElement = pos.containerNode()->rootEditableElement();
for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
if (isSpecialElement(n)) {
VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
if (isRenderedTable(n) && vPos == lastInElement.previous())
return n;
if (vPos == lastInElement)
return n;
}
return 0;
}
Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
{
Node* n = firstInSpecialElement(pos);
if (!n)
return pos;
Position result = positionInParentBeforeNode(*n);
if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
return pos;
if (containingSpecialElement)
*containingSpecialElement = n;
return result;
}
Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
{
Node* n = lastInSpecialElement(pos);
if (!n)
return pos;
Position result = positionInParentAfterNode(*n);
if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
return pos;
if (containingSpecialElement)
*containingSpecialElement = n;
return result;
}
Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
{
Position upstream(visiblePosition.deepEquivalent().upstream());
if (isRenderedTable(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode())
return upstream.deprecatedNode();
return 0;
}
Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
{
Position downstream(visiblePosition.deepEquivalent().downstream());
if (isRenderedTable(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode())
return downstream.deprecatedNode();
return 0;
}
VisiblePosition visiblePositionBeforeNode(Node& node)
{
if (node.hasChildren())
return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM);
ASSERT(node.parentNode());
ASSERT(!node.parentNode()->isShadowRoot());
return VisiblePosition(positionInParentBeforeNode(node));
}
VisiblePosition visiblePositionAfterNode(Node& node)
{
if (node.hasChildren())
return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM);
ASSERT(node.parentNode());
ASSERT(!node.parentNode()->isShadowRoot());
return VisiblePosition(positionInParentAfterNode(node));
}
PassRefPtrWillBeRawPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState)
{
RefPtrWillBeRawPtr<Range> selectedRange = Range::create(document);
selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
if (!exceptionState.hadException())
selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
return selectedRange.release();
}
bool isListElement(Node* n)
{
return (n && (isHTMLUListElement(*n) || isHTMLOListElement(*n) || isHTMLDListElement(*n)));
}
bool isListItem(const Node* n)
{
return n && n->renderer() && n->renderer()->isListItem();
}
Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
{
if (p.isNull())
return 0;
Node* root = highestEditableRoot(p);
for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
if (root && !n->rendererIsEditable())
continue;
if (n->hasTagName(tagName))
return n;
if (n == root)
return 0;
}
return 0;
}
Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
{
ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
if (p.isNull())
return 0;
Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
if (root && !n->rendererIsEditable())
continue;
if (nodeIsOfType(n))
return n;
if (n == root)
return 0;
}
return 0;
}
Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
{
Node* highest = 0;
Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
if (root && !n->rendererIsEditable())
continue;
if (nodeIsOfType(n))
highest = n;
if (n == root)
break;
}
return highest;
}
static bool hasARenderedDescendant(Node* node, Node* excludedNode)
{
for (Node* n = node->firstChild(); n;) {
if (n == excludedNode) {
n = NodeTraversal::nextSkippingChildren(*n, node);
continue;
}
if (n->renderer())
return true;
n = NodeTraversal::next(*n, node);
}
return false;
}
Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode)
{
Node* previousNode = 0;
Node* rootEditableElement = node ? node->rootEditableElement() : 0;
for (; node; node = node->parentNode()) {
if (RenderObject* renderer = node->renderer()) {
if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node)
return previousNode;
}
previousNode = node;
}
return 0;
}
Node* enclosingTableCell(const Position& p)
{
return toElement(enclosingNodeOfType(p, isTableCell));
}
Element* enclosingAnchorElement(const Position& p)
{
if (p.isNull())
return 0;
Node* node = p.deprecatedNode();
while (node && !(node->isElementNode() && node->isLink()))
node = node->parentNode();
return toElement(node);
}
HTMLElement* enclosingList(Node* node)
{
if (!node)
return 0;
Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
if (isHTMLUListElement(*n) || isHTMLOListElement(*n))
return toHTMLElement(n);
if (n == root)
return 0;
}
return 0;
}
Node* enclosingListChild(Node *node)
{
if (!node)
return 0;
Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
if (isHTMLLIElement(*n) || (isListElement(n->parentNode()) && n != root))
return n;
if (n == root || isTableCell(n))
return 0;
}
return 0;
}
Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
{
Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
return 0;
VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
if (firstInListChild != visiblePos || lastInListChild != visiblePos)
return 0;
return listChildNode;
}
HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
{
HTMLElement* list = enclosingList(node);
if (!list)
return 0;
while (HTMLElement* nextList = enclosingList(list)) {
if (nextList == rootList)
break;
list = nextList;
}
return list;
}
bool canMergeLists(Element* firstList, Element* secondList)
{
if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
return false;
return firstList->hasTagName(secondList->tagQName())
&& firstList->rendererIsEditable() && secondList->rendererIsEditable()
&& firstList->rootEditableElement() == secondList->rootEditableElement()
&& isVisiblyAdjacent(positionInParentAfterNode(*firstList), positionInParentBeforeNode(*secondList));
}
bool isRenderedTableElement(const Node* node)
{
return isHTMLTableElement(*node) && node->renderer();
}
bool isRenderedTable(const Node* node)
{
if (!node || !node->isElementNode())
return false;
RenderObject* renderer = node->renderer();
return (renderer && renderer->isTable());
}
bool isTableCell(const Node* node)
{
ASSERT(node);
RenderObject* r = node->renderer();
return r ? r->isTableCell() : isHTMLTableCellElement(*node);
}
bool isEmptyTableCell(const Node* node)
{
while (node && !node->renderer())
node = node->parentNode();
if (!node)
return false;
RenderObject* renderer = node->renderer();
if (renderer->isBR()) {
renderer = renderer->parent();
if (!renderer)
return false;
}
if (!renderer->isTableCell())
return false;
RenderObject* childRenderer = renderer->firstChild();
if (!childRenderer)
return true;
if (!childRenderer->isBR())
return false;
return !childRenderer->nextSibling();
}
PassRefPtr<HTMLElement> createDefaultParagraphElement(Document& document)
{
switch (document.frame()->editor().defaultParagraphSeparator()) {
case EditorParagraphSeparatorIsDiv:
return HTMLDivElement::create(document);
case EditorParagraphSeparatorIsP:
return HTMLParagraphElement::create(document);
}
ASSERT_NOT_REACHED();
return nullptr;
}
PassRefPtr<HTMLElement> createBreakElement(Document& document)
{
return HTMLBRElement::create(document);
}
PassRefPtr<HTMLElement> createOrderedListElement(Document& document)
{
return HTMLOListElement::create(document);
}
PassRefPtr<HTMLElement> createUnorderedListElement(Document& document)
{
return HTMLUListElement::create(document);
}
PassRefPtr<HTMLElement> createListItemElement(Document& document)
{
return HTMLLIElement::create(document);
}
PassRefPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
{
return createHTMLElement(document, name.localName());
}
PassRefPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
{
return HTMLElementFactory::createHTMLElement(tagName, document, 0, false);
}
bool isTabSpanNode(const Node* node)
{
return isHTMLSpanElement(node) && toElement(node)->getAttribute(classAttr) == AppleTabSpanClass;
}
bool isTabSpanTextNode(const Node* node)
{
return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
}
Node* tabSpanNode(const Node* node)
{
return isTabSpanTextNode(node) ? node->parentNode() : 0;
}
PassRefPtr<Element> createTabSpanElement(Document& document, PassRefPtr<Node> prpTabTextNode)
{
RefPtr<Node> tabTextNode = prpTabTextNode;
RefPtr<Element> spanElement = document.createElement(spanTag, false);
spanElement->setAttribute(classAttr, AppleTabSpanClass);
spanElement->setAttribute(styleAttr, "white-space:pre");
if (!tabTextNode)
tabTextNode = document.createEditingTextNode("\t");
spanElement->appendChild(tabTextNode.release());
return spanElement.release();
}
PassRefPtr<Element> createTabSpanElement(Document& document, const String& tabText)
{
return createTabSpanElement(document, document.createTextNode(tabText));
}
PassRefPtr<Element> createTabSpanElement(Document& document)
{
return createTabSpanElement(document, PassRefPtr<Node>());
}
bool isNodeRendered(const Node *node)
{
if (!node)
return false;
RenderObject* renderer = node->renderer();
if (!renderer)
return false;
return renderer->style()->visibility() == VISIBLE;
}
unsigned numEnclosingMailBlockquotes(const Position& p)
{
unsigned num = 0;
for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
if (isMailBlockquote(n))
num++;
return num;
}
void updatePositionForNodeRemoval(Position& position, Node& node)
{
if (position.isNull())
return;
switch (position.anchorType()) {
case Position::PositionIsBeforeChildren:
if (position.containerNode() == node)
position = positionInParentBeforeNode(node);
break;
case Position::PositionIsAfterChildren:
if (position.containerNode() == node)
position = positionInParentAfterNode(node);
break;
case Position::PositionIsOffsetInAnchor:
if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.nodeIndex())
position.moveToOffset(position.offsetInContainerNode() - 1);
else if (node.containsIncludingShadowDOM(position.containerNode()))
position = positionInParentBeforeNode(node);
break;
case Position::PositionIsAfterAnchor:
if (node.containsIncludingShadowDOM(position.anchorNode()))
position = positionInParentAfterNode(node);
break;
case Position::PositionIsBeforeAnchor:
if (node.containsIncludingShadowDOM(position.anchorNode()))
position = positionInParentBeforeNode(node);
break;
}
}
bool isMailBlockquote(const Node *node)
{
if (!node || !node->hasTagName(blockquoteTag))
return false;
return toElement(node)->getAttribute("type") == "cite";
}
int caretMinOffset(const Node* n)
{
RenderObject* r = n->renderer();
ASSERT(!n->isCharacterDataNode() || !r || r->isText());
return r ? r->caretMinOffset() : 0;
}
int caretMaxOffset(const Node* n)
{
if (n->isTextNode() && n->renderer())
return n->renderer()->caretMaxOffset();
return lastOffsetForEditing(n);
}
bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
{
return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
}
bool lineBreakExistsAtPosition(const Position& position)
{
if (position.isNull())
return false;
if (isHTMLBRElement(*position.anchorNode()) && position.atFirstEditingPositionForNode())
return true;
if (!position.anchorNode()->renderer())
return false;
if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline())
return false;
Text* textNode = toText(position.anchorNode());
unsigned offset = position.offsetInContainerNode();
return offset < textNode->length() && textNode->data()[offset] == '\n';
}
VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
{
VisibleSelection newSelection(original);
VisiblePosition startOfSelection(newSelection.visibleStart());
VisiblePosition endOfSelection(newSelection.visibleEnd());
if (Node* table = isFirstPositionAfterTable(endOfSelection))
if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
if (Node* table = isLastPositionBeforeTable(startOfSelection))
if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
return newSelection;
}
int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtr<ContainerNode>& scope)
{
if (visiblePosition.isNull())
return 0;
Position p(visiblePosition.deepEquivalent());
Document& document = *p.document();
ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
if (shadowRoot)
scope = shadowRoot;
else
scope = document.documentElement();
RefPtrWillBeRawPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
return TextIterator::rangeLength(range.get(), true);
}
VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
{
if (!scope)
return VisiblePosition();
RefPtrWillBeRawPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope);
if (!range)
return VisiblePosition();
return VisiblePosition(range->startPosition());
}
bool isVisiblyAdjacent(const Position& first, const Position& second)
{
return VisiblePosition(first) == VisiblePosition(second.upstream());
}
bool isNodeVisiblyContainedWithin(Node& node, const Range& selectedRange)
{
if (selectedRange.compareNode(&node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
return true;
bool startIsVisuallySame = visiblePositionBeforeNode(node) == VisiblePosition(selectedRange.startPosition());
if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange.endPosition()) < 0)
return true;
bool endIsVisuallySame = visiblePositionAfterNode(node) == VisiblePosition(selectedRange.endPosition());
if (endIsVisuallySame && comparePositions(selectedRange.startPosition(), positionInParentBeforeNode(node)) < 0)
return true;
return startIsVisuallySame && endIsVisuallySame;
}
bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
{
if (!node)
return false;
RenderObject* renderer = node->renderer();
return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
}
bool areIdenticalElements(const Node* first, const Node* second)
{
if (!first->isElementNode() || !second->isElementNode())
return false;
const Element* firstElement = toElement(first);
const Element* secondElement = toElement(second);
if (!firstElement->hasTagName(secondElement->tagQName()))
return false;
return firstElement->hasEquivalentAttributes(secondElement);
}
bool isNonTableCellHTMLBlockElement(const Node* node)
{
return node->hasTagName(listingTag)
|| node->hasTagName(olTag)
|| node->hasTagName(preTag)
|| node->hasTagName(tableTag)
|| node->hasTagName(ulTag)
|| node->hasTagName(xmpTag)
|| node->hasTagName(h1Tag)
|| node->hasTagName(h2Tag)
|| node->hasTagName(h3Tag)
|| node->hasTagName(h4Tag)
|| node->hasTagName(h5Tag);
}
Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
{
VisiblePosition visiblePosition(selection.start());
if (visiblePosition.isNull())
return Position();
if (selection.isCaret())
return visiblePosition.deepEquivalent();
if (isEndOfParagraph(visiblePosition))
return visiblePosition.next().deepEquivalent().downstream();
return visiblePosition.deepEquivalent().downstream();
}
}