• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007 Apple 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 "CharacterNames.h"
30 #include "Document.h"
31 #include "EditingText.h"
32 #include "HTMLBRElement.h"
33 #include "HTMLDivElement.h"
34 #include "HTMLElementFactory.h"
35 #include "HTMLInterchange.h"
36 #include "HTMLLIElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLOListElement.h"
39 #include "HTMLUListElement.h"
40 #include "PositionIterator.h"
41 #include "RenderObject.h"
42 #include "Range.h"
43 #include "Selection.h"
44 #include "Text.h"
45 #include "TextIterator.h"
46 #include "VisiblePosition.h"
47 #include "visible_units.h"
48 #include <wtf/StdLibExtras.h>
49 
50 #if ENABLE(WML)
51 #include "WMLNames.h"
52 #endif
53 
54 using namespace std;
55 
56 namespace WebCore {
57 
58 using namespace HTMLNames;
59 
60 // Atomic means that the node has no children, or has children which are ignored for the
61 // purposes of editing.
isAtomicNode(const Node * node)62 bool isAtomicNode(const Node *node)
63 {
64     return node && (!node->hasChildNodes() || editingIgnoresContent(node));
65 }
66 
67 // Returns true for nodes that either have no content, or have content that is ignored (skipped
68 // over) while editing.  There are no VisiblePositions inside these nodes.
editingIgnoresContent(const Node * node)69 bool editingIgnoresContent(const Node* node)
70 {
71     return !canHaveChildrenForEditing(node) && !node->isTextNode();
72 }
73 
canHaveChildrenForEditing(const Node * node)74 bool canHaveChildrenForEditing(const Node* node)
75 {
76     return !node->hasTagName(hrTag) &&
77            !node->hasTagName(brTag) &&
78            !node->hasTagName(imgTag) &&
79            !node->hasTagName(buttonTag) &&
80            !node->hasTagName(inputTag) &&
81            !node->hasTagName(textareaTag) &&
82            !node->hasTagName(objectTag) &&
83            !node->hasTagName(iframeTag) &&
84            !node->hasTagName(embedTag) &&
85            !node->hasTagName(appletTag) &&
86            !node->hasTagName(selectTag) &&
87 #if ENABLE(WML)
88            !node->hasTagName(WMLNames::doTag) &&
89 #endif
90            !node->isTextNode();
91 }
92 
93 // Compare two positions, taking into account the possibility that one or both
94 // could be inside a shadow tree. Only works for non-null values.
comparePositions(const Position & a,const Position & b)95 int comparePositions(const Position& a, const Position& b)
96 {
97     Node* nodeA = a.node();
98     ASSERT(nodeA);
99     Node* nodeB = b.node();
100     ASSERT(nodeB);
101     int offsetA = a.offset();
102     int offsetB = b.offset();
103 
104     Node* shadowAncestorA = nodeA->shadowAncestorNode();
105     if (shadowAncestorA == nodeA)
106         shadowAncestorA = 0;
107     Node* shadowAncestorB = nodeB->shadowAncestorNode();
108     if (shadowAncestorB == nodeB)
109         shadowAncestorB = 0;
110 
111     int bias = 0;
112     if (shadowAncestorA != shadowAncestorB) {
113         if (shadowAncestorA) {
114             nodeA = shadowAncestorA;
115             offsetA = 0;
116             bias = 1;
117         }
118         if (shadowAncestorB) {
119             nodeB = shadowAncestorB;
120             offsetB = 0;
121             bias = -1;
122         }
123     }
124 
125     int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
126     return result ? result : bias;
127 }
128 
highestEditableRoot(const Position & position)129 Node* highestEditableRoot(const Position& position)
130 {
131     Node* node = position.node();
132     if (!node)
133         return 0;
134 
135     Node* highestRoot = editableRootForPosition(position);
136     if (!highestRoot)
137         return 0;
138 
139     node = highestRoot;
140     while (node) {
141         if (node->isContentEditable())
142             highestRoot = node;
143         if (node->hasTagName(bodyTag))
144             break;
145         node = node->parentNode();
146     }
147 
148     return highestRoot;
149 }
150 
lowestEditableAncestor(Node * node)151 Node* lowestEditableAncestor(Node* node)
152 {
153     if (!node)
154         return 0;
155 
156     Node *lowestRoot = 0;
157     while (node) {
158         if (node->isContentEditable())
159             return node->rootEditableElement();
160         if (node->hasTagName(bodyTag))
161             break;
162         node = node->parentNode();
163     }
164 
165     return lowestRoot;
166 }
167 
isEditablePosition(const Position & p)168 bool isEditablePosition(const Position& p)
169 {
170     Node* node = p.node();
171     if (!node)
172         return false;
173 
174     if (node->renderer() && node->renderer()->isTable())
175         node = node->parentNode();
176 
177     return node->isContentEditable();
178 }
179 
isRichlyEditablePosition(const Position & p)180 bool isRichlyEditablePosition(const Position& p)
181 {
182     Node* node = p.node();
183     if (!node)
184         return false;
185 
186     if (node->renderer() && node->renderer()->isTable())
187         node = node->parentNode();
188 
189     return node->isContentRichlyEditable();
190 }
191 
editableRootForPosition(const Position & p)192 Element* editableRootForPosition(const Position& p)
193 {
194     Node* node = p.node();
195     if (!node)
196         return 0;
197 
198     if (node->renderer() && node->renderer()->isTable())
199         node = node->parentNode();
200 
201     return node->rootEditableElement();
202 }
203 
isContentEditable(const Node * node)204 bool isContentEditable(const Node* node)
205 {
206     return node->isContentEditable();
207 }
208 
nextCandidate(const Position & position)209 Position nextCandidate(const Position& position)
210 {
211     PositionIterator p = position;
212     while (!p.atEnd()) {
213         p.increment();
214         if (p.isCandidate())
215             return p;
216     }
217     return Position();
218 }
219 
nextVisuallyDistinctCandidate(const Position & position)220 Position nextVisuallyDistinctCandidate(const Position& position)
221 {
222     Position p = position;
223     Position downstreamStart = p.downstream();
224     while (!p.atEnd()) {
225         p = p.next(UsingComposedCharacters);
226         if (p.isCandidate() && p.downstream() != downstreamStart)
227             return p;
228     }
229     return Position();
230 }
231 
previousCandidate(const Position & position)232 Position previousCandidate(const Position& position)
233 {
234     PositionIterator p = position;
235     while (!p.atStart()) {
236         p.decrement();
237         if (p.isCandidate())
238             return p;
239     }
240     return Position();
241 }
242 
previousVisuallyDistinctCandidate(const Position & position)243 Position previousVisuallyDistinctCandidate(const Position& position)
244 {
245     Position p = position;
246     Position downstreamStart = p.downstream();
247     while (!p.atStart()) {
248         p = p.previous(UsingComposedCharacters);
249         if (p.isCandidate() && p.downstream() != downstreamStart)
250             return p;
251     }
252     return Position();
253 }
254 
firstEditablePositionAfterPositionInRoot(const Position & position,Node * highestRoot)255 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
256 {
257     // position falls before highestRoot.
258     if (comparePositions(position, Position(highestRoot, 0)) == -1 && highestRoot->isContentEditable())
259         return VisiblePosition(Position(highestRoot, 0));
260 
261     Position p = position;
262 
263     if (Node* shadowAncestor = p.node()->shadowAncestorNode())
264         if (shadowAncestor != p.node())
265             p = Position(shadowAncestor, maxDeepOffset(shadowAncestor));
266 
267     while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
268         p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
269 
270     if (p.node() && !p.node()->isDescendantOf(highestRoot))
271         return VisiblePosition();
272 
273     return VisiblePosition(p);
274 }
275 
lastEditablePositionBeforePositionInRoot(const Position & position,Node * highestRoot)276 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
277 {
278     // When position falls after highestRoot, the result is easy to compute.
279     if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
280         return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
281 
282     Position p = position;
283 
284     if (Node* shadowAncestor = p.node()->shadowAncestorNode())
285         if (shadowAncestor != p.node())
286             p = Position(shadowAncestor, 0);
287 
288     while (p.node() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot))
289         p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
290 
291     if (p.node() && !p.node()->isDescendantOf(highestRoot))
292         return VisiblePosition();
293 
294     return VisiblePosition(p);
295 }
296 
297 // Whether or not content before and after this node will collapse onto the same line as it.
isBlock(const Node * node)298 bool isBlock(const Node* node)
299 {
300     return node && node->renderer() && !node->renderer()->isInline();
301 }
302 
303 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
304 // FIXME: Pass a position to this function.  The enclosing block of [table, x] for example, should be the
305 // block that contains the table and not the table, and this function should be the only one responsible for
306 // knowing about these kinds of special cases.
enclosingBlock(Node * node)307 Node* enclosingBlock(Node* node)
308 {
309     return static_cast<Element*>(enclosingNodeOfType(Position(node, 0), isBlock));
310 }
311 
rangeCompliantEquivalent(const Position & pos)312 Position rangeCompliantEquivalent(const Position& pos)
313 {
314     if (pos.isNull())
315         return Position();
316 
317     Node *node = pos.node();
318 
319     if (pos.offset() <= 0) {
320         if (node->parentNode() && (editingIgnoresContent(node) || isTableElement(node)))
321             return positionBeforeNode(node);
322         return Position(node, 0);
323     }
324 
325     if (node->offsetInCharacters())
326         return Position(node, min(node->maxCharacterOffset(), pos.offset()));
327 
328     int maxCompliantOffset = node->childNodeCount();
329     if (pos.offset() > maxCompliantOffset) {
330         if (node->parentNode())
331             return positionAfterNode(node);
332 
333         // there is no other option at this point than to
334         // use the highest allowed position in the node
335         return Position(node, maxCompliantOffset);
336     }
337 
338     // Editing should never generate positions like this.
339     if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
340         ASSERT_NOT_REACHED();
341         return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
342     }
343 
344     if (pos.offset() == maxCompliantOffset && (editingIgnoresContent(node) || isTableElement(node)))
345         return positionAfterNode(node);
346 
347     return Position(pos);
348 }
349 
rangeCompliantEquivalent(const VisiblePosition & vpos)350 Position rangeCompliantEquivalent(const VisiblePosition& vpos)
351 {
352     return rangeCompliantEquivalent(vpos.deepEquivalent());
353 }
354 
355 // This method is used to create positions in the DOM. It returns the maximum valid offset
356 // in a node.  It returns 1 for some elements even though they do not have children, which
357 // creates technically invalid DOM Positions.  Be sure to call rangeCompliantEquivalent
358 // on a Position before using it to create a DOM Range, or an exception will be thrown.
maxDeepOffset(const Node * node)359 int maxDeepOffset(const Node *node)
360 {
361     ASSERT(node);
362     if (!node)
363         return 0;
364     if (node->offsetInCharacters())
365         return node->maxCharacterOffset();
366 
367     if (node->hasChildNodes())
368         return node->childNodeCount();
369 
370     // NOTE: This should preempt the childNodeCount for, e.g., select nodes
371     if (editingIgnoresContent(node))
372         return 1;
373 
374     return 0;
375 }
376 
stringWithRebalancedWhitespace(const String & string,bool startIsStartOfParagraph,bool endIsEndOfParagraph)377 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
378 {
379     DEFINE_STATIC_LOCAL(String, twoSpaces, ("  "));
380     DEFINE_STATIC_LOCAL(String, nbsp, ("\xa0"));
381     DEFINE_STATIC_LOCAL(String, pattern, (" \xa0"));
382 
383     String rebalancedString = string;
384 
385     rebalancedString.replace(noBreakSpace, ' ');
386     rebalancedString.replace('\n', ' ');
387     rebalancedString.replace('\t', ' ');
388 
389     rebalancedString.replace(twoSpaces, pattern);
390 
391     if (startIsStartOfParagraph && rebalancedString[0] == ' ')
392         rebalancedString.replace(0, 1, nbsp);
393     int end = rebalancedString.length() - 1;
394     if (endIsEndOfParagraph && rebalancedString[end] == ' ')
395         rebalancedString.replace(end, 1, nbsp);
396 
397     return rebalancedString;
398 }
399 
isTableStructureNode(const Node * node)400 bool isTableStructureNode(const Node *node)
401 {
402     RenderObject *r = node->renderer();
403     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
404 }
405 
nonBreakingSpaceString()406 const String& nonBreakingSpaceString()
407 {
408     DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
409     return nonBreakingSpaceString;
410 }
411 
412 // FIXME: need to dump this
isSpecialElement(const Node * n)413 bool isSpecialElement(const Node *n)
414 {
415     if (!n)
416         return false;
417 
418     if (!n->isHTMLElement())
419         return false;
420 
421     if (n->isLink())
422         return true;
423 
424     RenderObject *renderer = n->renderer();
425     if (!renderer)
426         return false;
427 
428     if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
429         return true;
430 
431     if (renderer->style()->isFloating())
432         return true;
433 
434     if (renderer->style()->position() != StaticPosition)
435         return true;
436 
437     return false;
438 }
439 
440 // Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
validBlockTag(const String & blockTag)441 bool validBlockTag(const String& blockTag)
442 {
443     if (blockTag == "address" ||
444         blockTag == "blockquote" ||
445         blockTag == "dd" ||
446         blockTag == "div" ||
447         blockTag == "dl" ||
448         blockTag == "dt" ||
449         blockTag == "h1" ||
450         blockTag == "h2" ||
451         blockTag == "h3" ||
452         blockTag == "h4" ||
453         blockTag == "h5" ||
454         blockTag == "h6" ||
455         blockTag == "p" ||
456         blockTag == "pre")
457         return true;
458     return false;
459 }
460 
firstInSpecialElement(const Position & pos)461 static Node* firstInSpecialElement(const Position& pos)
462 {
463     Node* rootEditableElement = pos.node()->rootEditableElement();
464     for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
465         if (isSpecialElement(n)) {
466             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
467             VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM);
468             if (isTableElement(n) && vPos == firstInElement.next())
469                 return n;
470             if (vPos == firstInElement)
471                 return n;
472         }
473     return 0;
474 }
475 
lastInSpecialElement(const Position & pos)476 static Node* lastInSpecialElement(const Position& pos)
477 {
478     Node* rootEditableElement = pos.node()->rootEditableElement();
479     for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
480         if (isSpecialElement(n)) {
481             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
482             VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM);
483             if (isTableElement(n) && vPos == lastInElement.previous())
484                 return n;
485             if (vPos == lastInElement)
486                 return n;
487         }
488     return 0;
489 }
490 
isFirstVisiblePositionInSpecialElement(const Position & pos)491 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
492 {
493     return firstInSpecialElement(pos);
494 }
495 
positionBeforeContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)496 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
497 {
498     Node* n = firstInSpecialElement(pos);
499     if (!n)
500         return pos;
501     Position result = positionBeforeNode(n);
502     if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
503         return pos;
504     if (containingSpecialElement)
505         *containingSpecialElement = n;
506     return result;
507 }
508 
isLastVisiblePositionInSpecialElement(const Position & pos)509 bool isLastVisiblePositionInSpecialElement(const Position& pos)
510 {
511     return lastInSpecialElement(pos);
512 }
513 
positionAfterContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)514 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
515 {
516     Node* n = lastInSpecialElement(pos);
517     if (!n)
518         return pos;
519     Position result = positionAfterNode(n);
520     if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
521         return pos;
522     if (containingSpecialElement)
523         *containingSpecialElement = n;
524     return result;
525 }
526 
positionOutsideContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)527 Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
528 {
529     if (isFirstVisiblePositionInSpecialElement(pos))
530         return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
531     if (isLastVisiblePositionInSpecialElement(pos))
532         return positionAfterContainingSpecialElement(pos, containingSpecialElement);
533     return pos;
534 }
535 
isFirstPositionAfterTable(const VisiblePosition & visiblePosition)536 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
537 {
538     Position upstream(visiblePosition.deepEquivalent().upstream());
539     if (upstream.node() && upstream.node()->renderer() && upstream.node()->renderer()->isTable() && upstream.offset() == maxDeepOffset(upstream.node()))
540         return upstream.node();
541 
542     return 0;
543 }
544 
isLastPositionBeforeTable(const VisiblePosition & visiblePosition)545 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
546 {
547     Position downstream(visiblePosition.deepEquivalent().downstream());
548     if (downstream.node() && downstream.node()->renderer() && downstream.node()->renderer()->isTable() && downstream.offset() == 0)
549         return downstream.node();
550 
551     return 0;
552 }
553 
positionBeforeNode(const Node * node)554 Position positionBeforeNode(const Node *node)
555 {
556     return Position(node->parentNode(), node->nodeIndex());
557 }
558 
positionAfterNode(const Node * node)559 Position positionAfterNode(const Node *node)
560 {
561     return Position(node->parentNode(), node->nodeIndex() + 1);
562 }
563 
isListElement(Node * n)564 bool isListElement(Node *n)
565 {
566     return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
567 }
568 
enclosingNodeWithTag(const Position & p,const QualifiedName & tagName)569 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
570 {
571     if (p.isNull())
572         return 0;
573 
574     Node* root = highestEditableRoot(p);
575     for (Node* n = p.node(); n; n = n->parentNode()) {
576         if (root && !isContentEditable(n))
577             continue;
578         if (n->hasTagName(tagName))
579             return n;
580         if (n == root)
581             return 0;
582     }
583 
584     return 0;
585 }
586 
enclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *),bool onlyReturnEditableNodes)587 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), bool onlyReturnEditableNodes)
588 {
589     if (p.isNull())
590         return 0;
591 
592     Node* root = highestEditableRoot(p);
593     for (Node* n = p.node(); n; n = n->parentNode()) {
594         // Don't return a non-editable node if the input position was editable, since
595         // the callers from editing will no doubt want to perform editing inside the returned node.
596         if (root && !isContentEditable(n) && onlyReturnEditableNodes)
597             continue;
598         if ((*nodeIsOfType)(n))
599             return n;
600         if (n == root)
601             return 0;
602     }
603 
604     return 0;
605 }
606 
highestEnclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *))607 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*))
608 {
609     Node* highest = 0;
610     Node* root = highestEditableRoot(p);
611     for (Node* n = p.node(); n; n = n->parentNode()) {
612         if ((*nodeIsOfType)(n))
613             highest = n;
614         if (n == root)
615             break;
616     }
617 
618     return highest;
619 }
620 
enclosingTableCell(const Position & p)621 Node* enclosingTableCell(const Position& p)
622 {
623     return static_cast<Element*>(enclosingNodeOfType(p, isTableCell));
624 }
625 
enclosingAnchorElement(const Position & p)626 Node* enclosingAnchorElement(const Position& p)
627 {
628     if (p.isNull())
629         return 0;
630 
631     Node* node = p.node();
632     while (node && !(node->isElementNode() && node->isLink()))
633         node = node->parentNode();
634     return node;
635 }
636 
enclosingList(Node * node)637 HTMLElement* enclosingList(Node* node)
638 {
639     if (!node)
640         return 0;
641 
642     Node* root = highestEditableRoot(Position(node, 0));
643 
644     for (Node* n = node->parentNode(); n; n = n->parentNode()) {
645         if (n->hasTagName(ulTag) || n->hasTagName(olTag))
646             return static_cast<HTMLElement*>(n);
647         if (n == root)
648             return 0;
649     }
650 
651     return 0;
652 }
653 
enclosingListChild(Node * node)654 Node* enclosingListChild(Node *node)
655 {
656     if (!node)
657         return 0;
658     // Check for a list item element, or for a node whose parent is a list element.  Such a node
659     // will appear visually as a list item (but without a list marker)
660     Node* root = highestEditableRoot(Position(node, 0));
661 
662     // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
663     for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
664         if (n->hasTagName(liTag) || isListElement(n->parentNode()))
665             return n;
666         if (n == root || isTableCell(n))
667             return 0;
668     }
669 
670     return 0;
671 }
672 
embeddedSublist(Node * listItem)673 static HTMLElement* embeddedSublist(Node* listItem)
674 {
675     // Check the DOM so that we'll find collapsed sublists without renderers.
676     for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
677         if (isListElement(n))
678             return static_cast<HTMLElement*>(n);
679     }
680 
681     return 0;
682 }
683 
appendedSublist(Node * listItem)684 static Node* appendedSublist(Node* listItem)
685 {
686     // Check the DOM so that we'll find collapsed sublists without renderers.
687     for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
688         if (isListElement(n))
689             return static_cast<HTMLElement*>(n);
690         if (n->renderer() && n->renderer()->isListItem())
691             return 0;
692     }
693 
694     return 0;
695 }
696 
enclosingEmptyListItem(const VisiblePosition & visiblePos)697 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
698 {
699     // Check that position is on a line by itself inside a list item
700     Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node());
701     if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
702         return 0;
703 
704     VisiblePosition firstInListChild(Position(listChildNode, 0));
705     VisiblePosition lastInListChild(Position(listChildNode, maxDeepOffset(listChildNode)));
706 
707     if (firstInListChild != visiblePos || lastInListChild != visiblePos)
708         return 0;
709 
710     if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
711         return 0;
712 
713     return listChildNode;
714 }
715 
outermostEnclosingList(Node * node)716 HTMLElement* outermostEnclosingList(Node* node)
717 {
718     HTMLElement* list = enclosingList(node);
719     if (!list)
720         return 0;
721     while (HTMLElement* nextList = enclosingList(list))
722         list = nextList;
723     return list;
724 }
725 
highestAncestor(Node * node)726 Node* highestAncestor(Node* node)
727 {
728     ASSERT(node);
729     Node* parent = node;
730     while ((node = node->parentNode()))
731         parent = node;
732     return parent;
733 }
734 
735 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
isTableElement(Node * n)736 bool isTableElement(Node* n)
737 {
738     if (!n || !n->isElementNode())
739         return false;
740 
741     RenderObject* renderer = n->renderer();
742     return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
743 }
744 
isTableCell(const Node * node)745 bool isTableCell(const Node* node)
746 {
747     RenderObject* r = node->renderer();
748     if (!r)
749         return node->hasTagName(tdTag) || node->hasTagName(thTag);
750 
751     return r->isTableCell();
752 }
753 
createDefaultParagraphElement(Document * document)754 PassRefPtr<HTMLElement> createDefaultParagraphElement(Document* document)
755 {
756     return new HTMLDivElement(divTag, document);
757 }
758 
createBreakElement(Document * document)759 PassRefPtr<HTMLElement> createBreakElement(Document* document)
760 {
761     return new HTMLBRElement(brTag, document);
762 }
763 
createOrderedListElement(Document * document)764 PassRefPtr<HTMLElement> createOrderedListElement(Document* document)
765 {
766     return new HTMLOListElement(olTag, document);
767 }
768 
createUnorderedListElement(Document * document)769 PassRefPtr<HTMLElement> createUnorderedListElement(Document* document)
770 {
771     return new HTMLUListElement(ulTag, document);
772 }
773 
createListItemElement(Document * document)774 PassRefPtr<HTMLElement> createListItemElement(Document* document)
775 {
776     return new HTMLLIElement(liTag, document);
777 }
778 
createHTMLElement(Document * document,const QualifiedName & name)779 PassRefPtr<HTMLElement> createHTMLElement(Document* document, const QualifiedName& name)
780 {
781     return HTMLElementFactory::createHTMLElement(name, document, 0, false);
782 }
783 
createHTMLElement(Document * document,const AtomicString & tagName)784 PassRefPtr<HTMLElement> createHTMLElement(Document* document, const AtomicString& tagName)
785 {
786     return createHTMLElement(document, QualifiedName(nullAtom, tagName, xhtmlNamespaceURI));
787 }
788 
isTabSpanNode(const Node * node)789 bool isTabSpanNode(const Node *node)
790 {
791     return node && node->hasTagName(spanTag) && node->isElementNode() && static_cast<const Element *>(node)->getAttribute(classAttr) == AppleTabSpanClass;
792 }
793 
isTabSpanTextNode(const Node * node)794 bool isTabSpanTextNode(const Node *node)
795 {
796     return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
797 }
798 
tabSpanNode(const Node * node)799 Node *tabSpanNode(const Node *node)
800 {
801     return isTabSpanTextNode(node) ? node->parentNode() : 0;
802 }
803 
positionBeforeTabSpan(const Position & pos)804 Position positionBeforeTabSpan(const Position& pos)
805 {
806     Node *node = pos.node();
807     if (isTabSpanTextNode(node))
808         node = tabSpanNode(node);
809     else if (!isTabSpanNode(node))
810         return pos;
811 
812     return positionBeforeNode(node);
813 }
814 
createTabSpanElement(Document * document,PassRefPtr<Node> tabTextNode)815 PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode)
816 {
817     // make the span to hold the tab
818     ExceptionCode ec = 0;
819     RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
820     ASSERT(ec == 0);
821     spanElement->setAttribute(classAttr, AppleTabSpanClass);
822     spanElement->setAttribute(styleAttr, "white-space:pre");
823 
824     // add tab text to that span
825     if (!tabTextNode)
826         tabTextNode = document->createEditingTextNode("\t");
827     spanElement->appendChild(tabTextNode, ec);
828     ASSERT(ec == 0);
829 
830     return spanElement.release();
831 }
832 
createTabSpanElement(Document * document,const String & tabText)833 PassRefPtr<Element> createTabSpanElement(Document* document, const String& tabText)
834 {
835     return createTabSpanElement(document, document->createTextNode(tabText));
836 }
837 
createTabSpanElement(Document * document)838 PassRefPtr<Element> createTabSpanElement(Document* document)
839 {
840     return createTabSpanElement(document, PassRefPtr<Node>());
841 }
842 
isNodeRendered(const Node * node)843 bool isNodeRendered(const Node *node)
844 {
845     if (!node)
846         return false;
847 
848     RenderObject *renderer = node->renderer();
849     if (!renderer)
850         return false;
851 
852     return renderer->style()->visibility() == VISIBLE;
853 }
854 
nearestMailBlockquote(const Node * node)855 Node *nearestMailBlockquote(const Node *node)
856 {
857     for (Node *n = const_cast<Node *>(node); n; n = n->parentNode()) {
858         if (isMailBlockquote(n))
859             return n;
860     }
861     return 0;
862 }
863 
numEnclosingMailBlockquotes(const Position & p)864 unsigned numEnclosingMailBlockquotes(const Position& p)
865 {
866     unsigned num = 0;
867     for (Node* n = p.node(); n; n = n->parentNode())
868         if (isMailBlockquote(n))
869             num++;
870 
871     return num;
872 }
873 
isMailBlockquote(const Node * node)874 bool isMailBlockquote(const Node *node)
875 {
876     if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag))
877         return false;
878 
879     return static_cast<const Element *>(node)->getAttribute("type") == "cite";
880 }
881 
caretMinOffset(const Node * n)882 int caretMinOffset(const Node* n)
883 {
884     RenderObject* r = n->renderer();
885     ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
886     return r ? r->caretMinOffset() : 0;
887 }
888 
889 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
890 // return the number of children for container nodes and the length for unrendered text nodes.
caretMaxOffset(const Node * n)891 int caretMaxOffset(const Node* n)
892 {
893     // For rendered text nodes, return the last position that a caret could occupy.
894     if (n->isTextNode() && n->renderer())
895         return n->renderer()->caretMaxOffset();
896     // For containers return the number of children.  For others do the same as above.
897     return maxDeepOffset(n);
898 }
899 
lineBreakExistsAtPosition(const VisiblePosition & visiblePosition)900 bool lineBreakExistsAtPosition(const VisiblePosition& visiblePosition)
901 {
902     if (visiblePosition.isNull())
903         return false;
904 
905     Position downstream(visiblePosition.deepEquivalent().downstream());
906     return downstream.node()->hasTagName(brTag) ||
907            downstream.node()->isTextNode() && downstream.node()->renderer()->style()->preserveNewline() && visiblePosition.characterAfter() == '\n';
908 }
909 
910 // Modifies selections that have an end point at the edge of a table
911 // that contains the other endpoint so that they don't confuse
912 // code that iterates over selected paragraphs.
selectionForParagraphIteration(const Selection & original)913 Selection selectionForParagraphIteration(const Selection& original)
914 {
915     Selection newSelection(original);
916     VisiblePosition startOfSelection(newSelection.visibleStart());
917     VisiblePosition endOfSelection(newSelection.visibleEnd());
918 
919     // If the end of the selection to modify is just after a table, and
920     // if the start of the selection is inside that table, then the last paragraph
921     // that we'll want modify is the last one inside the table, not the table itself
922     // (a table is itself a paragraph).
923     if (Node* table = isFirstPositionAfterTable(endOfSelection))
924         if (startOfSelection.deepEquivalent().node()->isDescendantOf(table))
925             newSelection = Selection(startOfSelection, endOfSelection.previous(true));
926 
927     // If the start of the selection to modify is just before a table,
928     // and if the end of the selection is inside that table, then the first paragraph
929     // we'll want to modify is the first one inside the table, not the paragraph
930     // containing the table itself.
931     if (Node* table = isLastPositionBeforeTable(startOfSelection))
932         if (endOfSelection.deepEquivalent().node()->isDescendantOf(table))
933             newSelection = Selection(startOfSelection.next(true), endOfSelection);
934 
935     return newSelection;
936 }
937 
938 
indexForVisiblePosition(VisiblePosition & visiblePosition)939 int indexForVisiblePosition(VisiblePosition& visiblePosition)
940 {
941     if (visiblePosition.isNull())
942         return 0;
943     Position p(visiblePosition.deepEquivalent());
944     RefPtr<Range> range = Range::create(p.node()->document(), Position(p.node()->document(), 0), rangeCompliantEquivalent(p));
945     return TextIterator::rangeLength(range.get(), true);
946 }
947 
avoidIntersectionWithNode(const Range * range,Node * node)948 PassRefPtr<Range> avoidIntersectionWithNode(const Range* range, Node* node)
949 {
950     if (!range)
951         return 0;
952 
953     Document* document = range->ownerDocument();
954 
955     Node* startContainer = range->startContainer();
956     int startOffset = range->startOffset();
957     Node* endContainer = range->endContainer();
958     int endOffset = range->endOffset();
959 
960     if (!startContainer)
961         return 0;
962 
963     ASSERT(endContainer);
964 
965     if (startContainer == node || startContainer->isDescendantOf(node)) {
966         ASSERT(node->parentNode());
967         startContainer = node->parentNode();
968         startOffset = node->nodeIndex();
969     }
970     if (endContainer == node || endContainer->isDescendantOf(node)) {
971         ASSERT(node->parentNode());
972         endContainer = node->parentNode();
973         endOffset = node->nodeIndex();
974     }
975 
976     return Range::create(document, startContainer, startOffset, endContainer, endOffset);
977 }
978 
avoidIntersectionWithNode(const Selection & selection,Node * node)979 Selection avoidIntersectionWithNode(const Selection& selection, Node* node)
980 {
981     if (selection.isNone())
982         return Selection(selection);
983 
984     Selection updatedSelection(selection);
985     Node* base = selection.base().node();
986     Node* extent = selection.extent().node();
987     ASSERT(base);
988     ASSERT(extent);
989 
990     if (base == node || base->isDescendantOf(node)) {
991         ASSERT(node->parentNode());
992         updatedSelection.setBase(Position(node->parentNode(), node->nodeIndex()));
993     }
994 
995     if (extent == node || extent->isDescendantOf(node)) {
996         ASSERT(node->parentNode());
997         updatedSelection.setExtent(Position(node->parentNode(), node->nodeIndex()));
998     }
999 
1000     return updatedSelection;
1001 }
1002 
1003 } // namespace WebCore
1004