2 * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "htmlediting.h"
29 #include "CharacterNames.h"
31 #include "EditingText.h"
32 #include "HTMLBRElement.h"
33 #include "HTMLDivElement.h"
34 #include "HTMLElementFactory.h"
35 #include "HTMLInterchange.h"
36 #include "HTMLLIElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLOListElement.h"
39 #include "HTMLUListElement.h"
40 #include "PositionIterator.h"
41 #include "RenderObject.h"
43 #include "VisibleSelection.h"
45 #include "TextIterator.h"
46 #include "VisiblePosition.h"
47 #include "visible_units.h"
48 #include <wtf/StdLibExtras.h>
58 using namespace HTMLNames
;
60 // Atomic means that the node has no children, or has children which are ignored for the
61 // purposes of editing.
62 bool isAtomicNode(const Node
*node
)
64 return node
&& (!node
->hasChildNodes() || editingIgnoresContent(node
));
67 // Returns true for nodes that either have no content, or have content that is ignored (skipped
68 // over) while editing. There are no VisiblePositions inside these nodes.
69 bool editingIgnoresContent(const Node
* node
)
71 return !canHaveChildrenForEditing(node
) && !node
->isTextNode();
74 bool canHaveChildrenForEditing(const Node
* node
)
76 return !node
->hasTagName(hrTag
) &&
77 !node
->hasTagName(brTag
) &&
78 !node
->hasTagName(imgTag
) &&
79 !node
->hasTagName(buttonTag
) &&
80 !node
->hasTagName(inputTag
) &&
81 !node
->hasTagName(textareaTag
) &&
82 !node
->hasTagName(objectTag
) &&
83 !node
->hasTagName(iframeTag
) &&
84 !node
->hasTagName(embedTag
) &&
85 !node
->hasTagName(appletTag
) &&
86 !node
->hasTagName(selectTag
) &&
87 !node
->hasTagName(datagridTag
) &&
89 !node
->hasTagName(WMLNames::doTag
) &&
94 // Compare two positions, taking into account the possibility that one or both
95 // could be inside a shadow tree. Only works for non-null values.
96 int comparePositions(const Position
& a
, const Position
& b
)
98 Node
* nodeA
= a
.node();
100 Node
* nodeB
= b
.node();
102 int offsetA
= a
.deprecatedEditingOffset();
103 int offsetB
= b
.deprecatedEditingOffset();
105 Node
* shadowAncestorA
= nodeA
->shadowAncestorNode();
106 if (shadowAncestorA
== nodeA
)
108 Node
* shadowAncestorB
= nodeB
->shadowAncestorNode();
109 if (shadowAncestorB
== nodeB
)
113 if (shadowAncestorA
!= shadowAncestorB
) {
114 if (shadowAncestorA
) {
115 nodeA
= shadowAncestorA
;
119 if (shadowAncestorB
) {
120 nodeB
= shadowAncestorB
;
126 int result
= Range::compareBoundaryPoints(nodeA
, offsetA
, nodeB
, offsetB
);
127 return result
? result
: bias
;
130 int comparePositions(const VisiblePosition
& a
, const VisiblePosition
& b
)
132 return comparePositions(a
.deepEquivalent(), b
.deepEquivalent());
135 Node
* highestEditableRoot(const Position
& position
)
137 Node
* node
= position
.node();
141 Node
* highestRoot
= editableRootForPosition(position
);
147 if (node
->isContentEditable())
149 if (node
->hasTagName(bodyTag
))
151 node
= node
->parentNode();
157 Node
* lowestEditableAncestor(Node
* node
)
162 Node
*lowestRoot
= 0;
164 if (node
->isContentEditable())
165 return node
->rootEditableElement();
166 if (node
->hasTagName(bodyTag
))
168 node
= node
->parentNode();
174 bool isEditablePosition(const Position
& p
)
176 Node
* node
= p
.node();
180 if (node
->renderer() && node
->renderer()->isTable())
181 node
= node
->parentNode();
183 return node
->isContentEditable();
186 bool isAtUnsplittableElement(const Position
& pos
)
188 Node
* node
= pos
.node();
189 return (node
== editableRootForPosition(pos
) || node
== enclosingNodeOfType(pos
, &isTableCell
));
193 bool isRichlyEditablePosition(const Position
& p
)
195 Node
* node
= p
.node();
199 if (node
->renderer() && node
->renderer()->isTable())
200 node
= node
->parentNode();
202 return node
->isContentRichlyEditable();
205 Element
* editableRootForPosition(const Position
& p
)
207 Node
* node
= p
.node();
211 if (node
->renderer() && node
->renderer()->isTable())
212 node
= node
->parentNode();
214 return node
->rootEditableElement();
217 // Finds the enclosing element until which the tree can be split.
218 // When a user hits ENTER, he/she won't expect this element to be split into two.
219 // You may pass it as the second argument of splitTreeToNode.
220 Element
* unsplittableElementForPosition(const Position
& p
)
222 // Since enclosingNodeOfType won't search beyond the highest root editable node,
223 // this code works even if the closest table cell was outside of the root editable node.
224 Element
* enclosingCell
= static_cast<Element
*>(enclosingNodeOfType(p
, &isTableCell
, true));
226 return enclosingCell
;
228 return editableRootForPosition(p
);
231 Position
nextCandidate(const Position
& position
)
233 PositionIterator p
= position
;
242 Position
nextVisuallyDistinctCandidate(const Position
& position
)
244 Position p
= position
;
245 Position downstreamStart
= p
.downstream();
246 while (!p
.atEndOfTree()) {
247 p
= p
.next(Character
);
248 if (p
.isCandidate() && p
.downstream() != downstreamStart
)
254 Position
previousCandidate(const Position
& position
)
256 PositionIterator p
= position
;
257 while (!p
.atStart()) {
265 Position
previousVisuallyDistinctCandidate(const Position
& position
)
267 Position p
= position
;
268 Position downstreamStart
= p
.downstream();
269 while (!p
.atStartOfTree()) {
270 p
= p
.previous(Character
);
271 if (p
.isCandidate() && p
.downstream() != downstreamStart
)
277 VisiblePosition
firstEditablePositionAfterPositionInRoot(const Position
& position
, Node
* highestRoot
)
279 // position falls before highestRoot.
280 if (comparePositions(position
, firstDeepEditingPositionForNode(highestRoot
)) == -1 && highestRoot
->isContentEditable())
281 return firstDeepEditingPositionForNode(highestRoot
);
283 Position p
= position
;
285 if (Node
* shadowAncestor
= p
.node()->shadowAncestorNode())
286 if (shadowAncestor
!= p
.node())
287 p
= lastDeepEditingPositionForNode(shadowAncestor
);
289 while (p
.node() && !isEditablePosition(p
) && p
.node()->isDescendantOf(highestRoot
))
290 p
= isAtomicNode(p
.node()) ? positionInParentAfterNode(p
.node()) : nextVisuallyDistinctCandidate(p
);
292 if (p
.node() && p
.node() != highestRoot
&& !p
.node()->isDescendantOf(highestRoot
))
293 return VisiblePosition();
295 return VisiblePosition(p
);
298 VisiblePosition
lastEditablePositionBeforePositionInRoot(const Position
& position
, Node
* highestRoot
)
300 // When position falls after highestRoot, the result is easy to compute.
301 if (comparePositions(position
, lastDeepEditingPositionForNode(highestRoot
)) == 1)
302 return lastDeepEditingPositionForNode(highestRoot
);
304 Position p
= position
;
306 if (Node
* shadowAncestor
= p
.node()->shadowAncestorNode())
307 if (shadowAncestor
!= p
.node())
308 p
= firstDeepEditingPositionForNode(shadowAncestor
);
310 while (p
.node() && !isEditablePosition(p
) && p
.node()->isDescendantOf(highestRoot
))
311 p
= isAtomicNode(p
.node()) ? positionInParentBeforeNode(p
.node()) : previousVisuallyDistinctCandidate(p
);
313 if (p
.node() && p
.node() != highestRoot
&& !p
.node()->isDescendantOf(highestRoot
))
314 return VisiblePosition();
316 return VisiblePosition(p
);
319 // FIXME: The method name, comment, and code say three different things here!
320 // Whether or not content before and after this node will collapse onto the same line as it.
321 bool isBlock(const Node
* node
)
323 return node
&& node
->renderer() && !node
->renderer()->isInline();
326 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
327 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the
328 // block that contains the table and not the table, and this function should be the only one responsible for
329 // knowing about these kinds of special cases.
330 Node
* enclosingBlock(Node
* node
)
332 return static_cast<Element
*>(enclosingNodeOfType(Position(node
, 0), isBlock
));
335 // Internally editing uses "invalid" positions for historical reasons. For
336 // example, in <div><img /></div>, Editing might use (img, 1) for the position
337 // after <img>, but we have to convert that to (div, 1) before handing the
338 // position to a Range object. Ideally all internal positions should
339 // be "range compliant" for simplicity.
340 Position
rangeCompliantEquivalent(const Position
& pos
)
345 Node
* node
= pos
.node();
347 if (pos
.deprecatedEditingOffset() <= 0) {
348 if (node
->parentNode() && (editingIgnoresContent(node
) || isTableElement(node
)))
349 return positionInParentBeforeNode(node
);
350 return Position(node
, 0);
353 if (node
->offsetInCharacters())
354 return Position(node
, min(node
->maxCharacterOffset(), pos
.deprecatedEditingOffset()));
356 int maxCompliantOffset
= node
->childNodeCount();
357 if (pos
.deprecatedEditingOffset() > maxCompliantOffset
) {
358 if (node
->parentNode())
359 return positionInParentAfterNode(node
);
361 // there is no other option at this point than to
362 // use the highest allowed position in the node
363 return Position(node
, maxCompliantOffset
);
366 // Editing should never generate positions like this.
367 if ((pos
.deprecatedEditingOffset() < maxCompliantOffset
) && editingIgnoresContent(node
)) {
368 ASSERT_NOT_REACHED();
369 return node
->parentNode() ? positionInParentBeforeNode(node
) : Position(node
, 0);
372 if (pos
.deprecatedEditingOffset() == maxCompliantOffset
&& (editingIgnoresContent(node
) || isTableElement(node
)))
373 return positionInParentAfterNode(node
);
375 return Position(pos
);
378 Position
rangeCompliantEquivalent(const VisiblePosition
& vpos
)
380 return rangeCompliantEquivalent(vpos
.deepEquivalent());
383 // This method is used to create positions in the DOM. It returns the maximum valid offset
384 // in a node. It returns 1 for some elements even though they do not have children, which
385 // creates technically invalid DOM Positions. Be sure to call rangeCompliantEquivalent
386 // on a Position before using it to create a DOM Range, or an exception will be thrown.
387 int lastOffsetForEditing(const Node
* node
)
392 if (node
->offsetInCharacters())
393 return node
->maxCharacterOffset();
395 if (node
->hasChildNodes())
396 return node
->childNodeCount();
398 // NOTE: This should preempt the childNodeCount for, e.g., select nodes
399 if (editingIgnoresContent(node
))
405 String
stringWithRebalancedWhitespace(const String
& string
, bool startIsStartOfParagraph
, bool endIsEndOfParagraph
)
407 DEFINE_STATIC_LOCAL(String
, twoSpaces
, (" "));
408 DEFINE_STATIC_LOCAL(String
, nbsp
, ("\xa0"));
409 DEFINE_STATIC_LOCAL(String
, pattern
, (" \xa0"));
411 String rebalancedString
= string
;
413 rebalancedString
.replace(noBreakSpace
, ' ');
414 rebalancedString
.replace('\n', ' ');
415 rebalancedString
.replace('\t', ' ');
417 rebalancedString
.replace(twoSpaces
, pattern
);
419 if (startIsStartOfParagraph
&& rebalancedString
[0] == ' ')
420 rebalancedString
.replace(0, 1, nbsp
);
421 int end
= rebalancedString
.length() - 1;
422 if (endIsEndOfParagraph
&& rebalancedString
[end
] == ' ')
423 rebalancedString
.replace(end
, 1, nbsp
);
425 return rebalancedString
;
428 bool isTableStructureNode(const Node
*node
)
430 RenderObject
*r
= node
->renderer();
431 return (r
&& (r
->isTableCell() || r
->isTableRow() || r
->isTableSection() || r
->isTableCol()));
434 const String
& nonBreakingSpaceString()
436 DEFINE_STATIC_LOCAL(String
, nonBreakingSpaceString
, (&noBreakSpace
, 1));
437 return nonBreakingSpaceString
;
440 // FIXME: need to dump this
441 bool isSpecialElement(const Node
*n
)
446 if (!n
->isHTMLElement())
452 RenderObject
*renderer
= n
->renderer();
456 if (renderer
->style()->display() == TABLE
|| renderer
->style()->display() == INLINE_TABLE
)
459 if (renderer
->style()->isFloating())
462 if (renderer
->style()->position() != StaticPosition
)
468 // Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
469 bool validBlockTag(const AtomicString
& blockTag
)
471 if (blockTag
.isEmpty())
474 DEFINE_STATIC_LOCAL(HashSet
<AtomicString
>, blockTags
, ());
475 if (blockTags
.isEmpty()) {
476 blockTags
.add(addressTag
.localName());
477 blockTags
.add(blockquoteTag
.localName());
478 blockTags
.add(ddTag
.localName());
479 blockTags
.add(divTag
.localName());
480 blockTags
.add(dlTag
.localName());
481 blockTags
.add(dtTag
.localName());
482 blockTags
.add(h1Tag
.localName());
483 blockTags
.add(h2Tag
.localName());
484 blockTags
.add(h3Tag
.localName());
485 blockTags
.add(h4Tag
.localName());
486 blockTags
.add(h5Tag
.localName());
487 blockTags
.add(h6Tag
.localName());
488 blockTags
.add(navTag
.localName());
489 blockTags
.add(pTag
.localName());
490 blockTags
.add(preTag
.localName());
492 return blockTags
.contains(blockTag
);
495 static Node
* firstInSpecialElement(const Position
& pos
)
497 // FIXME: This begins at pos.node(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>.
498 Node
* rootEditableElement
= pos
.node()->rootEditableElement();
499 for (Node
* n
= pos
.node(); n
&& n
->rootEditableElement() == rootEditableElement
; n
= n
->parentNode())
500 if (isSpecialElement(n
)) {
501 VisiblePosition vPos
= VisiblePosition(pos
, DOWNSTREAM
);
502 VisiblePosition firstInElement
= VisiblePosition(n
, 0, DOWNSTREAM
);
503 if (isTableElement(n
) && vPos
== firstInElement
.next())
505 if (vPos
== firstInElement
)
511 static Node
* lastInSpecialElement(const Position
& pos
)
513 // FIXME: This begins at pos.node(), which doesn't necessarily contain pos (suppose pos was [img, 0]). See <rdar://problem/5027702>.
514 Node
* rootEditableElement
= pos
.node()->rootEditableElement();
515 for (Node
* n
= pos
.node(); n
&& n
->rootEditableElement() == rootEditableElement
; n
= n
->parentNode())
516 if (isSpecialElement(n
)) {
517 VisiblePosition vPos
= VisiblePosition(pos
, DOWNSTREAM
);
518 VisiblePosition lastInElement
= VisiblePosition(n
, n
->childNodeCount(), DOWNSTREAM
);
519 if (isTableElement(n
) && vPos
== lastInElement
.previous())
521 if (vPos
== lastInElement
)
527 bool isFirstVisiblePositionInSpecialElement(const Position
& pos
)
529 return firstInSpecialElement(pos
);
532 Position
positionBeforeContainingSpecialElement(const Position
& pos
, Node
** containingSpecialElement
)
534 Node
* n
= firstInSpecialElement(pos
);
537 Position result
= positionInParentBeforeNode(n
);
538 if (result
.isNull() || result
.node()->rootEditableElement() != pos
.node()->rootEditableElement())
540 if (containingSpecialElement
)
541 *containingSpecialElement
= n
;
545 bool isLastVisiblePositionInSpecialElement(const Position
& pos
)
547 return lastInSpecialElement(pos
);
550 Position
positionAfterContainingSpecialElement(const Position
& pos
, Node
**containingSpecialElement
)
552 Node
* n
= lastInSpecialElement(pos
);
555 Position result
= positionInParentAfterNode(n
);
556 if (result
.isNull() || result
.node()->rootEditableElement() != pos
.node()->rootEditableElement())
558 if (containingSpecialElement
)
559 *containingSpecialElement
= n
;
563 Position
positionOutsideContainingSpecialElement(const Position
&pos
, Node
**containingSpecialElement
)
565 if (isFirstVisiblePositionInSpecialElement(pos
))
566 return positionBeforeContainingSpecialElement(pos
, containingSpecialElement
);
567 if (isLastVisiblePositionInSpecialElement(pos
))
568 return positionAfterContainingSpecialElement(pos
, containingSpecialElement
);
572 Node
* isFirstPositionAfterTable(const VisiblePosition
& visiblePosition
)
574 Position
upstream(visiblePosition
.deepEquivalent().upstream());
575 if (upstream
.node() && upstream
.node()->renderer() && upstream
.node()->renderer()->isTable() && upstream
.atLastEditingPositionForNode())
576 return upstream
.node();
581 Node
* isLastPositionBeforeTable(const VisiblePosition
& visiblePosition
)
583 Position
downstream(visiblePosition
.deepEquivalent().downstream());
584 if (downstream
.node() && downstream
.node()->renderer() && downstream
.node()->renderer()->isTable() && downstream
.atFirstEditingPositionForNode())
585 return downstream
.node();
590 // Returns the visible position at the beginning of a node
591 VisiblePosition
visiblePositionBeforeNode(Node
* node
)
594 if (node
->childNodeCount())
595 return VisiblePosition(node
, 0, DOWNSTREAM
);
596 ASSERT(node
->parentNode());
597 return positionInParentBeforeNode(node
);
600 // Returns the visible position at the ending of a node
601 VisiblePosition
visiblePositionAfterNode(Node
* node
)
604 if (node
->childNodeCount())
605 return VisiblePosition(node
, node
->childNodeCount(), DOWNSTREAM
);
606 ASSERT(node
->parentNode());
607 return positionInParentAfterNode(node
);
610 // Create a range object with two visible positions, start and end.
611 // create(PassRefPtr<Document>, const Position&, const Position&); will use deprecatedEditingOffset
612 // Use this function instead of create a regular range object (avoiding editing offset).
613 PassRefPtr
<Range
> createRange(PassRefPtr
<Document
> document
, const VisiblePosition
& start
, const VisiblePosition
& end
, ExceptionCode
& ec
)
616 RefPtr
<Range
> selectedRange
= Range::create(document
);
617 selectedRange
->setStart(start
.deepEquivalent().containerNode(), start
.deepEquivalent().computeOffsetInContainerNode(), ec
);
619 selectedRange
->setEnd(end
.deepEquivalent().containerNode(), end
.deepEquivalent().computeOffsetInContainerNode(), ec
);
620 return selectedRange
.release();
623 // Extend rangeToExtend to include nodes that wraps range and visibly starts and ends inside or at the boudnaries of maximumRange
624 // e.g. if the original range spaned "hello" in <div>hello</div>, then this function extends the range to contain div's around it.
625 // Call this function before copying / moving paragraphs to contain all wrapping nodes.
626 // This function stops extending the range immediately below rootNode; i.e. the extended range can contain a child node of rootNode
627 // but it can never contain rootNode itself.
628 PassRefPtr
<Range
> extendRangeToWrappingNodes(PassRefPtr
<Range
> range
, const Range
* maximumRange
, const Node
* rootNode
)
631 ASSERT(maximumRange
);
633 ExceptionCode ec
= 0;
634 Node
* ancestor
= range
->commonAncestorContainer(ec
);// find the cloeset common ancestor
635 Node
* highestNode
= 0;
636 // traverse through ancestors as long as they are contained within the range, content-editable, and below rootNode (could be =0).
637 while (ancestor
&& ancestor
->isContentEditable() && isNodeVisiblyContainedWithin(ancestor
, maximumRange
) && ancestor
!= rootNode
) {
638 highestNode
= ancestor
;
639 ancestor
= ancestor
->parentNode();
645 // Create new range with the highest editable node contained within the range
646 RefPtr
<Range
> extendedRange
= Range::create(range
->ownerDocument());
647 extendedRange
->selectNode(highestNode
, ec
);
648 return extendedRange
.release();
651 bool isListElement(Node
*n
)
653 return (n
&& (n
->hasTagName(ulTag
) || n
->hasTagName(olTag
) || n
->hasTagName(dlTag
)));
656 Node
* enclosingNodeWithTag(const Position
& p
, const QualifiedName
& tagName
)
661 Node
* root
= highestEditableRoot(p
);
662 for (Node
* n
= p
.node(); n
; n
= n
->parentNode()) {
663 if (root
&& !n
->isContentEditable())
665 if (n
->hasTagName(tagName
))
674 Node
* enclosingNodeOfType(const Position
& p
, bool (*nodeIsOfType
)(const Node
*), bool onlyReturnEditableNodes
)
679 Node
* root
= highestEditableRoot(p
);
680 for (Node
* n
= p
.node(); n
; n
= n
->parentNode()) {
681 // Don't return a non-editable node if the input position was editable, since
682 // the callers from editing will no doubt want to perform editing inside the returned node.
683 if (root
&& !n
->isContentEditable() && onlyReturnEditableNodes
)
685 if ((*nodeIsOfType
)(n
))
694 Node
* highestEnclosingNodeOfType(const Position
& p
, bool (*nodeIsOfType
)(const Node
*))
697 Node
* root
= highestEditableRoot(p
);
698 for (Node
* n
= p
.node(); n
; n
= n
->parentNode()) {
699 if ((*nodeIsOfType
)(n
))
708 Node
* enclosingTableCell(const Position
& p
)
710 return static_cast<Element
*>(enclosingNodeOfType(p
, isTableCell
));
713 Node
* enclosingAnchorElement(const Position
& p
)
718 Node
* node
= p
.node();
719 while (node
&& !(node
->isElementNode() && node
->isLink()))
720 node
= node
->parentNode();
724 HTMLElement
* enclosingList(Node
* node
)
729 Node
* root
= highestEditableRoot(Position(node
, 0));
731 for (Node
* n
= node
->parentNode(); n
; n
= n
->parentNode()) {
732 if (n
->hasTagName(ulTag
) || n
->hasTagName(olTag
))
733 return static_cast<HTMLElement
*>(n
);
741 HTMLElement
* enclosingListChild(Node
*node
)
745 // Check for a list item element, or for a node whose parent is a list element. Such a node
746 // will appear visually as a list item (but without a list marker)
747 Node
* root
= highestEditableRoot(Position(node
, 0));
749 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
750 for (Node
* n
= node
; n
&& n
->parentNode(); n
= n
->parentNode()) {
751 if (n
->hasTagName(liTag
) || isListElement(n
->parentNode()))
752 return static_cast<HTMLElement
*>(n
);
753 if (n
== root
|| isTableCell(n
))
760 static HTMLElement
* embeddedSublist(Node
* listItem
)
762 // Check the DOM so that we'll find collapsed sublists without renderers.
763 for (Node
* n
= listItem
->firstChild(); n
; n
= n
->nextSibling()) {
764 if (isListElement(n
))
765 return static_cast<HTMLElement
*>(n
);
771 static Node
* appendedSublist(Node
* listItem
)
773 // Check the DOM so that we'll find collapsed sublists without renderers.
774 for (Node
* n
= listItem
->nextSibling(); n
; n
= n
->nextSibling()) {
775 if (isListElement(n
))
776 return static_cast<HTMLElement
*>(n
);
777 if (n
->renderer() && n
->renderer()->isListItem())
784 // FIXME: This method should not need to call isStartOfParagraph/isEndOfParagraph
785 Node
* enclosingEmptyListItem(const VisiblePosition
& visiblePos
)
787 // Check that position is on a line by itself inside a list item
788 Node
* listChildNode
= enclosingListChild(visiblePos
.deepEquivalent().node());
789 if (!listChildNode
|| !isStartOfParagraph(visiblePos
) || !isEndOfParagraph(visiblePos
))
792 VisiblePosition
firstInListChild(firstDeepEditingPositionForNode(listChildNode
));
793 VisiblePosition
lastInListChild(lastDeepEditingPositionForNode(listChildNode
));
795 if (firstInListChild
!= visiblePos
|| lastInListChild
!= visiblePos
)
798 if (embeddedSublist(listChildNode
) || appendedSublist(listChildNode
))
801 return listChildNode
;
804 HTMLElement
* outermostEnclosingList(Node
* node
)
806 HTMLElement
* list
= enclosingList(node
);
809 while (HTMLElement
* nextList
= enclosingList(list
))
814 bool canMergeLists(Element
* firstList
, Element
* secondList
)
816 if (!firstList
|| !secondList
)
819 return firstList
->hasTagName(secondList
->tagQName())// make sure the list types match (ol vs. ul)
820 && firstList
->isContentEditable() && secondList
->isContentEditable()// both lists are editable
821 && firstList
->rootEditableElement() == secondList
->rootEditableElement()// don't cross editing boundaries
822 && isVisiblyAdjacent(positionInParentAfterNode(firstList
), positionInParentBeforeNode(secondList
));
823 // Make sure there is no visible content between this li and the previous list
826 Node
* highestAncestor(Node
* node
)
830 while ((node
= node
->parentNode()))
835 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
836 bool isTableElement(Node
* n
)
838 if (!n
|| !n
->isElementNode())
841 RenderObject
* renderer
= n
->renderer();
842 return (renderer
&& (renderer
->style()->display() == TABLE
|| renderer
->style()->display() == INLINE_TABLE
));
845 bool isTableCell(const Node
* node
)
847 RenderObject
* r
= node
->renderer();
849 return node
->hasTagName(tdTag
) || node
->hasTagName(thTag
);
851 return r
->isTableCell();
854 PassRefPtr
<HTMLElement
> createDefaultParagraphElement(Document
* document
)
856 return new HTMLDivElement(divTag
, document
);
859 PassRefPtr
<HTMLElement
> createBreakElement(Document
* document
)
861 return new HTMLBRElement(brTag
, document
);
864 PassRefPtr
<HTMLElement
> createOrderedListElement(Document
* document
)
866 return new HTMLOListElement(olTag
, document
);
869 PassRefPtr
<HTMLElement
> createUnorderedListElement(Document
* document
)
871 return new HTMLUListElement(ulTag
, document
);
874 PassRefPtr
<HTMLElement
> createListItemElement(Document
* document
)
876 return new HTMLLIElement(liTag
, document
);
879 PassRefPtr
<HTMLElement
> createHTMLElement(Document
* document
, const QualifiedName
& name
)
881 return HTMLElementFactory::createHTMLElement(name
, document
, 0, false);
884 PassRefPtr
<HTMLElement
> createHTMLElement(Document
* document
, const AtomicString
& tagName
)
886 return createHTMLElement(document
, QualifiedName(nullAtom
, tagName
, xhtmlNamespaceURI
));
889 bool isTabSpanNode(const Node
*node
)
891 return node
&& node
->hasTagName(spanTag
) && node
->isElementNode() && static_cast<const Element
*>(node
)->getAttribute(classAttr
) == AppleTabSpanClass
;
894 bool isTabSpanTextNode(const Node
*node
)
896 return node
&& node
->isTextNode() && node
->parentNode() && isTabSpanNode(node
->parentNode());
899 Node
*tabSpanNode(const Node
*node
)
901 return isTabSpanTextNode(node
) ? node
->parentNode() : 0;
904 Position
positionBeforeTabSpan(const Position
& pos
)
906 Node
*node
= pos
.node();
907 if (isTabSpanTextNode(node
))
908 node
= tabSpanNode(node
);
909 else if (!isTabSpanNode(node
))
912 return positionInParentBeforeNode(node
);
915 PassRefPtr
<Element
> createTabSpanElement(Document
* document
, PassRefPtr
<Node
> tabTextNode
)
917 // Make the span to hold the tab.
918 RefPtr
<Element
> spanElement
= document
->createElement(spanTag
, false);
919 spanElement
->setAttribute(classAttr
, AppleTabSpanClass
);
920 spanElement
->setAttribute(styleAttr
, "white-space:pre");
922 // Add tab text to that span.
924 tabTextNode
= document
->createEditingTextNode("\t");
926 ExceptionCode ec
= 0;
927 spanElement
->appendChild(tabTextNode
, ec
);
930 return spanElement
.release();
933 PassRefPtr
<Element
> createTabSpanElement(Document
* document
, const String
& tabText
)
935 return createTabSpanElement(document
, document
->createTextNode(tabText
));
938 PassRefPtr
<Element
> createTabSpanElement(Document
* document
)
940 return createTabSpanElement(document
, PassRefPtr
<Node
>());
943 bool isNodeRendered(const Node
*node
)
948 RenderObject
*renderer
= node
->renderer();
952 return renderer
->style()->visibility() == VISIBLE
;
955 Node
*nearestMailBlockquote(const Node
*node
)
957 for (Node
*n
= const_cast<Node
*>(node
); n
; n
= n
->parentNode()) {
958 if (isMailBlockquote(n
))
964 unsigned numEnclosingMailBlockquotes(const Position
& p
)
967 for (Node
* n
= p
.node(); n
; n
= n
->parentNode())
968 if (isMailBlockquote(n
))
974 bool isMailBlockquote(const Node
*node
)
976 if (!node
|| (!node
->isElementNode() && !node
->hasTagName(blockquoteTag
)))
979 return static_cast<const Element
*>(node
)->getAttribute("type") == "cite";
982 int caretMinOffset(const Node
* n
)
984 RenderObject
* r
= n
->renderer();
985 ASSERT(!n
->isCharacterDataNode() || !r
|| r
->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now.
986 return r
? r
->caretMinOffset() : 0;
989 // If a node can contain candidates for VisiblePositions, return the offset of the last candidate, otherwise
990 // return the number of children for container nodes and the length for unrendered text nodes.
991 int caretMaxOffset(const Node
* n
)
993 // For rendered text nodes, return the last position that a caret could occupy.
994 if (n
->isTextNode() && n
->renderer())
995 return n
->renderer()->caretMaxOffset();
996 // For containers return the number of children. For others do the same as above.
997 return lastOffsetForEditing(n
);
1000 bool lineBreakExistsAtVisiblePosition(const VisiblePosition
& visiblePosition
)
1002 return lineBreakExistsAtPosition(visiblePosition
.deepEquivalent().downstream());
1005 bool lineBreakExistsAtPosition(const Position
& position
)
1007 if (position
.isNull())
1010 if (position
.anchorNode()->hasTagName(brTag
) && position
.atFirstEditingPositionForNode())
1013 if (!position
.anchorNode()->isTextNode() || !position
.anchorNode()->renderer()->style()->preserveNewline())
1016 Text
* textNode
= static_cast<Text
*>(position
.anchorNode());
1017 unsigned offset
= position
.offsetInContainerNode();
1018 return offset
< textNode
->length() && textNode
->data()[offset
] == '\n';
1021 // Modifies selections that have an end point at the edge of a table
1022 // that contains the other endpoint so that they don't confuse
1023 // code that iterates over selected paragraphs.
1024 VisibleSelection
selectionForParagraphIteration(const VisibleSelection
& original
)
1026 VisibleSelection
newSelection(original
);
1027 VisiblePosition
startOfSelection(newSelection
.visibleStart());
1028 VisiblePosition
endOfSelection(newSelection
.visibleEnd());
1030 // If the end of the selection to modify is just after a table, and
1031 // if the start of the selection is inside that table, then the last paragraph
1032 // that we'll want modify is the last one inside the table, not the table itself
1033 // (a table is itself a paragraph).
1034 if (Node
* table
= isFirstPositionAfterTable(endOfSelection
))
1035 if (startOfSelection
.deepEquivalent().node()->isDescendantOf(table
))
1036 newSelection
= VisibleSelection(startOfSelection
, endOfSelection
.previous(true));
1038 // If the start of the selection to modify is just before a table,
1039 // and if the end of the selection is inside that table, then the first paragraph
1040 // we'll want to modify is the first one inside the table, not the paragraph
1041 // containing the table itself.
1042 if (Node
* table
= isLastPositionBeforeTable(startOfSelection
))
1043 if (endOfSelection
.deepEquivalent().node()->isDescendantOf(table
))
1044 newSelection
= VisibleSelection(startOfSelection
.next(true), endOfSelection
);
1046 return newSelection
;
1050 int indexForVisiblePosition(const VisiblePosition
& visiblePosition
)
1052 if (visiblePosition
.isNull())
1054 Position
p(visiblePosition
.deepEquivalent());
1055 RefPtr
<Range
> range
= Range::create(p
.node()->document(), Position(p
.node()->document(), 0), rangeCompliantEquivalent(p
));
1056 return TextIterator::rangeLength(range
.get(), true);
1059 // Determines whether two positions are visibly next to each other (first then second)
1060 // while ignoring whitespaces and unrendered nodes
1061 bool isVisiblyAdjacent(const Position
& first
, const Position
& second
)
1063 return VisiblePosition(first
) == VisiblePosition(second
.upstream());
1066 // Determines whether a node is inside a range or visibly starts and ends at the boundaries of the range.
1067 // Call this function to determine whether a node is visibly fit inside selectedRange
1068 bool isNodeVisiblyContainedWithin(Node
* node
, const Range
* selectedRange
)
1071 ASSERT(selectedRange
);
1072 // If the node is inside the range, then it surely is contained within
1073 ExceptionCode ec
= 0;
1074 if (selectedRange
->compareNode(node
, ec
) == Range::NODE_INSIDE
)
1077 // If the node starts and ends at where selectedRange starts and ends, the node is contained within
1078 return visiblePositionBeforeNode(node
) == selectedRange
->startPosition()
1079 && visiblePositionAfterNode(node
) == selectedRange
->endPosition();
1082 bool isRenderedAsNonInlineTableImageOrHR(const Node
* node
)
1086 RenderObject
* renderer
= node
->renderer();
1087 return renderer
&& ((renderer
->isTable() && !renderer
->isInline()) || (renderer
->isImage() && !renderer
->isInline()) || renderer
->isHR());
1090 PassRefPtr
<Range
> avoidIntersectionWithNode(const Range
* range
, Node
* node
)
1095 Document
* document
= range
->ownerDocument();
1097 Node
* startContainer
= range
->startContainer();
1098 int startOffset
= range
->startOffset();
1099 Node
* endContainer
= range
->endContainer();
1100 int endOffset
= range
->endOffset();
1102 if (!startContainer
)
1105 ASSERT(endContainer
);
1107 if (startContainer
== node
|| startContainer
->isDescendantOf(node
)) {
1108 ASSERT(node
->parentNode());
1109 startContainer
= node
->parentNode();
1110 startOffset
= node
->nodeIndex();
1112 if (endContainer
== node
|| endContainer
->isDescendantOf(node
)) {
1113 ASSERT(node
->parentNode());
1114 endContainer
= node
->parentNode();
1115 endOffset
= node
->nodeIndex();
1118 return Range::create(document
, startContainer
, startOffset
, endContainer
, endOffset
);
1121 VisibleSelection
avoidIntersectionWithNode(const VisibleSelection
& selection
, Node
* node
)
1123 if (selection
.isNone())
1124 return VisibleSelection(selection
);
1126 VisibleSelection
updatedSelection(selection
);
1127 Node
* base
= selection
.base().node();
1128 Node
* extent
= selection
.extent().node();
1132 if (base
== node
|| base
->isDescendantOf(node
)) {
1133 ASSERT(node
->parentNode());
1134 updatedSelection
.setBase(Position(node
->parentNode(), node
->nodeIndex()));
1137 if (extent
== node
|| extent
->isDescendantOf(node
)) {
1138 ASSERT(node
->parentNode());
1139 updatedSelection
.setExtent(Position(node
->parentNode(), node
->nodeIndex()));
1142 return updatedSelection
;
1145 } // namespace WebCore