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