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