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