2 * (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 2000 Gunnstein Lye (gunnstein@netcom.no)
4 * (C) 2000 Frederik Holljen (frederik.holljen@hig.no)
5 * (C) 2001 Peter Kelly (pmk@post.com)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
7 * Copyright (C) 2011 Motorola Mobility. All rights reserved.
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
26 #include "core/dom/Range.h"
28 #include "bindings/v8/ExceptionState.h"
29 #include "core/dom/ClientRect.h"
30 #include "core/dom/ClientRectList.h"
31 #include "core/dom/DocumentFragment.h"
32 #include "core/dom/ExceptionCode.h"
33 #include "core/dom/Node.h"
34 #include "core/dom/NodeTraversal.h"
35 #include "core/dom/NodeWithIndex.h"
36 #include "core/dom/ProcessingInstruction.h"
37 #include "core/dom/Text.h"
38 #include "core/editing/TextIterator.h"
39 #include "core/editing/VisiblePosition.h"
40 #include "core/editing/VisibleUnits.h"
41 #include "core/editing/markup.h"
42 #include "core/events/ScopedEventQueue.h"
43 #include "core/html/HTMLElement.h"
44 #include "core/rendering/RenderBoxModelObject.h"
45 #include "core/rendering/RenderText.h"
46 #include "platform/geometry/FloatQuad.h"
47 #include "wtf/RefCountedLeakCounter.h"
48 #include "wtf/text/CString.h"
49 #include "wtf/text/StringBuilder.h"
56 using namespace HTMLNames;
58 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, rangeCounter, ("Range"));
60 inline Range::Range(Document& ownerDocument)
61 : m_ownerDocument(&ownerDocument)
62 , m_start(m_ownerDocument)
63 , m_end(m_ownerDocument)
66 rangeCounter.increment();
69 ScriptWrappable::init(this);
70 m_ownerDocument->attachRange(this);
73 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument)
75 return adoptRefWillBeNoop(new Range(ownerDocument));
78 inline Range::Range(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset)
79 : m_ownerDocument(&ownerDocument)
80 , m_start(m_ownerDocument)
81 , m_end(m_ownerDocument)
84 rangeCounter.increment();
87 ScriptWrappable::init(this);
88 m_ownerDocument->attachRange(this);
90 // Simply setting the containers and offsets directly would not do any of the checking
91 // that setStart and setEnd do, so we call those functions.
92 setStart(startContainer, startOffset);
93 setEnd(endContainer, endOffset);
96 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset)
98 return adoptRefWillBeNoop(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset));
101 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end)
103 return adoptRefWillBeNoop(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode()));
109 // Always detach (even if we've already detached) to fix https://bugs.webkit.org/show_bug.cgi?id=26044
110 m_ownerDocument->detachRange(this);
114 rangeCounter.decrement();
118 void Range::setDocument(Document& document)
120 ASSERT(m_ownerDocument != document);
121 ASSERT(m_ownerDocument);
122 m_ownerDocument->detachRange(this);
123 m_ownerDocument = &document;
124 m_start.setToStartOfNode(document);
125 m_end.setToStartOfNode(document);
126 m_ownerDocument->attachRange(this);
129 Node* Range::commonAncestorContainer() const
131 return commonAncestorContainer(m_start.container(), m_end.container());
134 Node* Range::commonAncestorContainer(Node* containerA, Node* containerB)
136 for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) {
137 for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) {
138 if (parentA == parentB)
145 static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& start, const RangeBoundaryPoint& end)
147 Node* endRootContainer = end.container();
148 while (endRootContainer->parentNode())
149 endRootContainer = endRootContainer->parentNode();
150 Node* startRootContainer = start.container();
151 while (startRootContainer->parentNode())
152 startRootContainer = startRootContainer->parentNode();
154 return startRootContainer != endRootContainer || (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0);
157 void Range::setStart(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
160 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
164 bool didMoveDocument = false;
165 if (refNode->document() != m_ownerDocument) {
166 setDocument(refNode->document());
167 didMoveDocument = true;
170 Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
171 if (exceptionState.hadException())
174 m_start.set(refNode, offset, childNode);
176 if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
180 void Range::setEnd(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
183 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
187 bool didMoveDocument = false;
188 if (refNode->document() != m_ownerDocument) {
189 setDocument(refNode->document());
190 didMoveDocument = true;
193 Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
194 if (exceptionState.hadException())
197 m_end.set(refNode, offset, childNode);
199 if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
203 void Range::setStart(const Position& start, ExceptionState& exceptionState)
205 Position parentAnchored = start.parentAnchoredEquivalent();
206 setStart(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
209 void Range::setEnd(const Position& end, ExceptionState& exceptionState)
211 Position parentAnchored = end.parentAnchoredEquivalent();
212 setEnd(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
215 void Range::collapse(bool toStart)
223 bool Range::isPointInRange(Node* refNode, int offset, ExceptionState& exceptionState)
226 exceptionState.throwDOMException(HierarchyRequestError, "The node provided was null.");
230 if (!refNode->inActiveDocument() || refNode->document() != m_ownerDocument) {
234 checkNodeWOffset(refNode, offset, exceptionState);
235 if (exceptionState.hadException())
238 return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), exceptionState) >= 0 && !exceptionState.hadException()
239 && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), exceptionState) <= 0 && !exceptionState.hadException();
242 short Range::comparePoint(Node* refNode, int offset, ExceptionState& exceptionState) const
244 // http://developer.mozilla.org/en/docs/DOM:range.comparePoint
245 // This method returns -1, 0 or 1 depending on if the point described by the
246 // refNode node and an offset within the node is before, same as, or after the range respectively.
249 exceptionState.throwDOMException(HierarchyRequestError, "The node provided was null.");
253 if (!refNode->inActiveDocument()) {
254 exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in an active document.");
258 if (refNode->document() != m_ownerDocument) {
259 exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in this Range's Document.");
263 checkNodeWOffset(refNode, offset, exceptionState);
264 if (exceptionState.hadException())
267 // compare to start, and point comes before
268 if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), exceptionState) < 0)
271 if (exceptionState.hadException())
274 // compare to end, and point comes after
275 if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), exceptionState) > 0 && !exceptionState.hadException())
278 // point is in the middle of this range, or on the boundary points
282 Range::CompareResults Range::compareNode(Node* refNode, ExceptionState& exceptionState) const
284 // http://developer.mozilla.org/en/docs/DOM:range.compareNode
285 // This method returns 0, 1, 2, or 3 based on if the node is before, after,
286 // before and after(surrounds), or inside the range, respectively
289 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
293 if (!refNode->inActiveDocument()) {
294 // Firefox doesn't throw an exception for this case; it returns 0.
298 if (refNode->document() != m_ownerDocument) {
299 // Firefox doesn't throw an exception for this case; it returns 0.
303 ContainerNode* parentNode = refNode->parentNode();
304 int nodeIndex = refNode->nodeIndex();
307 // if the node is the top document we should return NODE_BEFORE_AND_AFTER
308 // but we throw to match firefox behavior
309 exceptionState.throwDOMException(NotFoundError, "The provided node has no parent.");
313 if (comparePoint(parentNode, nodeIndex, exceptionState) < 0) { // starts before
314 if (comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) // ends after the range
315 return NODE_BEFORE_AND_AFTER;
316 return NODE_BEFORE; // ends before or in the range
318 // starts at or after the range start
319 if (comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) // ends after the range
321 return NODE_INSIDE; // ends inside the range
324 short Range::compareBoundaryPoints(CompareHow how, const Range* sourceRange, ExceptionState& exceptionState) const
327 exceptionState.throwDOMException(NotFoundError, "The source range provided was null.");
331 Node* thisCont = commonAncestorContainer();
332 Node* sourceCont = sourceRange->commonAncestorContainer();
333 if (thisCont->document() != sourceCont->document()) {
334 exceptionState.throwDOMException(WrongDocumentError, "The source range is in a different document than this range.");
338 Node* thisTop = thisCont;
339 Node* sourceTop = sourceCont;
340 while (thisTop->parentNode())
341 thisTop = thisTop->parentNode();
342 while (sourceTop->parentNode())
343 sourceTop = sourceTop->parentNode();
344 if (thisTop != sourceTop) { // in different DocumentFragments
345 exceptionState.throwDOMException(WrongDocumentError, "The source range is in a different document than this range.");
351 return compareBoundaryPoints(m_start, sourceRange->m_start, exceptionState);
353 return compareBoundaryPoints(m_end, sourceRange->m_start, exceptionState);
355 return compareBoundaryPoints(m_end, sourceRange->m_end, exceptionState);
357 return compareBoundaryPoints(m_start, sourceRange->m_end, exceptionState);
360 exceptionState.throwDOMException(SyntaxError, "The comparison method provided must be one of 'START_TO_START', 'START_TO_END', 'END_TO_END', or 'END_TO_START'.");
364 short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionState& exceptionState)
374 // see DOM2 traversal & range section 2.5
376 // case 1: both points have the same container
377 if (containerA == containerB) {
378 if (offsetA == offsetB)
379 return 0; // A is equal to B
380 if (offsetA < offsetB)
381 return -1; // A is before B
383 return 1; // A is after B
386 // case 2: node C (container B or an ancestor) is a child node of A
387 Node* c = containerB;
388 while (c && c->parentNode() != containerA)
392 Node* n = containerA->firstChild();
393 while (n != c && offsetC < offsetA) {
395 n = n->nextSibling();
398 if (offsetA <= offsetC)
399 return -1; // A is before B
401 return 1; // A is after B
404 // case 3: node C (container A or an ancestor) is a child node of B
406 while (c && c->parentNode() != containerB)
410 Node* n = containerB->firstChild();
411 while (n != c && offsetC < offsetB) {
413 n = n->nextSibling();
416 if (offsetC < offsetB)
417 return -1; // A is before B
419 return 1; // A is after B
422 // case 4: containers A & B are siblings, or children of siblings
423 // ### we need to do a traversal here instead
424 Node* commonAncestor = commonAncestorContainer(containerA, containerB);
425 if (!commonAncestor) {
426 exceptionState.throwDOMException(WrongDocumentError, "The two ranges are in separate documents.");
429 Node* childA = containerA;
430 while (childA && childA->parentNode() != commonAncestor)
431 childA = childA->parentNode();
433 childA = commonAncestor;
434 Node* childB = containerB;
435 while (childB && childB->parentNode() != commonAncestor)
436 childB = childB->parentNode();
438 childB = commonAncestor;
440 if (childA == childB)
441 return 0; // A is equal to B
443 Node* n = commonAncestor->firstChild();
446 return -1; // A is before B
448 return 1; // A is after B
449 n = n->nextSibling();
452 // Should never reach this point.
453 ASSERT_NOT_REACHED();
457 short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionState& exceptionState)
459 return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset(), exceptionState);
462 bool Range::boundaryPointsValid() const
464 TrackExceptionState exceptionState;
465 return compareBoundaryPoints(m_start, m_end, exceptionState) <= 0 && !exceptionState.hadException();
468 void Range::deleteContents(ExceptionState& exceptionState)
470 checkDeleteExtract(exceptionState);
471 if (exceptionState.hadException())
475 EventQueueScope eventQueueScope;
476 processContents(DELETE_CONTENTS, exceptionState);
480 bool Range::intersectsNode(Node* refNode, ExceptionState& exceptionState)
482 // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode
483 // Returns a bool if the node intersects the range.
485 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
489 if (!refNode->inActiveDocument() || refNode->document() != m_ownerDocument) {
490 // Firefox doesn't throw an exception for these cases; it returns false.
494 ContainerNode* parentNode = refNode->parentNode();
495 int nodeIndex = refNode->nodeIndex();
498 // if the node is the top document we should return NODE_BEFORE_AND_AFTER
499 // but we throw to match firefox behavior
500 exceptionState.throwDOMException(NotFoundError, "The node provided has no parent.");
504 if (comparePoint(parentNode, nodeIndex, exceptionState) < 0 // starts before start
505 && comparePoint(parentNode, nodeIndex + 1, exceptionState) < 0) { // ends before start
509 if (comparePoint(parentNode, nodeIndex, exceptionState) > 0 // starts after end
510 && comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) { // ends after end
514 return true; // all other cases
517 static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot)
519 if (node == commonRoot)
522 ASSERT(commonRoot->contains(node));
524 while (node->parentNode() != commonRoot)
525 node = node->parentNode();
530 static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offset, Node* commonRoot)
535 if (!commonRoot->contains(container))
538 if (container == commonRoot) {
539 container = container->firstChild();
540 for (unsigned i = 0; container && i < offset; i++)
541 container = container->nextSibling();
543 while (container->parentNode() != commonRoot)
544 container = container->parentNode();
550 PassRefPtrWillBeRawPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionState& exceptionState)
552 typedef WillBeHeapVector<RefPtrWillBeMember<Node> > NodeVector;
554 RefPtrWillBeRawPtr<DocumentFragment> fragment = nullptr;
555 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
556 fragment = DocumentFragment::create(*m_ownerDocument.get());
559 return fragment.release();
561 RefPtrWillBeRawPtr<Node> commonRoot = commonAncestorContainer();
564 if (m_start.container() == m_end.container()) {
565 processContentsBetweenOffsets(action, fragment, m_start.container(), m_start.offset(), m_end.offset(), exceptionState);
569 // Since mutation observers can modify the range during the process, the boundary points need to be saved.
570 RangeBoundaryPoint originalStart(m_start);
571 RangeBoundaryPoint originalEnd(m_end);
573 // what is the highest node that partially selects the start / end of the range?
574 RefPtrWillBeRawPtr<Node> partialStart = highestAncestorUnderCommonRoot(originalStart.container(), commonRoot.get());
575 RefPtrWillBeRawPtr<Node> partialEnd = highestAncestorUnderCommonRoot(originalEnd.container(), commonRoot.get());
577 // Start and end containers are different.
578 // There are three possibilities here:
579 // 1. Start container == commonRoot (End container must be a descendant)
580 // 2. End container == commonRoot (Start container must be a descendant)
581 // 3. Neither is commonRoot, they are both descendants
583 // In case 3, we grab everything after the start (up until a direct child
584 // of commonRoot) into leftContents, and everything before the end (up until
585 // a direct child of commonRoot) into rightContents. Then we process all
586 // commonRoot children between leftContents and rightContents
588 // In case 1 or 2, we skip either processing of leftContents or rightContents,
589 // in which case the last lot of nodes either goes from the first or last
590 // child of commonRoot.
592 // These are deleted, cloned, or extracted (i.e. both) depending on action.
594 // Note that we are verifying that our common root hierarchy is still intact
595 // after any DOM mutation event, at various stages below. See webkit bug 60350.
597 RefPtrWillBeRawPtr<Node> leftContents = nullptr;
598 if (originalStart.container() != commonRoot && commonRoot->contains(originalStart.container())) {
599 leftContents = processContentsBetweenOffsets(action, nullptr, originalStart.container(), originalStart.offset(), originalStart.container()->lengthOfContents(), exceptionState);
600 leftContents = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, leftContents, commonRoot.get(), exceptionState);
603 RefPtrWillBeRawPtr<Node> rightContents = nullptr;
604 if (m_end.container() != commonRoot && commonRoot->contains(originalEnd.container())) {
605 rightContents = processContentsBetweenOffsets(action, nullptr, originalEnd.container(), 0, originalEnd.offset(), exceptionState);
606 rightContents = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, rightContents, commonRoot.get(), exceptionState);
609 // delete all children of commonRoot between the start and end container
610 RefPtrWillBeRawPtr<Node> processStart = childOfCommonRootBeforeOffset(originalStart.container(), originalStart.offset(), commonRoot.get());
611 if (processStart && originalStart.container() != commonRoot) // processStart contains nodes before m_start.
612 processStart = processStart->nextSibling();
613 RefPtrWillBeRawPtr<Node> processEnd = childOfCommonRootBeforeOffset(originalEnd.container(), originalEnd.offset(), commonRoot.get());
615 // Collapse the range, making sure that the result is not within a node that was partially selected.
616 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
617 if (partialStart && commonRoot->contains(partialStart.get())) {
618 // FIXME: We should not continue if we have an earlier error.
619 exceptionState.clearException();
620 setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, exceptionState);
621 } else if (partialEnd && commonRoot->contains(partialEnd.get())) {
622 // FIXME: We should not continue if we have an earlier error.
623 exceptionState.clearException();
624 setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), exceptionState);
626 if (exceptionState.hadException())
631 originalStart.clear();
634 // Now add leftContents, stuff in between, and rightContents to the fragment
635 // (or just delete the stuff in between)
637 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents)
638 fragment->appendChild(leftContents, exceptionState);
642 for (Node* n = processStart.get(); n && n != processEnd; n = n->nextSibling())
644 processNodes(action, nodes, commonRoot, fragment, exceptionState);
647 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
648 fragment->appendChild(rightContents, exceptionState);
650 return fragment.release();
653 static inline void deleteCharacterData(PassRefPtrWillBeRawPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
655 if (data->length() - endOffset)
656 data->deleteData(endOffset, data->length() - endOffset, exceptionState);
658 data->deleteData(0, startOffset, exceptionState);
661 PassRefPtrWillBeRawPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtrWillBeRawPtr<DocumentFragment> fragment,
662 Node* container, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
665 ASSERT(startOffset <= endOffset);
667 // This switch statement must be consistent with that of Node::lengthOfContents.
668 RefPtrWillBeRawPtr<Node> result = nullptr;
669 switch (container->nodeType()) {
670 case Node::TEXT_NODE:
671 case Node::CDATA_SECTION_NODE:
672 case Node::COMMENT_NODE:
673 endOffset = std::min(endOffset, toCharacterData(container)->length());
674 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
675 RefPtrWillBeRawPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true));
676 deleteCharacterData(c, startOffset, endOffset, exceptionState);
679 result->appendChild(c.release(), exceptionState);
681 result = c.release();
683 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
684 toCharacterData(container)->deleteData(startOffset, endOffset - startOffset, exceptionState);
686 case Node::PROCESSING_INSTRUCTION_NODE:
687 endOffset = std::min(endOffset, toProcessingInstruction(container)->data().length());
688 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
689 RefPtrWillBeRawPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true));
690 c->setData(c->data().substring(startOffset, endOffset - startOffset));
693 result->appendChild(c.release(), exceptionState);
695 result = c.release();
697 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
698 ProcessingInstruction* pi = toProcessingInstruction(container);
699 String data(pi->data());
700 data.remove(startOffset, endOffset - startOffset);
704 case Node::ELEMENT_NODE:
705 case Node::ATTRIBUTE_NODE:
706 case Node::DOCUMENT_NODE:
707 case Node::DOCUMENT_TYPE_NODE:
708 case Node::DOCUMENT_FRAGMENT_NODE:
709 // FIXME: Should we assert that some nodes never appear here?
710 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
714 result = container->cloneNode(false);
717 Node* n = container->firstChild();
718 WillBeHeapVector<RefPtrWillBeMember<Node> > nodes;
719 for (unsigned i = startOffset; n && i; i--)
720 n = n->nextSibling();
721 for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling())
724 processNodes(action, nodes, container, result, exceptionState);
728 return result.release();
731 void Range::processNodes(ActionType action, WillBeHeapVector<RefPtrWillBeMember<Node> >& nodes, PassRefPtrWillBeRawPtr<Node> oldContainer, PassRefPtrWillBeRawPtr<Node> newContainer, ExceptionState& exceptionState)
733 for (unsigned i = 0; i < nodes.size(); i++) {
735 case DELETE_CONTENTS:
736 oldContainer->removeChild(nodes[i].get(), exceptionState);
738 case EXTRACT_CONTENTS:
739 newContainer->appendChild(nodes[i].release(), exceptionState); // will remove n from its parent
742 newContainer->appendChild(nodes[i]->cloneNode(true), exceptionState);
748 PassRefPtrWillBeRawPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtrWillBeRawPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionState& exceptionState)
750 typedef WillBeHeapVector<RefPtrWillBeMember<Node> > NodeVector;
752 RefPtrWillBeRawPtr<Node> clonedContainer = passedClonedContainer;
753 NodeVector ancestors;
754 for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode())
757 RefPtrWillBeRawPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling();
758 for (NodeVector::const_iterator it = ancestors.begin(); it != ancestors.end(); ++it) {
759 RefPtrWillBeRawPtr<Node> ancestor = *it;
760 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
761 if (RefPtrWillBeRawPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event.
762 clonedAncestor->appendChild(clonedContainer, exceptionState);
763 clonedContainer = clonedAncestor;
767 // Copy siblings of an ancestor of start/end containers
768 // FIXME: This assertion may fail if DOM is modified during mutation event
769 // FIXME: Share code with Range::processNodes
770 ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor);
773 for (Node* child = firstChildInAncestorToProcess.get(); child;
774 child = (direction == ProcessContentsForward) ? child->nextSibling() : child->previousSibling())
777 for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
778 Node* child = it->get();
780 case DELETE_CONTENTS:
781 // Prior call of ancestor->removeChild() may cause a tree change due to DOMSubtreeModified event.
782 // Therefore, we need to make sure |ancestor| is still |child|'s parent.
783 if (ancestor == child->parentNode())
784 ancestor->removeChild(child, exceptionState);
786 case EXTRACT_CONTENTS: // will remove child from ancestor
787 if (direction == ProcessContentsForward)
788 clonedContainer->appendChild(child, exceptionState);
790 clonedContainer->insertBefore(child, clonedContainer->firstChild(), exceptionState);
793 if (direction == ProcessContentsForward)
794 clonedContainer->appendChild(child->cloneNode(true), exceptionState);
796 clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), exceptionState);
800 firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling();
803 return clonedContainer.release();
806 PassRefPtrWillBeRawPtr<DocumentFragment> Range::extractContents(ExceptionState& exceptionState)
808 checkDeleteExtract(exceptionState);
809 if (exceptionState.hadException())
812 return processContents(EXTRACT_CONTENTS, exceptionState);
815 PassRefPtrWillBeRawPtr<DocumentFragment> Range::cloneContents(ExceptionState& exceptionState)
817 return processContents(CLONE_CONTENTS, exceptionState);
820 void Range::insertNode(PassRefPtrWillBeRawPtr<Node> prpNewNode, ExceptionState& exceptionState)
822 RefPtrWillBeRawPtr<Node> newNode = prpNewNode;
825 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
829 // HierarchyRequestError: Raised if the container of the start of the Range is of a type that
830 // does not allow children of the type of newNode or if newNode is an ancestor of the container.
832 // an extra one here - if a text node is going to split, it must have a parent to insert into
833 bool startIsText = m_start.container()->isTextNode();
834 if (startIsText && !m_start.container()->parentNode()) {
835 exceptionState.throwDOMException(HierarchyRequestError, "This operation would split a text node, but there's no parent into which to insert.");
839 // In the case where the container is a text node, we check against the container's parent, because
840 // text nodes get split up upon insertion.
843 checkAgainst = m_start.container()->parentNode();
845 checkAgainst = m_start.container();
847 Node::NodeType newNodeType = newNode->nodeType();
849 if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE && !newNode->isShadowRoot()) {
850 // check each child node, not the DocumentFragment itself
852 for (Node* c = toDocumentFragment(newNode)->firstChild(); c; c = c->nextSibling()) {
853 if (!checkAgainst->childTypeAllowed(c->nodeType())) {
854 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains a '" + c->nodeName() + "' node, which may not be inserted here.");
861 if (!checkAgainst->childTypeAllowed(newNodeType)) {
862 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
867 for (Node* n = m_start.container(); n; n = n->parentNode()) {
869 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains the insertion point; it may not be inserted into itself.");
874 // InvalidNodeTypeError: Raised if newNode is an Attr, Entity, Notation, ShadowRoot or Document node.
875 switch (newNodeType) {
876 case Node::ATTRIBUTE_NODE:
877 case Node::DOCUMENT_NODE:
878 exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
881 if (newNode->isShadowRoot()) {
882 exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a shadow root, which may not be inserted here.");
888 EventQueueScope scope;
889 bool collapsed = m_start == m_end;
890 RefPtrWillBeRawPtr<Node> container = nullptr;
892 container = m_start.container();
893 RefPtrWillBeRawPtr<Text> newText = toText(container)->splitText(m_start.offset(), exceptionState);
894 if (exceptionState.hadException())
897 container = m_start.container();
898 container->parentNode()->insertBefore(newNode.release(), newText.get(), exceptionState);
899 if (exceptionState.hadException())
903 m_end.setToBeforeChild(*newText);
905 RefPtrWillBeRawPtr<Node> lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->lastChild() : newNode.get();
906 if (lastChild && lastChild == m_start.childBefore()) {
907 // The insertion will do nothing, but we need to extend the range to include
908 // the inserted nodes.
909 Node* firstChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->firstChild() : newNode.get();
911 m_start.setToBeforeChild(*firstChild);
915 container = m_start.container();
916 container->insertBefore(newNode.release(), container->traverseToChildAt(m_start.offset()), exceptionState);
917 if (exceptionState.hadException())
920 // Note that m_start.offset() may have changed as a result of container->insertBefore,
921 // when the node we are inserting comes before the range in the same container.
922 if (collapsed && numNewChildren)
923 m_end.set(m_start.container(), m_start.offset() + numNewChildren, lastChild.get());
927 String Range::toString() const
929 StringBuilder builder;
931 Node* pastLast = pastLastNode();
932 for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
933 Node::NodeType type = n->nodeType();
934 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
935 String data = toCharacterData(n)->data();
936 int length = data.length();
937 int start = (n == m_start.container()) ? std::min(std::max(0, m_start.offset()), length) : 0;
938 int end = (n == m_end.container()) ? std::min(std::max(start, m_end.offset()), length) : length;
939 builder.append(data, start, end - start);
943 return builder.toString();
946 String Range::toHTML() const
948 return createMarkup(this);
951 String Range::text() const
953 // We need to update layout, since plainText uses line boxes in the render tree.
954 // FIXME: As with innerText, we'd like this to work even if there are no render objects.
955 m_start.container()->document().updateLayout();
957 return plainText(this);
960 PassRefPtrWillBeRawPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionState& exceptionState)
962 Node* element = m_start.container()->isElementNode() ? m_start.container() : m_start.container()->parentNode();
963 if (!element || !element->isHTMLElement()) {
964 exceptionState.throwDOMException(NotSupportedError, "The range's container must be an HTML element.");
968 RefPtrWillBeRawPtr<DocumentFragment> fragment = WebCore::createContextualFragment(markup, toHTMLElement(element), AllowScriptingContentAndDoNotMarkAlreadyStarted, exceptionState);
972 return fragment.release();
978 // This is now a no-op as per the DOM specification.
981 Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionState& exceptionState) const
983 switch (n->nodeType()) {
984 case Node::DOCUMENT_TYPE_NODE:
985 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
987 case Node::CDATA_SECTION_NODE:
988 case Node::COMMENT_NODE:
989 case Node::TEXT_NODE:
990 if (static_cast<unsigned>(offset) > toCharacterData(n)->length())
991 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than or equal to the node's length (" + String::number(toCharacterData(n)->length()) + ").");
993 case Node::PROCESSING_INSTRUCTION_NODE:
994 if (static_cast<unsigned>(offset) > toProcessingInstruction(n)->data().length())
995 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than or equal to than the node's length (" + String::number(toProcessingInstruction(n)->data().length()) + ").");
997 case Node::ATTRIBUTE_NODE:
998 case Node::DOCUMENT_FRAGMENT_NODE:
999 case Node::DOCUMENT_NODE:
1000 case Node::ELEMENT_NODE: {
1003 Node* childBefore = n->traverseToChildAt(offset - 1);
1005 exceptionState.throwDOMException(IndexSizeError, "There is no child at offset " + String::number(offset) + ".");
1009 ASSERT_NOT_REACHED();
1013 void Range::checkNodeBA(Node* n, ExceptionState& exceptionState) const
1016 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1020 // InvalidNodeTypeError: Raised if the root container of refNode is not an
1021 // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG shadow DOM tree,
1022 // or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation node.
1024 if (!n->parentNode()) {
1025 exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1029 switch (n->nodeType()) {
1030 case Node::ATTRIBUTE_NODE:
1031 case Node::DOCUMENT_FRAGMENT_NODE:
1032 case Node::DOCUMENT_NODE:
1033 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1035 case Node::CDATA_SECTION_NODE:
1036 case Node::COMMENT_NODE:
1037 case Node::DOCUMENT_TYPE_NODE:
1038 case Node::ELEMENT_NODE:
1039 case Node::PROCESSING_INSTRUCTION_NODE:
1040 case Node::TEXT_NODE:
1045 while (ContainerNode* parent = root->parentNode())
1048 switch (root->nodeType()) {
1049 case Node::ATTRIBUTE_NODE:
1050 case Node::DOCUMENT_NODE:
1051 case Node::DOCUMENT_FRAGMENT_NODE:
1052 case Node::ELEMENT_NODE:
1054 case Node::CDATA_SECTION_NODE:
1055 case Node::COMMENT_NODE:
1056 case Node::DOCUMENT_TYPE_NODE:
1057 case Node::PROCESSING_INSTRUCTION_NODE:
1058 case Node::TEXT_NODE:
1059 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1064 PassRefPtrWillBeRawPtr<Range> Range::cloneRange() const
1066 return Range::create(*m_ownerDocument.get(), m_start.container(), m_start.offset(), m_end.container(), m_end.offset());
1069 void Range::setStartAfter(Node* refNode, ExceptionState& exceptionState)
1071 checkNodeBA(refNode, exceptionState);
1072 if (exceptionState.hadException())
1075 setStart(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1078 void Range::setEndBefore(Node* refNode, ExceptionState& exceptionState)
1080 checkNodeBA(refNode, exceptionState);
1081 if (exceptionState.hadException())
1084 setEnd(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1087 void Range::setEndAfter(Node* refNode, ExceptionState& exceptionState)
1089 checkNodeBA(refNode, exceptionState);
1090 if (exceptionState.hadException())
1093 setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1096 void Range::selectNode(Node* refNode, ExceptionState& exceptionState)
1099 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1103 if (!refNode->parentNode()) {
1104 exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1108 // InvalidNodeTypeError: Raised if an ancestor of refNode is an Entity, Notation or
1109 // DocumentType node or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation
1111 for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
1112 switch (anc->nodeType()) {
1113 case Node::ATTRIBUTE_NODE:
1114 case Node::CDATA_SECTION_NODE:
1115 case Node::COMMENT_NODE:
1116 case Node::DOCUMENT_FRAGMENT_NODE:
1117 case Node::DOCUMENT_NODE:
1118 case Node::ELEMENT_NODE:
1119 case Node::PROCESSING_INSTRUCTION_NODE:
1120 case Node::TEXT_NODE:
1122 case Node::DOCUMENT_TYPE_NODE:
1123 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided has an ancestor of type '" + anc->nodeName() + "'.");
1128 switch (refNode->nodeType()) {
1129 case Node::CDATA_SECTION_NODE:
1130 case Node::COMMENT_NODE:
1131 case Node::DOCUMENT_TYPE_NODE:
1132 case Node::ELEMENT_NODE:
1133 case Node::PROCESSING_INSTRUCTION_NODE:
1134 case Node::TEXT_NODE:
1136 case Node::ATTRIBUTE_NODE:
1137 case Node::DOCUMENT_FRAGMENT_NODE:
1138 case Node::DOCUMENT_NODE:
1139 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1143 if (m_ownerDocument != refNode->document())
1144 setDocument(refNode->document());
1146 setStartBefore(refNode);
1147 setEndAfter(refNode);
1150 void Range::selectNodeContents(Node* refNode, ExceptionState& exceptionState)
1153 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1157 // InvalidNodeTypeError: Raised if refNode or an ancestor of refNode is an Entity, Notation
1158 // or DocumentType node.
1159 for (Node* n = refNode; n; n = n->parentNode()) {
1160 switch (n->nodeType()) {
1161 case Node::ATTRIBUTE_NODE:
1162 case Node::CDATA_SECTION_NODE:
1163 case Node::COMMENT_NODE:
1164 case Node::DOCUMENT_FRAGMENT_NODE:
1165 case Node::DOCUMENT_NODE:
1166 case Node::ELEMENT_NODE:
1167 case Node::PROCESSING_INSTRUCTION_NODE:
1168 case Node::TEXT_NODE:
1170 case Node::DOCUMENT_TYPE_NODE:
1171 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1176 if (m_ownerDocument != refNode->document())
1177 setDocument(refNode->document());
1179 m_start.setToStartOfNode(*refNode);
1180 m_end.setToEndOfNode(*refNode);
1183 void Range::surroundContents(PassRefPtrWillBeRawPtr<Node> passNewParent, ExceptionState& exceptionState)
1185 RefPtrWillBeRawPtr<Node> newParent = passNewParent;
1187 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1191 // InvalidNodeTypeError: Raised if node is an Attr, Entity, DocumentType, Notation,
1192 // Document, or DocumentFragment node.
1193 switch (newParent->nodeType()) {
1194 case Node::ATTRIBUTE_NODE:
1195 case Node::DOCUMENT_FRAGMENT_NODE:
1196 case Node::DOCUMENT_NODE:
1197 case Node::DOCUMENT_TYPE_NODE:
1198 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + newParent->nodeName() + "'.");
1200 case Node::CDATA_SECTION_NODE:
1201 case Node::COMMENT_NODE:
1202 case Node::ELEMENT_NODE:
1203 case Node::PROCESSING_INSTRUCTION_NODE:
1204 case Node::TEXT_NODE:
1208 // Raise a HierarchyRequestError if m_start.container() doesn't accept children like newParent.
1209 Node* parentOfNewParent = m_start.container();
1211 // If m_start.container() is a character data node, it will be split and it will be its parent that will
1212 // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent,
1213 // although this will fail below for another reason).
1214 if (parentOfNewParent->isCharacterDataNode())
1215 parentOfNewParent = parentOfNewParent->parentNode();
1217 if (!parentOfNewParent) {
1218 exceptionState.throwDOMException(HierarchyRequestError, "The container node is a detached character data node; no parent node is available for insertion.");
1222 if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
1223 exceptionState.throwDOMException(HierarchyRequestError, "The node provided is of type '" + newParent->nodeName() + "', which may not be inserted here.");
1227 if (newParent->contains(m_start.container())) {
1228 exceptionState.throwDOMException(HierarchyRequestError, "The node provided contains the insertion point; it may not be inserted into itself.");
1232 // FIXME: Do we need a check if the node would end up with a child node of a type not
1233 // allowed by the type of node?
1235 // BAD_BOUNDARYPOINTS_ERR: Raised if the Range partially selects a non-Text node.
1236 Node* startNonTextContainer = m_start.container();
1237 if (startNonTextContainer->nodeType() == Node::TEXT_NODE)
1238 startNonTextContainer = startNonTextContainer->parentNode();
1239 Node* endNonTextContainer = m_end.container();
1240 if (endNonTextContainer->nodeType() == Node::TEXT_NODE)
1241 endNonTextContainer = endNonTextContainer->parentNode();
1242 if (startNonTextContainer != endNonTextContainer) {
1243 exceptionState.throwDOMException(InvalidStateError, "The Range has partially selected a non-Text node.");
1247 while (Node* n = newParent->firstChild()) {
1248 toContainerNode(newParent)->removeChild(n, exceptionState);
1249 if (exceptionState.hadException())
1252 RefPtrWillBeRawPtr<DocumentFragment> fragment = extractContents(exceptionState);
1253 if (exceptionState.hadException())
1255 insertNode(newParent, exceptionState);
1256 if (exceptionState.hadException())
1258 newParent->appendChild(fragment.release(), exceptionState);
1259 if (exceptionState.hadException())
1261 selectNode(newParent.get(), exceptionState);
1264 void Range::setStartBefore(Node* refNode, ExceptionState& exceptionState)
1266 checkNodeBA(refNode, exceptionState);
1267 if (exceptionState.hadException())
1270 setStart(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1273 void Range::checkDeleteExtract(ExceptionState& exceptionState)
1275 ASSERT(boundaryPointsValid());
1277 if (!commonAncestorContainer())
1280 Node* pastLast = pastLastNode();
1281 for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
1282 if (n->isDocumentTypeNode()) {
1283 exceptionState.throwDOMException(HierarchyRequestError, "The Range contains a doctype node.");
1289 Node* Range::firstNode() const
1291 if (m_start.container()->offsetInCharacters())
1292 return m_start.container();
1293 if (Node* child = m_start.container()->traverseToChildAt(m_start.offset()))
1295 if (!m_start.offset())
1296 return m_start.container();
1297 return NodeTraversal::nextSkippingChildren(*m_start.container());
1300 ShadowRoot* Range::shadowRoot() const
1302 return startContainer() ? startContainer()->containingShadowRoot() : 0;
1305 Node* Range::pastLastNode() const
1307 if (m_end.container()->offsetInCharacters())
1308 return NodeTraversal::nextSkippingChildren(*m_end.container());
1309 if (Node* child = m_end.container()->traverseToChildAt(m_end.offset()))
1311 return NodeTraversal::nextSkippingChildren(*m_end.container());
1314 IntRect Range::boundingBox() const
1317 Vector<IntRect> rects;
1319 const size_t n = rects.size();
1320 for (size_t i = 0; i < n; ++i)
1321 result.unite(rects[i]);
1325 void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1327 Node* startContainer = m_start.container();
1328 ASSERT(startContainer);
1329 Node* endContainer = m_end.container();
1330 ASSERT(endContainer);
1332 bool allFixed = true;
1333 bool someFixed = false;
1335 Node* stopNode = pastLastNode();
1336 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1337 RenderObject* r = node->renderer();
1338 if (!r || !r->isText())
1340 RenderText* renderText = toRenderText(r);
1341 int startOffset = node == startContainer ? m_start.offset() : 0;
1342 int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1343 bool isFixed = false;
1344 renderText->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight, &isFixed);
1345 allFixed &= isFixed;
1346 someFixed |= isFixed;
1350 *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1353 void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1355 Node* startContainer = m_start.container();
1356 ASSERT(startContainer);
1357 Node* endContainer = m_end.container();
1358 ASSERT(endContainer);
1360 bool allFixed = true;
1361 bool someFixed = false;
1363 Node* stopNode = pastLastNode();
1364 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1365 RenderObject* r = node->renderer();
1366 if (!r || !r->isText())
1368 RenderText* renderText = toRenderText(r);
1369 int startOffset = node == startContainer ? m_start.offset() : 0;
1370 int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1371 bool isFixed = false;
1372 renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight, &isFixed);
1373 allFixed &= isFixed;
1374 someFixed |= isFixed;
1378 *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1382 void Range::formatForDebugger(char* buffer, unsigned length) const
1384 StringBuilder result;
1386 const int FormatBufferSize = 1024;
1387 char s[FormatBufferSize];
1388 result.appendLiteral("from offset ");
1389 result.appendNumber(m_start.offset());
1390 result.appendLiteral(" of ");
1391 m_start.container()->formatForDebugger(s, FormatBufferSize);
1393 result.appendLiteral(" to offset ");
1394 result.appendNumber(m_end.offset());
1395 result.appendLiteral(" of ");
1396 m_end.container()->formatForDebugger(s, FormatBufferSize);
1399 strncpy(buffer, result.toString().utf8().data(), length - 1);
1403 bool areRangesEqual(const Range* a, const Range* b)
1409 return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition();
1412 PassRefPtrWillBeRawPtr<Range> rangeOfContents(Node* node)
1415 RefPtrWillBeRawPtr<Range> range = Range::create(node->document());
1416 range->selectNodeContents(node, IGNORE_EXCEPTION);
1417 return range.release();
1420 static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container)
1422 if (!boundary.childBefore())
1424 if (boundary.container() != container)
1426 boundary.invalidateOffset();
1429 void Range::nodeChildrenChanged(ContainerNode* container)
1432 ASSERT(container->document() == m_ownerDocument);
1433 boundaryNodeChildrenChanged(m_start, container);
1434 boundaryNodeChildrenChanged(m_end, container);
1437 static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode& container)
1439 for (Node* nodeToBeRemoved = container.firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) {
1440 if (boundary.childBefore() == nodeToBeRemoved) {
1441 boundary.setToStartOfNode(container);
1445 for (Node* n = boundary.container(); n; n = n->parentNode()) {
1446 if (n == nodeToBeRemoved) {
1447 boundary.setToStartOfNode(container);
1454 void Range::nodeChildrenWillBeRemoved(ContainerNode& container)
1456 ASSERT(container.document() == m_ownerDocument);
1457 boundaryNodeChildrenWillBeRemoved(m_start, container);
1458 boundaryNodeChildrenWillBeRemoved(m_end, container);
1461 static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node& nodeToBeRemoved)
1463 if (boundary.childBefore() == nodeToBeRemoved) {
1464 boundary.childBeforeWillBeRemoved();
1468 for (Node* n = boundary.container(); n; n = n->parentNode()) {
1469 if (n == nodeToBeRemoved) {
1470 boundary.setToBeforeChild(nodeToBeRemoved);
1476 void Range::nodeWillBeRemoved(Node& node)
1478 ASSERT(node.document() == m_ownerDocument);
1479 ASSERT(node != m_ownerDocument.get());
1481 // FIXME: Once DOMNodeRemovedFromDocument mutation event removed, we
1482 // should change following if-statement to ASSERT(!node->parentNode).
1483 if (!node.parentNode())
1485 boundaryNodeWillBeRemoved(m_start, node);
1486 boundaryNodeWillBeRemoved(m_end, node);
1489 static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1491 if (boundary.container() != text)
1493 unsigned boundaryOffset = boundary.offset();
1494 if (offset >= boundaryOffset)
1496 boundary.setOffset(boundaryOffset + length);
1499 void Range::didInsertText(Node* text, unsigned offset, unsigned length)
1502 ASSERT(text->document() == m_ownerDocument);
1503 boundaryTextInserted(m_start, text, offset, length);
1504 boundaryTextInserted(m_end, text, offset, length);
1507 static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1509 if (boundary.container() != text)
1511 unsigned boundaryOffset = boundary.offset();
1512 if (offset >= boundaryOffset)
1514 if (offset + length >= boundaryOffset)
1515 boundary.setOffset(offset);
1517 boundary.setOffset(boundaryOffset - length);
1520 void Range::didRemoveText(Node* text, unsigned offset, unsigned length)
1523 ASSERT(text->document() == m_ownerDocument);
1524 boundaryTextRemoved(m_start, text, offset, length);
1525 boundaryTextRemoved(m_end, text, offset, length);
1528 static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, const NodeWithIndex& oldNode, unsigned offset)
1530 if (boundary.container() == oldNode.node())
1531 boundary.set(oldNode.node().previousSibling(), boundary.offset() + offset, 0);
1532 else if (boundary.container() == oldNode.node().parentNode() && boundary.offset() == oldNode.index())
1533 boundary.set(oldNode.node().previousSibling(), offset, 0);
1536 void Range::didMergeTextNodes(const NodeWithIndex& oldNode, unsigned offset)
1538 ASSERT(oldNode.node().document() == m_ownerDocument);
1539 ASSERT(oldNode.node().parentNode());
1540 ASSERT(oldNode.node().isTextNode());
1541 ASSERT(oldNode.node().previousSibling());
1542 ASSERT(oldNode.node().previousSibling()->isTextNode());
1543 boundaryTextNodesMerged(m_start, oldNode, offset);
1544 boundaryTextNodesMerged(m_end, oldNode, offset);
1547 void Range::updateOwnerDocumentIfNeeded()
1549 ASSERT(m_start.container());
1550 ASSERT(m_end.container());
1551 Document& newDocument = m_start.container()->document();
1552 ASSERT(newDocument == m_end.container()->document());
1553 if (newDocument == m_ownerDocument)
1555 m_ownerDocument->detachRange(this);
1556 m_ownerDocument = &newDocument;
1557 m_ownerDocument->attachRange(this);
1560 static inline void boundaryTextNodeSplit(RangeBoundaryPoint& boundary, Text& oldNode)
1562 Node* boundaryContainer = boundary.container();
1563 unsigned boundaryOffset = boundary.offset();
1564 if (boundary.childBefore() == &oldNode)
1565 boundary.set(boundaryContainer, boundaryOffset + 1, oldNode.nextSibling());
1566 else if (boundary.container() == &oldNode && boundaryOffset > oldNode.length())
1567 boundary.set(oldNode.nextSibling(), boundaryOffset - oldNode.length(), 0);
1570 void Range::didSplitTextNode(Text& oldNode)
1572 ASSERT(oldNode.document() == m_ownerDocument);
1573 ASSERT(oldNode.parentNode());
1574 ASSERT(oldNode.isTextNode());
1575 ASSERT(oldNode.nextSibling());
1576 ASSERT(oldNode.nextSibling()->isTextNode());
1577 boundaryTextNodeSplit(m_start, oldNode);
1578 boundaryTextNodeSplit(m_end, oldNode);
1579 ASSERT(boundaryPointsValid());
1582 void Range::expand(const String& unit, ExceptionState& exceptionState)
1584 VisiblePosition start(startPosition());
1585 VisiblePosition end(endPosition());
1586 if (unit == "word") {
1587 start = startOfWord(start);
1588 end = endOfWord(end);
1589 } else if (unit == "sentence") {
1590 start = startOfSentence(start);
1591 end = endOfSentence(end);
1592 } else if (unit == "block") {
1593 start = startOfParagraph(start);
1594 end = endOfParagraph(end);
1595 } else if (unit == "document") {
1596 start = startOfDocument(start);
1597 end = endOfDocument(end);
1600 setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1601 setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1604 PassRefPtrWillBeRawPtr<ClientRectList> Range::getClientRects() const
1606 m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1608 Vector<FloatQuad> quads;
1609 getBorderAndTextQuads(quads);
1611 return ClientRectList::create(quads);
1614 PassRefPtrWillBeRawPtr<ClientRect> Range::getBoundingClientRect() const
1616 return ClientRect::create(boundingRect());
1619 void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
1621 Node* startContainer = m_start.container();
1622 Node* endContainer = m_end.container();
1623 Node* stopNode = pastLastNode();
1625 HashSet<Node*> nodeSet;
1626 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1627 if (node->isElementNode())
1631 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1632 if (node->isElementNode()) {
1633 if (!nodeSet.contains(node->parentNode())) {
1634 if (RenderBoxModelObject* renderBoxModelObject = toElement(node)->renderBoxModelObject()) {
1635 Vector<FloatQuad> elementQuads;
1636 renderBoxModelObject->absoluteQuads(elementQuads);
1637 m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(elementQuads, *renderBoxModelObject);
1639 quads.appendVector(elementQuads);
1642 } else if (node->isTextNode()) {
1643 if (RenderObject* renderer = toText(node)->renderer()) {
1644 RenderText& renderText = toRenderText(*renderer);
1645 int startOffset = (node == startContainer) ? m_start.offset() : 0;
1646 int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX;
1648 Vector<FloatQuad> textQuads;
1649 renderText.absoluteQuadsForRange(textQuads, startOffset, endOffset);
1650 m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(textQuads, renderText);
1652 quads.appendVector(textQuads);
1658 FloatRect Range::boundingRect() const
1660 m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1662 Vector<FloatQuad> quads;
1663 getBorderAndTextQuads(quads);
1664 if (quads.isEmpty())
1668 for (size_t i = 0; i < quads.size(); ++i)
1669 result.unite(quads[i].boundingBox());
1674 void Range::trace(Visitor* visitor)
1676 visitor->trace(m_ownerDocument);
1677 visitor->trace(m_start);
1678 visitor->trace(m_end);
1681 } // namespace WebCore
1685 void showTree(const WebCore::Range* range)
1687 if (range && range->boundaryPointsValid()) {
1688 range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E");
1689 fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset());