• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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.
8  *
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.
13  *
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.
18  *
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.
23  */
24 
25 #include "config.h"
26 #include "core/dom/Range.h"
27 
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"
52 #ifndef NDEBUG
53 #include <stdio.h>
54 #endif
55 
56 namespace blink {
57 
58 DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, rangeCounter, ("Range"));
59 
Range(Document & ownerDocument)60 inline Range::Range(Document& ownerDocument)
61     : m_ownerDocument(&ownerDocument)
62     , m_start(m_ownerDocument)
63     , m_end(m_ownerDocument)
64 {
65 #ifndef NDEBUG
66     rangeCounter.increment();
67 #endif
68 
69     m_ownerDocument->attachRange(this);
70 }
71 
create(Document & ownerDocument)72 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument)
73 {
74     return adoptRefWillBeNoop(new Range(ownerDocument));
75 }
76 
Range(Document & ownerDocument,Node * startContainer,int startOffset,Node * endContainer,int endOffset)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)
81 {
82 #ifndef NDEBUG
83     rangeCounter.increment();
84 #endif
85 
86     m_ownerDocument->attachRange(this);
87 
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);
92 }
93 
create(Document & ownerDocument,Node * startContainer,int startOffset,Node * endContainer,int endOffset)94 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, Node* startContainer, int startOffset, Node* endContainer, int endOffset)
95 {
96     return adoptRefWillBeNoop(new Range(ownerDocument, startContainer, startOffset, endContainer, endOffset));
97 }
98 
create(Document & ownerDocument,const Position & start,const Position & end)99 PassRefPtrWillBeRawPtr<Range> Range::create(Document& ownerDocument, const Position& start, const Position& end)
100 {
101     return adoptRefWillBeNoop(new Range(ownerDocument, start.containerNode(), start.computeOffsetInContainerNode(), end.containerNode(), end.computeOffsetInContainerNode()));
102 }
103 
~Range()104 Range::~Range()
105 {
106 #if !ENABLE(OILPAN)
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);
109 #endif
110 
111 #ifndef NDEBUG
112     rangeCounter.decrement();
113 #endif
114 }
115 
setDocument(Document & document)116 void Range::setDocument(Document& document)
117 {
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);
125 }
126 
commonAncestorContainer() const127 Node* Range::commonAncestorContainer() const
128 {
129     return commonAncestorContainer(m_start.container(), m_end.container());
130 }
131 
commonAncestorContainer(Node * containerA,Node * containerB)132 Node* Range::commonAncestorContainer(Node* containerA, Node* containerB)
133 {
134     for (Node* parentA = containerA; parentA; parentA = parentA->parentNode()) {
135         for (Node* parentB = containerB; parentB; parentB = parentB->parentNode()) {
136             if (parentA == parentB)
137                 return parentA;
138         }
139     }
140     return 0;
141 }
142 
checkForDifferentRootContainer(const RangeBoundaryPoint & start,const RangeBoundaryPoint & end)143 static inline bool checkForDifferentRootContainer(const RangeBoundaryPoint& start, const RangeBoundaryPoint& end)
144 {
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();
151 
152     return startRootContainer != endRootContainer || (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0);
153 }
154 
setStart(PassRefPtrWillBeRawPtr<Node> refNode,int offset,ExceptionState & exceptionState)155 void Range::setStart(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
156 {
157     if (!refNode) {
158         exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
159         return;
160     }
161 
162     bool didMoveDocument = false;
163     if (refNode->document() != m_ownerDocument) {
164         setDocument(refNode->document());
165         didMoveDocument = true;
166     }
167 
168     Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
169     if (exceptionState.hadException())
170         return;
171 
172     m_start.set(refNode, offset, childNode);
173 
174     if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
175         collapse(true);
176 }
177 
setEnd(PassRefPtrWillBeRawPtr<Node> refNode,int offset,ExceptionState & exceptionState)178 void Range::setEnd(PassRefPtrWillBeRawPtr<Node> refNode, int offset, ExceptionState& exceptionState)
179 {
180     if (!refNode) {
181         exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
182         return;
183     }
184 
185     bool didMoveDocument = false;
186     if (refNode->document() != m_ownerDocument) {
187         setDocument(refNode->document());
188         didMoveDocument = true;
189     }
190 
191     Node* childNode = checkNodeWOffset(refNode.get(), offset, exceptionState);
192     if (exceptionState.hadException())
193         return;
194 
195     m_end.set(refNode, offset, childNode);
196 
197     if (didMoveDocument || checkForDifferentRootContainer(m_start, m_end))
198         collapse(false);
199 }
200 
setStart(const Position & start,ExceptionState & exceptionState)201 void Range::setStart(const Position& start, ExceptionState& exceptionState)
202 {
203     Position parentAnchored = start.parentAnchoredEquivalent();
204     setStart(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
205 }
206 
setEnd(const Position & end,ExceptionState & exceptionState)207 void Range::setEnd(const Position& end, ExceptionState& exceptionState)
208 {
209     Position parentAnchored = end.parentAnchoredEquivalent();
210     setEnd(parentAnchored.containerNode(), parentAnchored.offsetInContainerNode(), exceptionState);
211 }
212 
collapse(bool toStart)213 void Range::collapse(bool toStart)
214 {
215     if (toStart)
216         m_end = m_start;
217     else
218         m_start = m_end;
219 }
220 
isPointInRange(Node * refNode,int offset,ExceptionState & exceptionState)221 bool Range::isPointInRange(Node* refNode, int offset, ExceptionState& exceptionState)
222 {
223     if (!refNode) {
224         exceptionState.throwDOMException(HierarchyRequestError, "The node provided was null.");
225         return false;
226     }
227 
228     if (!refNode->inActiveDocument() || refNode->document() != m_ownerDocument) {
229         return false;
230     }
231 
232     checkNodeWOffset(refNode, offset, exceptionState);
233     if (exceptionState.hadException())
234         return false;
235 
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();
238 }
239 
comparePoint(Node * refNode,int offset,ExceptionState & exceptionState) const240 short Range::comparePoint(Node* refNode, int offset, ExceptionState& exceptionState) const
241 {
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.
245 
246     if (!refNode->inActiveDocument()) {
247         exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in an active document.");
248         return 0;
249     }
250 
251     if (refNode->document() != m_ownerDocument) {
252         exceptionState.throwDOMException(WrongDocumentError, "The node provided is not in this Range's Document.");
253         return 0;
254     }
255 
256     checkNodeWOffset(refNode, offset, exceptionState);
257     if (exceptionState.hadException())
258         return 0;
259 
260     // compare to start, and point comes before
261     if (compareBoundaryPoints(refNode, offset, m_start.container(), m_start.offset(), exceptionState) < 0)
262         return -1;
263 
264     if (exceptionState.hadException())
265         return 0;
266 
267     // compare to end, and point comes after
268     if (compareBoundaryPoints(refNode, offset, m_end.container(), m_end.offset(), exceptionState) > 0 && !exceptionState.hadException())
269         return 1;
270 
271     // point is in the middle of this range, or on the boundary points
272     return 0;
273 }
274 
compareNode(Node * refNode,ExceptionState & exceptionState) const275 Range::CompareResults Range::compareNode(Node* refNode, ExceptionState& exceptionState) const
276 {
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
280 
281     if (!refNode) {
282         exceptionState.throwDOMException(NotFoundError, "The node provided was null.");
283         return NODE_BEFORE;
284     }
285 
286     if (!refNode->inActiveDocument()) {
287         // Firefox doesn't throw an exception for this case; it returns 0.
288         return NODE_BEFORE;
289     }
290 
291     if (refNode->document() != m_ownerDocument) {
292         // Firefox doesn't throw an exception for this case; it returns 0.
293         return NODE_BEFORE;
294     }
295 
296     ContainerNode* parentNode = refNode->parentNode();
297     int nodeIndex = refNode->nodeIndex();
298 
299     if (!parentNode) {
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.");
303         return NODE_BEFORE;
304     }
305 
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
310     }
311     // starts at or after the range start
312     if (comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) // ends after the range
313         return NODE_AFTER;
314     return NODE_INSIDE; // ends inside the range
315 }
316 
compareBoundaryPoints(unsigned how,const Range * sourceRange,ExceptionState & exceptionState) const317 short Range::compareBoundaryPoints(unsigned how, const Range* sourceRange, ExceptionState& exceptionState) const
318 {
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'.");
321         return 0;
322     }
323 
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.");
328         return 0;
329     }
330 
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.");
339         return 0;
340     }
341 
342     switch (how) {
343         case START_TO_START:
344             return compareBoundaryPoints(m_start, sourceRange->m_start, exceptionState);
345         case START_TO_END:
346             return compareBoundaryPoints(m_end, sourceRange->m_start, exceptionState);
347         case END_TO_END:
348             return compareBoundaryPoints(m_end, sourceRange->m_end, exceptionState);
349         case END_TO_START:
350             return compareBoundaryPoints(m_start, sourceRange->m_end, exceptionState);
351     }
352 
353     ASSERT_NOT_REACHED();
354     return 0;
355 }
356 
compareBoundaryPoints(Node * containerA,int offsetA,Node * containerB,int offsetB,ExceptionState & exceptionState)357 short Range::compareBoundaryPoints(Node* containerA, int offsetA, Node* containerB, int offsetB, ExceptionState& exceptionState)
358 {
359     ASSERT(containerA);
360     ASSERT(containerB);
361 
362     if (!containerA)
363         return -1;
364     if (!containerB)
365         return 1;
366 
367     // see DOM2 traversal & range section 2.5
368 
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
375         else
376             return 1;           // A is after B
377     }
378 
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)
382         c = c->parentNode();
383     if (c) {
384         int offsetC = 0;
385         Node* n = containerA->firstChild();
386         while (n != c && offsetC < offsetA) {
387             offsetC++;
388             n = n->nextSibling();
389         }
390 
391         if (offsetA <= offsetC)
392             return -1;              // A is before B
393         else
394             return 1;               // A is after B
395     }
396 
397     // case 3: node C (container A or an ancestor) is a child node of B
398     c = containerA;
399     while (c && c->parentNode() != containerB)
400         c = c->parentNode();
401     if (c) {
402         int offsetC = 0;
403         Node* n = containerB->firstChild();
404         while (n != c && offsetC < offsetB) {
405             offsetC++;
406             n = n->nextSibling();
407         }
408 
409         if (offsetC < offsetB)
410             return -1;              // A is before B
411         else
412             return 1;               // A is after B
413     }
414 
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.");
420         return 0;
421     }
422     Node* childA = containerA;
423     while (childA && childA->parentNode() != commonAncestor)
424         childA = childA->parentNode();
425     if (!childA)
426         childA = commonAncestor;
427     Node* childB = containerB;
428     while (childB && childB->parentNode() != commonAncestor)
429         childB = childB->parentNode();
430     if (!childB)
431         childB = commonAncestor;
432 
433     if (childA == childB)
434         return 0; // A is equal to B
435 
436     Node* n = commonAncestor->firstChild();
437     while (n) {
438         if (n == childA)
439             return -1; // A is before B
440         if (n == childB)
441             return 1; // A is after B
442         n = n->nextSibling();
443     }
444 
445     // Should never reach this point.
446     ASSERT_NOT_REACHED();
447     return 0;
448 }
449 
compareBoundaryPoints(const RangeBoundaryPoint & boundaryA,const RangeBoundaryPoint & boundaryB,ExceptionState & exceptionState)450 short Range::compareBoundaryPoints(const RangeBoundaryPoint& boundaryA, const RangeBoundaryPoint& boundaryB, ExceptionState& exceptionState)
451 {
452     return compareBoundaryPoints(boundaryA.container(), boundaryA.offset(), boundaryB.container(), boundaryB.offset(), exceptionState);
453 }
454 
boundaryPointsValid() const455 bool Range::boundaryPointsValid() const
456 {
457     TrackExceptionState exceptionState;
458     return compareBoundaryPoints(m_start, m_end, exceptionState) <= 0 && !exceptionState.hadException();
459 }
460 
deleteContents(ExceptionState & exceptionState)461 void Range::deleteContents(ExceptionState& exceptionState)
462 {
463     ASSERT(boundaryPointsValid());
464 
465     {
466         EventQueueScope eventQueueScope;
467         processContents(DELETE_CONTENTS, exceptionState);
468     }
469 }
470 
nodeValidForIntersects(Node * refNode,Document * expectedDocument,ExceptionState & exceptionState)471 static bool nodeValidForIntersects(Node* refNode, Document* expectedDocument, ExceptionState& exceptionState)
472 {
473     if (!refNode) {
474         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
475         return false;
476     }
477 
478     if (!refNode->inActiveDocument() || refNode->document() != expectedDocument) {
479         // Firefox doesn't throw an exception for these cases; it returns false.
480         return false;
481     }
482 
483     return true;
484 }
485 
intersectsNode(Node * refNode,ExceptionState & exceptionState)486 bool Range::intersectsNode(Node* refNode, ExceptionState& exceptionState)
487 {
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))
491         return false;
492 
493     ContainerNode* parentNode = refNode->parentNode();
494     int nodeIndex = refNode->nodeIndex();
495 
496     if (!parentNode) {
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.");
500         return false;
501     }
502 
503     if (comparePoint(parentNode, nodeIndex, exceptionState) < 0 // starts before start
504         && comparePoint(parentNode, nodeIndex + 1, exceptionState) < 0) { // ends before start
505         return false;
506     }
507 
508     if (comparePoint(parentNode, nodeIndex, exceptionState) > 0 // starts after end
509         && comparePoint(parentNode, nodeIndex + 1, exceptionState) > 0) { // ends after end
510         return false;
511     }
512 
513     return true; // all other cases
514 }
515 
intersectsNode(Node * refNode,const Position & start,const Position & end,ExceptionState & exceptionState)516 bool Range::intersectsNode(Node* refNode, const Position& start, const Position& end, ExceptionState& exceptionState)
517 {
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))
521         return false;
522 
523     ContainerNode* parentNode = refNode->parentNode();
524     int nodeIndex = refNode->nodeIndex();
525 
526     if (!parentNode) {
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.");
530         return false;
531     }
532 
533     Node* startContainerNode = start.containerNode();
534     int startOffset = start.computeOffsetInContainerNode();
535 
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());
539         return false;
540     }
541 
542     Node* endContainerNode = end.containerNode();
543     int endOffset = end.computeOffsetInContainerNode();
544 
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());
548         return false;
549     }
550 
551     return true; // all other cases
552 }
553 
highestAncestorUnderCommonRoot(Node * node,Node * commonRoot)554 static inline Node* highestAncestorUnderCommonRoot(Node* node, Node* commonRoot)
555 {
556     if (node == commonRoot)
557         return 0;
558 
559     ASSERT(commonRoot->contains(node));
560 
561     while (node->parentNode() != commonRoot)
562         node = node->parentNode();
563 
564     return node;
565 }
566 
childOfCommonRootBeforeOffset(Node * container,unsigned offset,Node * commonRoot)567 static inline Node* childOfCommonRootBeforeOffset(Node* container, unsigned offset, Node* commonRoot)
568 {
569     ASSERT(container);
570     ASSERT(commonRoot);
571 
572     if (!commonRoot->contains(container))
573         return 0;
574 
575     if (container == commonRoot) {
576         container = container->firstChild();
577         for (unsigned i = 0; container && i < offset; i++)
578             container = container->nextSibling();
579     } else {
580         while (container->parentNode() != commonRoot)
581             container = container->parentNode();
582     }
583 
584     return container;
585 }
586 
processContents(ActionType action,ExceptionState & exceptionState)587 PassRefPtrWillBeRawPtr<DocumentFragment> Range::processContents(ActionType action, ExceptionState& exceptionState)
588 {
589     typedef WillBeHeapVector<RefPtrWillBeMember<Node> > NodeVector;
590 
591     RefPtrWillBeRawPtr<DocumentFragment> fragment = nullptr;
592     if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
593         fragment = DocumentFragment::create(*m_ownerDocument.get());
594 
595     if (collapsed())
596         return fragment.release();
597 
598     RefPtrWillBeRawPtr<Node> commonRoot = commonAncestorContainer();
599     ASSERT(commonRoot);
600 
601     if (m_start.container() == m_end.container()) {
602         processContentsBetweenOffsets(action, fragment, m_start.container(), m_start.offset(), m_end.offset(), exceptionState);
603         return fragment;
604     }
605 
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);
609 
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());
613 
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
619     //
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
624     //
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.
628     //
629     // These are deleted, cloned, or extracted (i.e. both) depending on action.
630 
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.
633 
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);
638     }
639 
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);
644     }
645 
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());
651 
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);
662         }
663         if (exceptionState.hadException())
664             return nullptr;
665         m_end = m_start;
666     }
667 
668     originalStart.clear();
669     originalEnd.clear();
670 
671     // Now add leftContents, stuff in between, and rightContents to the fragment
672     // (or just delete the stuff in between)
673 
674     if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && leftContents)
675         fragment->appendChild(leftContents, exceptionState);
676 
677     if (processStart) {
678         NodeVector nodes;
679         for (Node* n = processStart.get(); n && n != processEnd; n = n->nextSibling())
680             nodes.append(n);
681         processNodes(action, nodes, commonRoot, fragment, exceptionState);
682     }
683 
684     if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && rightContents)
685         fragment->appendChild(rightContents, exceptionState);
686 
687     return fragment.release();
688 }
689 
deleteCharacterData(PassRefPtrWillBeRawPtr<CharacterData> data,unsigned startOffset,unsigned endOffset,ExceptionState & exceptionState)690 static inline void deleteCharacterData(PassRefPtrWillBeRawPtr<CharacterData> data, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
691 {
692     if (data->length() - endOffset)
693         data->deleteData(endOffset, data->length() - endOffset, exceptionState);
694     if (startOffset)
695         data->deleteData(0, startOffset, exceptionState);
696 }
697 
processContentsBetweenOffsets(ActionType action,PassRefPtrWillBeRawPtr<DocumentFragment> fragment,Node * container,unsigned startOffset,unsigned endOffset,ExceptionState & exceptionState)698 PassRefPtrWillBeRawPtr<Node> Range::processContentsBetweenOffsets(ActionType action, PassRefPtrWillBeRawPtr<DocumentFragment> fragment,
699     Node* container, unsigned startOffset, unsigned endOffset, ExceptionState& exceptionState)
700 {
701     ASSERT(container);
702     ASSERT(startOffset <= endOffset);
703 
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);
714             if (fragment) {
715                 result = fragment;
716                 result->appendChild(c.release(), exceptionState);
717             } else
718                 result = c.release();
719         }
720         if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
721             toCharacterData(container)->deleteData(startOffset, endOffset - startOffset, exceptionState);
722         break;
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));
728             if (fragment) {
729                 result = fragment;
730                 result->appendChild(c.release(), exceptionState);
731             } else
732                 result = c.release();
733         }
734         if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
735             ProcessingInstruction* pi = toProcessingInstruction(container);
736             String data(pi->data());
737             data.remove(startOffset, endOffset - startOffset);
738             pi->setData(data);
739         }
740         break;
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) {
748             if (fragment)
749                 result = fragment;
750             else
751                 result = container->cloneNode(false);
752         }
753 
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())
759             nodes.append(n);
760 
761         processNodes(action, nodes, container, result, exceptionState);
762         break;
763     }
764 
765     return result.release();
766 }
767 
processNodes(ActionType action,WillBeHeapVector<RefPtrWillBeMember<Node>> & nodes,PassRefPtrWillBeRawPtr<Node> oldContainer,PassRefPtrWillBeRawPtr<Node> newContainer,ExceptionState & exceptionState)768 void Range::processNodes(ActionType action, WillBeHeapVector<RefPtrWillBeMember<Node> >& nodes, PassRefPtrWillBeRawPtr<Node> oldContainer, PassRefPtrWillBeRawPtr<Node> newContainer, ExceptionState& exceptionState)
769 {
770     for (unsigned i = 0; i < nodes.size(); i++) {
771         switch (action) {
772         case DELETE_CONTENTS:
773             oldContainer->removeChild(nodes[i].get(), exceptionState);
774             break;
775         case EXTRACT_CONTENTS:
776             newContainer->appendChild(nodes[i].release(), exceptionState); // will remove n from its parent
777             break;
778         case CLONE_CONTENTS:
779             newContainer->appendChild(nodes[i]->cloneNode(true), exceptionState);
780             break;
781         }
782     }
783 }
784 
processAncestorsAndTheirSiblings(ActionType action,Node * container,ContentsProcessDirection direction,PassRefPtrWillBeRawPtr<Node> passedClonedContainer,Node * commonRoot,ExceptionState & exceptionState)785 PassRefPtrWillBeRawPtr<Node> Range::processAncestorsAndTheirSiblings(ActionType action, Node* container, ContentsProcessDirection direction, PassRefPtrWillBeRawPtr<Node> passedClonedContainer, Node* commonRoot, ExceptionState& exceptionState)
786 {
787     typedef WillBeHeapVector<RefPtrWillBeMember<Node> > NodeVector;
788 
789     RefPtrWillBeRawPtr<Node> clonedContainer = passedClonedContainer;
790     NodeVector ancestors;
791     for (ContainerNode* n = container->parentNode(); n && n != commonRoot; n = n->parentNode())
792         ancestors.append(n);
793 
794     RefPtrWillBeRawPtr<Node> firstChildInAncestorToProcess = direction == ProcessContentsForward ? container->nextSibling() : container->previousSibling();
795     for (NodeVector::const_iterator it = ancestors.begin(); it != ancestors.end(); ++it) {
796         RefPtrWillBeRawPtr<Node> ancestor = *it;
797         if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
798             if (RefPtrWillBeRawPtr<Node> clonedAncestor = ancestor->cloneNode(false)) { // Might have been removed already during mutation event.
799                 clonedAncestor->appendChild(clonedContainer, exceptionState);
800                 clonedContainer = clonedAncestor;
801             }
802         }
803 
804         // Copy siblings of an ancestor of start/end containers
805         // FIXME: This assertion may fail if DOM is modified during mutation event
806         // FIXME: Share code with Range::processNodes
807         ASSERT(!firstChildInAncestorToProcess || firstChildInAncestorToProcess->parentNode() == ancestor);
808 
809         NodeVector nodes;
810         for (Node* child = firstChildInAncestorToProcess.get(); child;
811             child = (direction == ProcessContentsForward) ? child->nextSibling() : child->previousSibling())
812             nodes.append(child);
813 
814         for (NodeVector::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
815             Node* child = it->get();
816             switch (action) {
817             case DELETE_CONTENTS:
818                 // Prior call of ancestor->removeChild() may cause a tree change due to DOMSubtreeModified event.
819                 // Therefore, we need to make sure |ancestor| is still |child|'s parent.
820                 if (ancestor == child->parentNode())
821                     ancestor->removeChild(child, exceptionState);
822                 break;
823             case EXTRACT_CONTENTS: // will remove child from ancestor
824                 if (direction == ProcessContentsForward)
825                     clonedContainer->appendChild(child, exceptionState);
826                 else
827                     clonedContainer->insertBefore(child, clonedContainer->firstChild(), exceptionState);
828                 break;
829             case CLONE_CONTENTS:
830                 if (direction == ProcessContentsForward)
831                     clonedContainer->appendChild(child->cloneNode(true), exceptionState);
832                 else
833                     clonedContainer->insertBefore(child->cloneNode(true), clonedContainer->firstChild(), exceptionState);
834                 break;
835             }
836         }
837         firstChildInAncestorToProcess = direction == ProcessContentsForward ? ancestor->nextSibling() : ancestor->previousSibling();
838     }
839 
840     return clonedContainer.release();
841 }
842 
extractContents(ExceptionState & exceptionState)843 PassRefPtrWillBeRawPtr<DocumentFragment> Range::extractContents(ExceptionState& exceptionState)
844 {
845     checkExtractPrecondition(exceptionState);
846     if (exceptionState.hadException())
847         return nullptr;
848 
849     return processContents(EXTRACT_CONTENTS, exceptionState);
850 }
851 
cloneContents(ExceptionState & exceptionState)852 PassRefPtrWillBeRawPtr<DocumentFragment> Range::cloneContents(ExceptionState& exceptionState)
853 {
854     return processContents(CLONE_CONTENTS, exceptionState);
855 }
856 
insertNode(PassRefPtrWillBeRawPtr<Node> prpNewNode,ExceptionState & exceptionState)857 void Range::insertNode(PassRefPtrWillBeRawPtr<Node> prpNewNode, ExceptionState& exceptionState)
858 {
859     RefPtrWillBeRawPtr<Node> newNode = prpNewNode;
860 
861     if (!newNode) {
862         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
863         return;
864     }
865 
866     // HierarchyRequestError: Raised if the container of the start of the Range is of a type that
867     // does not allow children of the type of newNode or if newNode is an ancestor of the container.
868 
869     // an extra one here - if a text node is going to split, it must have a parent to insert into
870     bool startIsText = m_start.container()->isTextNode();
871     if (startIsText && !m_start.container()->parentNode()) {
872         exceptionState.throwDOMException(HierarchyRequestError, "This operation would split a text node, but there's no parent into which to insert.");
873         return;
874     }
875 
876     // In the case where the container is a text node, we check against the container's parent, because
877     // text nodes get split up upon insertion.
878     Node* checkAgainst;
879     if (startIsText)
880         checkAgainst = m_start.container()->parentNode();
881     else
882         checkAgainst = m_start.container();
883 
884     Node::NodeType newNodeType = newNode->nodeType();
885     int numNewChildren;
886     if (newNodeType == Node::DOCUMENT_FRAGMENT_NODE && !newNode->isShadowRoot()) {
887         // check each child node, not the DocumentFragment itself
888         numNewChildren = 0;
889         for (Node* c = toDocumentFragment(newNode)->firstChild(); c; c = c->nextSibling()) {
890             if (!checkAgainst->childTypeAllowed(c->nodeType())) {
891                 exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains a '" + c->nodeName() + "' node, which may not be inserted here.");
892                 return;
893             }
894             ++numNewChildren;
895         }
896     } else {
897         numNewChildren = 1;
898         if (!checkAgainst->childTypeAllowed(newNodeType)) {
899             exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
900             return;
901         }
902     }
903 
904     for (Node* n = m_start.container(); n; n = n->parentNode()) {
905         if (n == newNode) {
906             exceptionState.throwDOMException(HierarchyRequestError, "The node to be inserted contains the insertion point; it may not be inserted into itself.");
907             return;
908         }
909     }
910 
911     // InvalidNodeTypeError: Raised if newNode is an Attr, Entity, Notation, ShadowRoot or Document node.
912     switch (newNodeType) {
913     case Node::ATTRIBUTE_NODE:
914     case Node::DOCUMENT_NODE:
915         exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a '" + newNode->nodeName() + "' node, which may not be inserted here.");
916         return;
917     default:
918         if (newNode->isShadowRoot()) {
919             exceptionState.throwDOMException(InvalidNodeTypeError, "The node to be inserted is a shadow root, which may not be inserted here.");
920             return;
921         }
922         break;
923     }
924 
925     EventQueueScope scope;
926     bool collapsed = m_start == m_end;
927     RefPtrWillBeRawPtr<Node> container = nullptr;
928     if (startIsText) {
929         container = m_start.container();
930         RefPtrWillBeRawPtr<Text> newText = toText(container)->splitText(m_start.offset(), exceptionState);
931         if (exceptionState.hadException())
932             return;
933 
934         container = m_start.container();
935         container->parentNode()->insertBefore(newNode.release(), newText.get(), exceptionState);
936         if (exceptionState.hadException())
937             return;
938 
939         if (collapsed) {
940             // The load event would be fired regardless of EventQueueScope;
941             // e.g. by ContainerNode::updateTreeAfterInsertion
942             // Given circumstance may mutate the tree so newText->parentNode() may become null
943             if (!newText->parentNode()) {
944                 exceptionState.throwDOMException(HierarchyRequestError, "This operation would set range's end to parent with new offset, but there's no parent into which to continue.");
945                 return;
946             }
947             m_end.setToBeforeChild(*newText);
948         }
949     } else {
950         RefPtrWillBeRawPtr<Node> lastChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->lastChild() : newNode.get();
951         if (lastChild && lastChild == m_start.childBefore()) {
952             // The insertion will do nothing, but we need to extend the range to include
953             // the inserted nodes.
954             Node* firstChild = (newNodeType == Node::DOCUMENT_FRAGMENT_NODE) ? toDocumentFragment(newNode)->firstChild() : newNode.get();
955             ASSERT(firstChild);
956             m_start.setToBeforeChild(*firstChild);
957             return;
958         }
959 
960         container = m_start.container();
961         container->insertBefore(newNode.release(), NodeTraversal::childAt(*container, m_start.offset()), exceptionState);
962         if (exceptionState.hadException())
963             return;
964 
965         // Note that m_start.offset() may have changed as a result of container->insertBefore,
966         // when the node we are inserting comes before the range in the same container.
967         if (collapsed && numNewChildren)
968             m_end.set(m_start.container(), m_start.offset() + numNewChildren, lastChild.get());
969     }
970 }
971 
toString() const972 String Range::toString() const
973 {
974     StringBuilder builder;
975 
976     Node* pastLast = pastLastNode();
977     for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
978         Node::NodeType type = n->nodeType();
979         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
980             String data = toCharacterData(n)->data();
981             int length = data.length();
982             int start = (n == m_start.container()) ? std::min(std::max(0, m_start.offset()), length) : 0;
983             int end = (n == m_end.container()) ? std::min(std::max(start, m_end.offset()), length) : length;
984             builder.append(data, start, end - start);
985         }
986     }
987 
988     return builder.toString();
989 }
990 
toHTML() const991 String Range::toHTML() const
992 {
993     return createMarkup(this);
994 }
995 
text() const996 String Range::text() const
997 {
998     return plainText(this, TextIteratorEmitsObjectReplacementCharacter);
999 }
1000 
createContextualFragment(const String & markup,ExceptionState & exceptionState)1001 PassRefPtrWillBeRawPtr<DocumentFragment> Range::createContextualFragment(const String& markup, ExceptionState& exceptionState)
1002 {
1003     // Algorithm: http://domparsing.spec.whatwg.org/#extensions-to-the-range-interface
1004 
1005     Node* node = m_start.container();
1006 
1007     // Step 1.
1008     RefPtrWillBeRawPtr<Element> element;
1009     if (!m_start.offset() && (node->isDocumentNode() || node->isDocumentFragment()))
1010         element = nullptr;
1011     else if (node->isElementNode())
1012         element = toElement(node);
1013     else
1014         element = node->parentElement();
1015 
1016     // Step 2.
1017     if (!element || isHTMLHtmlElement(element)) {
1018         Document& document = node->document();
1019 
1020         if (document.isHTMLDocument() || document.isXHTMLDocument()) {
1021             // Optimization over spec: try to reuse the existing <body> element, if it is available.
1022             element = document.body();
1023             if (!element)
1024                 element = HTMLBodyElement::create(document);
1025         } else if (document.isSVGDocument()) {
1026             element = document.documentElement();
1027             if (!element)
1028                 element = SVGSVGElement::create(document);
1029         }
1030     }
1031 
1032     if (!element || (!element->isHTMLElement() && !element->isSVGElement())) {
1033         exceptionState.throwDOMException(NotSupportedError, "The range's container must be an HTML or SVG Element, Document, or DocumentFragment.");
1034         return nullptr;
1035     }
1036 
1037     // Steps 3, 4, 5.
1038     RefPtrWillBeRawPtr<DocumentFragment> fragment = blink::createContextualFragment(markup, element.get(), AllowScriptingContentAndDoNotMarkAlreadyStarted, exceptionState);
1039     if (!fragment)
1040         return nullptr;
1041 
1042     return fragment.release();
1043 }
1044 
1045 
detach()1046 void Range::detach()
1047 {
1048     // This is now a no-op as per the DOM specification.
1049 }
1050 
checkNodeWOffset(Node * n,int offset,ExceptionState & exceptionState) const1051 Node* Range::checkNodeWOffset(Node* n, int offset, ExceptionState& exceptionState) const
1052 {
1053     switch (n->nodeType()) {
1054         case Node::DOCUMENT_TYPE_NODE:
1055             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1056             return 0;
1057         case Node::CDATA_SECTION_NODE:
1058         case Node::COMMENT_NODE:
1059         case Node::TEXT_NODE:
1060             if (static_cast<unsigned>(offset) > toCharacterData(n)->length())
1061                 exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than or equal to the node's length (" + String::number(toCharacterData(n)->length()) + ").");
1062             return 0;
1063         case Node::PROCESSING_INSTRUCTION_NODE:
1064             if (static_cast<unsigned>(offset) > toProcessingInstruction(n)->data().length())
1065                 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             return 0;
1067         case Node::ATTRIBUTE_NODE:
1068         case Node::DOCUMENT_FRAGMENT_NODE:
1069         case Node::DOCUMENT_NODE:
1070         case Node::ELEMENT_NODE: {
1071             if (!offset)
1072                 return 0;
1073             Node* childBefore = NodeTraversal::childAt(*n, offset - 1);
1074             if (!childBefore)
1075                 exceptionState.throwDOMException(IndexSizeError, "There is no child at offset " + String::number(offset) + ".");
1076             return childBefore;
1077         }
1078     }
1079     ASSERT_NOT_REACHED();
1080     return 0;
1081 }
1082 
checkNodeBA(Node * n,ExceptionState & exceptionState) const1083 void Range::checkNodeBA(Node* n, ExceptionState& exceptionState) const
1084 {
1085     if (!n) {
1086         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1087         return;
1088     }
1089 
1090     // InvalidNodeTypeError: Raised if the root container of refNode is not an
1091     // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG shadow DOM tree,
1092     // or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation node.
1093 
1094     if (!n->parentNode()) {
1095         exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1096         return;
1097     }
1098 
1099     switch (n->nodeType()) {
1100         case Node::ATTRIBUTE_NODE:
1101         case Node::DOCUMENT_FRAGMENT_NODE:
1102         case Node::DOCUMENT_NODE:
1103             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1104             return;
1105         case Node::CDATA_SECTION_NODE:
1106         case Node::COMMENT_NODE:
1107         case Node::DOCUMENT_TYPE_NODE:
1108         case Node::ELEMENT_NODE:
1109         case Node::PROCESSING_INSTRUCTION_NODE:
1110         case Node::TEXT_NODE:
1111             break;
1112     }
1113 
1114     Node* root = n;
1115     while (ContainerNode* parent = root->parentNode())
1116         root = parent;
1117 
1118     switch (root->nodeType()) {
1119         case Node::ATTRIBUTE_NODE:
1120         case Node::DOCUMENT_NODE:
1121         case Node::DOCUMENT_FRAGMENT_NODE:
1122         case Node::ELEMENT_NODE:
1123             break;
1124         case Node::CDATA_SECTION_NODE:
1125         case Node::COMMENT_NODE:
1126         case Node::DOCUMENT_TYPE_NODE:
1127         case Node::PROCESSING_INSTRUCTION_NODE:
1128         case Node::TEXT_NODE:
1129             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + n->nodeName() + "'.");
1130             return;
1131     }
1132 }
1133 
cloneRange() const1134 PassRefPtrWillBeRawPtr<Range> Range::cloneRange() const
1135 {
1136     return Range::create(*m_ownerDocument.get(), m_start.container(), m_start.offset(), m_end.container(), m_end.offset());
1137 }
1138 
setStartAfter(Node * refNode,ExceptionState & exceptionState)1139 void Range::setStartAfter(Node* refNode, ExceptionState& exceptionState)
1140 {
1141     checkNodeBA(refNode, exceptionState);
1142     if (exceptionState.hadException())
1143         return;
1144 
1145     setStart(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1146 }
1147 
setEndBefore(Node * refNode,ExceptionState & exceptionState)1148 void Range::setEndBefore(Node* refNode, ExceptionState& exceptionState)
1149 {
1150     checkNodeBA(refNode, exceptionState);
1151     if (exceptionState.hadException())
1152         return;
1153 
1154     setEnd(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1155 }
1156 
setEndAfter(Node * refNode,ExceptionState & exceptionState)1157 void Range::setEndAfter(Node* refNode, ExceptionState& exceptionState)
1158 {
1159     checkNodeBA(refNode, exceptionState);
1160     if (exceptionState.hadException())
1161         return;
1162 
1163     setEnd(refNode->parentNode(), refNode->nodeIndex() + 1, exceptionState);
1164 }
1165 
selectNode(Node * refNode,ExceptionState & exceptionState)1166 void Range::selectNode(Node* refNode, ExceptionState& exceptionState)
1167 {
1168     if (!refNode) {
1169         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1170         return;
1171     }
1172 
1173     if (!refNode->parentNode()) {
1174         exceptionState.throwDOMException(InvalidNodeTypeError, "the given Node has no parent.");
1175         return;
1176     }
1177 
1178     // InvalidNodeTypeError: Raised if an ancestor of refNode is an Entity, Notation or
1179     // DocumentType node or if refNode is a Document, DocumentFragment, ShadowRoot, Attr, Entity, or Notation
1180     // node.
1181     for (ContainerNode* anc = refNode->parentNode(); anc; anc = anc->parentNode()) {
1182         switch (anc->nodeType()) {
1183             case Node::ATTRIBUTE_NODE:
1184             case Node::CDATA_SECTION_NODE:
1185             case Node::COMMENT_NODE:
1186             case Node::DOCUMENT_FRAGMENT_NODE:
1187             case Node::DOCUMENT_NODE:
1188             case Node::ELEMENT_NODE:
1189             case Node::PROCESSING_INSTRUCTION_NODE:
1190             case Node::TEXT_NODE:
1191                 break;
1192             case Node::DOCUMENT_TYPE_NODE:
1193                 exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided has an ancestor of type '" + anc->nodeName() + "'.");
1194                 return;
1195         }
1196     }
1197 
1198     switch (refNode->nodeType()) {
1199         case Node::CDATA_SECTION_NODE:
1200         case Node::COMMENT_NODE:
1201         case Node::DOCUMENT_TYPE_NODE:
1202         case Node::ELEMENT_NODE:
1203         case Node::PROCESSING_INSTRUCTION_NODE:
1204         case Node::TEXT_NODE:
1205             break;
1206         case Node::ATTRIBUTE_NODE:
1207         case Node::DOCUMENT_FRAGMENT_NODE:
1208         case Node::DOCUMENT_NODE:
1209             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1210             return;
1211     }
1212 
1213     if (m_ownerDocument != refNode->document())
1214         setDocument(refNode->document());
1215 
1216     setStartBefore(refNode);
1217     setEndAfter(refNode);
1218 }
1219 
selectNodeContents(Node * refNode,ExceptionState & exceptionState)1220 void Range::selectNodeContents(Node* refNode, ExceptionState& exceptionState)
1221 {
1222     if (!refNode) {
1223         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1224         return;
1225     }
1226 
1227     // InvalidNodeTypeError: Raised if refNode or an ancestor of refNode is an Entity, Notation
1228     // or DocumentType node.
1229     for (Node* n = refNode; n; n = n->parentNode()) {
1230         switch (n->nodeType()) {
1231         case Node::ATTRIBUTE_NODE:
1232         case Node::CDATA_SECTION_NODE:
1233         case Node::COMMENT_NODE:
1234         case Node::DOCUMENT_FRAGMENT_NODE:
1235         case Node::DOCUMENT_NODE:
1236         case Node::ELEMENT_NODE:
1237         case Node::PROCESSING_INSTRUCTION_NODE:
1238         case Node::TEXT_NODE:
1239             break;
1240         case Node::DOCUMENT_TYPE_NODE:
1241             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + refNode->nodeName() + "'.");
1242             return;
1243         }
1244     }
1245 
1246     if (m_ownerDocument != refNode->document())
1247         setDocument(refNode->document());
1248 
1249     m_start.setToStartOfNode(*refNode);
1250     m_end.setToEndOfNode(*refNode);
1251 }
1252 
selectNodeContents(Node * refNode,Position & start,Position & end)1253 bool Range::selectNodeContents(Node* refNode, Position& start, Position& end)
1254 {
1255     if (!refNode) {
1256         return false;
1257     }
1258 
1259     for (Node* n = refNode; n; n = n->parentNode()) {
1260         switch (n->nodeType()) {
1261         case Node::ATTRIBUTE_NODE:
1262         case Node::CDATA_SECTION_NODE:
1263         case Node::COMMENT_NODE:
1264         case Node::DOCUMENT_FRAGMENT_NODE:
1265         case Node::DOCUMENT_NODE:
1266         case Node::ELEMENT_NODE:
1267         case Node::PROCESSING_INSTRUCTION_NODE:
1268         case Node::TEXT_NODE:
1269             break;
1270         case Node::DOCUMENT_TYPE_NODE:
1271             return false;
1272         }
1273     }
1274 
1275     RangeBoundaryPoint startBoundaryPoint(refNode);
1276     startBoundaryPoint.setToStartOfNode(*refNode);
1277     start = startBoundaryPoint.toPosition();
1278     RangeBoundaryPoint endBoundaryPoint(refNode);
1279     endBoundaryPoint.setToEndOfNode(*refNode);
1280     end = endBoundaryPoint.toPosition();
1281     return true;
1282 }
1283 
surroundContents(PassRefPtrWillBeRawPtr<Node> passNewParent,ExceptionState & exceptionState)1284 void Range::surroundContents(PassRefPtrWillBeRawPtr<Node> passNewParent, ExceptionState& exceptionState)
1285 {
1286     RefPtrWillBeRawPtr<Node> newParent = passNewParent;
1287     if (!newParent) {
1288         exceptionState.throwDOMException(NotFoundError, "The node provided is null.");
1289         return;
1290     }
1291 
1292     // InvalidStateError: Raised if the Range partially selects a non-Text node.
1293     Node* startNonTextContainer = m_start.container();
1294     if (startNonTextContainer->nodeType() == Node::TEXT_NODE)
1295         startNonTextContainer = startNonTextContainer->parentNode();
1296     Node* endNonTextContainer = m_end.container();
1297     if (endNonTextContainer->nodeType() == Node::TEXT_NODE)
1298         endNonTextContainer = endNonTextContainer->parentNode();
1299     if (startNonTextContainer != endNonTextContainer) {
1300         exceptionState.throwDOMException(InvalidStateError, "The Range has partially selected a non-Text node.");
1301         return;
1302     }
1303 
1304     // InvalidNodeTypeError: Raised if node is an Attr, Entity, DocumentType, Notation,
1305     // Document, or DocumentFragment node.
1306     switch (newParent->nodeType()) {
1307         case Node::ATTRIBUTE_NODE:
1308         case Node::DOCUMENT_FRAGMENT_NODE:
1309         case Node::DOCUMENT_NODE:
1310         case Node::DOCUMENT_TYPE_NODE:
1311             exceptionState.throwDOMException(InvalidNodeTypeError, "The node provided is of type '" + newParent->nodeName() + "'.");
1312             return;
1313         case Node::CDATA_SECTION_NODE:
1314         case Node::COMMENT_NODE:
1315         case Node::ELEMENT_NODE:
1316         case Node::PROCESSING_INSTRUCTION_NODE:
1317         case Node::TEXT_NODE:
1318             break;
1319     }
1320 
1321     // Raise a HierarchyRequestError if m_start.container() doesn't accept children like newParent.
1322     Node* parentOfNewParent = m_start.container();
1323 
1324     // If m_start.container() is a character data node, it will be split and it will be its parent that will
1325     // need to accept newParent (or in the case of a comment, it logically "would" be inserted into the parent,
1326     // although this will fail below for another reason).
1327     if (parentOfNewParent->isCharacterDataNode())
1328         parentOfNewParent = parentOfNewParent->parentNode();
1329 
1330     if (!parentOfNewParent) {
1331         exceptionState.throwDOMException(HierarchyRequestError, "The container node is a detached character data node; no parent node is available for insertion.");
1332         return;
1333     }
1334 
1335     if (!parentOfNewParent->childTypeAllowed(newParent->nodeType())) {
1336         exceptionState.throwDOMException(HierarchyRequestError, "The node provided is of type '" + newParent->nodeName() + "', which may not be inserted here.");
1337         return;
1338     }
1339 
1340     if (newParent->contains(m_start.container())) {
1341         exceptionState.throwDOMException(HierarchyRequestError, "The node provided contains the insertion point; it may not be inserted into itself.");
1342         return;
1343     }
1344 
1345     // FIXME: Do we need a check if the node would end up with a child node of a type not
1346     // allowed by the type of node?
1347 
1348     while (Node* n = newParent->firstChild()) {
1349         toContainerNode(newParent)->removeChild(n, exceptionState);
1350         if (exceptionState.hadException())
1351             return;
1352     }
1353     RefPtrWillBeRawPtr<DocumentFragment> fragment = extractContents(exceptionState);
1354     if (exceptionState.hadException())
1355         return;
1356     insertNode(newParent, exceptionState);
1357     if (exceptionState.hadException())
1358         return;
1359     newParent->appendChild(fragment.release(), exceptionState);
1360     if (exceptionState.hadException())
1361         return;
1362     selectNode(newParent.get(), exceptionState);
1363 }
1364 
setStartBefore(Node * refNode,ExceptionState & exceptionState)1365 void Range::setStartBefore(Node* refNode, ExceptionState& exceptionState)
1366 {
1367     checkNodeBA(refNode, exceptionState);
1368     if (exceptionState.hadException())
1369         return;
1370 
1371     setStart(refNode->parentNode(), refNode->nodeIndex(), exceptionState);
1372 }
1373 
checkExtractPrecondition(ExceptionState & exceptionState)1374 void Range::checkExtractPrecondition(ExceptionState& exceptionState)
1375 {
1376     ASSERT(boundaryPointsValid());
1377 
1378     if (!commonAncestorContainer())
1379         return;
1380 
1381     Node* pastLast = pastLastNode();
1382     for (Node* n = firstNode(); n != pastLast; n = NodeTraversal::next(*n)) {
1383         if (n->isDocumentTypeNode()) {
1384             exceptionState.throwDOMException(HierarchyRequestError, "The Range contains a doctype node.");
1385             return;
1386         }
1387     }
1388 }
1389 
firstNode() const1390 Node* Range::firstNode() const
1391 {
1392     if (m_start.container()->offsetInCharacters())
1393         return m_start.container();
1394     if (Node* child = NodeTraversal::childAt(*m_start.container(), m_start.offset()))
1395         return child;
1396     if (!m_start.offset())
1397         return m_start.container();
1398     return NodeTraversal::nextSkippingChildren(*m_start.container());
1399 }
1400 
shadowRoot() const1401 ShadowRoot* Range::shadowRoot() const
1402 {
1403     return startContainer() ? startContainer()->containingShadowRoot() : 0;
1404 }
1405 
pastLastNode() const1406 Node* Range::pastLastNode() const
1407 {
1408     if (m_end.container()->offsetInCharacters())
1409         return NodeTraversal::nextSkippingChildren(*m_end.container());
1410     if (Node* child = NodeTraversal::childAt(*m_end.container(), m_end.offset()))
1411         return child;
1412     return NodeTraversal::nextSkippingChildren(*m_end.container());
1413 }
1414 
boundingBox() const1415 IntRect Range::boundingBox() const
1416 {
1417     IntRect result;
1418     Vector<IntRect> rects;
1419     textRects(rects);
1420     const size_t n = rects.size();
1421     for (size_t i = 0; i < n; ++i)
1422         result.unite(rects[i]);
1423     return result;
1424 }
1425 
textRects(Vector<IntRect> & rects,bool useSelectionHeight,RangeInFixedPosition * inFixed) const1426 void Range::textRects(Vector<IntRect>& rects, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1427 {
1428     Node* startContainer = m_start.container();
1429     ASSERT(startContainer);
1430     Node* endContainer = m_end.container();
1431     ASSERT(endContainer);
1432 
1433     bool allFixed = true;
1434     bool someFixed = false;
1435 
1436     Node* stopNode = pastLastNode();
1437     for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1438         RenderObject* r = node->renderer();
1439         if (!r || !r->isText())
1440             continue;
1441         RenderText* renderText = toRenderText(r);
1442         int startOffset = node == startContainer ? m_start.offset() : 0;
1443         int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1444         bool isFixed = false;
1445         renderText->absoluteRectsForRange(rects, startOffset, endOffset, useSelectionHeight, &isFixed);
1446         allFixed &= isFixed;
1447         someFixed |= isFixed;
1448     }
1449 
1450     if (inFixed)
1451         *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1452 }
1453 
textQuads(Vector<FloatQuad> & quads,bool useSelectionHeight,RangeInFixedPosition * inFixed) const1454 void Range::textQuads(Vector<FloatQuad>& quads, bool useSelectionHeight, RangeInFixedPosition* inFixed) const
1455 {
1456     Node* startContainer = m_start.container();
1457     ASSERT(startContainer);
1458     Node* endContainer = m_end.container();
1459     ASSERT(endContainer);
1460 
1461     bool allFixed = true;
1462     bool someFixed = false;
1463 
1464     Node* stopNode = pastLastNode();
1465     for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1466         RenderObject* r = node->renderer();
1467         if (!r || !r->isText())
1468             continue;
1469         RenderText* renderText = toRenderText(r);
1470         int startOffset = node == startContainer ? m_start.offset() : 0;
1471         int endOffset = node == endContainer ? m_end.offset() : std::numeric_limits<int>::max();
1472         bool isFixed = false;
1473         renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight, &isFixed);
1474         allFixed &= isFixed;
1475         someFixed |= isFixed;
1476     }
1477 
1478     if (inFixed)
1479         *inFixed = allFixed ? EntirelyFixedPosition : (someFixed ? PartiallyFixedPosition : NotFixedPosition);
1480 }
1481 
1482 #ifndef NDEBUG
formatForDebugger(char * buffer,unsigned length) const1483 void Range::formatForDebugger(char* buffer, unsigned length) const
1484 {
1485     StringBuilder result;
1486 
1487     const int FormatBufferSize = 1024;
1488     char s[FormatBufferSize];
1489     result.appendLiteral("from offset ");
1490     result.appendNumber(m_start.offset());
1491     result.appendLiteral(" of ");
1492     m_start.container()->formatForDebugger(s, FormatBufferSize);
1493     result.append(s);
1494     result.appendLiteral(" to offset ");
1495     result.appendNumber(m_end.offset());
1496     result.appendLiteral(" of ");
1497     m_end.container()->formatForDebugger(s, FormatBufferSize);
1498     result.append(s);
1499 
1500     strncpy(buffer, result.toString().utf8().data(), length - 1);
1501 }
1502 #endif
1503 
areRangesEqual(const Range * a,const Range * b)1504 bool areRangesEqual(const Range* a, const Range* b)
1505 {
1506     if (a == b)
1507         return true;
1508     if (!a || !b)
1509         return false;
1510     return a->startPosition() == b->startPosition() && a->endPosition() == b->endPosition();
1511 }
1512 
rangeOfContents(Node * node)1513 PassRefPtrWillBeRawPtr<Range> rangeOfContents(Node* node)
1514 {
1515     ASSERT(node);
1516     RefPtrWillBeRawPtr<Range> range = Range::create(node->document());
1517     range->selectNodeContents(node, IGNORE_EXCEPTION);
1518     return range.release();
1519 }
1520 
boundaryNodeChildrenChanged(RangeBoundaryPoint & boundary,ContainerNode * container)1521 static inline void boundaryNodeChildrenChanged(RangeBoundaryPoint& boundary, ContainerNode* container)
1522 {
1523     if (!boundary.childBefore())
1524         return;
1525     if (boundary.container() != container)
1526         return;
1527     boundary.invalidateOffset();
1528 }
1529 
nodeChildrenChanged(ContainerNode * container)1530 void Range::nodeChildrenChanged(ContainerNode* container)
1531 {
1532     ASSERT(container);
1533     ASSERT(container->document() == m_ownerDocument);
1534     boundaryNodeChildrenChanged(m_start, container);
1535     boundaryNodeChildrenChanged(m_end, container);
1536 }
1537 
boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint & boundary,ContainerNode & container)1538 static inline void boundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint& boundary, ContainerNode& container)
1539 {
1540     for (Node* nodeToBeRemoved = container.firstChild(); nodeToBeRemoved; nodeToBeRemoved = nodeToBeRemoved->nextSibling()) {
1541         if (boundary.childBefore() == nodeToBeRemoved) {
1542             boundary.setToStartOfNode(container);
1543             return;
1544         }
1545 
1546         for (Node* n = boundary.container(); n; n = n->parentNode()) {
1547             if (n == nodeToBeRemoved) {
1548                 boundary.setToStartOfNode(container);
1549                 return;
1550             }
1551         }
1552     }
1553 }
1554 
nodeChildrenWillBeRemoved(ContainerNode & container)1555 void Range::nodeChildrenWillBeRemoved(ContainerNode& container)
1556 {
1557     ASSERT(container.document() == m_ownerDocument);
1558     boundaryNodeChildrenWillBeRemoved(m_start, container);
1559     boundaryNodeChildrenWillBeRemoved(m_end, container);
1560 }
1561 
boundaryNodeWillBeRemoved(RangeBoundaryPoint & boundary,Node & nodeToBeRemoved)1562 static inline void boundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary, Node& nodeToBeRemoved)
1563 {
1564     if (boundary.childBefore() == nodeToBeRemoved) {
1565         boundary.childBeforeWillBeRemoved();
1566         return;
1567     }
1568 
1569     for (Node* n = boundary.container(); n; n = n->parentNode()) {
1570         if (n == nodeToBeRemoved) {
1571             boundary.setToBeforeChild(nodeToBeRemoved);
1572             return;
1573         }
1574     }
1575 }
1576 
nodeWillBeRemoved(Node & node)1577 void Range::nodeWillBeRemoved(Node& node)
1578 {
1579     ASSERT(node.document() == m_ownerDocument);
1580     ASSERT(node != m_ownerDocument.get());
1581 
1582     // FIXME: Once DOMNodeRemovedFromDocument mutation event removed, we
1583     // should change following if-statement to ASSERT(!node->parentNode).
1584     if (!node.parentNode())
1585         return;
1586     boundaryNodeWillBeRemoved(m_start, node);
1587     boundaryNodeWillBeRemoved(m_end, node);
1588 }
1589 
boundaryTextInserted(RangeBoundaryPoint & boundary,Node * text,unsigned offset,unsigned length)1590 static inline void boundaryTextInserted(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1591 {
1592     if (boundary.container() != text)
1593         return;
1594     unsigned boundaryOffset = boundary.offset();
1595     if (offset >= boundaryOffset)
1596         return;
1597     boundary.setOffset(boundaryOffset + length);
1598 }
1599 
didInsertText(Node * text,unsigned offset,unsigned length)1600 void Range::didInsertText(Node* text, unsigned offset, unsigned length)
1601 {
1602     ASSERT(text);
1603     ASSERT(text->document() == m_ownerDocument);
1604     boundaryTextInserted(m_start, text, offset, length);
1605     boundaryTextInserted(m_end, text, offset, length);
1606 }
1607 
boundaryTextRemoved(RangeBoundaryPoint & boundary,Node * text,unsigned offset,unsigned length)1608 static inline void boundaryTextRemoved(RangeBoundaryPoint& boundary, Node* text, unsigned offset, unsigned length)
1609 {
1610     if (boundary.container() != text)
1611         return;
1612     unsigned boundaryOffset = boundary.offset();
1613     if (offset >= boundaryOffset)
1614         return;
1615     if (offset + length >= boundaryOffset)
1616         boundary.setOffset(offset);
1617     else
1618         boundary.setOffset(boundaryOffset - length);
1619 }
1620 
didRemoveText(Node * text,unsigned offset,unsigned length)1621 void Range::didRemoveText(Node* text, unsigned offset, unsigned length)
1622 {
1623     ASSERT(text);
1624     ASSERT(text->document() == m_ownerDocument);
1625     boundaryTextRemoved(m_start, text, offset, length);
1626     boundaryTextRemoved(m_end, text, offset, length);
1627 }
1628 
boundaryTextNodesMerged(RangeBoundaryPoint & boundary,const NodeWithIndex & oldNode,unsigned offset)1629 static inline void boundaryTextNodesMerged(RangeBoundaryPoint& boundary, const NodeWithIndex& oldNode, unsigned offset)
1630 {
1631     if (boundary.container() == oldNode.node())
1632         boundary.set(oldNode.node().previousSibling(), boundary.offset() + offset, 0);
1633     else if (boundary.container() == oldNode.node().parentNode() && boundary.offset() == oldNode.index())
1634         boundary.set(oldNode.node().previousSibling(), offset, 0);
1635 }
1636 
didMergeTextNodes(const NodeWithIndex & oldNode,unsigned offset)1637 void Range::didMergeTextNodes(const NodeWithIndex& oldNode, unsigned offset)
1638 {
1639     ASSERT(oldNode.node().document() == m_ownerDocument);
1640     ASSERT(oldNode.node().parentNode());
1641     ASSERT(oldNode.node().isTextNode());
1642     ASSERT(oldNode.node().previousSibling());
1643     ASSERT(oldNode.node().previousSibling()->isTextNode());
1644     boundaryTextNodesMerged(m_start, oldNode, offset);
1645     boundaryTextNodesMerged(m_end, oldNode, offset);
1646 }
1647 
updateOwnerDocumentIfNeeded()1648 void Range::updateOwnerDocumentIfNeeded()
1649 {
1650     ASSERT(m_start.container());
1651     ASSERT(m_end.container());
1652     Document& newDocument = m_start.container()->document();
1653     ASSERT(newDocument == m_end.container()->document());
1654     if (newDocument == m_ownerDocument)
1655         return;
1656     m_ownerDocument->detachRange(this);
1657     m_ownerDocument = &newDocument;
1658     m_ownerDocument->attachRange(this);
1659 }
1660 
boundaryTextNodeSplit(RangeBoundaryPoint & boundary,Text & oldNode)1661 static inline void boundaryTextNodeSplit(RangeBoundaryPoint& boundary, Text& oldNode)
1662 {
1663     Node* boundaryContainer = boundary.container();
1664     unsigned boundaryOffset = boundary.offset();
1665     if (boundary.childBefore() == &oldNode)
1666         boundary.set(boundaryContainer, boundaryOffset + 1, oldNode.nextSibling());
1667     else if (boundary.container() == &oldNode && boundaryOffset > oldNode.length())
1668         boundary.set(oldNode.nextSibling(), boundaryOffset - oldNode.length(), 0);
1669 }
1670 
didSplitTextNode(Text & oldNode)1671 void Range::didSplitTextNode(Text& oldNode)
1672 {
1673     ASSERT(oldNode.document() == m_ownerDocument);
1674     ASSERT(oldNode.parentNode());
1675     ASSERT(oldNode.nextSibling());
1676     ASSERT(oldNode.nextSibling()->isTextNode());
1677     boundaryTextNodeSplit(m_start, oldNode);
1678     boundaryTextNodeSplit(m_end, oldNode);
1679     ASSERT(boundaryPointsValid());
1680 }
1681 
expand(const String & unit,ExceptionState & exceptionState)1682 void Range::expand(const String& unit, ExceptionState& exceptionState)
1683 {
1684     VisiblePosition start(startPosition());
1685     VisiblePosition end(endPosition());
1686     if (unit == "word") {
1687         start = startOfWord(start);
1688         end = endOfWord(end);
1689     } else if (unit == "sentence") {
1690         start = startOfSentence(start);
1691         end = endOfSentence(end);
1692     } else if (unit == "block") {
1693         start = startOfParagraph(start);
1694         end = endOfParagraph(end);
1695     } else if (unit == "document") {
1696         start = startOfDocument(start);
1697         end = endOfDocument(end);
1698     } else
1699         return;
1700     setStart(start.deepEquivalent().containerNode(), start.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1701     setEnd(end.deepEquivalent().containerNode(), end.deepEquivalent().computeOffsetInContainerNode(), exceptionState);
1702 }
1703 
getClientRects() const1704 PassRefPtrWillBeRawPtr<ClientRectList> Range::getClientRects() const
1705 {
1706     m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1707 
1708     Vector<FloatQuad> quads;
1709     getBorderAndTextQuads(quads);
1710 
1711     return ClientRectList::create(quads);
1712 }
1713 
getBoundingClientRect() const1714 PassRefPtrWillBeRawPtr<ClientRect> Range::getBoundingClientRect() const
1715 {
1716     return ClientRect::create(boundingRect());
1717 }
1718 
getBorderAndTextQuads(Vector<FloatQuad> & quads) const1719 void Range::getBorderAndTextQuads(Vector<FloatQuad>& quads) const
1720 {
1721     Node* startContainer = m_start.container();
1722     Node* endContainer = m_end.container();
1723     Node* stopNode = pastLastNode();
1724 
1725     WillBeHeapHashSet<RawPtrWillBeMember<Node> > nodeSet;
1726     for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1727         if (node->isElementNode())
1728             nodeSet.add(node);
1729     }
1730 
1731     for (Node* node = firstNode(); node != stopNode; node = NodeTraversal::next(*node)) {
1732         if (node->isElementNode()) {
1733             if (!nodeSet.contains(node->parentNode())) {
1734                 if (RenderBoxModelObject* renderBoxModelObject = toElement(node)->renderBoxModelObject()) {
1735                     Vector<FloatQuad> elementQuads;
1736                     renderBoxModelObject->absoluteQuads(elementQuads);
1737                     m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(elementQuads, *renderBoxModelObject);
1738 
1739                     quads.appendVector(elementQuads);
1740                 }
1741             }
1742         } else if (node->isTextNode()) {
1743             if (RenderText* renderText = toText(node)->renderer()) {
1744                 int startOffset = (node == startContainer) ? m_start.offset() : 0;
1745                 int endOffset = (node == endContainer) ? m_end.offset() : INT_MAX;
1746 
1747                 Vector<FloatQuad> textQuads;
1748                 renderText->absoluteQuadsForRange(textQuads, startOffset, endOffset);
1749                 m_ownerDocument->adjustFloatQuadsForScrollAndAbsoluteZoom(textQuads, *renderText);
1750 
1751                 quads.appendVector(textQuads);
1752             }
1753         }
1754     }
1755 }
1756 
boundingRect() const1757 FloatRect Range::boundingRect() const
1758 {
1759     m_ownerDocument->updateLayoutIgnorePendingStylesheets();
1760 
1761     Vector<FloatQuad> quads;
1762     getBorderAndTextQuads(quads);
1763     if (quads.isEmpty())
1764         return FloatRect();
1765 
1766     FloatRect result;
1767     for (size_t i = 0; i < quads.size(); ++i)
1768         result.unite(quads[i].boundingBox());
1769 
1770     return result;
1771 }
1772 
trace(Visitor * visitor)1773 void Range::trace(Visitor* visitor)
1774 {
1775     visitor->trace(m_ownerDocument);
1776     visitor->trace(m_start);
1777     visitor->trace(m_end);
1778 }
1779 
1780 } // namespace blink
1781 
1782 #ifndef NDEBUG
1783 
showTree(const blink::Range * range)1784 void showTree(const blink::Range* range)
1785 {
1786     if (range && range->boundaryPointsValid()) {
1787         range->startContainer()->showTreeAndMark(range->startContainer(), "S", range->endContainer(), "E");
1788         fprintf(stderr, "start offset: %d, end offset: %d\n", range->startOffset(), range->endOffset());
1789     }
1790 }
1791 
1792 #endif
1793