2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / editing / htmlediting.cpp
blob68ea7546c138576bfb9a76e7b6a119e1ee4be85a
1 /*
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
6 * are met:
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.
26 #include "config.h"
27 #include "htmlediting.h"
29 #include "CharacterNames.h"
30 #include "Document.h"
31 #include "EditingText.h"
32 #include "HTMLElement.h"
33 #include "HTMLInterchange.h"
34 #include "HTMLNames.h"
35 #include "PositionIterator.h"
36 #include "RenderObject.h"
37 #include "RegularExpression.h"
38 #include "Range.h"
39 #include "Selection.h"
40 #include "Text.h"
41 #include "TextIterator.h"
42 #include "VisiblePosition.h"
43 #include "visible_units.h"
45 using namespace std;
47 namespace WebCore {
49 using namespace HTMLNames;
51 // Atomic means that the node has no children, or has children which are ignored for the
52 // purposes of editing.
53 bool isAtomicNode(const Node *node)
55 return node && (!node->hasChildNodes() || editingIgnoresContent(node));
58 // Returns true for nodes that either have no content, or have content that is ignored (skipped
59 // over) while editing. There are no VisiblePositions inside these nodes.
60 bool editingIgnoresContent(const Node* node)
62 return !canHaveChildrenForEditing(node) && !node->isTextNode();
65 bool canHaveChildrenForEditing(const Node* node)
67 return !node->hasTagName(hrTag) &&
68 !node->hasTagName(brTag) &&
69 !node->hasTagName(imgTag) &&
70 !node->hasTagName(buttonTag) &&
71 !node->hasTagName(inputTag) &&
72 !node->hasTagName(textareaTag) &&
73 !node->hasTagName(objectTag) &&
74 !node->hasTagName(iframeTag) &&
75 !node->hasTagName(embedTag) &&
76 !node->hasTagName(appletTag) &&
77 !node->hasTagName(selectTag) &&
78 !node->isTextNode();
81 // Compare two positions, taking into account the possibility that one or both
82 // could be inside a shadow tree. Only works for non-null values.
83 int comparePositions(const Position& a, const Position& b)
85 Node* nodeA = a.node();
86 ASSERT(nodeA);
87 Node* nodeB = b.node();
88 ASSERT(nodeB);
89 int offsetA = a.offset();
90 int offsetB = b.offset();
92 Node* shadowAncestorA = nodeA->shadowAncestorNode();
93 if (shadowAncestorA == nodeA)
94 shadowAncestorA = 0;
95 Node* shadowAncestorB = nodeB->shadowAncestorNode();
96 if (shadowAncestorB == nodeB)
97 shadowAncestorB = 0;
99 int bias = 0;
100 if (shadowAncestorA != shadowAncestorB) {
101 if (shadowAncestorA) {
102 nodeA = shadowAncestorA;
103 offsetA = 0;
104 bias = 1;
106 if (shadowAncestorB) {
107 nodeB = shadowAncestorB;
108 offsetB = 0;
109 bias = -1;
113 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
114 return result ? result : bias;
117 Node* highestEditableRoot(const Position& position)
119 Node* node = position.node();
120 if (!node)
121 return 0;
123 Node* highestRoot = editableRootForPosition(position);
124 if (!highestRoot)
125 return 0;
127 node = highestRoot;
128 while (node) {
129 if (node->isContentEditable())
130 highestRoot = node;
131 if (node->hasTagName(bodyTag))
132 break;
133 node = node->parentNode();
136 return highestRoot;
139 Node* lowestEditableAncestor(Node* node)
141 if (!node)
142 return 0;
144 Node *lowestRoot = 0;
145 while (node) {
146 if (node->isContentEditable())
147 return node->rootEditableElement();
148 if (node->hasTagName(bodyTag))
149 break;
150 node = node->parentNode();
153 return lowestRoot;
156 bool isEditablePosition(const Position& p)
158 Node* node = p.node();
159 if (!node)
160 return false;
162 if (node->renderer() && node->renderer()->isTable())
163 node = node->parentNode();
165 return node->isContentEditable();
168 bool isRichlyEditablePosition(const Position& p)
170 Node* node = p.node();
171 if (!node)
172 return false;
174 if (node->renderer() && node->renderer()->isTable())
175 node = node->parentNode();
177 return node->isContentRichlyEditable();
180 Element* editableRootForPosition(const Position& p)
182 Node* node = p.node();
183 if (!node)
184 return 0;
186 if (node->renderer() && node->renderer()->isTable())
187 node = node->parentNode();
189 return node->rootEditableElement();
192 bool isContentEditable(const Node* node)
194 return node->isContentEditable();
197 Position nextCandidate(const Position& position)
199 PositionIterator p = position;
200 while (!p.atEnd()) {
201 p.increment();
202 if (p.isCandidate())
203 return p;
205 return Position();
208 Position nextVisuallyDistinctCandidate(const Position& position)
210 Position p = position;
211 Position downstreamStart = p.downstream();
212 while (!p.atEnd()) {
213 p = p.next(UsingComposedCharacters);
214 if (p.isCandidate() && p.downstream() != downstreamStart)
215 return p;
217 return Position();
220 Position previousCandidate(const Position& position)
222 PositionIterator p = position;
223 while (!p.atStart()) {
224 p.decrement();
225 if (p.isCandidate())
226 return p;
228 return Position();
231 Position previousVisuallyDistinctCandidate(const Position& position)
233 Position p = position;
234 Position downstreamStart = p.downstream();
235 while (!p.atStart()) {
236 p = p.previous(UsingComposedCharacters);
237 if (p.isCandidate() && p.downstream() != downstreamStart)
238 return p;
240 return Position();
243 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
245 // position falls before highestRoot.
246 if (comparePositions(position, Position(highestRoot, 0)) == -1 && highestRoot->isContentEditable())
247 return VisiblePosition(Position(highestRoot, 0));
249 Position p = position;
251 if (Node* shadowAncestor = p.node()->shadowAncestorNode())
252 if (shadowAncestor != p.node())
253 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
255 while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
256 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
258 if (p.node() && !p.node()->isDescendantOf(highestRoot))
259 return VisiblePosition();
261 return VisiblePosition(p);
264 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
266 // When position falls after highestRoot, the result is easy to compute.
267 if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
268 return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
270 Position p = position;
272 if (Node* shadowAncestor = p.node()->shadowAncestorNode())
273 if (shadowAncestor != p.node())
274 p = Position(shadowAncestor, 0);
276 while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
277 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
279 if (p.node() && !p.node()->isDescendantOf(highestRoot))
280 return VisiblePosition();
282 return VisiblePosition(p);
285 // Whether or not content before and after this node will collapse onto the same line as it.
286 bool isBlock(const Node* node)
288 return node && node->renderer() && !node->renderer()->isInline();
291 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
292 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the
293 // block that contains the table and not the table, and this function should be the only one responsible for
294 // knowing about these kinds of special cases.
295 Node* enclosingBlock(Node* node)
297 return enclosingNodeOfType(Position(node, 0), &isBlock);
300 Position rangeCompliantEquivalent(const Position& pos)
302 if (pos.isNull())
303 return Position();
305 Node *node = pos.node();
307 if (pos.offset() <= 0) {
308 if (node->parentNode() && (editingIgnoresContent(node) || isTableElement(node)))
309 return positionBeforeNode(node);
310 return Position(node, 0);
313 if (node->offsetInCharacters())
314 return Position(node, min(node->maxCharacterOffset(), pos.offset()));
316 int maxCompliantOffset = node->childNodeCount();
317 if (pos.offset() > maxCompliantOffset) {
318 if (node->parentNode())
319 return positionAfterNode(node);
321 // there is no other option at this point than to
322 // use the highest allowed position in the node
323 return Position(node, maxCompliantOffset);
326 // Editing should never generate positions like this.
327 if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
328 ASSERT_NOT_REACHED();
329 return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
332 if (pos.offset() == maxCompliantOffset && (editingIgnoresContent(node) || isTableElement(node)))
333 return positionAfterNode(node);
335 return Position(pos);
338 Position rangeCompliantEquivalent(const VisiblePosition& vpos)
340 return rangeCompliantEquivalent(vpos.deepEquivalent());
343 // This method is used to create positions in the DOM. It returns the maximum valid offset
344 // in a node. It returns 1 for some elements even though they do not have children, which
345 // creates technically invalid DOM Positions. Be sure to call rangeCompliantEquivalent
346 // on a Position before using it to create a DOM Range, or an exception will be thrown.
347 int maxDeepOffset(const Node *node)
349 ASSERT(node);
350 if (!node)
351 return 0;
352 if (node->offsetInCharacters())
353 return node->maxCharacterOffset();
355 if (node->hasChildNodes())
356 return node->childNodeCount();
358 // NOTE: This should preempt the childNodeCount for, e.g., select nodes
359 if (editingIgnoresContent(node))
360 return 1;
362 return 0;
365 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
367 static String twoSpaces(" ");
368 static String nbsp("\xa0");
369 static String pattern(" \xa0");
371 String rebalancedString = string;
373 rebalancedString.replace(noBreakSpace, ' ');
374 rebalancedString.replace('\n', ' ');
375 rebalancedString.replace('\t', ' ');
377 rebalancedString.replace(twoSpaces, pattern);
379 if (startIsStartOfParagraph && rebalancedString[0] == ' ')
380 rebalancedString.replace(0, 1, nbsp);
381 int end = rebalancedString.length() - 1;
382 if (endIsEndOfParagraph && rebalancedString[end] == ' ')
383 rebalancedString.replace(end, 1, nbsp);
385 return rebalancedString;
388 bool isTableStructureNode(const Node *node)
390 RenderObject *r = node->renderer();
391 return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
394 const String& nonBreakingSpaceString()
396 static String nonBreakingSpaceString = String(&noBreakSpace, 1);
397 return nonBreakingSpaceString;
400 // FIXME: need to dump this
401 bool isSpecialElement(const Node *n)
403 if (!n)
404 return false;
406 if (!n->isHTMLElement())
407 return false;
409 if (n->isLink())
410 return true;
412 RenderObject *renderer = n->renderer();
413 if (!renderer)
414 return false;
416 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
417 return true;
419 if (renderer->style()->isFloating())
420 return true;
422 if (renderer->style()->position() != StaticPosition)
423 return true;
425 return false;
428 // Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
429 bool validBlockTag(const String& blockTag)
431 if (blockTag == "address" ||
432 blockTag == "blockquote" ||
433 blockTag == "dd" ||
434 blockTag == "div" ||
435 blockTag == "dl" ||
436 blockTag == "dt" ||
437 blockTag == "h1" ||
438 blockTag == "h2" ||
439 blockTag == "h3" ||
440 blockTag == "h4" ||
441 blockTag == "h5" ||
442 blockTag == "h6" ||
443 blockTag == "p" ||
444 blockTag == "pre")
445 return true;
446 return false;
449 static Node* firstInSpecialElement(const Position& pos)
451 Node* rootEditableElement = pos.node()->rootEditableElement();
452 for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
453 if (isSpecialElement(n)) {
454 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
455 VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM);
456 if (isTableElement(n) && vPos == firstInElement.next())
457 return n;
458 if (vPos == firstInElement)
459 return n;
461 return 0;
464 static Node* lastInSpecialElement(const Position& pos)
466 Node* rootEditableElement = pos.node()->rootEditableElement();
467 for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
468 if (isSpecialElement(n)) {
469 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
470 VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM);
471 if (isTableElement(n) && vPos == lastInElement.previous())
472 return n;
473 if (vPos == lastInElement)
474 return n;
476 return 0;
479 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
481 return firstInSpecialElement(pos);
484 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
486 Node* n = firstInSpecialElement(pos);
487 if (!n)
488 return pos;
489 Position result = positionBeforeNode(n);
490 if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
491 return pos;
492 if (containingSpecialElement)
493 *containingSpecialElement = n;
494 return result;
497 bool isLastVisiblePositionInSpecialElement(const Position& pos)
499 return lastInSpecialElement(pos);
502 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
504 Node* n = lastInSpecialElement(pos);
505 if (!n)
506 return pos;
507 Position result = positionAfterNode(n);
508 if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
509 return pos;
510 if (containingSpecialElement)
511 *containingSpecialElement = n;
512 return result;
515 Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
517 if (isFirstVisiblePositionInSpecialElement(pos))
518 return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
519 if (isLastVisiblePositionInSpecialElement(pos))
520 return positionAfterContainingSpecialElement(pos, containingSpecialElement);
521 return pos;
524 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
526 Position upstream(visiblePosition.deepEquivalent().upstream());
527 if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.offset() == maxDeepOffset(upstream.node()))
528 return upstream.node();
530 return 0;
533 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
535 Position downstream(visiblePosition.deepEquivalent().downstream());
536 if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.offset() == 0)
537 return downstream.node();
539 return 0;
542 Position positionBeforeNode(const Node *node)
544 return Position(node->parentNode(), node->nodeIndex());
547 Position positionAfterNode(const Node *node)
549 return Position(node->parentNode(), node->nodeIndex() + 1);
552 bool isListElement(Node *n)
554 return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
557 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
559 if (p.isNull())
560 return 0;
562 Node* root = highestEditableRoot(p);
563 for (Node* n = p.node(); n; n = n->parentNode()) {
564 if (root && !isContentEditable(n))
565 continue;
566 if (n->hasTagName(tagName))
567 return n;
568 if (n == root)
569 return 0;
572 return 0;
575 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), bool onlyReturnEditableNodes)
577 if (p.isNull())
578 return 0;
580 Node* root = highestEditableRoot(p);
581 for (Node* n = p.node(); n; n = n->parentNode()) {
582 // Don't return a non-editable node if the input position was editable, since
583 // the callers from editing will no doubt want to perform editing inside the returned node.
584 if (root && !isContentEditable(n) && onlyReturnEditableNodes)
585 continue;
586 if ((*nodeIsOfType)(n))
587 return n;
588 if (n == root)
589 return 0;
592 return 0;
595 Node* enclosingTableCell(const Position& p)
597 return enclosingNodeOfType(p, &isTableCell);
600 Node* enclosingAnchorElement(const Position& p)
602 if (p.isNull())
603 return 0;
605 Node* node = p.node();
606 while (node && !(node->isElementNode() && node->isLink()))
607 node = node->parentNode();
608 return node;
611 Node* enclosingList(Node* node)
613 if (!node)
614 return 0;
616 Node* root = highestEditableRoot(Position(node, 0));
618 for (Node* n = node->parentNode(); n; n = n->parentNode()) {
619 if (n->hasTagName(ulTag) || n->hasTagName(olTag))
620 return n;
621 if (n == root)
622 return 0;
625 return 0;
628 Node* enclosingListChild(Node *node)
630 if (!node)
631 return 0;
632 // Check for a list item element, or for a node whose parent is a list element. Such a node
633 // will appear visually as a list item (but without a list marker)
634 Node* root = highestEditableRoot(Position(node, 0));
636 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
637 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
638 if (n->hasTagName(liTag) || isListElement(n->parentNode()))
639 return n;
640 if (n == root || isTableCell(n))
641 return 0;
644 return 0;
647 static Node* embeddedSublist(Node* listItem)
649 // Check the DOM so that we'll find collapsed sublists without renderers.
650 for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
651 if (isListElement(n))
652 return n;
655 return 0;
658 static Node* appendedSublist(Node* listItem)
660 // Check the DOM so that we'll find collapsed sublists without renderers.
661 for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
662 if (isListElement(n))
663 return n;
664 if (n->renderer() && n->renderer()->isListItem())
665 return 0;
668 return 0;
671 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
673 // Check that position is on a line by itself inside a list item
674 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node());
675 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
676 return 0;
678 VisiblePosition firstInListChild(Position(listChildNode, 0));
679 VisiblePosition lastInListChild(Position(listChildNode, maxDeepOffset(listChildNode)));
681 if (firstInListChild != visiblePos || lastInListChild != visiblePos)
682 return 0;
684 if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
685 return 0;
687 return listChildNode;
690 Node* outermostEnclosingListChild(Node* node)
692 Node* listNode = 0;
693 Node* nextNode = node;
694 while ((nextNode = enclosingListChild(nextNode)))
695 listNode = nextNode;
696 return listNode;
699 Node* outermostEnclosingList(Node* node)
701 Node* listNode = 0;
702 Node* nextNode = node;
703 while ((nextNode = enclosingList(nextNode)))
704 listNode = nextNode;
705 return listNode;
708 Node* highestAncestor(Node* node)
710 ASSERT(node);
711 Node* parent = node;
712 while ((node = node->parentNode()))
713 parent = node;
714 return parent;
717 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
718 bool isTableElement(Node* n)
720 if (!n || !n->isElementNode())
721 return false;
723 RenderObject* renderer = n->renderer();
724 return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
727 bool isTableCell(const Node* node)
729 RenderObject* r = node->renderer();
730 if (!r)
731 return node->hasTagName(tdTag) || node->hasTagName(thTag);
733 return r->isTableCell();
736 PassRefPtr<Element> createDefaultParagraphElement(Document *document)
738 ExceptionCode ec = 0;
739 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "div", ec);
740 ASSERT(ec == 0);
741 return element.release();
744 PassRefPtr<Element> createBreakElement(Document *document)
746 ExceptionCode ec = 0;
747 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
748 ASSERT(ec == 0);
749 return breakNode.release();
752 PassRefPtr<Element> createOrderedListElement(Document *document)
754 ExceptionCode ec = 0;
755 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ol", ec);
756 ASSERT(ec == 0);
757 return element.release();
760 PassRefPtr<Element> createUnorderedListElement(Document *document)
762 ExceptionCode ec = 0;
763 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ul", ec);
764 ASSERT(ec == 0);
765 return element.release();
768 PassRefPtr<Element> createListItemElement(Document *document)
770 ExceptionCode ec = 0;
771 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "li", ec);
772 ASSERT(ec == 0);
773 return breakNode.release();
776 PassRefPtr<Element> createElement(Document* document, const String& tagName)
778 ExceptionCode ec = 0;
779 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, tagName, ec);
780 ASSERT(ec == 0);
781 return breakNode.release();
784 bool isTabSpanNode(const Node *node)
786 return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass;
789 bool isTabSpanTextNode(const Node *node)
791 return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
794 Node *tabSpanNode(const Node *node)
796 return isTabSpanTextNode(node) ? node->parentNode() : 0;
799 Position positionBeforeTabSpan(const Position& pos)
801 Node *node = pos.node();
802 if (isTabSpanTextNode(node))
803 node = tabSpanNode(node);
804 else if (!isTabSpanNode(node))
805 return pos;
807 return positionBeforeNode(node);
810 PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode)
812 // make the span to hold the tab
813 ExceptionCode ec = 0;
814 RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
815 ASSERT(ec == 0);
816 spanElement->setAttribute(classAttr, AppleTabSpanClass);
817 spanElement->setAttribute(styleAttr, "white-space:pre");
819 // add tab text to that span
820 if (!tabTextNode)
821 tabTextNode = document->createEditingTextNode("\t");
822 spanElement->appendChild(tabTextNode, ec);
823 ASSERT(ec == 0);
825 return spanElement.release();
828 PassRefPtr<Element> createTabSpanElement(Document* document, const String& tabText)
830 return createTabSpanElement(document, document->createTextNode(tabText));
833 PassRefPtr<Element> createTabSpanElement(Document* document)
835 return createTabSpanElement(document, PassRefPtr<Node>());
838 bool isNodeRendered(const Node *node)
840 if (!node)
841 return false;
843 RenderObject *renderer = node->renderer();
844 if (!renderer)
845 return false;
847 return renderer->style()->visibility() == VISIBLE;
850 Node *nearestMailBlockquote(const Node *node)
852 for (Node *n = const_cast<Node *>(node); n; n = n->parentNode()) {
853 if (isMailBlockquote(n))
854 return n;
856 return 0;
859 bool isMailBlockquote(const Node *node)
861 if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag))
862 return false;
864 return static_cast<const Element *>(node)->getAttribute("type") == "cite";
867 int caretMinOffset(const Node* n)
869 RenderObject* r = n->renderer();
870 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
871 return r ? r->caretMinOffset() : 0;
874 int caretMaxOffset(const Node* n)
876 RenderObject* r = n->renderer();
877 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
878 if (r)
879 return r->caretMaxOffset();
881 if (n->isCharacterDataNode()) {
882 const CharacterData* c = static_cast<const CharacterData*>(n);
883 return static_cast<int>(c->length());
885 return 1;
888 bool lineBreakExistsAtPosition(const VisiblePosition& visiblePosition)
890 if (visiblePosition.isNull())
891 return false;
893 Position downstream(visiblePosition.deepEquivalent().downstream());
894 return downstream.node()->hasTagName(brTag) ||
895 downstream.node()->isTextNode() && downstream.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n';
898 // Modifies selections that have an end point at the edge of a table
899 // that contains the other endpoint so that they don't confuse
900 // code that iterates over selected paragraphs.
901 Selection selectionForParagraphIteration(const Selection& original)
903 Selection newSelection(original);
904 VisiblePosition startOfSelection(newSelection.visibleStart());
905 VisiblePosition endOfSelection(newSelection.visibleEnd());
907 // If the end of the selection to modify is just after a table, and
908 // if the start of the selection is inside that table, then the last paragraph
909 // that we'll want modify is the last one inside the table, not the table itself
910 // (a table is itself a paragraph).
911 if (Node* table = isFirstPositionAfterTable(endOfSelection))
912 if (startOfSelection.deepEquivalent().node()->isDescendantOf(table))
913 newSelection = Selection(startOfSelection, endOfSelection.previous(true));
915 // If the start of the selection to modify is just before a table,
916 // and if the end of the selection is inside that table, then the first paragraph
917 // we'll want to modify is the first one inside the table, not the paragraph
918 // containing the table itself.
919 if (Node* table = isLastPositionBeforeTable(startOfSelection))
920 if (endOfSelection.deepEquivalent().node()->isDescendantOf(table))
921 newSelection = Selection(startOfSelection.next(true), endOfSelection);
923 return newSelection;
927 int indexForVisiblePosition(VisiblePosition& visiblePosition)
929 if (visiblePosition.isNull())
930 return 0;
931 Position p(visiblePosition.deepEquivalent());
932 RefPtr<Range> range = Range::create(p.node()->document(), Position(p.node()->document(), 0), rangeCompliantEquivalent(p));
933 return TextIterator::rangeLength(range.get(), true);
936 PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node)
938 if (!range)
939 return 0;
941 Document* document = range->ownerDocument();
943 Node* startContainer = range->startContainer();
944 int startOffset = range->startOffset();
945 Node* endContainer = range->endContainer();
946 int endOffset = range->endOffset();
948 if (!startContainer)
949 return 0;
951 ASSERT(endContainer);
953 if (startContainer == node || startContainer->isDescendantOf(node)) {
954 ASSERT(node->parentNode());
955 startContainer = node->parentNode();
956 startOffset = node->nodeIndex();
958 if (endContainer == node || endContainer->isDescendantOf(node)) {
959 ASSERT(node->parentNode());
960 endContainer = node->parentNode();
961 endOffset = node->nodeIndex();
964 return Range::create(document, startContainer, startOffset, endContainer, endOffset);
967 Selection avoidIntersectionWithNode(const Selection& selection, Node* node)
969 if (selection.isNone())
970 return Selection(selection);
972 Selection updatedSelection(selection);
973 Node* base = selection.base().node();
974 Node* extent = selection.extent().node();
975 ASSERT(base);
976 ASSERT(extent);
978 if (base == node || base->isDescendantOf(node)) {
979 ASSERT(node->parentNode());
980 updatedSelection.setBase(Position(node->parentNode(), node->nodeIndex()));
983 if (extent == node || extent->isDescendantOf(node)) {
984 ASSERT(node->parentNode());
985 updatedSelection.setExtent(Position(node->parentNode(), node->nodeIndex()));
988 return updatedSelection;
991 } // namespace WebCore