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