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