• 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 "core/editing/htmlediting.h"
28 
29 #include "HTMLElementFactory.h"
30 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "bindings/v8/ExceptionStatePlaceholder.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/PositionIterator.h"
36 #include "core/dom/Range.h"
37 #include "core/dom/Text.h"
38 #include "core/dom/shadow/ShadowRoot.h"
39 #include "core/editing/Editor.h"
40 #include "core/editing/HTMLInterchange.h"
41 #include "core/editing/PlainTextRange.h"
42 #include "core/editing/TextIterator.h"
43 #include "core/editing/VisiblePosition.h"
44 #include "core/editing/VisibleSelection.h"
45 #include "core/editing/VisibleUnits.h"
46 #include "core/html/HTMLBRElement.h"
47 #include "core/html/HTMLDivElement.h"
48 #include "core/html/HTMLLIElement.h"
49 #include "core/html/HTMLOListElement.h"
50 #include "core/html/HTMLParagraphElement.h"
51 #include "core/html/HTMLTableElement.h"
52 #include "core/html/HTMLUListElement.h"
53 #include "core/frame/Frame.h"
54 #include "core/rendering/RenderObject.h"
55 #include "wtf/Assertions.h"
56 #include "wtf/StdLibExtras.h"
57 #include "wtf/text/StringBuilder.h"
58 
59 using namespace std;
60 
61 namespace WebCore {
62 
63 using namespace HTMLNames;
64 
65 // Atomic means that the node has no children, or has children which are ignored for the
66 // purposes of editing.
isAtomicNode(const Node * node)67 bool isAtomicNode(const Node *node)
68 {
69     return node && (!node->hasChildNodes() || editingIgnoresContent(node));
70 }
71 
72 // Compare two positions, taking into account the possibility that one or both
73 // could be inside a shadow tree. Only works for non-null values.
comparePositions(const Position & a,const Position & b)74 int comparePositions(const Position& a, const Position& b)
75 {
76     TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
77 
78     ASSERT(commonScope);
79     if (!commonScope)
80         return 0;
81 
82     Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
83     ASSERT(nodeA);
84     bool hasDescendentA = nodeA != a.containerNode();
85     int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
86 
87     Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
88     ASSERT(nodeB);
89     bool hasDescendentB = nodeB != b.containerNode();
90     int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
91 
92     int bias = 0;
93     if (nodeA == nodeB) {
94         if (hasDescendentA)
95             bias = -1;
96         else if (hasDescendentB)
97             bias = 1;
98     }
99 
100     int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
101     return result ? result : bias;
102 }
103 
comparePositions(const PositionWithAffinity & a,const PositionWithAffinity & b)104 int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b)
105 {
106     return comparePositions(a.position(), b.position());
107 }
108 
comparePositions(const VisiblePosition & a,const VisiblePosition & b)109 int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
110 {
111     return comparePositions(a.deepEquivalent(), b.deepEquivalent());
112 }
113 
highestEditableRoot(const Position & position,EditableType editableType)114 Node* highestEditableRoot(const Position& position, EditableType editableType)
115 {
116     Node* node = position.deprecatedNode();
117     if (!node)
118         return 0;
119 
120     Node* highestRoot = editableRootForPosition(position, editableType);
121     if (!highestRoot)
122         return 0;
123 
124     if (highestRoot->hasTagName(bodyTag))
125         return highestRoot;
126 
127     node = highestRoot->parentNode();
128     while (node) {
129         if (node->rendererIsEditable(editableType))
130             highestRoot = node;
131         if (node->hasTagName(bodyTag))
132             break;
133         node = node->parentNode();
134     }
135 
136     return highestRoot;
137 }
138 
lowestEditableAncestor(Node * node)139 Node* lowestEditableAncestor(Node* node)
140 {
141     while (node) {
142         if (node->rendererIsEditable())
143             return node->rootEditableElement();
144         if (node->hasTagName(bodyTag))
145             break;
146         node = node->parentNode();
147     }
148 
149     return 0;
150 }
151 
isEditablePosition(const Position & p,EditableType editableType,EUpdateStyle updateStyle)152 bool isEditablePosition(const Position& p, EditableType editableType, EUpdateStyle updateStyle)
153 {
154     Node* node = p.deprecatedNode();
155     if (!node)
156         return false;
157     if (updateStyle == UpdateStyle)
158         node->document().updateLayoutIgnorePendingStylesheets();
159     else
160         ASSERT(updateStyle == DoNotUpdateStyle);
161 
162     if (isTableElement(node))
163         node = node->parentNode();
164 
165     return node->rendererIsEditable(editableType);
166 }
167 
isAtUnsplittableElement(const Position & pos)168 bool isAtUnsplittableElement(const Position& pos)
169 {
170     Node* node = pos.deprecatedNode();
171     return (node == editableRootForPosition(pos) || node == enclosingNodeOfType(pos, &isTableCell));
172 }
173 
174 
isRichlyEditablePosition(const Position & p,EditableType editableType)175 bool isRichlyEditablePosition(const Position& p, EditableType editableType)
176 {
177     Node* node = p.deprecatedNode();
178     if (!node)
179         return false;
180 
181     if (isTableElement(node))
182         node = node->parentNode();
183 
184     return node->rendererIsRichlyEditable(editableType);
185 }
186 
editableRootForPosition(const Position & p,EditableType editableType)187 Element* editableRootForPosition(const Position& p, EditableType editableType)
188 {
189     Node* node = p.containerNode();
190     if (!node)
191         return 0;
192 
193     if (isTableElement(node))
194         node = node->parentNode();
195 
196     return node->rootEditableElement(editableType);
197 }
198 
199 // Finds the enclosing element until which the tree can be split.
200 // When a user hits ENTER, he/she won't expect this element to be split into two.
201 // You may pass it as the second argument of splitTreeToNode.
unsplittableElementForPosition(const Position & p)202 Element* unsplittableElementForPosition(const Position& p)
203 {
204     // Since enclosingNodeOfType won't search beyond the highest root editable node,
205     // this code works even if the closest table cell was outside of the root editable node.
206     Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell));
207     if (enclosingCell)
208         return enclosingCell;
209 
210     return editableRootForPosition(p);
211 }
212 
nextCandidate(const Position & position)213 Position nextCandidate(const Position& position)
214 {
215     PositionIterator p = position;
216     while (!p.atEnd()) {
217         p.increment();
218         if (p.isCandidate())
219             return p;
220     }
221     return Position();
222 }
223 
nextVisuallyDistinctCandidate(const Position & position)224 Position nextVisuallyDistinctCandidate(const Position& position)
225 {
226     Position p = position;
227     Position downstreamStart = p.downstream();
228     while (!p.atEndOfTree()) {
229         p = p.next(Character);
230         if (p.isCandidate() && p.downstream() != downstreamStart)
231             return p;
232     }
233     return Position();
234 }
235 
previousCandidate(const Position & position)236 Position previousCandidate(const Position& position)
237 {
238     PositionIterator p = position;
239     while (!p.atStart()) {
240         p.decrement();
241         if (p.isCandidate())
242             return p;
243     }
244     return Position();
245 }
246 
previousVisuallyDistinctCandidate(const Position & position)247 Position previousVisuallyDistinctCandidate(const Position& position)
248 {
249     Position p = position;
250     Position downstreamStart = p.downstream();
251     while (!p.atStartOfTree()) {
252         p = p.previous(Character);
253         if (p.isCandidate() && p.downstream() != downstreamStart)
254             return p;
255     }
256     return Position();
257 }
258 
firstEditablePositionAfterPositionInRoot(const Position & position,Node * highestRoot)259 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
260 {
261     // position falls before highestRoot.
262     if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->rendererIsEditable())
263         return firstPositionInNode(highestRoot);
264 
265     Position p = position;
266 
267     if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
268         Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
269         if (!shadowAncestor)
270             return VisiblePosition();
271 
272         p = positionAfterNode(shadowAncestor);
273     }
274 
275     while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
276         p = isAtomicNode(p.deprecatedNode()) ? positionInParentAfterNode(p.deprecatedNode()) : nextVisuallyDistinctCandidate(p);
277 
278     if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
279         return VisiblePosition();
280 
281     return VisiblePosition(p);
282 }
283 
lastEditablePositionBeforePositionInRoot(const Position & position,Node * highestRoot)284 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
285 {
286     // When position falls after highestRoot, the result is easy to compute.
287     if (comparePositions(position, lastPositionInNode(highestRoot)) == 1)
288         return lastPositionInNode(highestRoot);
289 
290     Position p = position;
291 
292     if (position.deprecatedNode()->treeScope() != highestRoot->treeScope()) {
293         Node* shadowAncestor = highestRoot->treeScope().ancestorInThisScope(p.deprecatedNode());
294         if (!shadowAncestor)
295             return VisiblePosition();
296 
297         p = firstPositionInOrBeforeNode(shadowAncestor);
298     }
299 
300     while (p.deprecatedNode() && !isEditablePosition(p) && p.deprecatedNode()->isDescendantOf(highestRoot))
301         p = isAtomicNode(p.deprecatedNode()) ? positionInParentBeforeNode(p.deprecatedNode()) : previousVisuallyDistinctCandidate(p);
302 
303     if (p.deprecatedNode() && p.deprecatedNode() != highestRoot && !p.deprecatedNode()->isDescendantOf(highestRoot))
304         return VisiblePosition();
305 
306     return VisiblePosition(p);
307 }
308 
309 // FIXME: The method name, comment, and code say three different things here!
310 // Whether or not content before and after this node will collapse onto the same line as it.
isBlock(const Node * node)311 bool isBlock(const Node* node)
312 {
313     return node && node->isElementNode() && node->renderer() && !node->renderer()->isInline() && !node->renderer()->isRubyText();
314 }
315 
isInline(const Node * node)316 bool isInline(const Node* node)
317 {
318     return node && node->isElementNode() && node->renderer() && node->renderer()->isInline();
319 }
320 
321 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
322 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the
323 // block that contains the table and not the table, and this function should be the only one responsible for
324 // knowing about these kinds of special cases.
enclosingBlock(Node * node,EditingBoundaryCrossingRule rule)325 Element* enclosingBlock(Node* node, EditingBoundaryCrossingRule rule)
326 {
327     Node* enclosingNode = enclosingNodeOfType(firstPositionInOrBeforeNode(node), isBlock, rule);
328     return toElement(enclosingNode);
329 }
330 
directionOfEnclosingBlock(const Position & position)331 TextDirection directionOfEnclosingBlock(const Position& position)
332 {
333     Node* enclosingBlockNode = enclosingBlock(position.containerNode());
334     if (!enclosingBlockNode)
335         return LTR;
336     RenderObject* renderer = enclosingBlockNode->renderer();
337     return renderer ? renderer->style()->direction() : LTR;
338 }
339 
340 // This method is used to create positions in the DOM. It returns the maximum valid offset
341 // in a node. It returns 1 for some elements even though they do not have children, which
342 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
343 // on a Position before using it to create a DOM Range, or an exception will be thrown.
lastOffsetForEditing(const Node * node)344 int lastOffsetForEditing(const Node* node)
345 {
346     ASSERT(node);
347     if (!node)
348         return 0;
349     if (node->offsetInCharacters())
350         return node->maxCharacterOffset();
351 
352     if (node->hasChildNodes())
353         return node->childNodeCount();
354 
355     // NOTE: This should preempt the childNodeCount for, e.g., select nodes
356     if (editingIgnoresContent(node))
357         return 1;
358 
359     return 0;
360 }
361 
stringWithRebalancedWhitespace(const String & string,bool startIsStartOfParagraph,bool endIsEndOfParagraph)362 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
363 {
364     unsigned length = string.length();
365 
366     StringBuilder rebalancedString;
367     rebalancedString.reserveCapacity(length);
368 
369     bool previousCharacterWasSpace = false;
370     for (size_t i = 0; i < length; i++) {
371         UChar c = string[i];
372         if (!isWhitespace(c)) {
373             rebalancedString.append(c);
374             previousCharacterWasSpace = false;
375             continue;
376         }
377 
378         if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) {
379             rebalancedString.append(noBreakSpace);
380             previousCharacterWasSpace = false;
381         } else {
382             rebalancedString.append(' ');
383             previousCharacterWasSpace = true;
384         }
385     }
386 
387     ASSERT(rebalancedString.length() == length);
388 
389     return rebalancedString.toString();
390 }
391 
isTableStructureNode(const Node * node)392 bool isTableStructureNode(const Node *node)
393 {
394     RenderObject* renderer = node->renderer();
395     return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
396 }
397 
nonBreakingSpaceString()398 const String& nonBreakingSpaceString()
399 {
400     DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
401     return nonBreakingSpaceString;
402 }
403 
404 // FIXME: need to dump this
isSpecialElement(const Node * n)405 bool isSpecialElement(const Node *n)
406 {
407     if (!n)
408         return false;
409 
410     if (!n->isHTMLElement())
411         return false;
412 
413     if (n->isLink())
414         return true;
415 
416     RenderObject* renderer = n->renderer();
417     if (!renderer)
418         return false;
419 
420     if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
421         return true;
422 
423     if (renderer->style()->isFloating())
424         return true;
425 
426     return false;
427 }
428 
firstInSpecialElement(const Position & pos)429 static Node* firstInSpecialElement(const Position& pos)
430 {
431     Node* rootEditableElement = pos.containerNode()->rootEditableElement();
432     for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
433         if (isSpecialElement(n)) {
434             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
435             VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
436             if (isRenderedTable(n) && vPos == firstInElement.next())
437                 return n;
438             if (vPos == firstInElement)
439                 return n;
440         }
441     return 0;
442 }
443 
lastInSpecialElement(const Position & pos)444 static Node* lastInSpecialElement(const Position& pos)
445 {
446     Node* rootEditableElement = pos.containerNode()->rootEditableElement();
447     for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
448         if (isSpecialElement(n)) {
449             VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
450             VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
451             if (isRenderedTable(n) && vPos == lastInElement.previous())
452                 return n;
453             if (vPos == lastInElement)
454                 return n;
455         }
456     return 0;
457 }
458 
positionBeforeContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)459 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
460 {
461     Node* n = firstInSpecialElement(pos);
462     if (!n)
463         return pos;
464     Position result = positionInParentBeforeNode(n);
465     if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
466         return pos;
467     if (containingSpecialElement)
468         *containingSpecialElement = n;
469     return result;
470 }
471 
positionAfterContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)472 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
473 {
474     Node* n = lastInSpecialElement(pos);
475     if (!n)
476         return pos;
477     Position result = positionInParentAfterNode(n);
478     if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
479         return pos;
480     if (containingSpecialElement)
481         *containingSpecialElement = n;
482     return result;
483 }
484 
isFirstPositionAfterTable(const VisiblePosition & visiblePosition)485 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
486 {
487     Position upstream(visiblePosition.deepEquivalent().upstream());
488     if (isRenderedTable(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode())
489         return upstream.deprecatedNode();
490 
491     return 0;
492 }
493 
isLastPositionBeforeTable(const VisiblePosition & visiblePosition)494 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
495 {
496     Position downstream(visiblePosition.deepEquivalent().downstream());
497     if (isRenderedTable(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode())
498         return downstream.deprecatedNode();
499 
500     return 0;
501 }
502 
503 // Returns the visible position at the beginning of a node
visiblePositionBeforeNode(Node * node)504 VisiblePosition visiblePositionBeforeNode(Node* node)
505 {
506     ASSERT(node);
507     if (node->childNodeCount())
508         return VisiblePosition(firstPositionInOrBeforeNode(node), DOWNSTREAM);
509     ASSERT(node->parentNode());
510     ASSERT(!node->parentNode()->isShadowRoot());
511     return positionInParentBeforeNode(node);
512 }
513 
514 // Returns the visible position at the ending of a node
visiblePositionAfterNode(Node * node)515 VisiblePosition visiblePositionAfterNode(Node* node)
516 {
517     ASSERT(node);
518     if (node->childNodeCount())
519         return VisiblePosition(lastPositionInOrAfterNode(node), DOWNSTREAM);
520     ASSERT(node->parentNode());
521     ASSERT(!node->parentNode()->isShadowRoot());
522     return positionInParentAfterNode(node);
523 }
524 
525 // Create a range object with two visible positions, start and end.
526 // create(Document*, const Position&, const Position&); will use deprecatedEditingOffset
527 // Use this function instead of create a regular range object (avoiding editing offset).
createRange(Document & document,const VisiblePosition & start,const VisiblePosition & end,ExceptionState & exceptionState)528 PassRefPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState)
529 {
530     RefPtr<Range> selectedRange = Range::create(document);
531     selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
532     if (!exceptionState.hadException())
533         selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
534     return selectedRange.release();
535 }
536 
isListElement(Node * n)537 bool isListElement(Node *n)
538 {
539     return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
540 }
541 
isListItem(const Node * n)542 bool isListItem(const Node *n)
543 {
544     return n && n->renderer() && n->renderer()->isListItem();
545 }
546 
enclosingNodeWithTag(const Position & p,const QualifiedName & tagName)547 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
548 {
549     if (p.isNull())
550         return 0;
551 
552     Node* root = highestEditableRoot(p);
553     for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
554         if (root && !n->rendererIsEditable())
555             continue;
556         if (n->hasTagName(tagName))
557             return n;
558         if (n == root)
559             return 0;
560     }
561 
562     return 0;
563 }
564 
enclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *),EditingBoundaryCrossingRule rule)565 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
566 {
567     // FIXME: support CanSkipCrossEditingBoundary
568     ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
569     if (p.isNull())
570         return 0;
571 
572     Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
573     for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
574         // Don't return a non-editable node if the input position was editable, since
575         // the callers from editing will no doubt want to perform editing inside the returned node.
576         if (root && !n->rendererIsEditable())
577             continue;
578         if (nodeIsOfType(n))
579             return n;
580         if (n == root)
581             return 0;
582     }
583 
584     return 0;
585 }
586 
highestEnclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *),EditingBoundaryCrossingRule rule,Node * stayWithin)587 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
588 {
589     Node* highest = 0;
590     Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
591     for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
592         if (root && !n->rendererIsEditable())
593             continue;
594         if (nodeIsOfType(n))
595             highest = n;
596         if (n == root)
597             break;
598     }
599 
600     return highest;
601 }
602 
hasARenderedDescendant(Node * node,Node * excludedNode)603 static bool hasARenderedDescendant(Node* node, Node* excludedNode)
604 {
605     for (Node* n = node->firstChild(); n;) {
606         if (n == excludedNode) {
607             n = NodeTraversal::nextSkippingChildren(*n, node);
608             continue;
609         }
610         if (n->renderer())
611             return true;
612         n = NodeTraversal::next(*n, node);
613     }
614     return false;
615 }
616 
highestNodeToRemoveInPruning(Node * node,Node * excludeNode)617 Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode)
618 {
619     Node* previousNode = 0;
620     Node* rootEditableElement = node ? node->rootEditableElement() : 0;
621     for (; node; node = node->parentNode()) {
622         if (RenderObject* renderer = node->renderer()) {
623             if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node)
624                 return previousNode;
625         }
626         previousNode = node;
627     }
628     return 0;
629 }
630 
enclosingTableCell(const Position & p)631 Node* enclosingTableCell(const Position& p)
632 {
633     return toElement(enclosingNodeOfType(p, isTableCell));
634 }
635 
enclosingAnchorElement(const Position & p)636 Element* enclosingAnchorElement(const Position& p)
637 {
638     if (p.isNull())
639         return 0;
640 
641     Node* node = p.deprecatedNode();
642     while (node && !(node->isElementNode() && node->isLink()))
643         node = node->parentNode();
644     return toElement(node);
645 }
646 
enclosingList(Node * node)647 HTMLElement* enclosingList(Node* node)
648 {
649     if (!node)
650         return 0;
651 
652     Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
653 
654     for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
655         if (n->hasTagName(ulTag) || n->hasTagName(olTag))
656             return toHTMLElement(n);
657         if (n == root)
658             return 0;
659     }
660 
661     return 0;
662 }
663 
enclosingListChild(Node * node)664 Node* enclosingListChild(Node *node)
665 {
666     if (!node)
667         return 0;
668     // Check for a list item element, or for a node whose parent is a list element. Such a node
669     // will appear visually as a list item (but without a list marker)
670     Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
671 
672     // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
673     for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
674         if (n->hasTagName(liTag) || (isListElement(n->parentNode()) && n != root))
675             return n;
676         if (n == root || isTableCell(n))
677             return 0;
678     }
679 
680     return 0;
681 }
682 
embeddedSublist(Node * listItem)683 static HTMLElement* embeddedSublist(Node* listItem)
684 {
685     // Check the DOM so that we'll find collapsed sublists without renderers.
686     for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
687         if (isListElement(n))
688             return toHTMLElement(n);
689     }
690 
691     return 0;
692 }
693 
appendedSublist(Node * listItem)694 static Node* appendedSublist(Node* listItem)
695 {
696     // Check the DOM so that we'll find collapsed sublists without renderers.
697     for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
698         if (isListElement(n))
699             return toHTMLElement(n);
700         if (isListItem(listItem))
701             return 0;
702     }
703 
704     return 0;
705 }
706 
707 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
enclosingEmptyListItem(const VisiblePosition & visiblePos)708 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
709 {
710     // Check that position is on a line by itself inside a list item
711     Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
712     if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
713         return 0;
714 
715     VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
716     VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
717 
718     if (firstInListChild != visiblePos || lastInListChild != visiblePos)
719         return 0;
720 
721     if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
722         return 0;
723 
724     return listChildNode;
725 }
726 
outermostEnclosingList(Node * node,Node * rootList)727 HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
728 {
729     HTMLElement* list = enclosingList(node);
730     if (!list)
731         return 0;
732 
733     while (HTMLElement* nextList = enclosingList(list)) {
734         if (nextList == rootList)
735             break;
736         list = nextList;
737     }
738 
739     return list;
740 }
741 
canMergeLists(Element * firstList,Element * secondList)742 bool canMergeLists(Element* firstList, Element* secondList)
743 {
744     if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
745         return false;
746 
747     return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
748     && firstList->rendererIsEditable() && secondList->rendererIsEditable() // both lists are editable
749     && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
750     && isVisiblyAdjacent(positionInParentAfterNode(firstList), positionInParentBeforeNode(secondList));
751     // Make sure there is no visible content between this li and the previous list
752 }
753 
isTableElement(const Node * node)754 bool isTableElement(const Node* node)
755 {
756     if (!node || !node->isElementNode())
757         return false;
758 
759     return node->hasTagName(tableTag);
760 }
761 
isRenderedTable(const Node * node)762 bool isRenderedTable(const Node* node)
763 {
764     if (!node || !node->isElementNode())
765         return false;
766 
767     RenderObject* renderer = node->renderer();
768     return (renderer && renderer->isTable());
769 }
770 
isTableCell(const Node * node)771 bool isTableCell(const Node* node)
772 {
773     RenderObject* r = node->renderer();
774     if (!r)
775         return node->hasTagName(tdTag) || node->hasTagName(thTag);
776 
777     return r->isTableCell();
778 }
779 
isEmptyTableCell(const Node * node)780 bool isEmptyTableCell(const Node* node)
781 {
782     // Returns true IFF the passed in node is one of:
783     //   .) a table cell with no children,
784     //   .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers
785     //   .) the BR child of such a table cell
786 
787     // Find rendered node
788     while (node && !node->renderer())
789         node = node->parentNode();
790     if (!node)
791         return false;
792 
793     // Make sure the rendered node is a table cell or <br>.
794     // If it's a <br>, then the parent node has to be a table cell.
795     RenderObject* renderer = node->renderer();
796     if (renderer->isBR()) {
797         renderer = renderer->parent();
798         if (!renderer)
799             return false;
800     }
801     if (!renderer->isTableCell())
802         return false;
803 
804     // Check that the table cell contains no child renderers except for perhaps a single <br>.
805     RenderObject* childRenderer = renderer->firstChild();
806     if (!childRenderer)
807         return true;
808     if (!childRenderer->isBR())
809         return false;
810     return !childRenderer->nextSibling();
811 }
812 
createDefaultParagraphElement(Document & document)813 PassRefPtr<HTMLElement> createDefaultParagraphElement(Document& document)
814 {
815     switch (document.frame()->editor().defaultParagraphSeparator()) {
816     case EditorParagraphSeparatorIsDiv:
817         return HTMLDivElement::create(document);
818     case EditorParagraphSeparatorIsP:
819         return HTMLParagraphElement::create(document);
820     }
821 
822     ASSERT_NOT_REACHED();
823     return 0;
824 }
825 
createBreakElement(Document & document)826 PassRefPtr<HTMLElement> createBreakElement(Document& document)
827 {
828     return HTMLBRElement::create(document);
829 }
830 
createOrderedListElement(Document & document)831 PassRefPtr<HTMLElement> createOrderedListElement(Document& document)
832 {
833     return HTMLOListElement::create(document);
834 }
835 
createUnorderedListElement(Document & document)836 PassRefPtr<HTMLElement> createUnorderedListElement(Document& document)
837 {
838     return HTMLUListElement::create(document);
839 }
840 
createListItemElement(Document & document)841 PassRefPtr<HTMLElement> createListItemElement(Document& document)
842 {
843     return HTMLLIElement::create(document);
844 }
845 
createHTMLElement(Document & document,const QualifiedName & name)846 PassRefPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
847 {
848     return createHTMLElement(document, name.localName());
849 }
850 
createHTMLElement(Document & document,const AtomicString & tagName)851 PassRefPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
852 {
853     return HTMLElementFactory::createHTMLElement(tagName, document, 0, false);
854 }
855 
isTabSpanNode(const Node * node)856 bool isTabSpanNode(const Node *node)
857 {
858     return node && node->hasTagName(spanTag) && node->isElementNode() && toElement(node)->getAttribute(classAttr) == AppleTabSpanClass;
859 }
860 
isTabSpanTextNode(const Node * node)861 bool isTabSpanTextNode(const Node *node)
862 {
863     return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
864 }
865 
tabSpanNode(const Node * node)866 Node* tabSpanNode(const Node *node)
867 {
868     return isTabSpanTextNode(node) ? node->parentNode() : 0;
869 }
870 
positionOutsideTabSpan(const Position & pos)871 Position positionOutsideTabSpan(const Position& pos)
872 {
873     Node* node = pos.containerNode();
874     if (isTabSpanTextNode(node))
875         node = tabSpanNode(node);
876     else if (!isTabSpanNode(node))
877         return pos;
878 
879     if (node && VisiblePosition(pos) == lastPositionInNode(node))
880         return positionInParentAfterNode(node);
881 
882     return positionInParentBeforeNode(node);
883 }
884 
createTabSpanElement(Document & document,PassRefPtr<Node> prpTabTextNode)885 PassRefPtr<Element> createTabSpanElement(Document& document, PassRefPtr<Node> prpTabTextNode)
886 {
887     RefPtr<Node> tabTextNode = prpTabTextNode;
888 
889     // Make the span to hold the tab.
890     RefPtr<Element> spanElement = document.createElement(spanTag, false);
891     spanElement->setAttribute(classAttr, AppleTabSpanClass);
892     spanElement->setAttribute(styleAttr, "white-space:pre");
893 
894     // Add tab text to that span.
895     if (!tabTextNode)
896         tabTextNode = document.createEditingTextNode("\t");
897 
898     spanElement->appendChild(tabTextNode.release());
899 
900     return spanElement.release();
901 }
902 
createTabSpanElement(Document & document,const String & tabText)903 PassRefPtr<Element> createTabSpanElement(Document& document, const String& tabText)
904 {
905     return createTabSpanElement(document, document.createTextNode(tabText));
906 }
907 
createTabSpanElement(Document & document)908 PassRefPtr<Element> createTabSpanElement(Document& document)
909 {
910     return createTabSpanElement(document, PassRefPtr<Node>());
911 }
912 
isNodeRendered(const Node * node)913 bool isNodeRendered(const Node *node)
914 {
915     if (!node)
916         return false;
917 
918     RenderObject* renderer = node->renderer();
919     if (!renderer)
920         return false;
921 
922     return renderer->style()->visibility() == VISIBLE;
923 }
924 
numEnclosingMailBlockquotes(const Position & p)925 unsigned numEnclosingMailBlockquotes(const Position& p)
926 {
927     unsigned num = 0;
928     for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
929         if (isMailBlockquote(n))
930             num++;
931 
932     return num;
933 }
934 
updatePositionForNodeRemoval(Position & position,Node * node)935 void updatePositionForNodeRemoval(Position& position, Node* node)
936 {
937     if (position.isNull())
938         return;
939     switch (position.anchorType()) {
940     case Position::PositionIsBeforeChildren:
941         if (position.containerNode() == node)
942             position = positionInParentBeforeNode(node);
943         break;
944     case Position::PositionIsAfterChildren:
945         if (position.containerNode() == node)
946             position = positionInParentAfterNode(node);
947         break;
948     case Position::PositionIsOffsetInAnchor:
949         if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
950             position.moveToOffset(position.offsetInContainerNode() - 1);
951         else if (node->containsIncludingShadowDOM(position.containerNode()))
952             position = positionInParentBeforeNode(node);
953         break;
954     case Position::PositionIsAfterAnchor:
955         if (node->containsIncludingShadowDOM(position.anchorNode()))
956             position = positionInParentAfterNode(node);
957         break;
958     case Position::PositionIsBeforeAnchor:
959         if (node->containsIncludingShadowDOM(position.anchorNode()))
960             position = positionInParentBeforeNode(node);
961         break;
962     }
963 }
964 
isMailBlockquote(const Node * node)965 bool isMailBlockquote(const Node *node)
966 {
967     if (!node || !node->hasTagName(blockquoteTag))
968         return false;
969 
970     return toElement(node)->getAttribute("type") == "cite";
971 }
972 
caretMinOffset(const Node * n)973 int caretMinOffset(const Node* n)
974 {
975     RenderObject* r = n->renderer();
976     ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
977     return r ? r->caretMinOffset() : 0;
978 }
979 
980 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
981 // return the number of children for container nodes and the length for unrendered text nodes.
caretMaxOffset(const Node * n)982 int caretMaxOffset(const Node* n)
983 {
984     // For rendered text nodes, return the last position that a caret could occupy.
985     if (n->isTextNode() && n->renderer())
986         return n->renderer()->caretMaxOffset();
987     // For containers return the number of children. For others do the same as above.
988     return lastOffsetForEditing(n);
989 }
990 
lineBreakExistsAtVisiblePosition(const VisiblePosition & visiblePosition)991 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
992 {
993     return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
994 }
995 
lineBreakExistsAtPosition(const Position & position)996 bool lineBreakExistsAtPosition(const Position& position)
997 {
998     if (position.isNull())
999         return false;
1000 
1001     if (position.anchorNode()->hasTagName(brTag) && position.atFirstEditingPositionForNode())
1002         return true;
1003 
1004     if (!position.anchorNode()->renderer())
1005         return false;
1006 
1007     if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline())
1008         return false;
1009 
1010     Text* textNode = toText(position.anchorNode());
1011     unsigned offset = position.offsetInContainerNode();
1012     return offset < textNode->length() && textNode->data()[offset] == '\n';
1013 }
1014 
1015 // Modifies selections that have an end point at the edge of a table
1016 // that contains the other endpoint so that they don't confuse
1017 // code that iterates over selected paragraphs.
selectionForParagraphIteration(const VisibleSelection & original)1018 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
1019 {
1020     VisibleSelection newSelection(original);
1021     VisiblePosition startOfSelection(newSelection.visibleStart());
1022     VisiblePosition endOfSelection(newSelection.visibleEnd());
1023 
1024     // If the end of the selection to modify is just after a table, and
1025     // if the start of the selection is inside that table, then the last paragraph
1026     // that we'll want modify is the last one inside the table, not the table itself
1027     // (a table is itself a paragraph).
1028     if (Node* table = isFirstPositionAfterTable(endOfSelection))
1029         if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1030             newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
1031 
1032     // If the start of the selection to modify is just before a table,
1033     // and if the end of the selection is inside that table, then the first paragraph
1034     // we'll want to modify is the first one inside the table, not the paragraph
1035     // containing the table itself.
1036     if (Node* table = isLastPositionBeforeTable(startOfSelection))
1037         if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
1038             newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
1039 
1040     return newSelection;
1041 }
1042 
1043 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between
1044 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions
1045 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing
1046 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed,
1047 // or these functions need to be changed to iterate using actual VisiblePositions.
1048 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices.
indexForVisiblePosition(const VisiblePosition & visiblePosition,RefPtr<ContainerNode> & scope)1049 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtr<ContainerNode>& scope)
1050 {
1051     if (visiblePosition.isNull())
1052         return 0;
1053 
1054     Position p(visiblePosition.deepEquivalent());
1055     Document& document = *p.document();
1056     ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
1057 
1058     if (shadowRoot)
1059         scope = shadowRoot;
1060     else
1061         scope = document.documentElement();
1062 
1063     RefPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
1064 
1065     return TextIterator::rangeLength(range.get(), true);
1066 }
1067 
visiblePositionForIndex(int index,ContainerNode * scope)1068 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
1069 {
1070     if (!scope)
1071         return VisiblePosition();
1072     RefPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope);
1073     // Check for an invalid index. Certain editing operations invalidate indices because
1074     // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
1075     if (!range)
1076         return VisiblePosition();
1077     return VisiblePosition(range->startPosition());
1078 }
1079 
1080 // Determines whether two positions are visibly next to each other (first then second)
1081 // while ignoring whitespaces and unrendered nodes
isVisiblyAdjacent(const Position & first,const Position & second)1082 bool isVisiblyAdjacent(const Position& first, const Position& second)
1083 {
1084     return VisiblePosition(first) == VisiblePosition(second.upstream());
1085 }
1086 
1087 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
1088 // Call this function to determine whether a node is visibly fit inside selectedRange
isNodeVisiblyContainedWithin(Node * node,const Range * selectedRange)1089 bool isNodeVisiblyContainedWithin(Node* node, const Range* selectedRange)
1090 {
1091     ASSERT(node);
1092     ASSERT(selectedRange);
1093     // If the node is inside the range, then it surely is contained within
1094     if (selectedRange->compareNode(node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
1095         return true;
1096 
1097     bool startIsVisuallySame = visiblePositionBeforeNode(node) == selectedRange->startPosition();
1098     if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange->endPosition()) < 0)
1099         return true;
1100 
1101     bool endIsVisuallySame = visiblePositionAfterNode(node) == selectedRange->endPosition();
1102     if (endIsVisuallySame && comparePositions(selectedRange->startPosition(), positionInParentBeforeNode(node)) < 0)
1103         return true;
1104 
1105     return startIsVisuallySame && endIsVisuallySame;
1106 }
1107 
isRenderedAsNonInlineTableImageOrHR(const Node * node)1108 bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
1109 {
1110     if (!node)
1111         return false;
1112     RenderObject* renderer = node->renderer();
1113     return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
1114 }
1115 
areIdenticalElements(const Node * first,const Node * second)1116 bool areIdenticalElements(const Node* first, const Node* second)
1117 {
1118     if (!first->isElementNode() || !second->isElementNode())
1119         return false;
1120 
1121     const Element* firstElement = toElement(first);
1122     const Element* secondElement = toElement(second);
1123     if (!firstElement->hasTagName(secondElement->tagQName()))
1124         return false;
1125 
1126     return firstElement->hasEquivalentAttributes(secondElement);
1127 }
1128 
isNonTableCellHTMLBlockElement(const Node * node)1129 bool isNonTableCellHTMLBlockElement(const Node* node)
1130 {
1131     return node->hasTagName(listingTag)
1132         || node->hasTagName(olTag)
1133         || node->hasTagName(preTag)
1134         || isHTMLTableElement(node)
1135         || node->hasTagName(ulTag)
1136         || node->hasTagName(xmpTag)
1137         || node->hasTagName(h1Tag)
1138         || node->hasTagName(h2Tag)
1139         || node->hasTagName(h3Tag)
1140         || node->hasTagName(h4Tag)
1141         || node->hasTagName(h5Tag);
1142 }
1143 
adjustedSelectionStartForStyleComputation(const VisibleSelection & selection)1144 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
1145 {
1146     // This function is used by range style computations to avoid bugs like:
1147     // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once
1148     // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
1149     // with a spurious "mixed" style.
1150 
1151     VisiblePosition visiblePosition = selection.start();
1152     if (visiblePosition.isNull())
1153         return Position();
1154 
1155     // if the selection is a caret, just return the position, since the style
1156     // behind us is relevant
1157     if (selection.isCaret())
1158         return visiblePosition.deepEquivalent();
1159 
1160     // if the selection starts just before a paragraph break, skip over it
1161     if (isEndOfParagraph(visiblePosition))
1162         return visiblePosition.next().deepEquivalent().downstream();
1163 
1164     // otherwise, make sure to be at the start of the first selected node,
1165     // instead of possibly at the end of the last node before the selection
1166     return visiblePosition.deepEquivalent().downstream();
1167 }
1168 
1169 } // namespace WebCore
1170