2 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "core/editing/htmlediting.h"
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
31 #include "core/HTMLElementFactory.h"
32 #include "core/HTMLNames.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/PositionIterator.h"
36 #include "core/dom/Range.h"
37 #include "core/dom/Text.h"
38 #include "core/dom/shadow/ShadowRoot.h"
39 #include "core/editing/Editor.h"
40 #include "core/editing/HTMLInterchange.h"
41 #include "core/editing/PlainTextRange.h"
42 #include "core/editing/TextIterator.h"
43 #include "core/editing/VisiblePosition.h"
44 #include "core/editing/VisibleSelection.h"
45 #include "core/editing/VisibleUnits.h"
46 #include "core/frame/LocalFrame.h"
47 #include "core/frame/UseCounter.h"
48 #include "core/html/HTMLBRElement.h"
49 #include "core/html/HTMLDivElement.h"
50 #include "core/html/HTMLLIElement.h"
51 #include "core/html/HTMLOListElement.h"
52 #include "core/html/HTMLParagraphElement.h"
53 #include "core/html/HTMLSpanElement.h"
54 #include "core/html/HTMLTableCellElement.h"
55 #include "core/html/HTMLUListElement.h"
56 #include "core/rendering/RenderObject.h"
57 #include "core/rendering/RenderTableCell.h"
58 #include "wtf/Assertions.h"
59 #include "wtf/StdLibExtras.h"
60 #include "wtf/text/StringBuilder.h"
64 using namespace HTMLNames;
66 // Atomic means that the node has no children, or has children which are ignored for the
67 // purposes of editing.
68 bool isAtomicNode(const Node *node)
70 return node && (!node->hasChildren() || editingIgnoresContent(node));
73 // Compare two positions, taking into account the possibility that one or both
74 // could be inside a shadow tree. Only works for non-null values.
75 int comparePositions(const Position& a, const Position& b)
77 ASSERT(a.isNotNull());
78 ASSERT(b.isNotNull());
79 TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
85 Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
87 bool hasDescendentA = nodeA != a.containerNode();
88 int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
90 Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
92 bool hasDescendentB = nodeB != b.containerNode();
93 int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
99 else if (hasDescendentB)
103 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
104 return result ? result : bias;
107 int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b)
109 return comparePositions(a.position(), b.position());
112 int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
114 return comparePositions(a.deepEquivalent(), b.deepEquivalent());
117 ContainerNode* highestEditableRoot(const Position& position, EditableType editableType)
119 if (position.isNull())
122 ContainerNode* highestRoot = editableRootForPosition(position, editableType);
126 if (isHTMLBodyElement(*highestRoot))
129 ContainerNode* node = highestRoot->parentNode();
131 if (node->hasEditableStyle(editableType))
133 if (isHTMLBodyElement(*node))
135 node = node->parentNode();
141 Element* lowestEditableAncestor(Node* node)
144 if (node->hasEditableStyle())
145 return node->rootEditableElement();
146 if (isHTMLBodyElement(*node))
148 node = node->parentNode();
154 bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
156 Node* node = p.parentAnchoredEquivalent().anchorNode();
159 if (updateStyle == UpdateStyle)
160 node->document().updateLayoutIgnorePendingStylesheets();
162 ASSERT(updateStyle == DoNotUpdateStyle);
164 if (isRenderedHTMLTableElement(node))
165 node = node->parentNode();
167 return node->hasEditableStyle(editableType);
170 bool isAtUnsplittableElement(const Position& pos)
172 Node* node = pos.deprecatedNode();
173 return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
177 bool isRichlyEditablePosition(const Position& p, EditableType editableType)
179 Node* node = p.deprecatedNode();
183 if (isRenderedHTMLTableElement(node))
184 node = node->parentNode();
186 return node->rendererIsRichlyEditable(editableType);
189 Element* editableRootForPosition(const Position& p, EditableType editableType)
191 Node* node = p.containerNode();
195 if (isRenderedHTMLTableElement(node))
196 node = node->parentNode();
198 return node->rootEditableElement(editableType);
201 // Finds the enclosing element until which the tree can be split.
202 // When a user hits ENTER, he/she won't expect this element to be split into two.
203 // You may pass it as the second argument of splitTreeToNode.
204 Element* unsplittableElementForPosition(const Position& p)
206 // Since enclosingNodeOfType won't search beyond the highest root editable node,
207 // this code works even if the closest table cell was outside of the root editable node.
208 Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
210 return enclosingCell;
212 return editableRootForPosition(p);
215 Position nextCandidate(const Position& position)
217 PositionIterator p = position;
226 Position nextVisuallyDistinctCandidate(const Position& position)
228 Position p = position;
229 Position downstreamStart = p.downstream();
230 while (!p.atEndOfTree()) {
231 p = p.next(Character);
232 if (p.isCandidate() && p.downstream() != downstreamStart)
238 Position previousCandidate(const Position& position)
240 PositionIterator p = position;
241 while (!p.atStart()) {
249 Position previousVisuallyDistinctCandidate(const Position& position)
251 Position p = position;
252 Position downstreamStart = p.downstream();
253 while (!p.atStartOfTree()) {
254 p = p.previous(Character);
255 if (p.isCandidate() && p.downstream() != downstreamStart)
261 VisiblePosition firstEditableVisiblePositionAfterPositionInRoot(const Position& position, ContainerNode* highestRoot)
263 // position falls before highestRoot.
264 if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->hasEditableStyle())
265 return VisiblePosition(firstPositionInNode(highestRoot));
267 Position editablePosition = position;
269 if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
270 Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(editablePosition.deprecatedNode());
272 return VisiblePosition();
274 editablePosition = positionAfterNode(shadowAncestor);
277 while (editablePosition.deprecatedNode() && !isEditablePosition(editablePosition) && editablePosition.deprecatedNode()->isDescendantOf(highestRoot))
278 editablePosition = isAtomicNode(editablePosition.deprecatedNode()) ? positionInParentAfterNode(*editablePosition.deprecatedNode()) : nextVisuallyDistinctCandidate(editablePosition);
280 if (editablePosition.deprecatedNode() && editablePosition.deprecatedNode() != highestRoot && !editablePosition.deprecatedNode()->isDescendantOf(highestRoot))
281 return VisiblePosition();
283 return VisiblePosition(editablePosition);
286 VisiblePosition lastEditableVisiblePositionBeforePositionInRoot(const Position& position, ContainerNode* highestRoot)
288 return VisiblePosition(lastEditablePositionBeforePositionInRoot(position, highestRoot));
291 Position lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
293 // When position falls after highestRoot, the result is easy to compute.
294 if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
295 return lastPositionInNode(highestRoot);
297 Position editablePosition = position;
299 if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
300 Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(editablePosition.deprecatedNode());
304 editablePosition = firstPositionInOrBeforeNode(shadowAncestor);
307 while (editablePosition.deprecatedNode() && !isEditablePosition(editablePosition) && editablePosition.deprecatedNode()->isDescendantOf(highestRoot))
308 editablePosition = isAtomicNode(editablePosition.deprecatedNode()) ? positionInParentBeforeNode(*editablePosition.deprecatedNode()) : previousVisuallyDistinctCandidate(editablePosition);
310 if (editablePosition.deprecatedNode() && editablePosition.deprecatedNode() != highestRoot && !editablePosition.deprecatedNode()->isDescendantOf(highestRoot))
312 return editablePosition;
315 // FIXME: The method name, comment, and code say three different things here!
316 // Whether or not content before and after this node will collapse onto the same line as it.
317 bool isBlock(const Node* node)
319 return node && node->renderer() && !node->renderer()->isInline() && !node->renderer()->isRubyText();
322 bool isInline(const Node* node)
324 return node && node->renderer() && node->renderer()->isInline();
327 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
328 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the
329 // block that contains the table and not the table, and this function should be the only one responsible for
330 // knowing about these kinds of special cases.
331 Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
333 Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
334 return enclosingNode && enclosingNode->isElementNode() ? toElement(enclosingNode) : 0;
337 Element* enclosingBlockFlowElement(Node& node)
339 if (isBlockFlowElement(node))
340 return &toElement(node);
342 for (Node* n = node.parentNode(); n; n = n->parentNode()) {
343 if (isBlockFlowElement(*n) || isHTMLBodyElement(*n))
349 bool inSameContainingBlockFlowElement(Node* a, Node* b)
351 return a && b && enclosingBlockFlowElement(*a) == enclosingBlockFlowElement(*b);
354 TextDirection directionOfEnclosingBlock(const Position& position)
356 Element* enclosingBlockElement = enclosingBlock(position.containerNode());
357 if (!enclosingBlockElement)
359 RenderObject* renderer = enclosingBlockElement->renderer();
360 return renderer ? renderer->style()->direction() : LTR;
363 // This method is used to create positions in the DOM. It returns the maximum valid offset
364 // in a node. It returns 1 for some elements even though they do not have children, which
365 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
366 // on a Position before using it to create a DOM Range, or an exception will be thrown.
367 int lastOffsetForEditing(const Node* node)
372 if (node->offsetInCharacters())
373 return node->maxCharacterOffset();
375 if (node->hasChildren())
376 return node->countChildren();
378 // NOTE: This should preempt the childNodeCount for, e.g., select nodes
379 if (editingIgnoresContent(node))
385 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
387 unsigned length = string.length();
389 StringBuilder rebalancedString;
390 rebalancedString.reserveCapacity(length);
392 bool previousCharacterWasSpace = false;
393 for (size_t i = 0; i < length; i++) {
395 if (!isWhitespace(c)) {
396 rebalancedString.append(c);
397 previousCharacterWasSpace = false;
401 if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) {
402 rebalancedString.append(noBreakSpace);
403 previousCharacterWasSpace = false;
405 rebalancedString.append(' ');
406 previousCharacterWasSpace = true;
410 ASSERT(rebalancedString.length() == length);
412 return rebalancedString.toString();
415 bool isTableStructureNode(const Node *node)
417 RenderObject* renderer = node->renderer();
418 return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
421 const String& nonBreakingSpaceString()
423 DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
424 return nonBreakingSpaceString;
427 // FIXME: need to dump this
428 bool isSpecialHTMLElement(const Node* n)
433 if (!n->isHTMLElement())
439 RenderObject* renderer = n->renderer();
443 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
446 if (renderer->style()->isFloating())
452 static HTMLElement* firstInSpecialElement(const Position& pos)
454 Element* rootEditableElement = pos.containerNode()->rootEditableElement();
455 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) {
456 if (isSpecialHTMLElement(n)) {
457 HTMLElement* specialElement = toHTMLElement(n);
458 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
459 VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(specialElement), DOWNSTREAM);
460 if (isRenderedTableElement(specialElement) && vPos == firstInElement.next())
461 return specialElement;
462 if (vPos == firstInElement)
463 return specialElement;
469 static HTMLElement* lastInSpecialElement(const Position& pos)
471 Element* rootEditableElement = pos.containerNode()->rootEditableElement();
472 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode()) {
473 if (isSpecialHTMLElement(n)) {
474 HTMLElement* specialElement = toHTMLElement(n);
475 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
476 VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(specialElement), DOWNSTREAM);
477 if (isRenderedTableElement(specialElement) && vPos == lastInElement.previous())
478 return specialElement;
479 if (vPos == lastInElement)
480 return specialElement;
486 Position positionBeforeContainingSpecialElement(const Position& pos, HTMLElement** containingSpecialElement)
488 HTMLElement* n = firstInSpecialElement(pos);
491 Position result = positionInParentBeforeNode(*n);
492 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
494 if (containingSpecialElement)
495 *containingSpecialElement = n;
499 Position positionAfterContainingSpecialElement(const Position& pos, HTMLElement** containingSpecialElement)
501 HTMLElement* n = lastInSpecialElement(pos);
504 Position result = positionInParentAfterNode(*n);
505 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
507 if (containingSpecialElement)
508 *containingSpecialElement = n;
512 Element* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
514 Position upstream(visiblePosition.deepEquivalent().upstream());
515 if (isRenderedTableElement(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode())
516 return toElement(upstream.deprecatedNode());
521 Element* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
523 Position downstream(visiblePosition.deepEquivalent().downstream());
524 if (isRenderedTableElement(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode())
525 return toElement(downstream.deprecatedNode());
530 // Returns the visible position at the beginning of a node
531 VisiblePosition visiblePositionBeforeNode(Node& node)
533 if (node.hasChildren())
534 return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM);
535 ASSERT(node.parentNode());
536 ASSERT(!node.parentNode()->isShadowRoot());
537 return VisiblePosition(positionInParentBeforeNode(node));
540 // Returns the visible position at the ending of a node
541 VisiblePosition visiblePositionAfterNode(Node& node)
543 if (node.hasChildren())
544 return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM);
545 ASSERT(node.parentNode());
546 ASSERT(!node.parentNode()->isShadowRoot());
547 return VisiblePosition(positionInParentAfterNode(node));
550 // Create a range object with two visible positions, start and end.
551 // create(Document*, const Position&, const Position&); will use deprecatedEditingOffset
552 // Use this function instead of create a regular range object (avoiding editing offset).
553 PassRefPtrWillBeRawPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState)
555 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(document);
556 selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
557 if (!exceptionState.hadException())
558 selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
559 return selectedRange.release();
562 bool isHTMLListElement(Node* n)
564 return (n && (isHTMLUListElement(*n) || isHTMLOListElement(*n) || isHTMLDListElement(*n)));
567 bool isListItem(const Node* n)
569 return n && n->renderer() && n->renderer()->isListItem();
572 Element* enclosingElementWithTag(const Position& p, const QualifiedName& tagName)
577 ContainerNode* root = highestEditableRoot(p);
578 for (Element* ancestor = ElementTraversal::firstAncestorOrSelf(*p.deprecatedNode()); ancestor; ancestor = ElementTraversal::firstAncestor(*ancestor)) {
579 if (root && !ancestor->hasEditableStyle())
581 if (ancestor->hasTagName(tagName))
583 if (ancestor == root)
590 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
592 // FIXME: support CanSkipCrossEditingBoundary
593 ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
597 ContainerNode* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
598 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
599 // Don't return a non-editable node if the input position was editable, since
600 // the callers from editing will no doubt want to perform editing inside the returned node.
601 if (root && !n->hasEditableStyle())
612 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
615 ContainerNode* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
616 for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
617 if (root && !n->hasEditableStyle())
628 static bool hasARenderedDescendant(Node* node, Node* excludedNode)
630 for (Node* n = node->firstChild(); n;) {
631 if (n == excludedNode) {
632 n = NodeTraversal::nextSkippingChildren(*n, node);
637 n = NodeTraversal::next(*n, node);
642 Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode)
644 Node* previousNode = 0;
645 Element* rootEditableElement = node ? node->rootEditableElement() : 0;
646 for (; node; node = node->parentNode()) {
647 if (RenderObject* renderer = node->renderer()) {
648 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node)
656 Element* enclosingTableCell(const Position& p)
658 return toElement(enclosingNodeOfType(p, isTableCell));
661 Element* enclosingAnchorElement(const Position& p)
666 for (Element* ancestor = ElementTraversal::firstAncestorOrSelf(*p.deprecatedNode()); ancestor; ancestor = ElementTraversal::firstAncestor(*ancestor)) {
667 if (ancestor->isLink())
673 HTMLElement* enclosingList(Node* node)
678 ContainerNode* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
680 for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
681 if (isHTMLUListElement(*n) || isHTMLOListElement(*n))
682 return toHTMLElement(n);
690 Node* enclosingListChild(Node *node)
694 // Check for a list item element, or for a node whose parent is a list element. Such a node
695 // will appear visually as a list item (but without a list marker)
696 ContainerNode* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
698 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
699 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
700 if (isHTMLLIElement(*n) || (isHTMLListElement(n->parentNode()) && n != root))
702 if (n == root || isTableCell(n))
709 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
710 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
712 // Check that position is on a line by itself inside a list item
713 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
714 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
717 VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
718 VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
720 if (firstInListChild != visiblePos || lastInListChild != visiblePos)
723 return listChildNode;
726 HTMLElement* outermostEnclosingList(Node* node, HTMLElement* rootList)
728 HTMLElement* list = enclosingList(node);
732 while (HTMLElement* nextList = enclosingList(list)) {
733 if (nextList == rootList)
741 bool canMergeLists(Element* firstList, Element* secondList)
743 if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
746 return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
747 && firstList->hasEditableStyle() && secondList->hasEditableStyle() // both lists are editable
748 && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
749 && isVisiblyAdjacent(positionInParentAfterNode(*firstList), positionInParentBeforeNode(*secondList));
750 // Make sure there is no visible content between this li and the previous list
753 bool isRenderedHTMLTableElement(const Node* node)
755 return isHTMLTableElement(*node) && node->renderer();
758 bool isRenderedTableElement(const Node* node)
760 if (!node || !node->isElementNode())
763 RenderObject* renderer = node->renderer();
764 return (renderer && renderer->isTable());
767 bool isTableCell(const Node* node)
770 RenderObject* r = node->renderer();
771 return r ? r->isTableCell() : isHTMLTableCellElement(*node);
774 bool isEmptyTableCell(const Node* node)
776 // Returns true IFF the passed in node is one of:
777 // .) a table cell with no children,
778 // .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers
779 // .) the BR child of such a table cell
781 // Find rendered node
782 while (node && !node->renderer())
783 node = node->parentNode();
787 // Make sure the rendered node is a table cell or <br>.
788 // If it's a <br>, then the parent node has to be a table cell.
789 RenderObject* renderer = node->renderer();
790 if (renderer->isBR()) {
791 renderer = renderer->parent();
795 if (!renderer->isTableCell())
798 // Check that the table cell contains no child renderers except for perhaps a single <br>.
799 RenderObject* childRenderer = toRenderTableCell(renderer)->firstChild();
802 if (!childRenderer->isBR())
804 return !childRenderer->nextSibling();
807 PassRefPtrWillBeRawPtr<HTMLElement> createDefaultParagraphElement(Document& document)
809 switch (document.frame()->editor().defaultParagraphSeparator()) {
810 case EditorParagraphSeparatorIsDiv:
811 return HTMLDivElement::create(document);
812 case EditorParagraphSeparatorIsP:
813 return HTMLParagraphElement::create(document);
816 ASSERT_NOT_REACHED();
820 PassRefPtrWillBeRawPtr<HTMLBRElement> createBreakElement(Document& document)
822 return HTMLBRElement::create(document);
825 PassRefPtrWillBeRawPtr<HTMLOListElement> createOrderedListElement(Document& document)
827 return HTMLOListElement::create(document);
830 PassRefPtrWillBeRawPtr<HTMLUListElement> createUnorderedListElement(Document& document)
832 return HTMLUListElement::create(document);
835 PassRefPtrWillBeRawPtr<HTMLLIElement> createListItemElement(Document& document)
837 return HTMLLIElement::create(document);
840 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
842 return createHTMLElement(document, name.localName());
845 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
847 return HTMLElementFactory::createHTMLElement(tagName, document, 0, false);
850 bool isTabHTMLSpanElement(const Node* node)
852 if (!isHTMLSpanElement(node) || toHTMLSpanElement(node)->getAttribute(classAttr) != AppleTabSpanClass)
854 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass);
858 bool isTabHTMLSpanElementTextNode(const Node* node)
860 return node && node->isTextNode() && node->parentNode() && isTabHTMLSpanElement(node->parentNode());
863 HTMLSpanElement* tabSpanElement(const Node* node)
865 return isTabHTMLSpanElementTextNode(node) ? toHTMLSpanElement(node->parentNode()) : 0;
868 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document, PassRefPtrWillBeRawPtr<Text> prpTabTextNode)
870 RefPtrWillBeRawPtr<Text> tabTextNode = prpTabTextNode;
872 // Make the span to hold the tab.
873 RefPtrWillBeRawPtr<HTMLSpanElement> spanElement = toHTMLSpanElement(document.createElement(spanTag, false).get());
874 spanElement->setAttribute(classAttr, AppleTabSpanClass);
875 spanElement->setAttribute(styleAttr, "white-space:pre");
877 // Add tab text to that span.
879 tabTextNode = document.createEditingTextNode("\t");
881 spanElement->appendChild(tabTextNode.release());
883 return spanElement.release();
886 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document, const String& tabText)
888 return createTabSpanElement(document, document.createTextNode(tabText));
891 PassRefPtrWillBeRawPtr<HTMLSpanElement> createTabSpanElement(Document& document)
893 return createTabSpanElement(document, PassRefPtrWillBeRawPtr<Text>(nullptr));
896 PassRefPtrWillBeRawPtr<HTMLBRElement> createBlockPlaceholderElement(Document& document)
898 return toHTMLBRElement(document.createElement(brTag, false).get());
901 bool isNodeRendered(const Node *node)
906 RenderObject* renderer = node->renderer();
910 return renderer->style()->visibility() == VISIBLE;
913 // return first preceding DOM position rendered at a different location, or "this"
914 static Position previousCharacterPosition(const Position& position, EAffinity affinity)
916 if (position.isNull())
919 Element* fromRootEditableElement = position.anchorNode()->rootEditableElement();
921 bool atStartOfLine = isStartOfLine(VisiblePosition(position, affinity));
922 bool rendered = position.isCandidate();
924 Position currentPos = position;
925 while (!currentPos.atStartOfTree()) {
926 currentPos = currentPos.previous();
928 if (currentPos.anchorNode()->rootEditableElement() != fromRootEditableElement)
931 if (atStartOfLine || !rendered) {
932 if (currentPos.isCandidate())
934 } else if (position.rendersInDifferentPosition(currentPos)) {
942 // This assumes that it starts in editable content.
943 Position leadingWhitespacePosition(const Position& position, EAffinity affinity, WhitespacePositionOption option)
945 ASSERT(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle));
946 if (position.isNull())
949 if (isHTMLBRElement(*position.upstream().anchorNode()))
952 Position prev = previousCharacterPosition(position, affinity);
953 if (prev != position && inSameContainingBlockFlowElement(prev.anchorNode(), position.anchorNode()) && prev.anchorNode()->isTextNode()) {
954 String string = toText(prev.anchorNode())->data();
955 UChar previousCharacter = string[prev.deprecatedEditingOffset()];
956 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewline(previousCharacter) || previousCharacter == noBreakSpace) : isCollapsibleWhitespace(previousCharacter);
957 if (isSpace && isEditablePosition(prev))
964 // This assumes that it starts in editable content.
965 Position trailingWhitespacePosition(const Position& position, EAffinity, WhitespacePositionOption option)
967 ASSERT(isEditablePosition(position, ContentIsEditable, DoNotUpdateStyle));
968 if (position.isNull())
971 VisiblePosition visiblePosition(position);
972 UChar characterAfterVisiblePosition = visiblePosition.characterAfter();
973 bool isSpace = option == ConsiderNonCollapsibleWhitespace ? (isSpaceOrNewline(characterAfterVisiblePosition) || characterAfterVisiblePosition == noBreakSpace) : isCollapsibleWhitespace(characterAfterVisiblePosition);
974 // The space must not be in another paragraph and it must be editable.
975 if (isSpace && !isEndOfParagraph(visiblePosition) && visiblePosition.next(CannotCrossEditingBoundary).isNotNull())
980 unsigned numEnclosingMailBlockquotes(const Position& p)
983 for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
984 if (isMailHTMLBlockquoteElement(n))
990 void updatePositionForNodeRemoval(Position& position, Node& node)
992 if (position.isNull())
994 switch (position.anchorType()) {
995 case Position::PositionIsBeforeChildren:
996 if (position.containerNode() == node)
997 position = positionInParentBeforeNode(node);
999 case Position::PositionIsAfterChildren:
1000 if (position.containerNode() == node)
1001 position = positionInParentAfterNode(node);
1003 case Position::PositionIsOffsetInAnchor:
1004 if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.nodeIndex())
1005 position.moveToOffset(position.offsetInContainerNode() - 1);
1006 else if (node.containsIncludingShadowDOM(position.containerNode()))
1007 position = positionInParentBeforeNode(node);
1009 case Position::PositionIsAfterAnchor:
1010 if (node.containsIncludingShadowDOM(position.anchorNode()))
1011 position = positionInParentAfterNode(node);
1013 case Position::PositionIsBeforeAnchor:
1014 if (node.containsIncludingShadowDOM(position.anchorNode()))
1015 position = positionInParentBeforeNode(node);
1020 bool isMailHTMLBlockquoteElement(const Node* node)
1022 if (!node || !node->isHTMLElement())
1025 const HTMLElement& element = toHTMLElement(*node);
1026 return element.hasTagName(blockquoteTag) && element.getAttribute("type") == "cite";
1029 int caretMinOffset(const Node* n)
1031 RenderObject* r = n->renderer();
1032 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
1033 return r ? r->caretMinOffset() : 0;
1036 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
1037 // return the number of children for container nodes and the length for unrendered text nodes.
1038 int caretMaxOffset(const Node* n)
1040 // For rendered text nodes, return the last position that a caret could occupy.
1041 if (n->isTextNode() && n->renderer())
1042 return n->renderer()->caretMaxOffset();
1043 // For containers return the number of children. For others do the same as above.
1044 return lastOffsetForEditing(n);
1047 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
1049 return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
1052 bool lineBreakExistsAtPosition(const Position& position)
1054 if (position.isNull())
1057 if (isHTMLBRElement(*position.anchorNode()) && position.atFirstEditingPositionForNode())
1060 if (!position.anchorNode()->renderer())
1063 if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline())
1066 Text* textNode = toText(position.anchorNode());
1067 unsigned offset = position.offsetInContainerNode();
1068 return offset < textNode->length() && textNode->data()[offset] == '\n';
1071 // Modifies selections that have an end point at the edge of a table
1072 // that contains the other endpoint so that they don't confuse
1073 // code that iterates over selected paragraphs.
1074 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
1076 VisibleSelection newSelection(original);
1077 VisiblePosition startOfSelection(newSelection.visibleStart());
1078 VisiblePosition endOfSelection(newSelection.visibleEnd());
1080 // If the end of the selection to modify is just after a table, and
1081 // if the start of the selection is inside that table, then the last paragraph
1082 // that we'll want modify is the last one inside the table, not the table itself
1083 // (a table is itself a paragraph).
1084 if (Element* table = isFirstPositionAfterTable(endOfSelection))
1085 if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1086 newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
1088 // If the start of the selection to modify is just before a table,
1089 // and if the end of the selection is inside that table, then the first paragraph
1090 // we'll want to modify is the first one inside the table, not the paragraph
1091 // containing the table itself.
1092 if (Element* table = isLastPositionBeforeTable(startOfSelection))
1093 if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1094 newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
1096 return newSelection;
1099 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between
1100 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions
1101 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing
1102 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed,
1103 // or these functions need to be changed to iterate using actual VisiblePositions.
1104 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices.
1105 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtrWillBeRawPtr<ContainerNode>& scope)
1107 if (visiblePosition.isNull())
1110 Position p(visiblePosition.deepEquivalent());
1111 Document& document = *p.document();
1112 ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
1117 scope = document.documentElement();
1119 RefPtrWillBeRawPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
1121 return TextIterator::rangeLength(range.get(), true);
1124 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
1127 return VisiblePosition();
1128 RefPtrWillBeRawPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope);
1129 // Check for an invalid index. Certain editing operations invalidate indices because
1130 // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
1132 return VisiblePosition();
1133 return VisiblePosition(range->startPosition());
1136 // Determines whether two positions are visibly next to each other (first then second)
1137 // while ignoring whitespaces and unrendered nodes
1138 bool isVisiblyAdjacent(const Position& first, const Position& second)
1140 return VisiblePosition(first) == VisiblePosition(second.upstream());
1143 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
1144 // Call this function to determine whether a node is visibly fit inside selectedRange
1145 bool isNodeVisiblyContainedWithin(Node& node, const Range& selectedRange)
1147 // If the node is inside the range, then it surely is contained within
1148 if (selectedRange.compareNode(&node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
1151 bool startIsVisuallySame = visiblePositionBeforeNode(node) == VisiblePosition(selectedRange.startPosition());
1152 if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange.endPosition()) < 0)
1155 bool endIsVisuallySame = visiblePositionAfterNode(node) == VisiblePosition(selectedRange.endPosition());
1156 if (endIsVisuallySame && comparePositions(selectedRange.startPosition(), positionInParentBeforeNode(node)) < 0)
1159 return startIsVisuallySame && endIsVisuallySame;
1162 bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
1166 RenderObject* renderer = node->renderer();
1167 return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
1170 bool areIdenticalElements(const Node* first, const Node* second)
1172 if (!first->isElementNode() || !second->isElementNode())
1175 const Element* firstElement = toElement(first);
1176 const Element* secondElement = toElement(second);
1177 if (!firstElement->hasTagName(secondElement->tagQName()))
1180 return firstElement->hasEquivalentAttributes(secondElement);
1183 bool isNonTableCellHTMLBlockElement(const Node* node)
1185 if (!node->isHTMLElement())
1188 const HTMLElement& element = toHTMLElement(*node);
1189 return element.hasTagName(listingTag)
1190 || element.hasTagName(olTag)
1191 || element.hasTagName(preTag)
1192 || element.hasTagName(tableTag)
1193 || element.hasTagName(ulTag)
1194 || element.hasTagName(xmpTag)
1195 || element.hasTagName(h1Tag)
1196 || element.hasTagName(h2Tag)
1197 || element.hasTagName(h3Tag)
1198 || element.hasTagName(h4Tag)
1199 || element.hasTagName(h5Tag);
1202 bool isBlockFlowElement(const Node& node)
1204 RenderObject* renderer = node.renderer();
1205 return node.isElementNode() && renderer && renderer->isRenderBlockFlow();
1208 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
1210 // This function is used by range style computations to avoid bugs like:
1211 // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once
1212 // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
1213 // with a spurious "mixed" style.
1215 VisiblePosition visiblePosition(selection.start());
1216 if (visiblePosition.isNull())
1219 // if the selection is a caret, just return the position, since the style
1220 // behind us is relevant
1221 if (selection.isCaret())
1222 return visiblePosition.deepEquivalent();
1224 // if the selection starts just before a paragraph break, skip over it
1225 if (isEndOfParagraph(visiblePosition))
1226 return visiblePosition.next().deepEquivalent().downstream();
1228 // otherwise, make sure to be at the start of the first selected node,
1229 // instead of possibly at the end of the last node before the selection
1230 return visiblePosition.deepEquivalent().downstream();
1233 } // namespace blink