feac8f3b119fdcd0f6a416285011f1b05bd8301b
[WebKit-https.git] / WebCore / editing / htmlediting.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
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.
12  *
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. 
24  */
25
26 #include "config.h"
27 #include "htmlediting.h"
28
29 #include "Document.h"
30 #include "EditingText.h"
31 #include "HTMLElement.h"
32 #include "HTMLInterchange.h"
33 #include "HTMLNames.h"
34 #include "RenderObject.h"
35 #include "RegularExpression.h"
36 #include "Range.h"
37 #include "Text.h"
38 #include "VisiblePosition.h"
39 #include "visible_units.h"
40
41 using namespace std;
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
47 // Atomic means that the node has no children, or has children which are ignored for the
48 // purposes of editing.
49 bool isAtomicNode(const Node *node)
50 {
51     return node && (!node->hasChildNodes() || editingIgnoresContent(node));
52 }
53
54 bool editingIgnoresContent(const Node *node)
55 {
56     if (!node || !node->isHTMLElement())
57         return false;
58     
59     // There doesn't seem to be a way to find out if a a node is a pop up box by looking at its renderer.
60     if (node->hasTagName(selectTag))
61         return true;
62     
63     if (node->renderer())
64         return node->renderer()->isWidget() || node->renderer()->isImage() || node->renderer()->isHR() || node->renderer()->isTextArea() || node->renderer()->isTextField();
65
66     return node->hasTagName(appletTag) ||
67            node->hasTagName(embedTag) ||
68            node->hasTagName(iframeTag) ||
69            node->hasTagName(imgTag) ||
70            node->hasTagName(hrTag) ||
71            static_cast<const HTMLElement *>(node)->isGenericFormElement();
72 }
73
74 // Some nodes, like brs, will technically accept children, but we don't want that to happen while editing. 
75 bool canHaveChildrenForEditing(const Node* node)
76 {
77     return !node->hasTagName(hrTag) &&
78            !node->hasTagName(brTag) &&
79            !node->hasTagName(imgTag) &&
80            !node->hasTagName(buttonTag) &&
81            !node->hasTagName(objectTag) &&
82            !node->hasTagName(iframeTag) &&
83            !node->isTextNode();
84 }
85
86 // Compare two positions, taking into account the possibility that one or both
87 // could be inside a shadow tree. Only works for non-null values.
88 int comparePositions(const Position& a, const Position& b)
89 {
90     Node* nodeA = a.node();
91     ASSERT(nodeA);
92     Node* nodeB = b.node();
93     ASSERT(nodeB);
94     int offsetA = a.offset();
95     int offsetB = b.offset();
96
97     Node* shadowAncestorA = nodeA->shadowAncestorNode();
98     if (shadowAncestorA == nodeA)
99         shadowAncestorA = 0;
100     Node* shadowAncestorB = nodeB->shadowAncestorNode();
101     if (shadowAncestorB == nodeB)
102         shadowAncestorB = 0;
103
104     int bias = 0;
105     if (shadowAncestorA != shadowAncestorB) {
106         if (shadowAncestorA) {
107             nodeA = shadowAncestorA;
108             offsetA = 0;
109             bias = 1;
110         }
111         if (shadowAncestorB) {
112             nodeB = shadowAncestorB;
113             offsetB = 0;
114             bias = -1;
115         }
116     }
117
118     int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
119     return result ? result : bias;
120 }
121
122 Node* highestEditableRoot(const Position& position)
123 {
124     Node* node = position.node();
125     if (!node)
126         return 0;
127         
128     Node* highestRoot = editableRootForPosition(position);
129     if (!highestRoot)
130         return 0;
131     
132     node = highestRoot;
133     while (node) {
134         if (node->isContentEditable())
135             highestRoot = node;
136         if (node->hasTagName(bodyTag))
137             break;
138         node = node->parentNode();
139     }
140     
141     return highestRoot;
142 }
143
144 Node* lowestEditableAncestor(Node* node)
145 {
146     if (!node)
147         return 0;
148     
149     Node *lowestRoot = 0;
150     while (node) {
151         if (node->isContentEditable())
152             return node->rootEditableElement();
153         if (node->hasTagName(bodyTag))
154             break;
155         node = node->parentNode();
156     }
157     
158     return lowestRoot;
159 }
160
161 bool isEditablePosition(const Position& p)
162 {
163     Node* node = p.node();
164     if (!node)
165         return false;
166         
167     if (node->renderer() && node->renderer()->isTable())
168         node = node->parentNode();
169     
170     return node->isContentEditable();
171 }
172
173 bool isRichlyEditablePosition(const Position& p)
174 {
175     Node* node = p.node();
176     if (!node)
177         return false;
178         
179     if (node->renderer() && node->renderer()->isTable())
180         node = node->parentNode();
181     
182     return node->isContentRichlyEditable();
183 }
184
185 Element* editableRootForPosition(const Position& p)
186 {
187     Node* node = p.node();
188     if (!node)
189         return 0;
190         
191     if (node->renderer() && node->renderer()->isTable())
192         node = node->parentNode();
193     
194     return node->rootEditableElement();
195 }
196
197 Position nextCandidate(const Position& position)
198 {
199     Position p = position;
200     while (!p.atEnd()) {
201         p = p.next(UsingComposedCharacters);
202         if (p.inRenderedContent())
203             return p;
204     }
205     return Position();
206 }
207
208 Position nextVisuallyDistinctCandidate(const Position& position)
209 {
210     Position p = position;
211     Position downstreamStart = p.downstream();
212     while (!p.atEnd()) {
213         p = p.next(UsingComposedCharacters);
214         if (p.inRenderedContent() && p.downstream() != downstreamStart)
215             return p;
216     }
217     return Position();
218 }
219
220 Position previousCandidate(const Position& position)
221 {
222     Position p = position;
223     while (!p.atStart()) {
224         p = p.previous(UsingComposedCharacters);
225         if (p.inRenderedContent())
226             return p;
227     }
228     return Position();
229 }
230
231 Position previousVisuallyDistinctCandidate(const Position& position)
232 {
233     Position p = position;
234     Position downstreamStart = p.downstream();
235     while (!p.atStart()) {
236         p = p.previous(UsingComposedCharacters);
237         if (p.inRenderedContent() && p.downstream() != downstreamStart)
238             return p;
239     }
240     return Position();
241 }
242
243 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
244 {
245     if (comparePositions(position, Position(highestRoot, 0)) == -1)
246         return VisiblePosition(Position(highestRoot, 0));
247
248     Position p = nextVisuallyDistinctCandidate(position);
249     while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
250         p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
251
252     return VisiblePosition(p);
253 }
254
255 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
256 {
257     if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
258         return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
259     
260     Position p = previousVisuallyDistinctCandidate(position);
261     while (p.isNotNull() && !isEditablePosition(p) && p.node()->isAncestor(highestRoot))
262         p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
263
264     return VisiblePosition(p);
265 }
266
267 // Whether or not content before and after this node will collapse onto the same line as it.
268 bool isBlock(Node* node)
269 {
270     return node && node->renderer() && !node->renderer()->isInline();
271 }
272
273 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
274 Node* enclosingBlock(Node* node)
275 {
276     if (isBlock(node))
277         return node;
278         
279     while (1) {
280         node = node->parentNode();
281         if (!node)
282             break;
283         if (isBlock(node))
284             return node;
285     }
286     return 0;
287 }
288
289 Position rangeCompliantEquivalent(const Position& pos)
290 {
291     if (pos.isNull())
292         return Position();
293
294     Node *node = pos.node();
295     
296     if (pos.offset() <= 0) {
297         if (node->parentNode() && (node->hasTagName(brTag) || editingIgnoresContent(node)))
298             return positionBeforeNode(node);
299         return Position(node, 0);
300     }
301     
302     if (node->offsetInCharacters())
303         return Position(node, min(node->maxOffset(), pos.offset()));
304     
305     int maxCompliantOffset = node->childNodeCount();
306     if (pos.offset() > maxCompliantOffset) {
307         if (node->parentNode())
308             return positionAfterNode(node);
309         
310         // there is no other option at this point than to
311         // use the highest allowed position in the node
312         return Position(node, maxCompliantOffset);
313     } 
314
315     // Editing should never generate positions like this.
316     if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
317         ASSERT_NOT_REACHED();
318         return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
319     }
320     
321     return Position(pos);
322 }
323
324 Position rangeCompliantEquivalent(const VisiblePosition& vpos)
325 {
326     return rangeCompliantEquivalent(vpos.deepEquivalent());
327 }
328
329 // This method is used to create positions in the DOM. It returns the maximum valid offset
330 // in a node.  It returns 1 for some elements even though they do not have children, which
331 // creates technically invalid DOM Positions.  Be sure to call rangeCompliantEquivalent
332 // on a Position before using it to create a DOM Range, or an exception will be thrown.
333 int maxDeepOffset(const Node *node)
334 {
335     if (node->offsetInCharacters())
336         return node->maxOffset();
337         
338     if (node->hasChildNodes())
339         return node->childNodeCount();
340     
341     // NOTE: This should preempt the childNodeCount for, e.g., select nodes
342     if (node->hasTagName(brTag) || editingIgnoresContent(node))
343         return 1;
344
345     return 0;
346 }
347
348 void rebalanceWhitespaceInTextNode(Node *node, unsigned int start, unsigned int length)
349 {
350     static RegularExpression nonRegularWhitespace("[\xa0\n]");
351     static DeprecatedString twoSpaces("  ");
352     static DeprecatedString nbsp("\xa0");
353     static DeprecatedString space(" ");
354      
355     ASSERT(node->isTextNode());
356     Text *textNode = static_cast<Text *>(node);
357     String text = textNode->data();
358     ASSERT(length <= text.length() && start + length <= text.length());
359     
360     DeprecatedString substring = text.substring(start, length).deprecatedString();
361
362     substring.replace(nonRegularWhitespace, space);
363     
364     // The sequence should alternate between spaces and nbsps, always ending in a regular space.
365     // Note: This pattern doesn't mimic TextEdit editing behavior on other clients that don't
366     // support our -webkit-nbsp-mode: space, but it comes close.
367     static DeprecatedString pattern("\xa0 ");
368     int end = length - 1; 
369     int i = substring.findRev(twoSpaces, end);
370     while (i >= 0) {
371         substring.replace(i , 2, pattern);
372         i = substring.findRev(twoSpaces, i);
373     }
374     
375     // Rendering will collapse any regular whitespace at the start or end of a line.  To prevent this, we use
376     // a nbsp at the start and end of a text node.  This is sometimes unnecessary,  E.G. <a>link</a> text
377     if (start == 0 && substring[0] == ' ')
378         substring.replace(0, 1, nbsp);
379     if (start + length == text.length() && substring[end] == ' ')
380         substring.replace(end, 1, nbsp);
381     
382     ExceptionCode ec = 0;
383     textNode->deleteData(start, length, ec);
384     ASSERT(!ec);
385     textNode->insertData(start, String(substring), ec);
386     ASSERT(!ec);
387 }
388
389 bool isTableStructureNode(const Node *node)
390 {
391     RenderObject *r = node->renderer();
392     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
393 }
394
395 const String& nonBreakingSpaceString()
396 {
397     static String nonBreakingSpaceString = DeprecatedString(DeprecatedChar(NON_BREAKING_SPACE));
398     return nonBreakingSpaceString;
399 }
400
401 // FIXME: Why use this instead of maxDeepOffset?
402 static int maxRangeOffset(Node *n)
403 {
404     if (n->offsetInCharacters())
405         return n->maxOffset();
406
407     if (n->isElementNode())
408         return n->childNodeCount();
409
410     return 1;
411 }
412
413 // FIXME: need to dump this
414 bool isSpecialElement(const Node *n)
415 {
416     if (!n)
417         return false;
418         
419     if (!n->isHTMLElement())
420         return false;
421
422     if (n->isLink())
423         return true;
424
425     if (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))
426         return true;
427
428     RenderObject *renderer = n->renderer();
429     if (!renderer)
430         return false;
431         
432     if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
433         return true;
434
435     if (renderer->style()->isFloating())
436         return true;
437
438     if (renderer->style()->position() != StaticPosition)
439         return true;
440         
441     return false;
442 }
443
444 // Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
445 bool validBlockTag(const String& blockTag)
446 {
447     if (blockTag == "address" ||
448         blockTag == "blockquote" ||
449         blockTag == "dd" ||
450         blockTag == "div" ||
451         blockTag == "dl" ||
452         blockTag == "dt" ||
453         blockTag == "h1" ||
454         blockTag == "h2" ||
455         blockTag == "h3" ||
456         blockTag == "h4" ||
457         blockTag == "h5" ||
458         blockTag == "h6" ||
459         blockTag == "p" ||
460         blockTag == "pre")
461         return true;
462     return false;
463 }
464
465 static Node* firstInSpecialElement(const Position& pos)
466 {
467     Node* rootEditableElement = pos.node()->rootEditableElement();
468     for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
469         if (isSpecialElement(n)) {
470             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
471             VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM);
472             if (isTableElement(n) && vPos == firstInElement.next())
473                 return n;
474             if (vPos == firstInElement)
475                 return n;
476         }
477     return 0;
478 }
479
480 static Node* lastInSpecialElement(const Position& pos)
481 {
482     Node* rootEditableElement = pos.node()->rootEditableElement();
483     for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
484         if (isSpecialElement(n)) {
485             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
486             VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM);
487             if (isTableElement(n) && vPos == lastInElement.previous())
488                 return n;
489             if (vPos == lastInElement)
490                 return n;
491         }
492     return 0;
493 }
494
495 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
496 {
497     return firstInSpecialElement(pos);
498 }
499
500 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
501 {
502     Node* n = firstInSpecialElement(pos);
503     ASSERT(n);
504     if (!n)
505         return pos;
506     Position result = positionBeforeNode(n);
507     if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
508         return pos;
509     if (containingSpecialElement)
510         *containingSpecialElement = n;
511     return result;
512 }
513
514 bool isLastVisiblePositionInSpecialElement(const Position& pos)
515 {
516     return lastInSpecialElement(pos);
517 }
518
519 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
520 {
521     Node* n = lastInSpecialElement(pos);
522     ASSERT(n);
523     if (!n)
524         return pos;
525     Position result = positionAfterNode(n);
526     if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
527         return pos;
528     if (containingSpecialElement)
529         *containingSpecialElement = n;
530     return result;
531 }
532
533 Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
534 {
535     if (isFirstVisiblePositionInSpecialElement(pos))
536         return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
537     if (isLastVisiblePositionInSpecialElement(pos))
538         return positionAfterContainingSpecialElement(pos, containingSpecialElement);
539     return pos;
540 }
541
542 Position positionBeforeNode(const Node *node)
543 {
544     return Position(node->parentNode(), node->nodeIndex());
545 }
546
547 Position positionAfterNode(const Node *node)
548 {
549     return Position(node->parentNode(), node->nodeIndex() + 1);
550 }
551
552 bool isListElement(Node *n)
553 {
554     return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
555 }
556
557 Node* enclosingNodeWithTag(Node* node, const QualifiedName& tagName)
558 {
559     if (!node)
560         return 0;
561     Node* root = (node->inDocument()) ? node->rootEditableElement() : highestAncestor(node);
562     ASSERT(root);
563     for (Node* n = node->parentNode(); n && (n == root || n->isAncestor(root)); n = n->parentNode())
564         if (n->hasTagName(tagName))
565             return n;
566             
567     return 0;
568 }
569
570 Node* enclosingTableCell(Node* node)
571 {
572     if (!node)
573         return 0;
574         
575     for (Node* n = node->parentNode(); n; n = n->parentNode())
576         if (n->renderer() && n->renderer()->isTableCell())
577             return n;
578             
579     return 0;
580 }
581
582 Node* enclosingList(Node* node)
583 {
584     if (!node)
585         return 0;
586     Node* root = (node->inDocument()) ? node->rootEditableElement() : highestAncestor(node);
587     ASSERT(root);
588     for (Node* n = node->parentNode(); n && (n == root || n->isAncestor(root)); n = n->parentNode())
589         if (n->hasTagName(ulTag) || n->hasTagName(olTag))
590             return n;
591             
592     return 0;
593 }
594
595 Node* enclosingListChild (Node *node)
596 {
597     if (!node)
598         return 0;
599     // Check for a list item element, or for a node whose parent is a list element.  Such a node
600     // will appear visually as a list item (but without a list marker)
601     Node* root = (node->inDocument()) ? node->rootEditableElement() : highestAncestor(node);
602     ASSERT(root);
603     for (Node *n = node; n && n->parentNode() && (n == root || n->isAncestor(root)); n = n->parentNode()) {
604         if (n->hasTagName(liTag) || isListElement(n->parentNode()))
605             return n;
606     }
607     
608     return 0;
609 }
610
611 static Node* embeddedSublist(Node* listItem)
612 {
613     // Check the DOM so that we'll find collapsed sublists without renderers.
614     for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
615         if (isListElement(n))
616             return n;
617     }
618     
619     return 0;
620 }
621
622 static Node* appendedSublist(Node* listItem)
623 {
624     // Check the DOM so that we'll find collapsed sublists without renderers.
625     for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
626         if (isListElement(n))
627             return n;
628         if (n->renderer() && n->renderer()->isListItem())
629             return 0;
630     }
631     
632     return 0;
633 }
634
635 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
636 {
637     // Check that position is on a line by itself inside a list item
638     Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node());
639     if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
640         return 0;
641     
642     if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
643         return 0;
644         
645     return listChildNode;
646 }
647
648 Node* outermostEnclosingListChild(Node* node)
649 {
650     Node* listNode = 0;
651     Node* nextNode = node;
652     while ((nextNode = enclosingListChild(nextNode)))
653         listNode = nextNode;
654     return listNode;
655 }
656
657 Node* outermostEnclosingList(Node* node)
658 {
659     Node* listNode = 0;
660     Node* nextNode = node;
661     while ((nextNode = enclosingList(nextNode)))
662         listNode = nextNode;
663     return listNode;
664 }
665
666 Node* highestAncestor(Node* node)
667 {
668     ASSERT(node);
669     Node* parent = node;
670     while ((node = node->parentNode()))
671         parent = node;
672     return parent;
673 }
674
675 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
676 bool isTableElement(Node *n)
677 {
678     RenderObject *renderer = n ? n->renderer() : 0;
679     return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
680 }
681
682 bool isFirstVisiblePositionAfterTableElement(const Position& pos)
683 {
684     return isTableElement(pos.upstream().node());
685 }
686
687 Position positionBeforePrecedingTableElement(const Position& pos)
688 {
689     ASSERT(isFirstVisiblePositionAfterTableElement(pos));
690     Position result = positionBeforeNode(pos.upstream().node());
691     if (result.isNull() || !result.node()->rootEditableElement())
692         return pos;
693     return result;
694 }
695
696 bool isLastVisiblePositionBeforeTableElement(const Position &pos)
697 {
698     return isTableElement(pos.downstream().node());
699 }
700
701 Position positionAfterFollowingTableElement(const Position &pos)
702 {
703     ASSERT(isLastVisiblePositionBeforeTableElement(pos));
704     Position result = positionAfterNode(pos.downstream().node());
705     if (result.isNull() || !result.node()->rootEditableElement())
706         return pos;
707     return result;
708 }
709
710 // This function is necessary because a VisiblePosition is allowed
711 // to be at the start or end of elements where we do not want to
712 // add content directly.  For example, clicking at the end of a hyperlink,
713 // then typing, needs to add the text after the link.  Also, table
714 // offset 0 and table offset childNodeCount are valid VisiblePostions,
715 // but we can not add more content right there... it needs to go before
716 // or after the table.
717 // FIXME: Consider editable/non-editable boundaries?
718 Position positionAvoidingSpecialElementBoundary(const Position &pos)
719 {
720     Node *compNode = pos.node();
721     if (!compNode)
722         return pos;
723     
724     if (compNode->parentNode() && compNode->parentNode()->isLink())
725         compNode = compNode->parentNode();
726     else if (!isTableElement(compNode))
727         return pos;
728     
729     // FIXME: rangePos isn't being used to create DOM Ranges, so why does it need to be range compliant?
730     Position rangePos = rangeCompliantEquivalent(VisiblePosition(pos, DOWNSTREAM));
731     VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
732
733     Position result;
734     if (VisiblePosition(compNode, maxRangeOffset(compNode), DOWNSTREAM) == vPos)
735         result = positionAfterNode(compNode);
736     else if (VisiblePosition(compNode, 0, DOWNSTREAM) == vPos)
737         result = positionBeforeNode(compNode);
738     else
739         return pos;
740         
741     if (result.isNull() || !result.node()->rootEditableElement())
742         result = pos;
743     
744     return result;
745 }
746
747 PassRefPtr<Element> createDefaultParagraphElement(Document *document)
748 {
749     ExceptionCode ec = 0;
750     RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "div", ec);
751     ASSERT(ec == 0);
752     return element.release();
753 }
754
755 PassRefPtr<Element> createBreakElement(Document *document)
756 {
757     ExceptionCode ec = 0;
758     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
759     ASSERT(ec == 0);
760     return breakNode.release();
761 }
762
763 PassRefPtr<Element> createOrderedListElement(Document *document)
764 {
765     ExceptionCode ec = 0;
766     RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ol", ec);
767     ASSERT(ec == 0);
768     return element.release();
769 }
770
771 PassRefPtr<Element> createUnorderedListElement(Document *document)
772 {
773     ExceptionCode ec = 0;
774     RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ul", ec);
775     ASSERT(ec == 0);
776     return element.release();
777 }
778
779 PassRefPtr<Element> createListItemElement(Document *document)
780 {
781     ExceptionCode ec = 0;
782     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "li", ec);
783     ASSERT(ec == 0);
784     return breakNode.release();
785 }
786
787 PassRefPtr<Element> createElement(Document* document, const String& tagName)
788 {
789     ExceptionCode ec = 0;
790     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, tagName, ec);
791     ASSERT(ec == 0);
792     return breakNode.release();
793 }
794
795 bool isTabSpanNode(const Node *node)
796 {
797     return (node && node->isElementNode() && static_cast<const Element *>(node)->getAttribute("class") == AppleTabSpanClass);
798 }
799
800 bool isTabSpanTextNode(const Node *node)
801 {
802     return (node && node->parentNode() && isTabSpanNode(node->parentNode()));
803 }
804
805 Node *tabSpanNode(const Node *node)
806 {
807     return isTabSpanTextNode(node) ? node->parentNode() : 0;
808 }
809
810 Position positionBeforeTabSpan(const Position& pos)
811 {
812     Node *node = pos.node();
813     if (isTabSpanTextNode(node))
814         node = tabSpanNode(node);
815     else if (!isTabSpanNode(node))
816         return pos;
817     
818     return positionBeforeNode(node);
819 }
820
821 PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode)
822 {
823     // make the span to hold the tab
824     ExceptionCode ec = 0;
825     RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
826     assert(ec == 0);
827     spanElement->setAttribute(classAttr, AppleTabSpanClass);
828     spanElement->setAttribute(styleAttr, "white-space:pre");
829
830     // add tab text to that span
831     if (!tabTextNode)
832         tabTextNode = document->createEditingTextNode("\t");
833     spanElement->appendChild(tabTextNode, ec);
834     assert(ec == 0);
835
836     return spanElement.release();
837 }
838
839 PassRefPtr<Element> createTabSpanElement(Document* document, const String& tabText)
840 {
841     return createTabSpanElement(document, document->createTextNode(tabText));
842 }
843
844 PassRefPtr<Element> createTabSpanElement(Document* document)
845 {
846     return createTabSpanElement(document, PassRefPtr<Node>());
847 }
848
849 bool isNodeRendered(const Node *node)
850 {
851     if (!node)
852         return false;
853
854     RenderObject *renderer = node->renderer();
855     if (!renderer)
856         return false;
857
858     return renderer->style()->visibility() == VISIBLE;
859 }
860
861 Node *nearestMailBlockquote(const Node *node)
862 {
863     for (Node *n = const_cast<Node *>(node); n; n = n->parentNode()) {
864         if (isMailBlockquote(n))
865             return n;
866     }
867     return 0;
868 }
869
870 bool isMailBlockquote(const Node *node)
871 {
872     if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag))
873         return false;
874         
875     return static_cast<const Element *>(node)->getAttribute("type") == "cite";
876 }
877
878 } // namespace WebCore