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