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/v8/ExceptionState.h"
30 #include "bindings/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/HTMLTableCellElement.h"
54 #include "core/html/HTMLUListElement.h"
55 #include "core/rendering/RenderObject.h"
56 #include "core/rendering/RenderTableCell.h"
57 #include "wtf/Assertions.h"
58 #include "wtf/StdLibExtras.h"
59 #include "wtf/text/StringBuilder.h"
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 // Atomic means that the node has no children, or has children which are ignored for the
66 // purposes of editing.
isAtomicNode(const Node * node)67 bool isAtomicNode(const Node *node)
68 {
69 return node && (!node->hasChildren() || editingIgnoresContent(node));
70 }
71
72 // Compare two positions, taking into account the possibility that one or both
73 // could be inside a shadow tree. Only works for non-null values.
comparePositions(const Position & a,const Position & b)74 int comparePositions(const Position& a, const Position& b)
75 {
76 ASSERT(a.isNotNull());
77 ASSERT(b.isNotNull());
78 TreeScope* commonScope = commonTreeScope(a.containerNode(), b.containerNode());
79
80 ASSERT(commonScope);
81 if (!commonScope)
82 return 0;
83
84 Node* nodeA = commonScope->ancestorInThisScope(a.containerNode());
85 ASSERT(nodeA);
86 bool hasDescendentA = nodeA != a.containerNode();
87 int offsetA = hasDescendentA ? 0 : a.computeOffsetInContainerNode();
88
89 Node* nodeB = commonScope->ancestorInThisScope(b.containerNode());
90 ASSERT(nodeB);
91 bool hasDescendentB = nodeB != b.containerNode();
92 int offsetB = hasDescendentB ? 0 : b.computeOffsetInContainerNode();
93
94 int bias = 0;
95 if (nodeA == nodeB) {
96 if (hasDescendentA)
97 bias = -1;
98 else if (hasDescendentB)
99 bias = 1;
100 }
101
102 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB, IGNORE_EXCEPTION);
103 return result ? result : bias;
104 }
105
comparePositions(const PositionWithAffinity & a,const PositionWithAffinity & b)106 int comparePositions(const PositionWithAffinity& a, const PositionWithAffinity& b)
107 {
108 return comparePositions(a.position(), b.position());
109 }
110
comparePositions(const VisiblePosition & a,const VisiblePosition & b)111 int comparePositions(const VisiblePosition& a, const VisiblePosition& b)
112 {
113 return comparePositions(a.deepEquivalent(), b.deepEquivalent());
114 }
115
highestEditableRoot(const Position & position,EditableType editableType)116 Node* highestEditableRoot(const Position& position, EditableType editableType)
117 {
118 Node* node = position.deprecatedNode();
119 if (!node)
120 return 0;
121
122 Node* highestRoot = editableRootForPosition(position, editableType);
123 if (!highestRoot)
124 return 0;
125
126 if (isHTMLBodyElement(*highestRoot))
127 return highestRoot;
128
129 node = highestRoot->parentNode();
130 while (node) {
131 if (node->rendererIsEditable(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 Node* lowestEditableAncestor(Node* node)
142 {
143 while (node) {
144 if (node->rendererIsEditable())
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 (isRenderedTableElement(node))
165 node = node->parentNode();
166
167 return node->rendererIsEditable(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 (isRenderedTableElement(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 (isRenderedTableElement(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,Node * highestRoot)261 VisiblePosition firstEditableVisiblePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
262 {
263 // position falls before highestRoot.
264 if (comparePositions(position, firstPositionInNode(highestRoot)) == -1 && highestRoot->rendererIsEditable())
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,Node * highestRoot)286 VisiblePosition lastEditableVisiblePositionBeforePositionInRoot(const Position& position, Node* 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
directionOfEnclosingBlock(const Position & position)337 TextDirection directionOfEnclosingBlock(const Position& position)
338 {
339 Node* enclosingBlockNode = enclosingBlock(position.containerNode());
340 if (!enclosingBlockNode)
341 return LTR;
342 RenderObject* renderer = enclosingBlockNode->renderer();
343 return renderer ? renderer->style()->direction() : LTR;
344 }
345
346 // This method is used to create positions in the DOM. It returns the maximum valid offset
347 // in a node. It returns 1 for some elements even though they do not have children, which
348 // creates technically invalid DOM Positions. Be sure to call parentAnchoredEquivalent
349 // on a Position before using it to create a DOM Range, or an exception will be thrown.
lastOffsetForEditing(const Node * node)350 int lastOffsetForEditing(const Node* node)
351 {
352 ASSERT(node);
353 if (!node)
354 return 0;
355 if (node->offsetInCharacters())
356 return node->maxCharacterOffset();
357
358 if (node->hasChildren())
359 return node->countChildren();
360
361 // NOTE: This should preempt the childNodeCount for, e.g., select nodes
362 if (editingIgnoresContent(node))
363 return 1;
364
365 return 0;
366 }
367
stringWithRebalancedWhitespace(const String & string,bool startIsStartOfParagraph,bool endIsEndOfParagraph)368 String stringWithRebalancedWhitespace(const String& string, bool startIsStartOfParagraph, bool endIsEndOfParagraph)
369 {
370 unsigned length = string.length();
371
372 StringBuilder rebalancedString;
373 rebalancedString.reserveCapacity(length);
374
375 bool previousCharacterWasSpace = false;
376 for (size_t i = 0; i < length; i++) {
377 UChar c = string[i];
378 if (!isWhitespace(c)) {
379 rebalancedString.append(c);
380 previousCharacterWasSpace = false;
381 continue;
382 }
383
384 if (previousCharacterWasSpace || (!i && startIsStartOfParagraph) || (i + 1 == length && endIsEndOfParagraph)) {
385 rebalancedString.append(noBreakSpace);
386 previousCharacterWasSpace = false;
387 } else {
388 rebalancedString.append(' ');
389 previousCharacterWasSpace = true;
390 }
391 }
392
393 ASSERT(rebalancedString.length() == length);
394
395 return rebalancedString.toString();
396 }
397
isTableStructureNode(const Node * node)398 bool isTableStructureNode(const Node *node)
399 {
400 RenderObject* renderer = node->renderer();
401 return (renderer && (renderer->isTableCell() || renderer->isTableRow() || renderer->isTableSection() || renderer->isRenderTableCol()));
402 }
403
nonBreakingSpaceString()404 const String& nonBreakingSpaceString()
405 {
406 DEFINE_STATIC_LOCAL(String, nonBreakingSpaceString, (&noBreakSpace, 1));
407 return nonBreakingSpaceString;
408 }
409
410 // FIXME: need to dump this
isSpecialElement(const Node * n)411 bool isSpecialElement(const Node *n)
412 {
413 if (!n)
414 return false;
415
416 if (!n->isHTMLElement())
417 return false;
418
419 if (n->isLink())
420 return true;
421
422 RenderObject* renderer = n->renderer();
423 if (!renderer)
424 return false;
425
426 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
427 return true;
428
429 if (renderer->style()->isFloating())
430 return true;
431
432 return false;
433 }
434
firstInSpecialElement(const Position & pos)435 static Node* firstInSpecialElement(const Position& pos)
436 {
437 Node* rootEditableElement = pos.containerNode()->rootEditableElement();
438 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
439 if (isSpecialElement(n)) {
440 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
441 VisiblePosition firstInElement = VisiblePosition(firstPositionInOrBeforeNode(n), DOWNSTREAM);
442 if (isRenderedTable(n) && vPos == firstInElement.next())
443 return n;
444 if (vPos == firstInElement)
445 return n;
446 }
447 return 0;
448 }
449
lastInSpecialElement(const Position & pos)450 static Node* lastInSpecialElement(const Position& pos)
451 {
452 Node* rootEditableElement = pos.containerNode()->rootEditableElement();
453 for (Node* n = pos.deprecatedNode(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
454 if (isSpecialElement(n)) {
455 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
456 VisiblePosition lastInElement = VisiblePosition(lastPositionInOrAfterNode(n), DOWNSTREAM);
457 if (isRenderedTable(n) && vPos == lastInElement.previous())
458 return n;
459 if (vPos == lastInElement)
460 return n;
461 }
462 return 0;
463 }
464
positionBeforeContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)465 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
466 {
467 Node* n = firstInSpecialElement(pos);
468 if (!n)
469 return pos;
470 Position result = positionInParentBeforeNode(*n);
471 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
472 return pos;
473 if (containingSpecialElement)
474 *containingSpecialElement = n;
475 return result;
476 }
477
positionAfterContainingSpecialElement(const Position & pos,Node ** containingSpecialElement)478 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
479 {
480 Node* n = lastInSpecialElement(pos);
481 if (!n)
482 return pos;
483 Position result = positionInParentAfterNode(*n);
484 if (result.isNull() || result.deprecatedNode()->rootEditableElement() != pos.deprecatedNode()->rootEditableElement())
485 return pos;
486 if (containingSpecialElement)
487 *containingSpecialElement = n;
488 return result;
489 }
490
isFirstPositionAfterTable(const VisiblePosition & visiblePosition)491 Node* isFirstPositionAfterTable(const VisiblePosition& visiblePosition)
492 {
493 Position upstream(visiblePosition.deepEquivalent().upstream());
494 if (isRenderedTable(upstream.deprecatedNode()) && upstream.atLastEditingPositionForNode())
495 return upstream.deprecatedNode();
496
497 return 0;
498 }
499
isLastPositionBeforeTable(const VisiblePosition & visiblePosition)500 Node* isLastPositionBeforeTable(const VisiblePosition& visiblePosition)
501 {
502 Position downstream(visiblePosition.deepEquivalent().downstream());
503 if (isRenderedTable(downstream.deprecatedNode()) && downstream.atFirstEditingPositionForNode())
504 return downstream.deprecatedNode();
505
506 return 0;
507 }
508
509 // Returns the visible position at the beginning of a node
visiblePositionBeforeNode(Node & node)510 VisiblePosition visiblePositionBeforeNode(Node& node)
511 {
512 if (node.hasChildren())
513 return VisiblePosition(firstPositionInOrBeforeNode(&node), DOWNSTREAM);
514 ASSERT(node.parentNode());
515 ASSERT(!node.parentNode()->isShadowRoot());
516 return VisiblePosition(positionInParentBeforeNode(node));
517 }
518
519 // Returns the visible position at the ending of a node
visiblePositionAfterNode(Node & node)520 VisiblePosition visiblePositionAfterNode(Node& node)
521 {
522 if (node.hasChildren())
523 return VisiblePosition(lastPositionInOrAfterNode(&node), DOWNSTREAM);
524 ASSERT(node.parentNode());
525 ASSERT(!node.parentNode()->isShadowRoot());
526 return VisiblePosition(positionInParentAfterNode(node));
527 }
528
529 // Create a range object with two visible positions, start and end.
530 // create(Document*, const Position&, const Position&); will use deprecatedEditingOffset
531 // Use this function instead of create a regular range object (avoiding editing offset).
createRange(Document & document,const VisiblePosition & start,const VisiblePosition & end,ExceptionState & exceptionState)532 PassRefPtrWillBeRawPtr<Range> createRange(Document& document, const VisiblePosition& start, const VisiblePosition& end, ExceptionState& exceptionState)
533 {
534 RefPtrWillBeRawPtr<Range> selectedRange = Range::create(document);
535 selectedRange->setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
536 if (!exceptionState.hadException())
537 selectedRange->setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
538 return selectedRange.release();
539 }
540
isListElement(Node * n)541 bool isListElement(Node* n)
542 {
543 return (n && (isHTMLUListElement(*n) || isHTMLOListElement(*n) || isHTMLDListElement(*n)));
544 }
545
isListItem(const Node * n)546 bool isListItem(const Node* n)
547 {
548 return n && n->renderer() && n->renderer()->isListItem();
549 }
550
enclosingNodeWithTag(const Position & p,const QualifiedName & tagName)551 Node* enclosingNodeWithTag(const Position& p, const QualifiedName& tagName)
552 {
553 if (p.isNull())
554 return 0;
555
556 Node* root = highestEditableRoot(p);
557 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
558 if (root && !n->rendererIsEditable())
559 continue;
560 if (n->hasTagName(tagName))
561 return n;
562 if (n == root)
563 return 0;
564 }
565
566 return 0;
567 }
568
enclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *),EditingBoundaryCrossingRule rule)569 Node* enclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule)
570 {
571 // FIXME: support CanSkipCrossEditingBoundary
572 ASSERT(rule == CanCrossEditingBoundary || rule == CannotCrossEditingBoundary);
573 if (p.isNull())
574 return 0;
575
576 Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
577 for (Node* n = p.deprecatedNode(); n; n = n->parentNode()) {
578 // Don't return a non-editable node if the input position was editable, since
579 // the callers from editing will no doubt want to perform editing inside the returned node.
580 if (root && !n->rendererIsEditable())
581 continue;
582 if (nodeIsOfType(n))
583 return n;
584 if (n == root)
585 return 0;
586 }
587
588 return 0;
589 }
590
highestEnclosingNodeOfType(const Position & p,bool (* nodeIsOfType)(const Node *),EditingBoundaryCrossingRule rule,Node * stayWithin)591 Node* highestEnclosingNodeOfType(const Position& p, bool (*nodeIsOfType)(const Node*), EditingBoundaryCrossingRule rule, Node* stayWithin)
592 {
593 Node* highest = 0;
594 Node* root = rule == CannotCrossEditingBoundary ? highestEditableRoot(p) : 0;
595 for (Node* n = p.containerNode(); n && n != stayWithin; n = n->parentNode()) {
596 if (root && !n->rendererIsEditable())
597 continue;
598 if (nodeIsOfType(n))
599 highest = n;
600 if (n == root)
601 break;
602 }
603
604 return highest;
605 }
606
hasARenderedDescendant(Node * node,Node * excludedNode)607 static bool hasARenderedDescendant(Node* node, Node* excludedNode)
608 {
609 for (Node* n = node->firstChild(); n;) {
610 if (n == excludedNode) {
611 n = NodeTraversal::nextSkippingChildren(*n, node);
612 continue;
613 }
614 if (n->renderer())
615 return true;
616 n = NodeTraversal::next(*n, node);
617 }
618 return false;
619 }
620
highestNodeToRemoveInPruning(Node * node,Node * excludeNode)621 Node* highestNodeToRemoveInPruning(Node* node, Node* excludeNode)
622 {
623 Node* previousNode = 0;
624 Node* rootEditableElement = node ? node->rootEditableElement() : 0;
625 for (; node; node = node->parentNode()) {
626 if (RenderObject* renderer = node->renderer()) {
627 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node || excludeNode == node)
628 return previousNode;
629 }
630 previousNode = node;
631 }
632 return 0;
633 }
634
enclosingTableCell(const Position & p)635 Node* enclosingTableCell(const Position& p)
636 {
637 return toElement(enclosingNodeOfType(p, isTableCell));
638 }
639
enclosingAnchorElement(const Position & p)640 Element* enclosingAnchorElement(const Position& p)
641 {
642 if (p.isNull())
643 return 0;
644
645 Node* node = p.deprecatedNode();
646 while (node && !(node->isElementNode() && node->isLink()))
647 node = node->parentNode();
648 return toElement(node);
649 }
650
enclosingList(Node * node)651 HTMLElement* enclosingList(Node* node)
652 {
653 if (!node)
654 return 0;
655
656 Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
657
658 for (ContainerNode* n = node->parentNode(); n; n = n->parentNode()) {
659 if (isHTMLUListElement(*n) || isHTMLOListElement(*n))
660 return toHTMLElement(n);
661 if (n == root)
662 return 0;
663 }
664
665 return 0;
666 }
667
enclosingListChild(Node * node)668 Node* enclosingListChild(Node *node)
669 {
670 if (!node)
671 return 0;
672 // Check for a list item element, or for a node whose parent is a list element. Such a node
673 // will appear visually as a list item (but without a list marker)
674 Node* root = highestEditableRoot(firstPositionInOrBeforeNode(node));
675
676 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
677 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
678 if (isHTMLLIElement(*n) || (isListElement(n->parentNode()) && n != root))
679 return n;
680 if (n == root || isTableCell(n))
681 return 0;
682 }
683
684 return 0;
685 }
686
687 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
enclosingEmptyListItem(const VisiblePosition & visiblePos)688 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
689 {
690 // Check that position is on a line by itself inside a list item
691 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().deprecatedNode());
692 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
693 return 0;
694
695 VisiblePosition firstInListChild(firstPositionInOrBeforeNode(listChildNode));
696 VisiblePosition lastInListChild(lastPositionInOrAfterNode(listChildNode));
697
698 if (firstInListChild != visiblePos || lastInListChild != visiblePos)
699 return 0;
700
701 return listChildNode;
702 }
703
outermostEnclosingList(Node * node,Node * rootList)704 HTMLElement* outermostEnclosingList(Node* node, Node* rootList)
705 {
706 HTMLElement* list = enclosingList(node);
707 if (!list)
708 return 0;
709
710 while (HTMLElement* nextList = enclosingList(list)) {
711 if (nextList == rootList)
712 break;
713 list = nextList;
714 }
715
716 return list;
717 }
718
canMergeLists(Element * firstList,Element * secondList)719 bool canMergeLists(Element* firstList, Element* secondList)
720 {
721 if (!firstList || !secondList || !firstList->isHTMLElement() || !secondList->isHTMLElement())
722 return false;
723
724 return firstList->hasTagName(secondList->tagQName()) // make sure the list types match (ol vs. ul)
725 && firstList->rendererIsEditable() && secondList->rendererIsEditable() // both lists are editable
726 && firstList->rootEditableElement() == secondList->rootEditableElement() // don't cross editing boundaries
727 && isVisiblyAdjacent(positionInParentAfterNode(*firstList), positionInParentBeforeNode(*secondList));
728 // Make sure there is no visible content between this li and the previous list
729 }
730
isRenderedTableElement(const Node * node)731 bool isRenderedTableElement(const Node* node)
732 {
733 return isHTMLTableElement(*node) && node->renderer();
734 }
735
isRenderedTable(const Node * node)736 bool isRenderedTable(const Node* node)
737 {
738 if (!node || !node->isElementNode())
739 return false;
740
741 RenderObject* renderer = node->renderer();
742 return (renderer && renderer->isTable());
743 }
744
isTableCell(const Node * node)745 bool isTableCell(const Node* node)
746 {
747 ASSERT(node);
748 RenderObject* r = node->renderer();
749 return r ? r->isTableCell() : isHTMLTableCellElement(*node);
750 }
751
isEmptyTableCell(const Node * node)752 bool isEmptyTableCell(const Node* node)
753 {
754 // Returns true IFF the passed in node is one of:
755 // .) a table cell with no children,
756 // .) a table cell with a single BR child, and which has no other child renderers, including :before and :after renderers
757 // .) the BR child of such a table cell
758
759 // Find rendered node
760 while (node && !node->renderer())
761 node = node->parentNode();
762 if (!node)
763 return false;
764
765 // Make sure the rendered node is a table cell or <br>.
766 // If it's a <br>, then the parent node has to be a table cell.
767 RenderObject* renderer = node->renderer();
768 if (renderer->isBR()) {
769 renderer = renderer->parent();
770 if (!renderer)
771 return false;
772 }
773 if (!renderer->isTableCell())
774 return false;
775
776 // Check that the table cell contains no child renderers except for perhaps a single <br>.
777 RenderObject* childRenderer = toRenderTableCell(renderer)->firstChild();
778 if (!childRenderer)
779 return true;
780 if (!childRenderer->isBR())
781 return false;
782 return !childRenderer->nextSibling();
783 }
784
createDefaultParagraphElement(Document & document)785 PassRefPtrWillBeRawPtr<HTMLElement> createDefaultParagraphElement(Document& document)
786 {
787 switch (document.frame()->editor().defaultParagraphSeparator()) {
788 case EditorParagraphSeparatorIsDiv:
789 return HTMLDivElement::create(document);
790 case EditorParagraphSeparatorIsP:
791 return HTMLParagraphElement::create(document);
792 }
793
794 ASSERT_NOT_REACHED();
795 return nullptr;
796 }
797
createBreakElement(Document & document)798 PassRefPtrWillBeRawPtr<HTMLElement> createBreakElement(Document& document)
799 {
800 return HTMLBRElement::create(document);
801 }
802
createOrderedListElement(Document & document)803 PassRefPtrWillBeRawPtr<HTMLElement> createOrderedListElement(Document& document)
804 {
805 return HTMLOListElement::create(document);
806 }
807
createUnorderedListElement(Document & document)808 PassRefPtrWillBeRawPtr<HTMLElement> createUnorderedListElement(Document& document)
809 {
810 return HTMLUListElement::create(document);
811 }
812
createListItemElement(Document & document)813 PassRefPtrWillBeRawPtr<HTMLElement> createListItemElement(Document& document)
814 {
815 return HTMLLIElement::create(document);
816 }
817
createHTMLElement(Document & document,const QualifiedName & name)818 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const QualifiedName& name)
819 {
820 return createHTMLElement(document, name.localName());
821 }
822
createHTMLElement(Document & document,const AtomicString & tagName)823 PassRefPtrWillBeRawPtr<HTMLElement> createHTMLElement(Document& document, const AtomicString& tagName)
824 {
825 return HTMLElementFactory::createHTMLElement(tagName, document, 0, false);
826 }
827
isTabSpanNode(const Node * node)828 bool isTabSpanNode(const Node* node)
829 {
830 if (!isHTMLSpanElement(node) || toElement(node)->getAttribute(classAttr) != AppleTabSpanClass)
831 return false;
832 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass);
833 return true;
834 }
835
isTabSpanTextNode(const Node * node)836 bool isTabSpanTextNode(const Node* node)
837 {
838 return node && node->isTextNode() && node->parentNode() && isTabSpanNode(node->parentNode());
839 }
840
tabSpanNode(const Node * node)841 Node* tabSpanNode(const Node* node)
842 {
843 return isTabSpanTextNode(node) ? node->parentNode() : 0;
844 }
845
createTabSpanElement(Document & document,PassRefPtrWillBeRawPtr<Node> prpTabTextNode)846 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document, PassRefPtrWillBeRawPtr<Node> prpTabTextNode)
847 {
848 RefPtrWillBeRawPtr<Node> tabTextNode = prpTabTextNode;
849
850 // Make the span to hold the tab.
851 RefPtrWillBeRawPtr<Element> spanElement = document.createElement(spanTag, false);
852 spanElement->setAttribute(classAttr, AppleTabSpanClass);
853 spanElement->setAttribute(styleAttr, "white-space:pre");
854
855 // Add tab text to that span.
856 if (!tabTextNode)
857 tabTextNode = document.createEditingTextNode("\t");
858
859 spanElement->appendChild(tabTextNode.release());
860
861 return spanElement.release();
862 }
863
createTabSpanElement(Document & document,const String & tabText)864 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document, const String& tabText)
865 {
866 return createTabSpanElement(document, document.createTextNode(tabText));
867 }
868
createTabSpanElement(Document & document)869 PassRefPtrWillBeRawPtr<Element> createTabSpanElement(Document& document)
870 {
871 return createTabSpanElement(document, PassRefPtrWillBeRawPtr<Node>(nullptr));
872 }
873
isNodeRendered(const Node * node)874 bool isNodeRendered(const Node *node)
875 {
876 if (!node)
877 return false;
878
879 RenderObject* renderer = node->renderer();
880 if (!renderer)
881 return false;
882
883 return renderer->style()->visibility() == VISIBLE;
884 }
885
numEnclosingMailBlockquotes(const Position & p)886 unsigned numEnclosingMailBlockquotes(const Position& p)
887 {
888 unsigned num = 0;
889 for (Node* n = p.deprecatedNode(); n; n = n->parentNode())
890 if (isMailBlockquote(n))
891 num++;
892
893 return num;
894 }
895
updatePositionForNodeRemoval(Position & position,Node & node)896 void updatePositionForNodeRemoval(Position& position, Node& node)
897 {
898 if (position.isNull())
899 return;
900 switch (position.anchorType()) {
901 case Position::PositionIsBeforeChildren:
902 if (position.containerNode() == node)
903 position = positionInParentBeforeNode(node);
904 break;
905 case Position::PositionIsAfterChildren:
906 if (position.containerNode() == node)
907 position = positionInParentAfterNode(node);
908 break;
909 case Position::PositionIsOffsetInAnchor:
910 if (position.containerNode() == node.parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node.nodeIndex())
911 position.moveToOffset(position.offsetInContainerNode() - 1);
912 else if (node.containsIncludingShadowDOM(position.containerNode()))
913 position = positionInParentBeforeNode(node);
914 break;
915 case Position::PositionIsAfterAnchor:
916 if (node.containsIncludingShadowDOM(position.anchorNode()))
917 position = positionInParentAfterNode(node);
918 break;
919 case Position::PositionIsBeforeAnchor:
920 if (node.containsIncludingShadowDOM(position.anchorNode()))
921 position = positionInParentBeforeNode(node);
922 break;
923 }
924 }
925
isMailBlockquote(const Node * node)926 bool isMailBlockquote(const Node *node)
927 {
928 if (!node || !node->hasTagName(blockquoteTag))
929 return false;
930
931 return toElement(node)->getAttribute("type") == "cite";
932 }
933
caretMinOffset(const Node * n)934 int caretMinOffset(const Node* n)
935 {
936 RenderObject* r = n->renderer();
937 ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
938 return r ? r->caretMinOffset() : 0;
939 }
940
941 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
942 // return the number of children for container nodes and the length for unrendered text nodes.
caretMaxOffset(const Node * n)943 int caretMaxOffset(const Node* n)
944 {
945 // For rendered text nodes, return the last position that a caret could occupy.
946 if (n->isTextNode() && n->renderer())
947 return n->renderer()->caretMaxOffset();
948 // For containers return the number of children. For others do the same as above.
949 return lastOffsetForEditing(n);
950 }
951
lineBreakExistsAtVisiblePosition(const VisiblePosition & visiblePosition)952 bool lineBreakExistsAtVisiblePosition(const VisiblePosition& visiblePosition)
953 {
954 return lineBreakExistsAtPosition(visiblePosition.deepEquivalent().downstream());
955 }
956
lineBreakExistsAtPosition(const Position & position)957 bool lineBreakExistsAtPosition(const Position& position)
958 {
959 if (position.isNull())
960 return false;
961
962 if (isHTMLBRElement(*position.anchorNode()) && position.atFirstEditingPositionForNode())
963 return true;
964
965 if (!position.anchorNode()->renderer())
966 return false;
967
968 if (!position.anchorNode()->isTextNode() || !position.anchorNode()->renderer()->style()->preserveNewline())
969 return false;
970
971 Text* textNode = toText(position.anchorNode());
972 unsigned offset = position.offsetInContainerNode();
973 return offset < textNode->length() && textNode->data()[offset] == '\n';
974 }
975
976 // Modifies selections that have an end point at the edge of a table
977 // that contains the other endpoint so that they don't confuse
978 // code that iterates over selected paragraphs.
selectionForParagraphIteration(const VisibleSelection & original)979 VisibleSelection selectionForParagraphIteration(const VisibleSelection& original)
980 {
981 VisibleSelection newSelection(original);
982 VisiblePosition startOfSelection(newSelection.visibleStart());
983 VisiblePosition endOfSelection(newSelection.visibleEnd());
984
985 // If the end of the selection to modify is just after a table, and
986 // if the start of the selection is inside that table, then the last paragraph
987 // that we'll want modify is the last one inside the table, not the table itself
988 // (a table is itself a paragraph).
989 if (Node* table = isFirstPositionAfterTable(endOfSelection))
990 if (startOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
991 newSelection = VisibleSelection(startOfSelection, endOfSelection.previous(CannotCrossEditingBoundary));
992
993 // If the start of the selection to modify is just before a table,
994 // and if the end of the selection is inside that table, then the first paragraph
995 // we'll want to modify is the first one inside the table, not the paragraph
996 // containing the table itself.
997 if (Node* table = isLastPositionBeforeTable(startOfSelection))
998 if (endOfSelection.deepEquivalent().deprecatedNode()->isDescendantOf(table))
999 newSelection = VisibleSelection(startOfSelection.next(CannotCrossEditingBoundary), endOfSelection);
1000
1001 return newSelection;
1002 }
1003
1004 // FIXME: indexForVisiblePosition and visiblePositionForIndex use TextIterators to convert between
1005 // VisiblePositions and indices. But TextIterator iteration using TextIteratorEmitsCharactersBetweenAllVisiblePositions
1006 // does not exactly match VisiblePosition iteration, so using them to preserve a selection during an editing
1007 // opertion is unreliable. TextIterator's TextIteratorEmitsCharactersBetweenAllVisiblePositions mode needs to be fixed,
1008 // or these functions need to be changed to iterate using actual VisiblePositions.
1009 // FIXME: Deploy these functions everywhere that TextIterators are used to convert between VisiblePositions and indices.
indexForVisiblePosition(const VisiblePosition & visiblePosition,RefPtrWillBeRawPtr<ContainerNode> & scope)1010 int indexForVisiblePosition(const VisiblePosition& visiblePosition, RefPtrWillBeRawPtr<ContainerNode>& scope)
1011 {
1012 if (visiblePosition.isNull())
1013 return 0;
1014
1015 Position p(visiblePosition.deepEquivalent());
1016 Document& document = *p.document();
1017 ShadowRoot* shadowRoot = p.anchorNode()->containingShadowRoot();
1018
1019 if (shadowRoot)
1020 scope = shadowRoot;
1021 else
1022 scope = document.documentElement();
1023
1024 RefPtrWillBeRawPtr<Range> range = Range::create(document, firstPositionInNode(scope.get()), p.parentAnchoredEquivalent());
1025
1026 return TextIterator::rangeLength(range.get(), true);
1027 }
1028
visiblePositionForIndex(int index,ContainerNode * scope)1029 VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
1030 {
1031 if (!scope)
1032 return VisiblePosition();
1033 RefPtrWillBeRawPtr<Range> range = PlainTextRange(index).createRangeForSelection(*scope);
1034 // Check for an invalid index. Certain editing operations invalidate indices because
1035 // of problems with TextIteratorEmitsCharactersBetweenAllVisiblePositions.
1036 if (!range)
1037 return VisiblePosition();
1038 return VisiblePosition(range->startPosition());
1039 }
1040
1041 // Determines whether two positions are visibly next to each other (first then second)
1042 // while ignoring whitespaces and unrendered nodes
isVisiblyAdjacent(const Position & first,const Position & second)1043 bool isVisiblyAdjacent(const Position& first, const Position& second)
1044 {
1045 return VisiblePosition(first) == VisiblePosition(second.upstream());
1046 }
1047
1048 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
1049 // Call this function to determine whether a node is visibly fit inside selectedRange
isNodeVisiblyContainedWithin(Node & node,const Range & selectedRange)1050 bool isNodeVisiblyContainedWithin(Node& node, const Range& selectedRange)
1051 {
1052 // If the node is inside the range, then it surely is contained within
1053 if (selectedRange.compareNode(&node, IGNORE_EXCEPTION) == Range::NODE_INSIDE)
1054 return true;
1055
1056 bool startIsVisuallySame = visiblePositionBeforeNode(node) == VisiblePosition(selectedRange.startPosition());
1057 if (startIsVisuallySame && comparePositions(positionInParentAfterNode(node), selectedRange.endPosition()) < 0)
1058 return true;
1059
1060 bool endIsVisuallySame = visiblePositionAfterNode(node) == VisiblePosition(selectedRange.endPosition());
1061 if (endIsVisuallySame && comparePositions(selectedRange.startPosition(), positionInParentBeforeNode(node)) < 0)
1062 return true;
1063
1064 return startIsVisuallySame && endIsVisuallySame;
1065 }
1066
isRenderedAsNonInlineTableImageOrHR(const Node * node)1067 bool isRenderedAsNonInlineTableImageOrHR(const Node* node)
1068 {
1069 if (!node)
1070 return false;
1071 RenderObject* renderer = node->renderer();
1072 return renderer && ((renderer->isTable() && !renderer->isInline()) || (renderer->isImage() && !renderer->isInline()) || renderer->isHR());
1073 }
1074
areIdenticalElements(const Node * first,const Node * second)1075 bool areIdenticalElements(const Node* first, const Node* second)
1076 {
1077 if (!first->isElementNode() || !second->isElementNode())
1078 return false;
1079
1080 const Element* firstElement = toElement(first);
1081 const Element* secondElement = toElement(second);
1082 if (!firstElement->hasTagName(secondElement->tagQName()))
1083 return false;
1084
1085 return firstElement->hasEquivalentAttributes(secondElement);
1086 }
1087
isNonTableCellHTMLBlockElement(const Node * node)1088 bool isNonTableCellHTMLBlockElement(const Node* node)
1089 {
1090 return node->hasTagName(listingTag)
1091 || node->hasTagName(olTag)
1092 || node->hasTagName(preTag)
1093 || node->hasTagName(tableTag)
1094 || node->hasTagName(ulTag)
1095 || node->hasTagName(xmpTag)
1096 || node->hasTagName(h1Tag)
1097 || node->hasTagName(h2Tag)
1098 || node->hasTagName(h3Tag)
1099 || node->hasTagName(h4Tag)
1100 || node->hasTagName(h5Tag);
1101 }
1102
adjustedSelectionStartForStyleComputation(const VisibleSelection & selection)1103 Position adjustedSelectionStartForStyleComputation(const VisibleSelection& selection)
1104 {
1105 // This function is used by range style computations to avoid bugs like:
1106 // <rdar://problem/4017641> REGRESSION (Mail): you can only bold/unbold a selection starting from end of line once
1107 // It is important to skip certain irrelevant content at the start of the selection, so we do not wind up
1108 // with a spurious "mixed" style.
1109
1110 VisiblePosition visiblePosition(selection.start());
1111 if (visiblePosition.isNull())
1112 return Position();
1113
1114 // if the selection is a caret, just return the position, since the style
1115 // behind us is relevant
1116 if (selection.isCaret())
1117 return visiblePosition.deepEquivalent();
1118
1119 // if the selection starts just before a paragraph break, skip over it
1120 if (isEndOfParagraph(visiblePosition))
1121 return visiblePosition.next().deepEquivalent().downstream();
1122
1123 // otherwise, make sure to be at the start of the first selected node,
1124 // instead of possibly at the end of the last node before the selection
1125 return visiblePosition.deepEquivalent().downstream();
1126 }
1127
1128 } // namespace WebCore
1129