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/core/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/HTMLBodyElement.h"
44 #include "core/html/HTMLElement.h"
45 #include "core/rendering/RenderBoxModelObject.h"
46 #include "core/rendering/RenderText.h"
47 #include "core/svg/SVGSVGElement.h"
48 #include "platform/geometry/FloatQuad.h"
49 #include "wtf/RefCountedLeakCounter.h"
50 #include "wtf/text/CString.h"
51 #include "wtf/text/StringBuilder.h"
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 m_ownerDocument->attachRange(this);
72 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument)
74 return adoptRefWillBeNoop(new Range(ownerDocument));
77 inline Range::Range(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset)
78 : m_ownerDocument(&ownerDocument)
79 , m_start(m_ownerDocument)
80 , m_end(m_ownerDocument)
83 rangeCounter.increment();
86 m_ownerDocument->attachRange(this);
88 // Simply setting the containers and offsets directly would not do any of the checking
89 // that setStart and setEnd do, so we call those functions.
90 setStart(startContainer, startOffset);
91 setEnd(endContainer, endOffset);
94 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset)
96 return adoptRefWillBeNoop(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset));
99 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end)
101 return adoptRefWillBeNoop(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode()));
107 // Always detach (even if we've already detached) to fix https://bugs.webkit.org/show_bug.cgi?id=26044
108 m_ownerDocument->detachRange(this);
112 rangeCounter.decrement();
116 void Range::setDocument(Document& document)
118 ASSERT(m_ownerDocument != document);
119 ASSERT(m_ownerDocument);
120 m_ownerDocument->detachRange(this);
121 m_ownerDocument = &document;
122 m_start.setToStartOfNode(document);
123 m_end.setToStartOfNode(document);
124 m_ownerDocument->attachRange(this);
127 Node* Range::commonAncestorContainer() const
129 return commonAncestorContainer(m_start.container(), m_end.container());
132 Node* Range::commonAncestorContainer(Node* containerA, Node* containerB)
134 for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) {
135 for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) {
136 if (parentA == parentB)
143 static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& start, const RangeBoundaryPoint& end)
145 Node* endRootContainer = end.container();
146 while (endRootContainer->parentNode())
147 endRootContainer = endRootContainer->parentNode();
148 Node* startRootContainer = start.container();
149 while (startRootContainer->parentNode())
150 startRootContainer = startRootContainer->parentNode();
152 return startRootContainer != endRootContainer || (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0);
155 void Range::setStart(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
158 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
162 bool didMoveDocument = false;
163 if (refNode->document() != m_ownerDocument) {
164 setDocument(refNode->document());
165 didMoveDocument = true;
168 Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
169 if (exceptionState.hadException())
172 m_start.set(refNode, offset, childNode);
174 if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
178 void Range::setEnd(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
181 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
185 bool didMoveDocument = false;
186 if (refNode->document() != m_ownerDocument) {
187 setDocument(refNode->document());
188 didMoveDocument = true;
191 Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
192 if (exceptionState.hadException())
195 m_end.set(refNode, offset, childNode);
197 if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
201 void Range::setStart(const Position& start, ExceptionState& exceptionState)
203 Position parentAnchored = start.parentAnchoredEquivalent();
204 setStart(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
207 void Range::setEnd(const Position& end, ExceptionState& exceptionState)
209 Position parentAnchored = end.parentAnchoredEquivalent();
210 setEnd(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
213 void Range::collapse(bool toStart)
221 bool Range::isPointInRange(Node* refNode, int offset, ExceptionState& exceptionState)
224 exceptionState.throwDOMException(HierarchyRequestError, "The node provided was null.");
228 if (!refNode->inActiveDocument() || refNode->document() != m_ownerDocument) {
232 checkNodeWOffset(refNode, offset, exceptionState);
233 if (exceptionState.hadException())
236 return compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), exceptionState) >= 0 && !exceptionState.hadException()
237 && compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), exceptionState) <= 0 && !exceptionState.hadException();
240 short Range::comparePoint(Node* refNode, int offset, ExceptionState& exceptionState) const
242 // http://developer.mozilla.org/en/docs/DOM:range.comparePoint
243 // This method returns -1, 0 or 1 depending on if the point described by the
244 // refNode node and an offset within the node is before, same as, or after the range respectively.
246 if (!refNode->inActiveDocument()) {
247 exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in an active document.");
251 if (refNode->document() != m_ownerDocument) {
252 exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in this Range's Document.");
256 checkNodeWOffset(refNode, offset, exceptionState);
257 if (exceptionState.hadException())
260 // compare to start, and point comes before
261 if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), exceptionState) < 0)
264 if (exceptionState.hadException())
267 // compare to end, and point comes after
268 if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), exceptionState) > 0 && !exceptionState.hadException())
271 // point is in the middle of this range, or on the boundary points
275 Range::CompareResults Range::compareNode(Node* refNode, ExceptionState& exceptionState) const
277 // http://developer.mozilla.org/en/docs/DOM:range.compareNode
278 // This method returns 0, 1, 2, or 3 based on if the node is before, after,
279 // before and after(surrounds), or inside the range, respectively
282 exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
286 if (!refNode->inActiveDocument()) {
287 // Firefox doesn't throw an exception for this case; it returns 0.
291 if (refNode->document() != m_ownerDocument) {
292 // Firefox doesn't throw an exception for this case; it returns 0.
296 ContainerNode* parentNode = refNode->parentNode();
297 int nodeIndex = refNode->nodeIndex();
300 // if the node is the top document we should return NODE_BEFORE_AND_AFTER
301 // but we throw to match firefox behavior
302 exceptionState.throwDOMException(NotFoundError, "The provided node has no parent.");
306 if (comparePoint(parentNode, nodeIndex, exceptionState) < 0) { // starts before
307 if (comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) // ends after the range
308 return NODE_BEFORE_AND_AFTER;
309 return NODE_BEFORE; // ends before or in the range
311 // starts at or after the range start
312 if (comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) // ends after the range
314 return NODE_INSIDE; // ends inside the range
317 short Range::compareBoundaryPoints(unsigned how, const Range* sourceRange, ExceptionState& exceptionState) const
319 if (!(how == START_TO_START || how == START_TO_END || how == END_TO_END || how == END_TO_START)) {
320 exceptionState.throwDOMException(NotSupportedError, "The comparison method provided must be one of 'START_TO_START', 'START_TO_END', 'END_TO_END', or 'END_TO_START'.");
324 Node* thisCont = commonAncestorContainer();
325 Node* sourceCont = sourceRange->commonAncestorContainer();
326 if (thisCont->document() != sourceCont->document()) {
327 exceptionState.throwDOMException(WrongDocumentError, "The source range is in a different document than this range.");
331 Node* thisTop = thisCont;
332 Node* sourceTop = sourceCont;
333 while (thisTop->parentNode())
334 thisTop = thisTop->parentNode();
335 while (sourceTop->parentNode())
336 sourceTop = sourceTop->parentNode();
337 if (thisTop != sourceTop) { // in different DocumentFragments
338 exceptionState.throwDOMException(WrongDocumentError, "The source range is in a different document than this range.");
344 return compareBoundaryPoints(m_start, sourceRange->m_start, exceptionState);
346 return compareBoundaryPoints(m_end, sourceRange->m_start, exceptionState);
348 return compareBoundaryPoints(m_end, sourceRange->m_end, exceptionState);
350 return compareBoundaryPoints(m_start, sourceRange->m_end, exceptionState);
353 ASSERT_NOT_REACHED();
357 short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionState& exceptionState)
367 // see DOM2 traversal & range section 2.5
369 // case 1: both points have the same container
370 if (containerA == containerB) {
371 if (offsetA == offsetB)
372 return 0; // A is equal to B
373 if (offsetA < offsetB)
374 return -1; // A is before B
376 return 1; // A is after B
379 // case 2: node C (container B or an ancestor) is a child node of A
380 Node* c = containerB;
381 while (c && c->parentNode() != containerA)
385 Node* n = containerA->firstChild();
386 while (n != c && offsetC < offsetA) {
388 n = n->nextSibling();
391 if (offsetA <= offsetC)
392 return -1; // A is before B
394 return 1; // A is after B
397 // case 3: node C (container A or an ancestor) is a child node of B
399 while (c && c->parentNode() != containerB)
403 Node* n = containerB->firstChild();
404 while (n != c && offsetC < offsetB) {
406 n = n->nextSibling();
409 if (offsetC < offsetB)
410 return -1; // A is before B
412 return 1; // A is after B
415 // case 4: containers A & B are siblings, or children of siblings
416 // ### we need to do a traversal here instead
417 Node* commonAncestor = commonAncestorContainer(containerA, containerB);
418 if (!commonAncestor) {
419 exceptionState.throwDOMException(WrongDocumentError, "The two ranges are in separate documents.");
422 Node* childA = containerA;
423 while (childA && childA->parentNode() != commonAncestor)
424 childA = childA->parentNode();
426 childA = commonAncestor;
427 Node* childB = containerB;
428 while (childB && childB->parentNode() != commonAncestor)
429 childB = childB->parentNode();
431 childB = commonAncestor;
433 if (childA == childB)
434 return 0; // A is equal to B
436 Node* n = commonAncestor->firstChild();
439 return -1; // A is before B
441 return 1; // A is after B
442 n = n->nextSibling();
445 // Should never reach this point.
446 ASSERT_NOT_REACHED();
450 short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionState& exceptionState)
452 return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset(), exceptionState);
455 bool Range::boundaryPointsValid() const
457 TrackExceptionState exceptionState;
458 return compareBoundaryPoints(m_start, m_end, exceptionState) <= 0 && !exceptionState.hadException();
461 void Range::deleteContents(ExceptionState& exceptionState)
463 ASSERT(boundaryPointsValid());
466 EventQueueScope eventQueueScope;
467 processContents(DELETE_CONTENTS, exceptionState);
471 static bool nodeValidForIntersects(Node* refNode, Document* expectedDocument, ExceptionState& exceptionState)
474 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
478 if (!refNode->inActiveDocument() || refNode->document() != expectedDocument) {
479 // Firefox doesn't throw an exception for these cases; it returns false.
486 bool Range::intersectsNode(Node* refNode, ExceptionState& exceptionState)
488 // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode
489 // Returns a bool if the node intersects the range.
490 if (!nodeValidForIntersects(refNode, m_ownerDocument.get(), exceptionState))
493 ContainerNode* parentNode = refNode->parentNode();
494 int nodeIndex = refNode->nodeIndex();
497 // if the node is the top document we should return NODE_BEFORE_AND_AFTER
498 // but we throw to match firefox behavior
499 exceptionState.throwDOMException(NotFoundError, "The node provided has no parent.");
503 if (comparePoint(parentNode, nodeIndex, exceptionState) < 0 // starts before start
504 && comparePoint(parentNode, nodeIndex + 1, exceptionState) < 0) { // ends before start
508 if (comparePoint(parentNode, nodeIndex, exceptionState) > 0 // starts after end
509 && comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) { // ends after end
513 return true; // all other cases
516 bool Range::intersectsNode(Node* refNode, const Position& start, const Position& end, ExceptionState& exceptionState)
518 // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode
519 // Returns a bool if the node intersects the range.
520 if (!nodeValidForIntersects(refNode, start.document(), exceptionState))
523 ContainerNode* parentNode = refNode->parentNode();
524 int nodeIndex = refNode->nodeIndex();
527 // if the node is the top document we should return NODE_BEFORE_AND_AFTER
528 // but we throw to match firefox behavior
529 exceptionState.throwDOMException(NotFoundError, "The node provided has no parent.");
533 Node* startContainerNode = start.containerNode();
534 int startOffset = start.computeOffsetInContainerNode();
536 if (compareBoundaryPoints(parentNode, nodeIndex, startContainerNode, startOffset, exceptionState) < 0 // starts before start
537 && compareBoundaryPoints(parentNode, nodeIndex + 1, startContainerNode, startOffset, exceptionState) < 0) { // ends before start
538 ASSERT(!exceptionState.hadException());
542 Node* endContainerNode = end.containerNode();
543 int endOffset = end.computeOffsetInContainerNode();
545 if (compareBoundaryPoints(parentNode, nodeIndex, endContainerNode, endOffset, exceptionState) > 0 // starts after end
546 && compareBoundaryPoints(parentNode, nodeIndex + 1, endContainerNode, endOffset, exceptionState) > 0) { // ends after end
547 ASSERT(!exceptionState.hadException());
551 return true; // all other cases
554 static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot)
556 if (node == commonRoot)
559 ASSERT(commonRoot->contains(node));
561 while (node->parentNode() != commonRoot)
562 node = node->parentNode();
567 static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offset, Node* commonRoot)
572 if (!commonRoot->contains(container))
575 if (container == commonRoot) {
576 container = container->firstChild();
577 for (unsigned i = 0; container && i < offset; i++)
578 container = container->nextSibling();
580 while (container->parentNode() != commonRoot)
581 container = container->parentNode();
587 PassRefPtrWillBeRawPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionState& exceptionState)
589 typedef WillBeHeapVector<RefPtrWillBeMember<Node>> NodeVector;
591 RefPtrWillBeRawPtr<DocumentFragment> fragment = nullptr;
592 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
593 fragment = DocumentFragment::create(*m_ownerDocument.get());
596 return fragment.release();
598 RefPtrWillBeRawPtr<Node> commonRoot = commonAncestorContainer();
601 if (m_start.container() == m_end.container()) {
602 processContentsBetweenOffsets(action, fragment, m_start.container(), m_start.offset(), m_end.offset(), exceptionState);
606 // Since mutation observers can modify the range during the process, the boundary points need to be saved.
607 RangeBoundaryPoint originalStart(m_start);
608 RangeBoundaryPoint originalEnd(m_end);
610 // what is the highest node that partially selects the start / end of the range?
611 RefPtrWillBeRawPtr<Node> partialStart = highestAncestorUnderCommonRoot(originalStart.container(), commonRoot.get());
612 RefPtrWillBeRawPtr<Node> partialEnd = highestAncestorUnderCommonRoot(originalEnd.container(), commonRoot.get());
614 // Start and end containers are different.
615 // There are three possibilities here:
616 // 1. Start container == commonRoot (End container must be a descendant)
617 // 2. End container == commonRoot (Start container must be a descendant)
618 // 3. Neither is commonRoot, they are both descendants
620 // In case 3, we grab everything after the start (up until a direct child
621 // of commonRoot) into leftContents, and everything before the end (up until
622 // a direct child of commonRoot) into rightContents. Then we process all
623 // commonRoot children between leftContents and rightContents
625 // In case 1 or 2, we skip either processing of leftContents or rightContents,
626 // in which case the last lot of nodes either goes from the first or last
627 // child of commonRoot.
629 // These are deleted, cloned, or extracted (i.e. both) depending on action.
631 // Note that we are verifying that our common root hierarchy is still intact
632 // after any DOM mutation event, at various stages below. See webkit bug 60350.
634 RefPtrWillBeRawPtr<Node> leftContents = nullptr;
635 if (originalStart.container() != commonRoot && commonRoot->contains(originalStart.container())) {
636 leftContents = processContentsBetweenOffsets(action, nullptr, originalStart.container(), originalStart.offset(), originalStart.container()->lengthOfContents(), exceptionState);
637 leftContents = processAncestorsAndTheirSiblings(action, originalStart.container(), ProcessContentsForward, leftContents, commonRoot.get(), exceptionState);
640 RefPtrWillBeRawPtr<Node> rightContents = nullptr;
641 if (m_end.container() != commonRoot && commonRoot->contains(originalEnd.container())) {
642 rightContents = processContentsBetweenOffsets(action, nullptr, originalEnd.container(), 0, originalEnd.offset(), exceptionState);
643 rightContents = processAncestorsAndTheirSiblings(action, originalEnd.container(), ProcessContentsBackward, rightContents, commonRoot.get(), exceptionState);
646 // delete all children of commonRoot between the start and end container
647 RefPtrWillBeRawPtr<Node> processStart = childOfCommonRootBeforeOffset(originalStart.container(), originalStart.offset(), commonRoot.get());
648 if (processStart && originalStart.container() != commonRoot) // processStart contains nodes before m_start.
649 processStart = processStart->nextSibling();
650 RefPtrWillBeRawPtr<Node> processEnd = childOfCommonRootBeforeOffset(originalEnd.container(), originalEnd.offset(), commonRoot.get());
652 // Collapse the range, making sure that the result is not within a node that was partially selected.
653 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
654 if (partialStart && commonRoot->contains(partialStart.get())) {
655 // FIXME: We should not continue if we have an earlier error.
656 exceptionState.clearException();
657 setStart(partialStart->parentNode(), partialStart->nodeIndex() + 1, exceptionState);
658 } else if (partialEnd && commonRoot->contains(partialEnd.get())) {
659 // FIXME: We should not continue if we have an earlier error.
660 exceptionState.clearException();
661 setStart(partialEnd->parentNode(), partialEnd->nodeIndex(), exceptionState);
663 if (exceptionState.hadException())
668 originalStart.clear();
671 // Now add leftContents, stuff in between, and rightContents to the fragment
672 // (or just delete the stuff in between)
674 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents)
675 fragment->appendChild(leftContents, exceptionState);
679 for (Node* n = processStart.get(); n && n != processEnd; n = n->nextSibling())
681 processNodes(action, nodes, commonRoot, fragment, exceptionState);
684 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
685 fragment->appendChild(rightContents, exceptionState);
687 return fragment.release();
690 static inline void deleteCharacterData(PassRefPtrWillBeRawPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
692 if (data->length() - endOffset)
693 data->deleteData(endOffset, data->length() - endOffset, exceptionState);
695 data->deleteData(0, startOffset, exceptionState);
698 PassRefPtrWillBeRawPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtrWillBeRawPtr<DocumentFragment> fragment,
699 Node* container, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
702 ASSERT(startOffset <= endOffset);
704 // This switch statement must be consistent with that of Node::lengthOfContents.
705 RefPtrWillBeRawPtr<Node> result = nullptr;
706 switch (container->nodeType()) {
707 case Node::TEXT_NODE:
708 case Node::CDATA_SECTION_NODE:
709 case Node::COMMENT_NODE:
710 endOffset = std::min(endOffset, toCharacterData(container)->length());
711 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
712 RefPtrWillBeRawPtr<CharacterData> c = static_pointer_cast<CharacterData>(container->cloneNode(true));
713 deleteCharacterData(c, startOffset, endOffset, exceptionState);
716 result->appendChild(c.release(), exceptionState);
718 result = c.release();
720 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
721 toCharacterData(container)->deleteData(startOffset, endOffset - startOffset, exceptionState);
723 case Node::PROCESSING_INSTRUCTION_NODE:
724 endOffset = std::min(endOffset, toProcessingInstruction(container)->data().length());
725 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
726 RefPtrWillBeRawPtr<ProcessingInstruction> c = static_pointer_cast<ProcessingInstruction>(container->cloneNode(true));
727 c->setData(c->data().substring(startOffset, endOffset - startOffset));
730 result->appendChild(c.release(), exceptionState);
732 result = c.release();
734 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
735 ProcessingInstruction* pi = toProcessingInstruction(container);
736 String data(pi->data());
737 data.remove(startOffset, endOffset - startOffset);
741 case Node::ELEMENT_NODE:
742 case Node::ATTRIBUTE_NODE:
743 case Node::DOCUMENT_NODE:
744 case Node::DOCUMENT_TYPE_NODE:
745 case Node::DOCUMENT_FRAGMENT_NODE:
746 // FIXME: Should we assert that some nodes never appear here?
747 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
751 result = container->cloneNode(false);
754 Node* n = container->firstChild();
755 WillBeHeapVector<RefPtrWillBeMember<Node>> nodes;
756 for (unsigned i = startOffset; n && i; i--)
757 n = n->nextSibling();
758 for (unsigned i = startOffset; n && i < endOffset; i++, n = n->nextSibling())
761 processNodes(action, nodes, container, result, exceptionState);
765 return result.release();
768 void Range::processNodes(ActionType action, WillBeHeapVector<RefPtrWillBeMember<Node>>& nodes, PassRefPtrWillBeRawPtr<Node> oldContainer, PassRefPtrWillBeRawPtr<Node> newContainer, ExceptionState& exceptionState)
770 for (auto& node : nodes) {
772 case DELETE_CONTENTS:
773 oldContainer->removeChild(node.get(), exceptionState);
775 case EXTRACT_CONTENTS:
776 newContainer->appendChild(node.release(), exceptionState); // Will remove n from its parent.
779 newContainer->appendChild(node->cloneNode(true), exceptionState);
785 PassRefPtrWillBeRawPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtrWillBeRawPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionState& exceptionState)
787 typedef WillBeHeapVector<RefPtrWillBeMember<Node>> NodeVector;
789 RefPtrWillBeRawPtr<Node> clonedContainer = passedClonedContainer;
790 NodeVector ancestors;
791 for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode())
794 RefPtrWillBeRawPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling();
795 for (const RefPtrWillBeRawPtr<Node>& ancestor : ancestors) {
796 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
797 if (RefPtrWillBeRawPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event.
798 clonedAncestor->appendChild(clonedContainer, exceptionState);
799 clonedContainer = clonedAncestor;
803 // Copy siblings of an ancestor of start/end containers
804 // FIXME: This assertion may fail if DOM is modified during mutation event
805 // FIXME: Share code with Range::processNodes
806 ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor);
809 for (Node* child = firstChildInAncestorToProcess.get(); child;
810 child = (direction == ProcessContentsForward) ? child->nextSibling() : child->previousSibling())
813 for (const RefPtrWillBeRawPtr<Node>& node : nodes) {
814 Node* child = node.get();
816 case DELETE_CONTENTS:
817 // Prior call of ancestor->removeChild() may cause a tree change due to DOMSubtreeModified event.
818 // Therefore, we need to make sure |ancestor| is still |child|'s parent.
819 if (ancestor == child->parentNode())
820 ancestor->removeChild(child, exceptionState);
822 case EXTRACT_CONTENTS: // will remove child from ancestor
823 if (direction == ProcessContentsForward)
824 clonedContainer->appendChild(child, exceptionState);
826 clonedContainer->insertBefore(child, clonedContainer->firstChild(), exceptionState);
829 if (direction == ProcessContentsForward)
830 clonedContainer->appendChild(child->cloneNode(true), exceptionState);
832 clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), exceptionState);
836 firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling();
839 return clonedContainer.release();
842 PassRefPtrWillBeRawPtr<DocumentFragment> Range::extractContents(ExceptionState& exceptionState)
844 checkExtractPrecondition(exceptionState);
845 if (exceptionState.hadException())
848 return processContents(EXTRACT_CONTENTS, exceptionState);
851 PassRefPtrWillBeRawPtr<DocumentFragment> Range::cloneContents(ExceptionState& exceptionState)
853 return processContents(CLONE_CONTENTS, exceptionState);
856 void Range::insertNode(PassRefPtrWillBeRawPtr<Node> prpNewNode, ExceptionState& exceptionState)
858 RefPtrWillBeRawPtr<Node> newNode = prpNewNode;
861 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
865 // HierarchyRequestError: Raised if the container of the start of the Range is of a type that
866 // does not allow children of the type of newNode or if newNode is an ancestor of the container.
868 // an extra one here - if a text node is going to split, it must have a parent to insert into
869 bool startIsText = m_start.container()->isTextNode();
870 if (startIsText && !m_start.container()->parentNode()) {
871 exceptionState.throwDOMException(HierarchyRequestError, "This operation would split a text node, but there's no parent into which to insert.");
875 // In the case where the container is a text node, we check against the container's parent, because
876 // text nodes get split up upon insertion.
879 checkAgainst = m_start.container()->parentNode();
881 checkAgainst = m_start.container();
883 Node::NodeType newNodeType = newNode->nodeType();
885 if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE && !newNode->isShadowRoot()) {
886 // check each child node, not the DocumentFragment itself
888 for (Node* c = toDocumentFragment(newNode)->firstChild(); c; c = c->nextSibling()) {
889 if (!checkAgainst->childTypeAllowed(c->nodeType())) {
890 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains a '" + c->nodeName() + "' node, which may not be inserted here.");
897 if (!checkAgainst->childTypeAllowed(newNodeType)) {
898 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
903 for (Node* n = m_start.container(); n; n = n->parentNode()) {
905 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains the insertion point; it may not be inserted into itself.");
910 // InvalidNodeTypeError: Raised if newNode is an Attr, Entity, Notation, ShadowRoot or Document node.
911 switch (newNodeType) {
912 case Node::ATTRIBUTE_NODE:
913 case Node::DOCUMENT_NODE:
914 exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
917 if (newNode->isShadowRoot()) {
918 exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a shadow root, which may not be inserted here.");
924 EventQueueScope scope;
925 bool collapsed = m_start == m_end;
926 RefPtrWillBeRawPtr<Node> container = nullptr;
928 container = m_start.container();
929 RefPtrWillBeRawPtr<Text> newText = toText(container)->splitText(m_start.offset(), exceptionState);
930 if (exceptionState.hadException())
933 container = m_start.container();
934 container->parentNode()->insertBefore(newNode.release(), newText.get(), exceptionState);
935 if (exceptionState.hadException())
939 // The load event would be fired regardless of EventQueueScope;
940 // e.g. by ContainerNode::updateTreeAfterInsertion
941 // Given circumstance may mutate the tree so newText->parentNode() may become null
942 if (!newText->parentNode()) {
943 exceptionState.throwDOMException(HierarchyRequestError, "This operation would set range's end to parent with new offset, but there's no parent into which to continue.");
946 m_end.setToBeforeChild(*newText);
949 RefPtrWillBeRawPtr<Node> lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->lastChild() : newNode.get();
950 if (lastChild && lastChild == m_start.childBefore()) {
951 // The insertion will do nothing, but we need to extend the range to include
952 // the inserted nodes.
953 Node* firstChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->firstChild() : newNode.get();
955 m_start.setToBeforeChild(*firstChild);
959 container = m_start.container();
960 container->insertBefore(newNode.release(), NodeTraversal::childAt(*container, m_start.offset()), exceptionState);
961 if (exceptionState.hadException())
964 // Note that m_start.offset() may have changed as a result of container->insertBefore,
965 // when the node we are inserting comes before the range in the same container.
966 if (collapsed && numNewChildren)
967 m_end.set(m_start.container(), m_start.offset() + numNewChildren, lastChild.get());
971 String Range::toString() const
973 StringBuilder builder;
975 Node* pastLast = pastLastNode();
976 for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
977 Node::NodeType type = n->nodeType();
978 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
979 String data = toCharacterData(n)->data();
980 int length = data.length();
981 int start = (n == m_start.container()) ? std::min(std::max(0, m_start.offset()), length) : 0;
982 int end = (n == m_end.container()) ? std::min(std::max(start, m_end.offset()), length) : length;
983 builder.append(data, start, end - start);
987 return builder.toString();
990 String Range::toHTML() const
992 return createMarkup(this);
995 String Range::text() const
997 return plainText(this, TextIteratorEmitsObjectReplacementCharacter);
1000 PassRefPtrWillBeRawPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionState& exceptionState)
1002 // Algorithm: http://domparsing.spec.whatwg.org/#extensions-to-the-range-interface
1004 Node* node = m_start.container();
1007 RefPtrWillBeRawPtr<Element> element;
1008 if (!m_start.offset() && (node->isDocumentNode() || node->isDocumentFragment()))
1010 else if (node->isElementNode())
1011 element = toElement(node);
1013 element = node->parentElement();
1016 if (!element || isHTMLHtmlElement(element)) {
1017 Document& document = node->document();
1019 if (document.isHTMLDocument() || document.isXHTMLDocument()) {
1020 // Optimization over spec: try to reuse the existing <body> element, if it is available.
1021 element = document.body();
1023 element = HTMLBodyElement::create(document);
1024 } else if (document.isSVGDocument()) {
1025 element = document.documentElement();
1027 element = SVGSVGElement::create(document);
1031 if (!element || (!element->isHTMLElement() && !element->isSVGElement())) {
1032 exceptionState.throwDOMException(NotSupportedError, "The range's container must be an HTML or SVG Element, Document, or DocumentFragment.");
1037 RefPtrWillBeRawPtr<DocumentFragment> fragment = blink::createContextualFragment(markup, element.get(), AllowScriptingContentAndDoNotMarkAlreadyStarted, exceptionState);
1041 return fragment.release();
1045 void Range::detach()
1047 // This is now a no-op as per the DOM specification.
1050 Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionState& exceptionState) const
1052 switch (n->nodeType()) {
1053 case Node::DOCUMENT_TYPE_NODE:
1054 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1056 case Node::CDATA_SECTION_NODE:
1057 case Node::COMMENT_NODE:
1058 case Node::TEXT_NODE:
1059 if (static_cast<unsigned>(offset) > toCharacterData(n)->length())
1060 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than or equal to the node's length (" + String::number(toCharacterData(n)->length()) + ").");
1062 case Node::PROCESSING_INSTRUCTION_NODE:
1063 if (static_cast<unsigned>(offset) > toProcessingInstruction(n)->data().length())
1064 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()) + ").");
1066 case Node::ATTRIBUTE_NODE:
1067 case Node::DOCUMENT_FRAGMENT_NODE:
1068 case Node::DOCUMENT_NODE:
1069 case Node::ELEMENT_NODE: {
1072 Node* childBefore = NodeTraversal::childAt(*n, offset - 1);
1074 exceptionState.throwDOMException(IndexSizeError, "There is no child at offset " + String::number(offset) + ".");
1078 ASSERT_NOT_REACHED();
1082 void Range::checkNodeBA(Node* n, ExceptionState& exceptionState) const
1085 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1089 // InvalidNodeTypeError: Raised if the root container of refNode is not an
1090 // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG shadow DOM tree,
1091 // or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation node.
1093 if (!n->parentNode()) {
1094 exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1098 switch (n->nodeType()) {
1099 case Node::ATTRIBUTE_NODE:
1100 case Node::DOCUMENT_FRAGMENT_NODE:
1101 case Node::DOCUMENT_NODE:
1102 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1104 case Node::CDATA_SECTION_NODE:
1105 case Node::COMMENT_NODE:
1106 case Node::DOCUMENT_TYPE_NODE:
1107 case Node::ELEMENT_NODE:
1108 case Node::PROCESSING_INSTRUCTION_NODE:
1109 case Node::TEXT_NODE:
1114 while (ContainerNode* parent = root->parentNode())
1117 switch (root->nodeType()) {
1118 case Node::ATTRIBUTE_NODE:
1119 case Node::DOCUMENT_NODE:
1120 case Node::DOCUMENT_FRAGMENT_NODE:
1121 case Node::ELEMENT_NODE:
1123 case Node::CDATA_SECTION_NODE:
1124 case Node::COMMENT_NODE:
1125 case Node::DOCUMENT_TYPE_NODE:
1126 case Node::PROCESSING_INSTRUCTION_NODE:
1127 case Node::TEXT_NODE:
1128 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1133 PassRefPtrWillBeRawPtr<Range> Range::cloneRange() const
1135 return Range::create(*m_ownerDocument.get(), m_start.container(), m_start.offset(), m_end.container(), m_end.offset());
1138 void Range::setStartAfter(Node* refNode, ExceptionState& exceptionState)
1140 checkNodeBA(refNode, exceptionState);
1141 if (exceptionState.hadException())
1144 setStart(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1147 void Range::setEndBefore(Node* refNode, ExceptionState& exceptionState)
1149 checkNodeBA(refNode, exceptionState);
1150 if (exceptionState.hadException())
1153 setEnd(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1156 void Range::setEndAfter(Node* refNode, ExceptionState& exceptionState)
1158 checkNodeBA(refNode, exceptionState);
1159 if (exceptionState.hadException())
1162 setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1165 void Range::selectNode(Node* refNode, ExceptionState& exceptionState)
1168 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1172 if (!refNode->parentNode()) {
1173 exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1177 // InvalidNodeTypeError: Raised if an ancestor of refNode is an Entity, Notation or
1178 // DocumentType node or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation
1180 for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
1181 switch (anc->nodeType()) {
1182 case Node::ATTRIBUTE_NODE:
1183 case Node::CDATA_SECTION_NODE:
1184 case Node::COMMENT_NODE:
1185 case Node::DOCUMENT_FRAGMENT_NODE:
1186 case Node::DOCUMENT_NODE:
1187 case Node::ELEMENT_NODE:
1188 case Node::PROCESSING_INSTRUCTION_NODE:
1189 case Node::TEXT_NODE:
1191 case Node::DOCUMENT_TYPE_NODE:
1192 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided has an ancestor of type '" + anc->nodeName() + "'.");
1197 switch (refNode->nodeType()) {
1198 case Node::CDATA_SECTION_NODE:
1199 case Node::COMMENT_NODE:
1200 case Node::DOCUMENT_TYPE_NODE:
1201 case Node::ELEMENT_NODE:
1202 case Node::PROCESSING_INSTRUCTION_NODE:
1203 case Node::TEXT_NODE:
1205 case Node::ATTRIBUTE_NODE:
1206 case Node::DOCUMENT_FRAGMENT_NODE:
1207 case Node::DOCUMENT_NODE:
1208 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1212 if (m_ownerDocument != refNode->document())
1213 setDocument(refNode->document());
1215 setStartBefore(refNode);
1216 setEndAfter(refNode);
1219 void Range::selectNodeContents(Node* refNode, ExceptionState& exceptionState)
1222 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1226 // InvalidNodeTypeError: Raised if refNode or an ancestor of refNode is an Entity, Notation
1227 // or DocumentType node.
1228 for (Node* n = refNode; n; n = n->parentNode()) {
1229 switch (n->nodeType()) {
1230 case Node::ATTRIBUTE_NODE:
1231 case Node::CDATA_SECTION_NODE:
1232 case Node::COMMENT_NODE:
1233 case Node::DOCUMENT_FRAGMENT_NODE:
1234 case Node::DOCUMENT_NODE:
1235 case Node::ELEMENT_NODE:
1236 case Node::PROCESSING_INSTRUCTION_NODE:
1237 case Node::TEXT_NODE:
1239 case Node::DOCUMENT_TYPE_NODE:
1240 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1245 if (m_ownerDocument != refNode->document())
1246 setDocument(refNode->document());
1248 m_start.setToStartOfNode(*refNode);
1249 m_end.setToEndOfNode(*refNode);
1252 bool Range::selectNodeContents(Node* refNode, Position& start, Position& end)
1258 for (Node* n = refNode; n; n = n->parentNode()) {
1259 switch (n->nodeType()) {
1260 case Node::ATTRIBUTE_NODE:
1261 case Node::CDATA_SECTION_NODE:
1262 case Node::COMMENT_NODE:
1263 case Node::DOCUMENT_FRAGMENT_NODE:
1264 case Node::DOCUMENT_NODE:
1265 case Node::ELEMENT_NODE:
1266 case Node::PROCESSING_INSTRUCTION_NODE:
1267 case Node::TEXT_NODE:
1269 case Node::DOCUMENT_TYPE_NODE:
1274 RangeBoundaryPoint startBoundaryPoint(refNode);
1275 startBoundaryPoint.setToStartOfNode(*refNode);
1276 start = startBoundaryPoint.toPosition();
1277 RangeBoundaryPoint endBoundaryPoint(refNode);
1278 endBoundaryPoint.setToEndOfNode(*refNode);
1279 end = endBoundaryPoint.toPosition();
1283 void Range::surroundContents(PassRefPtrWillBeRawPtr<Node> passNewParent, ExceptionState& exceptionState)
1285 RefPtrWillBeRawPtr<Node> newParent = passNewParent;
1287 exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1291 // InvalidStateError: Raised if the Range partially selects a non-Text node.
1292 Node* startNonTextContainer = m_start.container();
1293 if (startNonTextContainer->nodeType() == Node::TEXT_NODE)
1294 startNonTextContainer = startNonTextContainer->parentNode();
1295 Node* endNonTextContainer = m_end.container();
1296 if (endNonTextContainer->nodeType() == Node::TEXT_NODE)
1297 endNonTextContainer = endNonTextContainer->parentNode();
1298 if (startNonTextContainer != endNonTextContainer) {
1299 exceptionState.throwDOMException(InvalidStateError, "The Range has partially selected a non-Text node.");
1303 // InvalidNodeTypeError: Raised if node is an Attr, Entity, DocumentType, Notation,
1304 // Document, or DocumentFragment node.
1305 switch (newParent->nodeType()) {
1306 case Node::ATTRIBUTE_NODE:
1307 case Node::DOCUMENT_FRAGMENT_NODE:
1308 case Node::DOCUMENT_NODE:
1309 case Node::DOCUMENT_TYPE_NODE:
1310 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + newParent->nodeName() + "'.");
1312 case Node::CDATA_SECTION_NODE:
1313 case Node::COMMENT_NODE:
1314 case Node::ELEMENT_NODE:
1315 case Node::PROCESSING_INSTRUCTION_NODE:
1316 case Node::TEXT_NODE:
1320 // Raise a HierarchyRequestError if m_start.container() doesn't accept children like newParent.
1321 Node* parentOfNewParent = m_start.container();
1323 // If m_start.container() is a character data node, it will be split and it will be its parent that will
1324 // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent,
1325 // although this will fail below for another reason).
1326 if (parentOfNewParent->isCharacterDataNode())
1327 parentOfNewParent = parentOfNewParent->parentNode();
1329 if (!parentOfNewParent) {
1330 exceptionState.throwDOMException(HierarchyRequestError, "The container node is a detached character data node; no parent node is available for insertion.");
1334 if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
1335 exceptionState.throwDOMException(HierarchyRequestError, "The node provided is of type '" + newParent->nodeName() + "', which may not be inserted here.");
1339 if (newParent->contains(m_start.container())) {
1340 exceptionState.throwDOMException(HierarchyRequestError, "The node provided contains the insertion point; it may not be inserted into itself.");
1344 // FIXME: Do we need a check if the node would end up with a child node of a type not
1345 // allowed by the type of node?
1347 while (Node* n = newParent->firstChild()) {
1348 toContainerNode(newParent)->removeChild(n, exceptionState);
1349 if (exceptionState.hadException())
1352 RefPtrWillBeRawPtr<DocumentFragment> fragment = extractContents(exceptionState);
1353 if (exceptionState.hadException())
1355 insertNode(newParent, exceptionState);
1356 if (exceptionState.hadException())
1358 newParent->appendChild(fragment.release(), exceptionState);
1359 if (exceptionState.hadException())
1361 selectNode(newParent.get(), exceptionState);
1364 void Range::setStartBefore(Node* refNode, ExceptionState& exceptionState)
1366 checkNodeBA(refNode, exceptionState);
1367 if (exceptionState.hadException())
1370 setStart(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1373 void Range::checkExtractPrecondition(ExceptionState& exceptionState)
1375 ASSERT(boundaryPointsValid());
1377 if (!commonAncestorContainer())
1380 Node* pastLast = pastLastNode();
1381 for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
1382 if (n->isDocumentTypeNode()) {
1383 exceptionState.throwDOMException(HierarchyRequestError, "The Range contains a doctype node.");
1389 Node* Range::firstNode() const
1391 if (m_start.container()->offsetInCharacters())
1392 return m_start.container();
1393 if (Node* child = NodeTraversal::childAt(*m_start.container(), m_start.offset()))
1395 if (!m_start.offset())
1396 return m_start.container();
1397 return NodeTraversal::nextSkippingChildren(*m_start.container());
1400 ShadowRoot* Range::shadowRoot() const
1402 return startContainer() ? startContainer()->containingShadowRoot() : nullptr;
1405 Node* Range::pastLastNode() const
1407 if (m_end.container()->offsetInCharacters())
1408 return NodeTraversal::nextSkippingChildren(*m_end.container());
1409 if (Node* child = NodeTraversal::childAt(*m_end.container(), m_end.offset()))
1411 return NodeTraversal::nextSkippingChildren(*m_end.container());
1414 IntRect Range::boundingBox() const
1417 Vector<IntRect> rects;
1419 for (const IntRect& rect : rects)
1424 void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1426 Node* startContainer = m_start.container();
1427 ASSERT(startContainer);
1428 Node* endContainer = m_end.container();
1429 ASSERT(endContainer);
1431 bool allFixed = true;
1432 bool someFixed = false;
1434 Node* stopNode = pastLastNode();
1435 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1436 RenderObject* r = node->renderer();
1437 if (!r || !r->isText())
1439 RenderText* renderText = toRenderText(r);
1440 int startOffset = node == startContainer ? m_start.offset() : 0;
1441 int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1442 bool isFixed = false;
1443 renderText->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight, &isFixed);
1444 allFixed &= isFixed;
1445 someFixed |= isFixed;
1449 *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1452 void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1454 Node* startContainer = m_start.container();
1455 ASSERT(startContainer);
1456 Node* endContainer = m_end.container();
1457 ASSERT(endContainer);
1459 bool allFixed = true;
1460 bool someFixed = false;
1462 Node* stopNode = pastLastNode();
1463 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1464 RenderObject* r = node->renderer();
1465 if (!r || !r->isText())
1467 RenderText* renderText = toRenderText(r);
1468 int startOffset = node == startContainer ? m_start.offset() : 0;
1469 int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1470 bool isFixed = false;
1471 renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight, &isFixed);
1472 allFixed &= isFixed;
1473 someFixed |= isFixed;
1477 *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1481 void Range::formatForDebugger(char* buffer, unsigned length) const
1483 StringBuilder result;
1485 const int FormatBufferSize = 1024;
1486 char s[FormatBufferSize];
1487 result.appendLiteral("from offset ");
1488 result.appendNumber(m_start.offset());
1489 result.appendLiteral(" of ");
1490 m_start.container()->formatForDebugger(s, FormatBufferSize);
1492 result.appendLiteral(" to offset ");
1493 result.appendNumber(m_end.offset());
1494 result.appendLiteral(" of ");
1495 m_end.container()->formatForDebugger(s, FormatBufferSize);
1498 strncpy(buffer, result.toString().utf8().data(), length - 1);
1502 bool areRangesEqual(const Range* a, const Range* b)
1508 return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition();
1511 PassRefPtrWillBeRawPtr<Range> rangeOfContents(Node* node)
1514 RefPtrWillBeRawPtr<Range> range = Range::create(node->document());
1515 range->selectNodeContents(node, IGNORE_EXCEPTION);
1516 return range.release();
1519 static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container)
1521 if (!boundary.childBefore())
1523 if (boundary.container() != container)
1525 boundary.invalidateOffset();
1528 void Range::nodeChildrenChanged(ContainerNode* container)
1531 ASSERT(container->document() == m_ownerDocument);
1532 boundaryNodeChildrenChanged(m_start, container);
1533 boundaryNodeChildrenChanged(m_end, container);
1536 static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode& container)
1538 for (Node* nodeToBeRemoved = container.firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) {
1539 if (boundary.childBefore() == nodeToBeRemoved) {
1540 boundary.setToStartOfNode(container);
1544 for (Node* n = boundary.container(); n; n = n->parentNode()) {
1545 if (n == nodeToBeRemoved) {
1546 boundary.setToStartOfNode(container);
1553 void Range::nodeChildrenWillBeRemoved(ContainerNode& container)
1555 ASSERT(container.document() == m_ownerDocument);
1556 boundaryNodeChildrenWillBeRemoved(m_start, container);
1557 boundaryNodeChildrenWillBeRemoved(m_end, container);
1560 static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node& nodeToBeRemoved)
1562 if (boundary.childBefore() == nodeToBeRemoved) {
1563 boundary.childBeforeWillBeRemoved();
1567 for (Node* n = boundary.container(); n; n = n->parentNode()) {
1568 if (n == nodeToBeRemoved) {
1569 boundary.setToBeforeChild(nodeToBeRemoved);
1575 void Range::nodeWillBeRemoved(Node& node)
1577 ASSERT(node.document() == m_ownerDocument);
1578 ASSERT(node != m_ownerDocument.get());
1580 // FIXME: Once DOMNodeRemovedFromDocument mutation event removed, we
1581 // should change following if-statement to ASSERT(!node->parentNode).
1582 if (!node.parentNode())
1584 boundaryNodeWillBeRemoved(m_start, node);
1585 boundaryNodeWillBeRemoved(m_end, node);
1588 static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1590 if (boundary.container() != text)
1592 unsigned boundaryOffset = boundary.offset();
1593 if (offset >= boundaryOffset)
1595 boundary.setOffset(boundaryOffset + length);
1598 void Range::didInsertText(Node* text, unsigned offset, unsigned length)
1601 ASSERT(text->document() == m_ownerDocument);
1602 boundaryTextInserted(m_start, text, offset, length);
1603 boundaryTextInserted(m_end, text, offset, length);
1606 static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1608 if (boundary.container() != text)
1610 unsigned boundaryOffset = boundary.offset();
1611 if (offset >= boundaryOffset)
1613 if (offset + length >= boundaryOffset)
1614 boundary.setOffset(offset);
1616 boundary.setOffset(boundaryOffset - length);
1619 void Range::didRemoveText(Node* text, unsigned offset, unsigned length)
1622 ASSERT(text->document() == m_ownerDocument);
1623 boundaryTextRemoved(m_start, text, offset, length);
1624 boundaryTextRemoved(m_end, text, offset, length);
1627 static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, const NodeWithIndex& oldNode, unsigned offset)
1629 if (boundary.container() == oldNode.node())
1630 boundary.set(oldNode.node().previousSibling(), boundary.offset() + offset, 0);
1631 else if (boundary.container() == oldNode.node().parentNode() && boundary.offset() == oldNode.index())
1632 boundary.set(oldNode.node().previousSibling(), offset, 0);
1635 void Range::didMergeTextNodes(const NodeWithIndex& oldNode, unsigned offset)
1637 ASSERT(oldNode.node().document() == m_ownerDocument);
1638 ASSERT(oldNode.node().parentNode());
1639 ASSERT(oldNode.node().isTextNode());
1640 ASSERT(oldNode.node().previousSibling());
1641 ASSERT(oldNode.node().previousSibling()->isTextNode());
1642 boundaryTextNodesMerged(m_start, oldNode, offset);
1643 boundaryTextNodesMerged(m_end, oldNode, offset);
1646 void Range::updateOwnerDocumentIfNeeded()
1648 ASSERT(m_start.container());
1649 ASSERT(m_end.container());
1650 Document& newDocument = m_start.container()->document();
1651 ASSERT(newDocument == m_end.container()->document());
1652 if (newDocument == m_ownerDocument)
1654 m_ownerDocument->detachRange(this);
1655 m_ownerDocument = &newDocument;
1656 m_ownerDocument->attachRange(this);
1659 static inline void boundaryTextNodeSplit(RangeBoundaryPoint& boundary, Text& oldNode)
1661 Node* boundaryContainer = boundary.container();
1662 unsigned boundaryOffset = boundary.offset();
1663 if (boundary.childBefore() == &oldNode)
1664 boundary.set(boundaryContainer, boundaryOffset + 1, oldNode.nextSibling());
1665 else if (boundary.container() == &oldNode && boundaryOffset > oldNode.length())
1666 boundary.set(oldNode.nextSibling(), boundaryOffset - oldNode.length(), 0);
1669 void Range::didSplitTextNode(Text& oldNode)
1671 ASSERT(oldNode.document() == m_ownerDocument);
1672 ASSERT(oldNode.parentNode());
1673 ASSERT(oldNode.nextSibling());
1674 ASSERT(oldNode.nextSibling()->isTextNode());
1675 boundaryTextNodeSplit(m_start, oldNode);
1676 boundaryTextNodeSplit(m_end, oldNode);
1677 ASSERT(boundaryPointsValid());
1680 void Range::expand(const String& unit, ExceptionState& exceptionState)
1682 VisiblePosition start(startPosition());
1683 VisiblePosition end(endPosition());
1684 if (unit == "word") {
1685 start = startOfWord(start);
1686 end = endOfWord(end);
1687 } else if (unit == "sentence") {
1688 start = startOfSentence(start);
1689 end = endOfSentence(end);
1690 } else if (unit == "block") {
1691 start = startOfParagraph(start);
1692 end = endOfParagraph(end);
1693 } else if (unit == "document") {
1694 start = startOfDocument(start);
1695 end = endOfDocument(end);
1698 setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1699 setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1702 PassRefPtrWillBeRawPtr<ClientRectList> Range::getClientRects() const
1704 m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1706 Vector<FloatQuad> quads;
1707 getBorderAndTextQuads(quads);
1709 return ClientRectList::create(quads);
1712 PassRefPtrWillBeRawPtr<ClientRect> Range::getBoundingClientRect() const
1714 return ClientRect::create(boundingRect());
1717 void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
1719 Node* startContainer = m_start.container();
1720 Node* endContainer = m_end.container();
1721 Node* stopNode = pastLastNode();
1723 WillBeHeapHashSet<RawPtrWillBeMember<Node>> nodeSet;
1724 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1725 if (node->isElementNode())
1729 for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1730 if (node->isElementNode()) {
1731 if (!nodeSet.contains(node->parentNode())) {
1732 if (RenderBoxModelObject* renderBoxModelObject = toElement(node)->renderBoxModelObject()) {
1733 Vector<FloatQuad> elementQuads;
1734 renderBoxModelObject->absoluteQuads(elementQuads);
1735 m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(elementQuads, *renderBoxModelObject);
1737 quads.appendVector(elementQuads);
1740 } else if (node->isTextNode()) {
1741 if (RenderText* renderText = toText(node)->renderer()) {
1742 int startOffset = (node == startContainer) ? m_start.offset() : 0;
1743 int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX;
1745 Vector<FloatQuad> textQuads;
1746 renderText->absoluteQuadsForRange(textQuads, startOffset, endOffset);
1747 m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(textQuads, *renderText);
1749 quads.appendVector(textQuads);
1755 FloatRect Range::boundingRect() const
1757 m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1759 Vector<FloatQuad> quads;
1760 getBorderAndTextQuads(quads);
1763 for (const FloatQuad& quad : quads)
1764 result.unite(quad.boundingBox());
1769 void Range::trace(Visitor* visitor)
1771 visitor->trace(m_ownerDocument);
1772 visitor->trace(m_start);
1773 visitor->trace(m_end);
1776 } // namespace blink
1780 void showTree(const blink::Range* range)
1782 if (range && range->boundaryPointsValid()) {
1783 range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E");
1784 fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset());