2 * Copyright (C) 2004, 2005, 2006 Apple Computer, 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"
30 #include "EditingText.h"
31 #include "HTMLElement.h"
32 #include "HTMLInterchange.h"
33 #include "HTMLNames.h"
34 #include "RenderObject.h"
35 #include "RegularExpression.h"
38 #include "VisiblePosition.h"
39 #include "visible_units.h"
45 using namespace HTMLNames;
47 // Atomic means that the node has no children, or has children which are ignored for the
48 // purposes of editing.
49 bool isAtomicNode(const Node *node)
51 return node && (!node->hasChildNodes() || editingIgnoresContent(node));
54 // FIXME: This function needs a comment.
55 bool editingIgnoresContent(const Node *node)
57 if (!node || !node->isHTMLElement())
60 // There doesn't seem to be a way to find out if a a node is a pop up box by looking at its renderer.
61 if (node->hasTagName(selectTag))
65 return node->renderer()->isWidget() || node->renderer()->isImage() || node->renderer()->isHR() || node->renderer()->isTextArea() || node->renderer()->isTextField();
67 return node->hasTagName(appletTag) ||
68 node->hasTagName(embedTag) ||
69 node->hasTagName(iframeTag) ||
70 node->hasTagName(imgTag) ||
71 node->hasTagName(hrTag) ||
72 static_cast<const HTMLElement *>(node)->isGenericFormElement();
75 // Some nodes, like brs, will technically accept children, but we don't want that to happen while editing.
76 bool canHaveChildrenForEditing(const Node* node)
78 return !node->hasTagName(hrTag) &&
79 !node->hasTagName(brTag) &&
80 !node->hasTagName(imgTag) &&
81 !node->hasTagName(buttonTag) &&
82 !node->hasTagName(inputTag) &&
83 !node->hasTagName(textareaTag) &&
84 !node->hasTagName(objectTag) &&
85 !node->hasTagName(iframeTag) &&
89 // Compare two positions, taking into account the possibility that one or both
90 // could be inside a shadow tree. Only works for non-null values.
91 int comparePositions(const Position& a, const Position& b)
93 Node* nodeA = a.node();
95 Node* nodeB = b.node();
97 int offsetA = a.offset();
98 int offsetB = b.offset();
100 Node* shadowAncestorA = nodeA->shadowAncestorNode();
101 if (shadowAncestorA == nodeA)
103 Node* shadowAncestorB = nodeB->shadowAncestorNode();
104 if (shadowAncestorB == nodeB)
108 if (shadowAncestorA != shadowAncestorB) {
109 if (shadowAncestorA) {
110 nodeA = shadowAncestorA;
114 if (shadowAncestorB) {
115 nodeB = shadowAncestorB;
121 int result = Range::compareBoundaryPoints(nodeA, offsetA, nodeB, offsetB);
122 return result ? result : bias;
125 Node* highestEditableRoot(const Position& position)
127 Node* node = position.node();
131 Node* highestRoot = editableRootForPosition(position);
137 if (node->isContentEditable())
139 if (node->hasTagName(bodyTag))
141 node = node->parentNode();
147 Node* lowestEditableAncestor(Node* node)
152 Node *lowestRoot = 0;
154 if (node->isContentEditable())
155 return node->rootEditableElement();
156 if (node->hasTagName(bodyTag))
158 node = node->parentNode();
164 bool isEditablePosition(const Position& p)
166 Node* node = p.node();
170 if (node->renderer() && node->renderer()->isTable())
171 node = node->parentNode();
173 return node->isContentEditable();
176 bool isRichlyEditablePosition(const Position& p)
178 Node* node = p.node();
182 if (node->renderer() && node->renderer()->isTable())
183 node = node->parentNode();
185 return node->isContentRichlyEditable();
188 Element* editableRootForPosition(const Position& p)
190 Node* node = p.node();
194 if (node->renderer() && node->renderer()->isTable())
195 node = node->parentNode();
197 return node->rootEditableElement();
200 Position nextCandidate(const Position& position)
202 Position p = position;
204 p = p.next(UsingComposedCharacters);
205 if (p.inRenderedContent())
211 Position nextVisuallyDistinctCandidate(const Position& position)
213 Position p = position;
214 Position downstreamStart = p.downstream();
216 p = p.next(UsingComposedCharacters);
217 if (p.inRenderedContent() && p.downstream() != downstreamStart)
223 Position previousCandidate(const Position& position)
225 Position p = position;
226 while (!p.atStart()) {
227 p = p.previous(UsingComposedCharacters);
228 if (p.inRenderedContent())
234 Position previousVisuallyDistinctCandidate(const Position& position)
236 Position p = position;
237 Position downstreamStart = p.downstream();
238 while (!p.atStart()) {
239 p = p.previous(UsingComposedCharacters);
240 if (p.inRenderedContent() && p.downstream() != downstreamStart)
246 VisiblePosition firstEditablePositionAfterPositionInRoot(const Position& position, Node* highestRoot)
248 if (comparePositions(position, Position(highestRoot, 0)) == -1)
249 return VisiblePosition(Position(highestRoot, 0));
251 Position p = nextVisuallyDistinctCandidate(position);
252 Node* root = editableRootForPosition(position);
253 if (p.isNull() && root && root->isShadowNode())
254 p = Position(root->shadowParentNode(), maxDeepOffset(root->shadowParentNode()));
255 while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) {
256 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p);
258 Node* root = editableRootForPosition(position);
259 if (p.isNull() && root && root->isShadowNode())
260 p = Position(root->shadowParentNode(), maxDeepOffset(root->shadowParentNode()));
263 return VisiblePosition(p);
266 VisiblePosition lastEditablePositionBeforePositionInRoot(const Position& position, Node* highestRoot)
268 if (comparePositions(position, Position(highestRoot, maxDeepOffset(highestRoot))) == 1)
269 return VisiblePosition(Position(highestRoot, maxDeepOffset(highestRoot)));
271 Position p = previousVisuallyDistinctCandidate(position);
272 Node* root = editableRootForPosition(position);
273 if (p.isNull() && root && root->isShadowNode())
274 p = Position(root->shadowParentNode(), 0);
275 while (p.isNotNull() && !isEditablePosition(p) && p.node()->isDescendantOf(highestRoot)) {
276 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p);
278 Node* root = editableRootForPosition(position);
279 if (p.isNull() && root && root->isShadowNode())
280 p = Position(root->shadowParentNode(), 0);
283 return VisiblePosition(p);
286 // Whether or not content before and after this node will collapse onto the same line as it.
287 bool isBlock(Node* node)
289 return node && node->renderer() && !node->renderer()->isInline();
292 // FIXME: Deploy this in all of the places where enclosingBlockFlow/enclosingBlockFlowOrTableElement are used.
293 // FIXME: Pass a position to this function. The enclosing block of [table, x] for example, should be the
294 // block that contains the table and not the table, and this function should be the only one responsible for
295 // knowing about these kinds of special cases.
296 Node* enclosingBlock(Node* node)
302 node = node->parentNode();
311 Position rangeCompliantEquivalent(const Position& pos)
316 Node *node = pos.node();
318 if (pos.offset() <= 0) {
319 if (node->parentNode() && (node->hasTagName(brTag) || editingIgnoresContent(node)))
320 return positionBeforeNode(node);
321 return Position(node, 0);
324 if (node->offsetInCharacters())
325 return Position(node, min(node->maxOffset(), pos.offset()));
327 int maxCompliantOffset = node->childNodeCount();
328 if (pos.offset() > maxCompliantOffset) {
329 if (node->parentNode())
330 return positionAfterNode(node);
332 // there is no other option at this point than to
333 // use the highest allowed position in the node
334 return Position(node, maxCompliantOffset);
337 // Editing should never generate positions like this.
338 if ((pos.offset() < maxCompliantOffset) && editingIgnoresContent(node)) {
339 ASSERT_NOT_REACHED();
340 return node->parentNode() ? positionBeforeNode(node) : Position(node, 0);
343 return Position(pos);
346 Position rangeCompliantEquivalent(const VisiblePosition& vpos)
348 return rangeCompliantEquivalent(vpos.deepEquivalent());
351 // This method is used to create positions in the DOM. It returns the maximum valid offset
352 // in a node. It returns 1 for some elements even though they do not have children, which
353 // creates technically invalid DOM Positions. Be sure to call rangeCompliantEquivalent
354 // on a Position before using it to create a DOM Range, or an exception will be thrown.
355 int maxDeepOffset(const Node *node)
357 if (node->offsetInCharacters())
358 return node->maxOffset();
360 if (node->hasChildNodes())
361 return node->childNodeCount();
363 // NOTE: This should preempt the childNodeCount for, e.g., select nodes
364 if (node->hasTagName(brTag) || editingIgnoresContent(node))
370 void rebalanceWhitespaceInTextNode(Node *node, unsigned int start, unsigned int length)
372 ASSERT(node->isTextNode());
373 Text *textNode = static_cast<Text *>(node);
374 String text = textNode->data();
375 ASSERT(length <= text.length() && start + length <= text.length());
377 String substring = text.substring(start, length);
379 // FIXME: We rebalance with all nbsps, for simplicity (we don't need crazy sequences while editing
380 // because all editable regions will have -webkit-nbsp-mode: space. We should produce sequences of
381 // regular spaces and nbsps that are better for interchange when we serialize (10636).
382 substring.replace(' ', NON_BREAKING_SPACE);
383 substring.replace('\t', NON_BREAKING_SPACE);
384 substring.replace('\n', NON_BREAKING_SPACE);
386 ExceptionCode ec = 0;
387 textNode->deleteData(start, length, ec);
389 textNode->insertData(start, String(substring), ec);
393 bool isTableStructureNode(const Node *node)
395 RenderObject *r = node->renderer();
396 return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
399 const String& nonBreakingSpaceString()
401 static String nonBreakingSpaceString = DeprecatedString(DeprecatedChar(NON_BREAKING_SPACE));
402 return nonBreakingSpaceString;
405 // FIXME: need to dump this
406 bool isSpecialElement(const Node *n)
411 if (!n->isHTMLElement())
417 if (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag))
420 RenderObject *renderer = n->renderer();
424 if (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE)
427 if (renderer->style()->isFloating())
430 if (renderer->style()->position() != StaticPosition)
436 // Checks if a string is a valid tag for the FormatBlockCommand function of execCommand. Expects lower case strings.
437 bool validBlockTag(const String& blockTag)
439 if (blockTag == "address" ||
440 blockTag == "blockquote" ||
457 static Node* firstInSpecialElement(const Position& pos)
459 Node* rootEditableElement = pos.node()->rootEditableElement();
460 for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
461 if (isSpecialElement(n)) {
462 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
463 VisiblePosition firstInElement = VisiblePosition(n, 0, DOWNSTREAM);
464 if (isTableElement(n) && vPos == firstInElement.next())
466 if (vPos == firstInElement)
472 static Node* lastInSpecialElement(const Position& pos)
474 Node* rootEditableElement = pos.node()->rootEditableElement();
475 for (Node* n = pos.node(); n && n->rootEditableElement() == rootEditableElement; n = n->parentNode())
476 if (isSpecialElement(n)) {
477 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
478 VisiblePosition lastInElement = VisiblePosition(n, n->childNodeCount(), DOWNSTREAM);
479 if (isTableElement(n) && vPos == lastInElement.previous())
481 if (vPos == lastInElement)
487 bool isFirstVisiblePositionInSpecialElement(const Position& pos)
489 return firstInSpecialElement(pos);
492 Position positionBeforeContainingSpecialElement(const Position& pos, Node** containingSpecialElement)
494 Node* n = firstInSpecialElement(pos);
498 Position result = positionBeforeNode(n);
499 if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
501 if (containingSpecialElement)
502 *containingSpecialElement = n;
506 bool isLastVisiblePositionInSpecialElement(const Position& pos)
508 return lastInSpecialElement(pos);
511 Position positionAfterContainingSpecialElement(const Position& pos, Node **containingSpecialElement)
513 Node* n = lastInSpecialElement(pos);
517 Position result = positionAfterNode(n);
518 if (result.isNull() || result.node()->rootEditableElement() != pos.node()->rootEditableElement())
520 if (containingSpecialElement)
521 *containingSpecialElement = n;
525 Position positionOutsideContainingSpecialElement(const Position &pos, Node **containingSpecialElement)
527 if (isFirstVisiblePositionInSpecialElement(pos))
528 return positionBeforeContainingSpecialElement(pos, containingSpecialElement);
529 if (isLastVisiblePositionInSpecialElement(pos))
530 return positionAfterContainingSpecialElement(pos, containingSpecialElement);
534 Position positionBeforeNode(const Node *node)
536 return Position(node->parentNode(), node->nodeIndex());
539 Position positionAfterNode(const Node *node)
541 return Position(node->parentNode(), node->nodeIndex() + 1);
544 bool isListElement(Node *n)
546 return (n && (n->hasTagName(ulTag) || n->hasTagName(olTag) || n->hasTagName(dlTag)));
549 Node* enclosingNodeWithTag(Node* node, const QualifiedName& tagName)
554 Node* root = highestEditableRoot(Position(node, 0));
556 for (Node* n = node->parentNode(); n; n = n->parentNode()) {
557 if (n->hasTagName(tagName))
566 Node* enclosingNodeOfType(Node* node, bool (*nodeIsOfType)(Node*))
571 Node* root = highestEditableRoot(Position(node, 0));
573 for (Node* n = node->parentNode(); n; n = n->parentNode()) {
574 if ((*nodeIsOfType)(n))
583 Node* enclosingTableCell(Node* node)
588 for (Node* n = node->parentNode(); n; n = n->parentNode())
589 if (n->renderer() && n->renderer()->isTableCell())
595 Node* enclosingList(Node* node)
600 Node* root = highestEditableRoot(Position(node, 0));
602 for (Node* n = node->parentNode(); n; n = n->parentNode()) {
603 if (n->hasTagName(ulTag) || n->hasTagName(olTag))
612 Node* enclosingListChild (Node *node)
616 // Check for a list item element, or for a node whose parent is a list element. Such a node
617 // will appear visually as a list item (but without a list marker)
618 Node* root = highestEditableRoot(Position(node, 0));
620 // FIXME: This function is inappropriately named if it starts with node instead of node->parentNode()
621 for (Node* n = node; n && n->parentNode(); n = n->parentNode()) {
622 if (n->hasTagName(liTag) || isListElement(n->parentNode()))
631 static Node* embeddedSublist(Node* listItem)
633 // Check the DOM so that we'll find collapsed sublists without renderers.
634 for (Node* n = listItem->firstChild(); n; n = n->nextSibling()) {
635 if (isListElement(n))
642 static Node* appendedSublist(Node* listItem)
644 // Check the DOM so that we'll find collapsed sublists without renderers.
645 for (Node* n = listItem->nextSibling(); n; n = n->nextSibling()) {
646 if (isListElement(n))
648 if (n->renderer() && n->renderer()->isListItem())
655 Node* enclosingEmptyListItem(const VisiblePosition& visiblePos)
657 // Check that position is on a line by itself inside a list item
658 Node* listChildNode = enclosingListChild(visiblePos.deepEquivalent().node());
659 if (!listChildNode || !isStartOfParagraph(visiblePos) || !isEndOfParagraph(visiblePos))
662 if (embeddedSublist(listChildNode) || appendedSublist(listChildNode))
665 return listChildNode;
668 Node* outermostEnclosingListChild(Node* node)
671 Node* nextNode = node;
672 while ((nextNode = enclosingListChild(nextNode)))
677 Node* outermostEnclosingList(Node* node)
680 Node* nextNode = node;
681 while ((nextNode = enclosingList(nextNode)))
686 Node* highestAncestor(Node* node)
690 while ((node = node->parentNode()))
695 // FIXME: do not require renderer, so that this can be used within fragments, or rename to isRenderedTable()
696 bool isTableElement(Node *n)
698 RenderObject *renderer = n ? n->renderer() : 0;
699 return (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE));
702 bool isFirstVisiblePositionAfterTableElement(const Position& pos)
704 return isTableElement(pos.upstream().node());
707 Position positionBeforePrecedingTableElement(const Position& pos)
709 ASSERT(isFirstVisiblePositionAfterTableElement(pos));
710 Position result = positionBeforeNode(pos.upstream().node());
711 if (result.isNull() || !result.node()->rootEditableElement())
716 bool isLastVisiblePositionBeforeTableElement(const Position &pos)
718 return isTableElement(pos.downstream().node());
721 Position positionAfterFollowingTableElement(const Position &pos)
723 ASSERT(isLastVisiblePositionBeforeTableElement(pos));
724 Position result = positionAfterNode(pos.downstream().node());
725 if (result.isNull() || !result.node()->rootEditableElement())
730 // This function is necessary because a VisiblePosition is allowed
731 // to be at the start or end of elements where we do not want to
732 // add content directly. For example, clicking at the end of a hyperlink,
733 // then typing, needs to add the text after the link. Also, table
734 // offset 0 and table offset childNodeCount are valid VisiblePostions,
735 // but we can not add more content right there... it needs to go before
736 // or after the table.
737 Position positionAvoidingSpecialElementBoundary(const Position &pos, bool avoidAnchor)
742 VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
743 Node* enclosingAnchor = enclosingNodeWithTag(pos.node(), aTag);
745 if (enclosingAnchor) {
746 // If the caret is after an anchor, do insertion inside the anchor unless it's the last
747 // VisiblePosition in the document, to match TextEdit.
748 if (VisiblePosition(Position(enclosingAnchor, maxDeepOffset(enclosingAnchor))) == vPos && (isEndOfDocument(vPos) || avoidAnchor))
749 result = positionAfterNode(enclosingAnchor);
750 // If the caret is before an anchor, do insertion outside the anchor unless it's the first
751 // VisiblePosition in a paragraph, to match TextEdit.
752 if (VisiblePosition(Position(enclosingAnchor, 0)) == vPos && (!isStartOfParagraph(vPos) || avoidAnchor))
753 result = positionBeforeNode(enclosingAnchor);
754 } else if (isTableElement(pos.node())) {
755 if (VisiblePosition(Position(pos.node(), maxDeepOffset(pos.node()))) == vPos)
756 result = positionAfterNode(pos.node());
757 if (VisiblePosition(Position(pos.node(), 0)) == vPos)
758 result = positionBeforeNode(pos.node());
762 if (result.isNull() || !editableRootForPosition(result))
768 PassRefPtr<Element> createDefaultParagraphElement(Document *document)
770 ExceptionCode ec = 0;
771 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "div", ec);
773 return element.release();
776 PassRefPtr<Element> createBreakElement(Document *document)
778 ExceptionCode ec = 0;
779 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
781 return breakNode.release();
784 PassRefPtr<Element> createOrderedListElement(Document *document)
786 ExceptionCode ec = 0;
787 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ol", ec);
789 return element.release();
792 PassRefPtr<Element> createUnorderedListElement(Document *document)
794 ExceptionCode ec = 0;
795 RefPtr<Element> element = document->createElementNS(xhtmlNamespaceURI, "ul", ec);
797 return element.release();
800 PassRefPtr<Element> createListItemElement(Document *document)
802 ExceptionCode ec = 0;
803 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "li", ec);
805 return breakNode.release();
808 PassRefPtr<Element> createElement(Document* document, const String& tagName)
810 ExceptionCode ec = 0;
811 RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, tagName, ec);
813 return breakNode.release();
816 bool isTabSpanNode(const Node *node)
818 return (node && node->isElementNode() && static_cast<const Element *>(node)->getAttribute("class") == AppleTabSpanClass);
821 bool isTabSpanTextNode(const Node *node)
823 return (node && node->parentNode() && isTabSpanNode(node->parentNode()));
826 Node *tabSpanNode(const Node *node)
828 return isTabSpanTextNode(node) ? node->parentNode() : 0;
831 Position positionBeforeTabSpan(const Position& pos)
833 Node *node = pos.node();
834 if (isTabSpanTextNode(node))
835 node = tabSpanNode(node);
836 else if (!isTabSpanNode(node))
839 return positionBeforeNode(node);
842 PassRefPtr<Element> createTabSpanElement(Document* document, PassRefPtr<Node> tabTextNode)
844 // make the span to hold the tab
845 ExceptionCode ec = 0;
846 RefPtr<Element> spanElement = document->createElementNS(xhtmlNamespaceURI, "span", ec);
848 spanElement->setAttribute(classAttr, AppleTabSpanClass);
849 spanElement->setAttribute(styleAttr, "white-space:pre");
851 // add tab text to that span
853 tabTextNode = document->createEditingTextNode("\t");
854 spanElement->appendChild(tabTextNode, ec);
857 return spanElement.release();
860 PassRefPtr<Element> createTabSpanElement(Document* document, const String& tabText)
862 return createTabSpanElement(document, document->createTextNode(tabText));
865 PassRefPtr<Element> createTabSpanElement(Document* document)
867 return createTabSpanElement(document, PassRefPtr<Node>());
870 bool isNodeRendered(const Node *node)
875 RenderObject *renderer = node->renderer();
879 return renderer->style()->visibility() == VISIBLE;
882 Node *nearestMailBlockquote(const Node *node)
884 for (Node *n = const_cast<Node *>(node); n; n = n->parentNode()) {
885 if (isMailBlockquote(n))
891 bool isMailBlockquote(const Node *node)
893 if (!node || !node->isElementNode() && !node->hasTagName(blockquoteTag))
896 return static_cast<const Element *>(node)->getAttribute("type") == "cite";
899 } // namespace WebCore