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
7 * rights reserved.
8 * Copyright (C) 2011 Motorola Mobility. All rights reserved.
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26 #include "third_party/blink/renderer/core/dom/range.h"
27
28 #include "third_party/blink/renderer/core/dom/character_data.h"
29 #include "third_party/blink/renderer/core/dom/container_node.h"
30 #include "third_party/blink/renderer/core/dom/document_fragment.h"
31 #include "third_party/blink/renderer/core/dom/events/event_dispatch_forbidden_scope.h"
32 #include "third_party/blink/renderer/core/dom/events/scoped_event_queue.h"
33 #include "third_party/blink/renderer/core/dom/node.h"
34 #include "third_party/blink/renderer/core/dom/node_traversal.h"
35 #include "third_party/blink/renderer/core/dom/node_with_index.h"
36 #include "third_party/blink/renderer/core/dom/processing_instruction.h"
37 #include "third_party/blink/renderer/core/dom/text.h"
38 #include "third_party/blink/renderer/core/editing/editing_utilities.h"
39 #include "third_party/blink/renderer/core/editing/ephemeral_range.h"
40 #include "third_party/blink/renderer/core/editing/frame_selection.h"
41 #include "third_party/blink/renderer/core/editing/iterators/text_iterator.h"
42 #include "third_party/blink/renderer/core/editing/selection_template.h"
43 #include "third_party/blink/renderer/core/editing/serializers/serialization.h"
44 #include "third_party/blink/renderer/core/editing/set_selection_options.h"
45 #include "third_party/blink/renderer/core/editing/visible_position.h"
46 #include "third_party/blink/renderer/core/editing/visible_units.h"
47 #include "third_party/blink/renderer/core/frame/local_frame.h"
48 #include "third_party/blink/renderer/core/frame/settings.h"
49 #include "third_party/blink/renderer/core/geometry/dom_rect.h"
50 #include "third_party/blink/renderer/core/geometry/dom_rect_list.h"
51 #include "third_party/blink/renderer/core/html/html_body_element.h"
52 #include "third_party/blink/renderer/core/html/html_element.h"
53 #include "third_party/blink/renderer/core/layout/layout_object.h"
54 #include "third_party/blink/renderer/core/layout/layout_text.h"
55 #include "third_party/blink/renderer/core/layout/layout_text_fragment.h"
56 #include "third_party/blink/renderer/core/svg/svg_svg_element.h"
57 #include "third_party/blink/renderer/core/trustedtypes/trusted_types_util.h"
58 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
59 #include "third_party/blink/renderer/platform/geometry/float_quad.h"
60 #include "third_party/blink/renderer/platform/heap/heap.h"
61 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
62 #include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
63
64 namespace blink {
65
66 class RangeUpdateScope {
67 STACK_ALLOCATED();
68
69 public:
RangeUpdateScope(Range * range)70 explicit RangeUpdateScope(Range* range) {
71 DCHECK(range);
72 if (++scope_count_ == 1) {
73 range_ = range;
74 old_document_ = &range->OwnerDocument();
75 #if DCHECK_IS_ON()
76 current_range_ = range;
77 } else {
78 DCHECK_EQ(current_range_, range);
79 #endif
80 }
81 }
82
~RangeUpdateScope()83 ~RangeUpdateScope() {
84 DCHECK_GE(scope_count_, 1);
85 if (--scope_count_ > 0)
86 return;
87 Settings* settings = old_document_->GetFrame()
88 ? old_document_->GetFrame()->GetSettings()
89 : nullptr;
90 if (!settings ||
91 !settings->GetDoNotUpdateSelectionOnMutatingSelectionRange()) {
92 range_->RemoveFromSelectionIfInDifferentRoot(*old_document_);
93 range_->UpdateSelectionIfAddedToSelection();
94 }
95 #if DCHECK_IS_ON()
96 current_range_ = nullptr;
97 #endif
98 }
99
100 private:
101 static int scope_count_;
102 #if DCHECK_IS_ON()
103 // This raw pointer is safe because
104 // - s_currentRange has a valid pointer only if RangeUpdateScope instance is
105 // live.
106 // - RangeUpdateScope is used only in Range member functions.
107 static Range* current_range_;
108 #endif
109 Range* range_ = nullptr;
110 Document* old_document_ = nullptr;
111
112 DISALLOW_COPY_AND_ASSIGN(RangeUpdateScope);
113 };
114
115 int RangeUpdateScope::scope_count_ = 0;
116 #if DCHECK_IS_ON()
117 Range* RangeUpdateScope::current_range_;
118 #endif
119
Range(Document & owner_document)120 Range::Range(Document& owner_document)
121 : owner_document_(&owner_document),
122 start_(*owner_document_),
123 end_(*owner_document_) {
124 owner_document_->AttachRange(this);
125 }
126
Create(Document & owner_document)127 Range* Range::Create(Document& owner_document) {
128 return MakeGarbageCollected<Range>(owner_document);
129 }
130
Range(Document & owner_document,Node * start_container,unsigned start_offset,Node * end_container,unsigned end_offset)131 Range::Range(Document& owner_document,
132 Node* start_container,
133 unsigned start_offset,
134 Node* end_container,
135 unsigned end_offset)
136 : owner_document_(&owner_document),
137 start_(*owner_document_),
138 end_(*owner_document_) {
139 owner_document_->AttachRange(this);
140
141 // Simply setting the containers and offsets directly would not do any of the
142 // checking that setStart and setEnd do, so we call those functions.
143 setStart(start_container, start_offset);
144 setEnd(end_container, end_offset);
145 }
146
Range(Document & owner_document,const Position & start,const Position & end)147 Range::Range(Document& owner_document,
148 const Position& start,
149 const Position& end)
150 : Range(owner_document,
151 start.ComputeContainerNode(),
152 start.ComputeOffsetInContainerNode(),
153 end.ComputeContainerNode(),
154 end.ComputeOffsetInContainerNode()) {}
155
Dispose()156 void Range::Dispose() {
157 // A prompt detach from the owning Document helps avoid GC overhead.
158 owner_document_->DetachRange(this);
159 }
160
IsConnected() const161 bool Range::IsConnected() const {
162 DCHECK_EQ(start_.IsConnected(), end_.IsConnected());
163 return start_.IsConnected();
164 }
165
SetDocument(Document & document)166 void Range::SetDocument(Document& document) {
167 DCHECK_NE(owner_document_, document);
168 DCHECK(owner_document_);
169 owner_document_->DetachRange(this);
170 owner_document_ = &document;
171 start_.SetToStartOfNode(document);
172 end_.SetToStartOfNode(document);
173 owner_document_->AttachRange(this);
174 }
175
commonAncestorContainer() const176 Node* Range::commonAncestorContainer() const {
177 return commonAncestorContainer(&start_.Container(), &end_.Container());
178 }
179
commonAncestorContainer(const Node * container_a,const Node * container_b)180 Node* Range::commonAncestorContainer(const Node* container_a,
181 const Node* container_b) {
182 if (!container_a || !container_b)
183 return nullptr;
184 return container_a->CommonAncestor(*container_b, NodeTraversal::Parent);
185 }
186
CheckForDifferentRootContainer(const RangeBoundaryPoint & start,const RangeBoundaryPoint & end)187 static inline bool CheckForDifferentRootContainer(
188 const RangeBoundaryPoint& start,
189 const RangeBoundaryPoint& end) {
190 Node* end_root_container = &end.Container();
191 while (end_root_container->parentNode())
192 end_root_container = end_root_container->parentNode();
193 Node* start_root_container = &start.Container();
194 while (start_root_container->parentNode())
195 start_root_container = start_root_container->parentNode();
196
197 return start_root_container != end_root_container ||
198 (Range::compareBoundaryPoints(start, end, ASSERT_NO_EXCEPTION) > 0);
199 }
200
setStart(Node * ref_node,unsigned offset,ExceptionState & exception_state)201 void Range::setStart(Node* ref_node,
202 unsigned offset,
203 ExceptionState& exception_state) {
204 if (!ref_node) {
205 // FIXME: Generated bindings code never calls with null, and neither should
206 // other callers!
207 exception_state.ThrowTypeError("The node provided is null.");
208 return;
209 }
210
211 RangeUpdateScope scope(this);
212 bool did_move_document = false;
213 if (ref_node->GetDocument() != owner_document_) {
214 SetDocument(ref_node->GetDocument());
215 did_move_document = true;
216 }
217
218 Node* child_node = CheckNodeWOffset(ref_node, offset, exception_state);
219 if (exception_state.HadException())
220 return;
221
222 start_.Set(*ref_node, offset, child_node);
223
224 if (did_move_document || CheckForDifferentRootContainer(start_, end_))
225 collapse(true);
226 }
227
setEnd(Node * ref_node,unsigned offset,ExceptionState & exception_state)228 void Range::setEnd(Node* ref_node,
229 unsigned offset,
230 ExceptionState& exception_state) {
231 if (!ref_node) {
232 // FIXME: Generated bindings code never calls with null, and neither should
233 // other callers!
234 exception_state.ThrowTypeError("The node provided is null.");
235 return;
236 }
237
238 RangeUpdateScope scope(this);
239 bool did_move_document = false;
240 if (ref_node->GetDocument() != owner_document_) {
241 SetDocument(ref_node->GetDocument());
242 did_move_document = true;
243 }
244
245 Node* child_node = CheckNodeWOffset(ref_node, offset, exception_state);
246 if (exception_state.HadException())
247 return;
248
249 end_.Set(*ref_node, offset, child_node);
250
251 if (did_move_document || CheckForDifferentRootContainer(start_, end_))
252 collapse(false);
253 }
254
setStart(const Position & start,ExceptionState & exception_state)255 void Range::setStart(const Position& start, ExceptionState& exception_state) {
256 Position parent_anchored = start.ParentAnchoredEquivalent();
257 setStart(parent_anchored.ComputeContainerNode(),
258 parent_anchored.OffsetInContainerNode(), exception_state);
259 }
260
setEnd(const Position & end,ExceptionState & exception_state)261 void Range::setEnd(const Position& end, ExceptionState& exception_state) {
262 Position parent_anchored = end.ParentAnchoredEquivalent();
263 setEnd(parent_anchored.ComputeContainerNode(),
264 parent_anchored.OffsetInContainerNode(), exception_state);
265 }
266
collapse(bool to_start)267 void Range::collapse(bool to_start) {
268 RangeUpdateScope scope(this);
269 if (to_start)
270 end_ = start_;
271 else
272 start_ = end_;
273 }
274
HasSameRoot(const Node & node) const275 bool Range::HasSameRoot(const Node& node) const {
276 if (node.GetDocument() != owner_document_)
277 return false;
278 // commonAncestorContainer() is O(depth). We should avoid to call it in common
279 // cases.
280 if (node.IsInTreeScope() && start_.Container().IsInTreeScope() &&
281 &node.GetTreeScope() == &start_.Container().GetTreeScope())
282 return true;
283 return node.CommonAncestor(start_.Container(), NodeTraversal::Parent);
284 }
285
isPointInRange(Node * ref_node,unsigned offset,ExceptionState & exception_state) const286 bool Range::isPointInRange(Node* ref_node,
287 unsigned offset,
288 ExceptionState& exception_state) const {
289 if (!ref_node) {
290 // FIXME: Generated bindings code never calls with null, and neither should
291 // other callers!
292 exception_state.ThrowTypeError("The node provided is null.");
293 return false;
294 }
295 if (!HasSameRoot(*ref_node))
296 return false;
297
298 CheckNodeWOffset(ref_node, offset, exception_state);
299 if (exception_state.HadException())
300 return false;
301
302 return compareBoundaryPoints(ref_node, offset, &start_.Container(),
303 start_.Offset(), exception_state) >= 0 &&
304 !exception_state.HadException() &&
305 compareBoundaryPoints(ref_node, offset, &end_.Container(),
306 end_.Offset(), exception_state) <= 0 &&
307 !exception_state.HadException();
308 }
309
comparePoint(Node * ref_node,unsigned offset,ExceptionState & exception_state) const310 int16_t Range::comparePoint(Node* ref_node,
311 unsigned offset,
312 ExceptionState& exception_state) const {
313 // http://developer.mozilla.org/en/docs/DOM:range.comparePoint
314 // This method returns -1, 0 or 1 depending on if the point described by the
315 // refNode node and an offset within the node is before, same as, or after the
316 // range respectively.
317
318 if (!HasSameRoot(*ref_node)) {
319 exception_state.ThrowDOMException(
320 DOMExceptionCode::kWrongDocumentError,
321 "The node provided and the Range are not in the same tree.");
322 return 0;
323 }
324
325 CheckNodeWOffset(ref_node, offset, exception_state);
326 if (exception_state.HadException())
327 return 0;
328
329 // compare to start, and point comes before
330 if (compareBoundaryPoints(ref_node, offset, &start_.Container(),
331 start_.Offset(), exception_state) < 0)
332 return -1;
333
334 if (exception_state.HadException())
335 return 0;
336
337 // compare to end, and point comes after
338 if (compareBoundaryPoints(ref_node, offset, &end_.Container(), end_.Offset(),
339 exception_state) > 0 &&
340 !exception_state.HadException())
341 return 1;
342
343 // point is in the middle of this range, or on the boundary points
344 return 0;
345 }
346
compareBoundaryPoints(unsigned how,const Range * source_range,ExceptionState & exception_state) const347 int16_t Range::compareBoundaryPoints(unsigned how,
348 const Range* source_range,
349 ExceptionState& exception_state) const {
350 if (!(how == kStartToStart || how == kStartToEnd || how == kEndToEnd ||
351 how == kEndToStart)) {
352 exception_state.ThrowDOMException(
353 DOMExceptionCode::kNotSupportedError,
354 "The comparison method provided must be "
355 "one of 'START_TO_START', 'START_TO_END', "
356 "'END_TO_END', or 'END_TO_START'.");
357 return 0;
358 }
359
360 Node* this_cont = commonAncestorContainer();
361 Node* source_cont = source_range->commonAncestorContainer();
362 if (this_cont->GetDocument() != source_cont->GetDocument()) {
363 exception_state.ThrowDOMException(
364 DOMExceptionCode::kWrongDocumentError,
365 "The source range is in a different document than this range.");
366 return 0;
367 }
368
369 Node* this_top = this_cont;
370 Node* source_top = source_cont;
371 while (this_top->parentNode())
372 this_top = this_top->parentNode();
373 while (source_top->parentNode())
374 source_top = source_top->parentNode();
375 if (this_top != source_top) { // in different DocumentFragments
376 exception_state.ThrowDOMException(
377 DOMExceptionCode::kWrongDocumentError,
378 "The source range is in a different document than this range.");
379 return 0;
380 }
381
382 switch (how) {
383 case kStartToStart:
384 return compareBoundaryPoints(start_, source_range->start_,
385 exception_state);
386 case kStartToEnd:
387 return compareBoundaryPoints(end_, source_range->start_, exception_state);
388 case kEndToEnd:
389 return compareBoundaryPoints(end_, source_range->end_, exception_state);
390 case kEndToStart:
391 return compareBoundaryPoints(start_, source_range->end_, exception_state);
392 }
393
394 NOTREACHED();
395 return 0;
396 }
397
compareBoundaryPoints(Node * container_a,unsigned offset_a,Node * container_b,unsigned offset_b,ExceptionState & exception_state)398 int16_t Range::compareBoundaryPoints(Node* container_a,
399 unsigned offset_a,
400 Node* container_b,
401 unsigned offset_b,
402 ExceptionState& exception_state) {
403 bool disconnected = false;
404 int16_t result = ComparePositionsInDOMTree(container_a, offset_a, container_b,
405 offset_b, &disconnected);
406 if (disconnected) {
407 exception_state.ThrowDOMException(
408 DOMExceptionCode::kWrongDocumentError,
409 "The two ranges are in separate documents.");
410 return 0;
411 }
412 return result;
413 }
414
compareBoundaryPoints(const RangeBoundaryPoint & boundary_a,const RangeBoundaryPoint & boundary_b,ExceptionState & exception_state)415 int16_t Range::compareBoundaryPoints(const RangeBoundaryPoint& boundary_a,
416 const RangeBoundaryPoint& boundary_b,
417 ExceptionState& exception_state) {
418 return compareBoundaryPoints(&boundary_a.Container(), boundary_a.Offset(),
419 &boundary_b.Container(), boundary_b.Offset(),
420 exception_state);
421 }
422
BoundaryPointsValid() const423 bool Range::BoundaryPointsValid() const {
424 DummyExceptionStateForTesting exception_state;
425 return compareBoundaryPoints(start_, end_, exception_state) <= 0 &&
426 !exception_state.HadException();
427 }
428
deleteContents(ExceptionState & exception_state)429 void Range::deleteContents(ExceptionState& exception_state) {
430 DCHECK(BoundaryPointsValid());
431
432 {
433 EventQueueScope event_queue_scope;
434 ProcessContents(DELETE_CONTENTS, exception_state);
435 }
436 }
437
intersectsNode(Node * ref_node,ExceptionState & exception_state)438 bool Range::intersectsNode(Node* ref_node, ExceptionState& exception_state) {
439 // http://developer.mozilla.org/en/docs/DOM:range.intersectsNode
440 // Returns a bool if the node intersects the range.
441 if (!ref_node) {
442 // FIXME: Generated bindings code never calls with null, and neither should
443 // other callers!
444 exception_state.ThrowTypeError("The node provided is null.");
445 return false;
446 }
447 if (!HasSameRoot(*ref_node))
448 return false;
449
450 ContainerNode* parent_node = ref_node->parentNode();
451 if (!parent_node)
452 return true;
453
454 int node_index = ref_node->NodeIndex();
455 return Position(parent_node, node_index) < end_.ToPosition() &&
456 Position(parent_node, node_index + 1) > start_.ToPosition();
457 }
458
HighestAncestorUnderCommonRoot(Node * node,Node * common_root)459 static inline Node* HighestAncestorUnderCommonRoot(Node* node,
460 Node* common_root) {
461 if (node == common_root)
462 return nullptr;
463
464 DCHECK(common_root->contains(node));
465
466 while (node->parentNode() != common_root)
467 node = node->parentNode();
468
469 return node;
470 }
471
ChildOfCommonRootBeforeOffset(Node * container,unsigned offset,Node * common_root)472 static inline Node* ChildOfCommonRootBeforeOffset(Node* container,
473 unsigned offset,
474 Node* common_root) {
475 DCHECK(container);
476 DCHECK(common_root);
477
478 if (!common_root->contains(container))
479 return nullptr;
480
481 if (container == common_root) {
482 container = container->firstChild();
483 for (unsigned i = 0; container && i < offset; i++)
484 container = container->nextSibling();
485 } else {
486 while (container->parentNode() != common_root)
487 container = container->parentNode();
488 }
489
490 return container;
491 }
492
LengthOfContents(const Node * node)493 static unsigned LengthOfContents(const Node* node) {
494 // This switch statement must be consistent with that of
495 // Range::processContentsBetweenOffsets.
496 switch (node->getNodeType()) {
497 case Node::kTextNode:
498 case Node::kCdataSectionNode:
499 case Node::kCommentNode:
500 case Node::kProcessingInstructionNode:
501 return To<CharacterData>(node)->length();
502 case Node::kElementNode:
503 case Node::kDocumentNode:
504 case Node::kDocumentFragmentNode:
505 return To<ContainerNode>(node)->CountChildren();
506 case Node::kAttributeNode:
507 case Node::kDocumentTypeNode:
508 return 0;
509 }
510 NOTREACHED();
511 return 0;
512 }
513
ProcessContents(ActionType action,ExceptionState & exception_state)514 DocumentFragment* Range::ProcessContents(ActionType action,
515 ExceptionState& exception_state) {
516 DocumentFragment* fragment = nullptr;
517 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS)
518 fragment = DocumentFragment::Create(*owner_document_.Get());
519
520 if (collapsed())
521 return fragment;
522
523 Node* common_root = commonAncestorContainer();
524 DCHECK(common_root);
525
526 if (start_.Container() == end_.Container()) {
527 ProcessContentsBetweenOffsets(action, fragment, &start_.Container(),
528 start_.Offset(), end_.Offset(),
529 exception_state);
530 return fragment;
531 }
532
533 // Since mutation observers can modify the range during the process, the
534 // boundary points need to be saved.
535 const RangeBoundaryPoint original_start(start_);
536 const RangeBoundaryPoint original_end(end_);
537
538 // what is the highest node that partially selects the start / end of the
539 // range?
540 Node* partial_start =
541 HighestAncestorUnderCommonRoot(&original_start.Container(), common_root);
542 Node* partial_end =
543 HighestAncestorUnderCommonRoot(&original_end.Container(), common_root);
544
545 // Start and end containers are different.
546 // There are three possibilities here:
547 // 1. Start container == commonRoot (End container must be a descendant)
548 // 2. End container == commonRoot (Start container must be a descendant)
549 // 3. Neither is commonRoot, they are both descendants
550 //
551 // In case 3, we grab everything after the start (up until a direct child
552 // of commonRoot) into leftContents, and everything before the end (up until
553 // a direct child of commonRoot) into rightContents. Then we process all
554 // commonRoot children between leftContents and rightContents
555 //
556 // In case 1 or 2, we skip either processing of leftContents or rightContents,
557 // in which case the last lot of nodes either goes from the first or last
558 // child of commonRoot.
559 //
560 // These are deleted, cloned, or extracted (i.e. both) depending on action.
561
562 // Note that we are verifying that our common root hierarchy is still intact
563 // after any DOM mutation event, at various stages below. See webkit bug
564 // 60350.
565
566 Node* left_contents = nullptr;
567 if (original_start.Container() != common_root &&
568 common_root->contains(&original_start.Container())) {
569 left_contents = ProcessContentsBetweenOffsets(
570 action, nullptr, &original_start.Container(), original_start.Offset(),
571 LengthOfContents(&original_start.Container()), exception_state);
572 left_contents = ProcessAncestorsAndTheirSiblings(
573 action, &original_start.Container(), kProcessContentsForward,
574 left_contents, common_root, exception_state);
575 }
576
577 Node* right_contents = nullptr;
578 if (end_.Container() != common_root &&
579 common_root->contains(&original_end.Container())) {
580 right_contents = ProcessContentsBetweenOffsets(
581 action, nullptr, &original_end.Container(), 0, original_end.Offset(),
582 exception_state);
583 right_contents = ProcessAncestorsAndTheirSiblings(
584 action, &original_end.Container(), kProcessContentsBackward,
585 right_contents, common_root, exception_state);
586 }
587
588 // delete all children of commonRoot between the start and end container
589 Node* process_start = ChildOfCommonRootBeforeOffset(
590 &original_start.Container(), original_start.Offset(), common_root);
591 // process_start contains nodes before start_.
592 if (process_start && original_start.Container() != common_root)
593 process_start = process_start->nextSibling();
594 Node* process_end = ChildOfCommonRootBeforeOffset(
595 &original_end.Container(), original_end.Offset(), common_root);
596
597 // Collapse the range, making sure that the result is not within a node that
598 // was partially selected.
599 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS) {
600 if (partial_start && common_root->contains(partial_start)) {
601 // FIXME: We should not continue if we have an earlier error.
602 exception_state.ClearException();
603 setStart(partial_start->parentNode(), partial_start->NodeIndex() + 1,
604 exception_state);
605 } else if (partial_end && common_root->contains(partial_end)) {
606 // FIXME: We should not continue if we have an earlier error.
607 exception_state.ClearException();
608 setStart(partial_end->parentNode(), partial_end->NodeIndex(),
609 exception_state);
610 }
611 if (exception_state.HadException())
612 return nullptr;
613 end_ = start_;
614 }
615
616 // Now add leftContents, stuff in between, and rightContents to the fragment
617 // (or just delete the stuff in between)
618
619 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) && left_contents)
620 fragment->AppendChild(left_contents, exception_state);
621
622 if (process_start) {
623 NodeVector nodes;
624 for (Node* n = process_start; n && n != process_end; n = n->nextSibling())
625 nodes.push_back(n);
626 ProcessNodes(action, nodes, common_root, fragment, exception_state);
627 }
628
629 if ((action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) &&
630 right_contents)
631 fragment->AppendChild(right_contents, exception_state);
632
633 return fragment;
634 }
635
DeleteCharacterData(CharacterData * data,unsigned start_offset,unsigned end_offset,ExceptionState & exception_state)636 static inline void DeleteCharacterData(CharacterData* data,
637 unsigned start_offset,
638 unsigned end_offset,
639 ExceptionState& exception_state) {
640 if (data->length() - end_offset)
641 data->deleteData(end_offset, data->length() - end_offset, exception_state);
642 if (start_offset)
643 data->deleteData(0, start_offset, exception_state);
644 }
645
ProcessContentsBetweenOffsets(ActionType action,DocumentFragment * fragment,Node * container,unsigned start_offset,unsigned end_offset,ExceptionState & exception_state)646 Node* Range::ProcessContentsBetweenOffsets(ActionType action,
647 DocumentFragment* fragment,
648 Node* container,
649 unsigned start_offset,
650 unsigned end_offset,
651 ExceptionState& exception_state) {
652 DCHECK(container);
653 DCHECK_LE(start_offset, end_offset);
654
655 // This switch statement must be consistent with that of
656 // lengthOfContents.
657 Node* result = nullptr;
658 switch (container->getNodeType()) {
659 case Node::kTextNode:
660 case Node::kCdataSectionNode:
661 case Node::kCommentNode:
662 case Node::kProcessingInstructionNode:
663 end_offset = std::min(end_offset, To<CharacterData>(container)->length());
664 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
665 CharacterData* c =
666 static_cast<CharacterData*>(container->cloneNode(true));
667 DeleteCharacterData(c, start_offset, end_offset, exception_state);
668 if (fragment) {
669 result = fragment;
670 result->appendChild(c, exception_state);
671 } else {
672 result = c;
673 }
674 }
675 if (action == EXTRACT_CONTENTS || action == DELETE_CONTENTS)
676 To<CharacterData>(container)->deleteData(
677 start_offset, end_offset - start_offset, exception_state);
678 break;
679 case Node::kElementNode:
680 case Node::kAttributeNode:
681 case Node::kDocumentNode:
682 case Node::kDocumentTypeNode:
683 case Node::kDocumentFragmentNode:
684 // FIXME: Should we assert that some nodes never appear here?
685 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
686 if (fragment)
687 result = fragment;
688 else
689 result = container->cloneNode(false);
690 }
691
692 Node* n = container->firstChild();
693 NodeVector nodes;
694 for (unsigned i = start_offset; n && i; i--)
695 n = n->nextSibling();
696 for (unsigned i = start_offset; n && i < end_offset;
697 i++, n = n->nextSibling())
698 nodes.push_back(n);
699
700 ProcessNodes(action, nodes, container, result, exception_state);
701 break;
702 }
703
704 return result;
705 }
706
ProcessNodes(ActionType action,NodeVector & nodes,Node * old_container,Node * new_container,ExceptionState & exception_state)707 void Range::ProcessNodes(ActionType action,
708 NodeVector& nodes,
709 Node* old_container,
710 Node* new_container,
711 ExceptionState& exception_state) {
712 for (auto& node : nodes) {
713 switch (action) {
714 case DELETE_CONTENTS:
715 old_container->removeChild(node.Get(), exception_state);
716 break;
717 case EXTRACT_CONTENTS:
718 new_container->appendChild(
719 node.Release(), exception_state); // Will remove n from its parent.
720 break;
721 case CLONE_CONTENTS:
722 new_container->appendChild(node->cloneNode(true), exception_state);
723 break;
724 }
725 }
726 }
727
ProcessAncestorsAndTheirSiblings(ActionType action,Node * container,ContentsProcessDirection direction,Node * cloned_container,Node * common_root,ExceptionState & exception_state)728 Node* Range::ProcessAncestorsAndTheirSiblings(
729 ActionType action,
730 Node* container,
731 ContentsProcessDirection direction,
732 Node* cloned_container,
733 Node* common_root,
734 ExceptionState& exception_state) {
735 NodeVector ancestors;
736 for (Node& runner : NodeTraversal::AncestorsOf(*container)) {
737 if (runner == common_root)
738 break;
739 ancestors.push_back(runner);
740 }
741
742 Node* first_child_in_ancestor_to_process =
743 direction == kProcessContentsForward ? container->nextSibling()
744 : container->previousSibling();
745 for (const auto& ancestor : ancestors) {
746 if (action == EXTRACT_CONTENTS || action == CLONE_CONTENTS) {
747 // Might have been removed already during mutation event.
748 if (Node* cloned_ancestor = ancestor->cloneNode(false)) {
749 cloned_ancestor->appendChild(cloned_container, exception_state);
750 cloned_container = cloned_ancestor;
751 }
752 }
753
754 // Copy siblings of an ancestor of start/end containers
755 // FIXME: This assertion may fail if DOM is modified during mutation event
756 // FIXME: Share code with Range::processNodes
757 DCHECK(!first_child_in_ancestor_to_process ||
758 first_child_in_ancestor_to_process->parentNode() == ancestor);
759
760 NodeVector nodes;
761 for (Node* child = first_child_in_ancestor_to_process; child;
762 child = (direction == kProcessContentsForward)
763 ? child->nextSibling()
764 : child->previousSibling())
765 nodes.push_back(child);
766
767 for (const auto& node : nodes) {
768 Node* child = node.Get();
769 switch (action) {
770 case DELETE_CONTENTS:
771 // Prior call of ancestor->removeChild() may cause a tree change due
772 // to DOMSubtreeModified event. Therefore, we need to make sure
773 // |ancestor| is still |child|'s parent.
774 if (ancestor == child->parentNode())
775 ancestor->removeChild(child, exception_state);
776 break;
777 case EXTRACT_CONTENTS: // will remove child from ancestor
778 if (direction == kProcessContentsForward)
779 cloned_container->appendChild(child, exception_state);
780 else
781 cloned_container->insertBefore(
782 child, cloned_container->firstChild(), exception_state);
783 break;
784 case CLONE_CONTENTS:
785 if (direction == kProcessContentsForward)
786 cloned_container->appendChild(child->cloneNode(true),
787 exception_state);
788 else
789 cloned_container->insertBefore(child->cloneNode(true),
790 cloned_container->firstChild(),
791 exception_state);
792 break;
793 }
794 }
795 first_child_in_ancestor_to_process = direction == kProcessContentsForward
796 ? ancestor->nextSibling()
797 : ancestor->previousSibling();
798 }
799
800 return cloned_container;
801 }
802
extractContents(ExceptionState & exception_state)803 DocumentFragment* Range::extractContents(ExceptionState& exception_state) {
804 CheckExtractPrecondition(exception_state);
805 if (exception_state.HadException())
806 return nullptr;
807
808 EventQueueScope scope;
809 DocumentFragment* fragment = ProcessContents(EXTRACT_CONTENTS,
810 exception_state);
811 // |extractContents| has extended attributes [NewObject, DoNotTestNewObject],
812 // so it's better to have a test that exercises the following condition:
813 //
814 // !fragment || DOMDataStore::GetWrapper(fragment, isolate).IsEmpty()
815 //
816 // however, there is no access to |isolate| so far. So, we simply omit the
817 // test so far.
818 return fragment;
819 }
820
cloneContents(ExceptionState & exception_state)821 DocumentFragment* Range::cloneContents(ExceptionState& exception_state) {
822 return ProcessContents(CLONE_CONTENTS, exception_state);
823 }
824
825 // https://dom.spec.whatwg.org/#concept-range-insert
insertNode(Node * new_node,ExceptionState & exception_state)826 void Range::insertNode(Node* new_node, ExceptionState& exception_state) {
827 if (!new_node) {
828 // FIXME: Generated bindings code never calls with null, and neither should
829 // other callers!
830 exception_state.ThrowTypeError("The node provided is null.");
831 return;
832 }
833
834 // 1. If range’s start node is a ProcessingInstruction or Comment node, is a
835 // Text node whose parent is null, or is node, then throw a
836 // HierarchyRequestError.
837 Node& start_node = start_.Container();
838 if (start_node.getNodeType() == Node::kProcessingInstructionNode ||
839 start_node.getNodeType() == Node::kCommentNode) {
840 exception_state.ThrowDOMException(
841 DOMExceptionCode::kHierarchyRequestError,
842 "Nodes of type '" + new_node->nodeName() +
843 "' may not be inserted inside nodes of type '" +
844 start_node.nodeName() + "'.");
845 return;
846 }
847 const bool start_is_text = start_node.IsTextNode();
848 if (start_is_text && !start_node.parentNode()) {
849 exception_state.ThrowDOMException(DOMExceptionCode::kHierarchyRequestError,
850 "This operation would split a text node, "
851 "but there's no parent into which to "
852 "insert.");
853 return;
854 }
855 if (start_node == new_node) {
856 exception_state.ThrowDOMException(
857 DOMExceptionCode::kHierarchyRequestError,
858 "Unable to insert a node into a Range starting from the node itself.");
859 return;
860 }
861
862 // According to the specification, the following condition is checked in the
863 // step 6. However our EnsurePreInsertionValidity() supports only
864 // ContainerNode parent.
865 if (start_node.IsAttributeNode()) {
866 exception_state.ThrowDOMException(
867 DOMExceptionCode::kHierarchyRequestError,
868 "Nodes of type '" + new_node->nodeName() +
869 "' may not be inserted inside nodes of type 'Attr'.");
870 return;
871 }
872
873 // 2. Let referenceNode be null.
874 Node* reference_node = nullptr;
875 // 3. If range’s start node is a Text node, set referenceNode to that Text
876 // node.
877 // 4. Otherwise, set referenceNode to the child of start node whose index is
878 // start offset, and null if there is no such child.
879 if (start_is_text)
880 reference_node = &start_node;
881 else
882 reference_node = NodeTraversal::ChildAt(start_node, start_.Offset());
883
884 // 5. Let parent be range’s start node if referenceNode is null, and
885 // referenceNode’s parent otherwise.
886 ContainerNode& parent = reference_node ? *reference_node->parentNode()
887 : To<ContainerNode>(start_node);
888
889 // 6. Ensure pre-insertion validity of node into parent before referenceNode.
890 if (!parent.EnsurePreInsertionValidity(*new_node, reference_node, nullptr,
891 exception_state))
892 return;
893
894 EventQueueScope scope;
895 // 7. If range's start node is a Text node, set referenceNode to the result of
896 // splitting it with offset range’s start offset.
897 if (start_is_text) {
898 reference_node =
899 To<Text>(start_node).splitText(start_.Offset(), exception_state);
900 if (exception_state.HadException())
901 return;
902 }
903
904 // 8. If node is referenceNode, set referenceNode to its next sibling.
905 if (new_node == reference_node)
906 reference_node = reference_node->nextSibling();
907
908 // 9. If node's parent is not null, remove node from its parent.
909 if (new_node->parentNode()) {
910 new_node->remove(exception_state);
911 if (exception_state.HadException())
912 return;
913 }
914
915 // 10. Let newOffset be parent's length if referenceNode is null, and
916 // referenceNode's index otherwise.
917 unsigned new_offset =
918 reference_node ? reference_node->NodeIndex() : LengthOfContents(&parent);
919
920 // 11. Increase newOffset by node's length if node is a DocumentFragment node,
921 // and one otherwise.
922 new_offset += new_node->IsDocumentFragment() ? LengthOfContents(new_node) : 1;
923
924 // 12. Pre-insert node into parent before referenceNode.
925 parent.insertBefore(new_node, reference_node, exception_state);
926 if (exception_state.HadException())
927 return;
928
929 // 13. If range's start and end are the same, set range's end to (parent,
930 // newOffset).
931 if (start_ == end_)
932 setEnd(&parent, new_offset, exception_state);
933 }
934
toString() const935 String Range::toString() const {
936 StringBuilder builder;
937
938 Node* past_last = PastLastNode();
939 for (Node* n = FirstNode(); n != past_last; n = NodeTraversal::Next(*n)) {
940 Node::NodeType type = n->getNodeType();
941 if (type == Node::kTextNode || type == Node::kCdataSectionNode) {
942 String data = To<CharacterData>(n)->data();
943 unsigned length = data.length();
944 unsigned start =
945 (n == start_.Container()) ? std::min(start_.Offset(), length) : 0;
946 unsigned end = (n == end_.Container())
947 ? std::min(std::max(start, end_.Offset()), length)
948 : length;
949 builder.Append(data, start, end - start);
950 }
951 }
952
953 return builder.ToString();
954 }
955
GetText() const956 String Range::GetText() const {
957 DCHECK(!owner_document_->NeedsLayoutTreeUpdate());
958 return PlainText(EphemeralRange(this),
959 TextIteratorBehavior::Builder()
960 .SetEmitsObjectReplacementCharacter(true)
961 .Build());
962 }
963
createContextualFragment(const String & markup,ExceptionState & exception_state)964 DocumentFragment* Range::createContextualFragment(
965 const String& markup,
966 ExceptionState& exception_state) {
967 // Algorithm:
968 // http://domparsing.spec.whatwg.org/#extensions-to-the-range-interface
969
970 DCHECK(!markup.IsNull());
971
972 Node* node = &start_.Container();
973
974 // Step 1.
975 Element* element;
976 if (!start_.Offset() &&
977 (node->IsDocumentNode() || node->IsDocumentFragment()))
978 element = nullptr;
979 else if (auto* node_element = DynamicTo<Element>(node))
980 element = node_element;
981 else
982 element = node->parentElement();
983
984 // Step 2.
985 if (!element || IsA<HTMLHtmlElement>(element)) {
986 Document& document = node->GetDocument();
987
988 if (document.IsSVGDocument()) {
989 element = document.documentElement();
990 if (!element)
991 element = MakeGarbageCollected<SVGSVGElement>(document);
992 } else {
993 // Optimization over spec: try to reuse the existing <body> element, if it
994 // is available.
995 element = document.body();
996 if (!element)
997 element = MakeGarbageCollected<HTMLBodyElement>(document);
998 }
999 }
1000
1001 // Steps 3, 4, 5.
1002 return blink::CreateContextualFragment(
1003 markup, element, kAllowScriptingContentAndDoNotMarkAlreadyStarted,
1004 exception_state);
1005 }
1006
detach()1007 void Range::detach() {
1008 // This is now a no-op as per the DOM specification.
1009 }
1010
CheckNodeWOffset(Node * n,unsigned offset,ExceptionState & exception_state)1011 Node* Range::CheckNodeWOffset(Node* n,
1012 unsigned offset,
1013 ExceptionState& exception_state) {
1014 switch (n->getNodeType()) {
1015 case Node::kDocumentTypeNode:
1016 exception_state.ThrowDOMException(
1017 DOMExceptionCode::kInvalidNodeTypeError,
1018 "The node provided is of type '" + n->nodeName() + "'.");
1019 return nullptr;
1020 case Node::kCdataSectionNode:
1021 case Node::kCommentNode:
1022 case Node::kTextNode:
1023 if (offset > To<CharacterData>(n)->length()) {
1024 exception_state.ThrowDOMException(
1025 DOMExceptionCode::kIndexSizeError,
1026 "The offset " + String::Number(offset) +
1027 " is larger than the node's length (" +
1028 String::Number(To<CharacterData>(n)->length()) + ").");
1029 } else if (offset >
1030 static_cast<unsigned>(std::numeric_limits<int>::max())) {
1031 exception_state.ThrowDOMException(
1032 DOMExceptionCode::kIndexSizeError,
1033 "The offset " + String::Number(offset) + " is invalid.");
1034 }
1035 return nullptr;
1036 case Node::kProcessingInstructionNode:
1037 if (offset > To<ProcessingInstruction>(n)->data().length()) {
1038 exception_state.ThrowDOMException(
1039 DOMExceptionCode::kIndexSizeError,
1040 "The offset " + String::Number(offset) +
1041 " is larger than the node's length (" +
1042 String::Number(To<ProcessingInstruction>(n)->data().length()) +
1043 ").");
1044 } else if (offset >
1045 static_cast<unsigned>(std::numeric_limits<int>::max())) {
1046 exception_state.ThrowDOMException(
1047 DOMExceptionCode::kIndexSizeError,
1048 "The offset " + String::Number(offset) + " is invalid.");
1049 }
1050 return nullptr;
1051 case Node::kAttributeNode:
1052 case Node::kDocumentFragmentNode:
1053 case Node::kDocumentNode:
1054 case Node::kElementNode: {
1055 if (!offset)
1056 return nullptr;
1057 if (offset > static_cast<unsigned>(std::numeric_limits<int>::max())) {
1058 exception_state.ThrowDOMException(
1059 DOMExceptionCode::kIndexSizeError,
1060 "The offset " + String::Number(offset) + " is invalid.");
1061 return nullptr;
1062 }
1063 Node* child_before = NodeTraversal::ChildAt(*n, offset - 1);
1064 if (!child_before) {
1065 exception_state.ThrowDOMException(
1066 DOMExceptionCode::kIndexSizeError,
1067 "There is no child at offset " + String::Number(offset) + ".");
1068 }
1069 return child_before;
1070 }
1071 }
1072 NOTREACHED();
1073 return nullptr;
1074 }
1075
CheckNodeBA(Node * n,ExceptionState & exception_state) const1076 void Range::CheckNodeBA(Node* n, ExceptionState& exception_state) const {
1077 if (!n) {
1078 // FIXME: Generated bindings code never calls with null, and neither should
1079 // other callers!
1080 exception_state.ThrowTypeError("The node provided is null.");
1081 return;
1082 }
1083
1084 // InvalidNodeTypeError: Raised if the root container of refNode is not an
1085 // Attr, Document, DocumentFragment or ShadowRoot node, or part of a SVG
1086 // shadow DOM tree, or if refNode is a Document, DocumentFragment, ShadowRoot,
1087 // Attr, Entity, or Notation node.
1088
1089 if (!n->parentNode()) {
1090 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidNodeTypeError,
1091 "the given Node has no parent.");
1092 return;
1093 }
1094
1095 switch (n->getNodeType()) {
1096 case Node::kAttributeNode:
1097 case Node::kDocumentFragmentNode:
1098 case Node::kDocumentNode:
1099 exception_state.ThrowDOMException(
1100 DOMExceptionCode::kInvalidNodeTypeError,
1101 "The node provided is of type '" + n->nodeName() + "'.");
1102 return;
1103 case Node::kCdataSectionNode:
1104 case Node::kCommentNode:
1105 case Node::kDocumentTypeNode:
1106 case Node::kElementNode:
1107 case Node::kProcessingInstructionNode:
1108 case Node::kTextNode:
1109 break;
1110 }
1111
1112 Node* root = n;
1113 while (ContainerNode* parent = root->parentNode())
1114 root = parent;
1115
1116 switch (root->getNodeType()) {
1117 case Node::kAttributeNode:
1118 case Node::kDocumentNode:
1119 case Node::kDocumentFragmentNode:
1120 case Node::kElementNode:
1121 break;
1122 case Node::kCdataSectionNode:
1123 case Node::kCommentNode:
1124 case Node::kDocumentTypeNode:
1125 case Node::kProcessingInstructionNode:
1126 case Node::kTextNode:
1127 exception_state.ThrowDOMException(
1128 DOMExceptionCode::kInvalidNodeTypeError,
1129 "The node provided is of type '" + n->nodeName() + "'.");
1130 return;
1131 }
1132 }
1133
cloneRange() const1134 Range* Range::cloneRange() const {
1135 return MakeGarbageCollected<Range>(*owner_document_.Get(),
1136 &start_.Container(), start_.Offset(),
1137 &end_.Container(), end_.Offset());
1138 }
1139
setStartAfter(Node * ref_node,ExceptionState & exception_state)1140 void Range::setStartAfter(Node* ref_node, ExceptionState& exception_state) {
1141 CheckNodeBA(ref_node, exception_state);
1142 if (exception_state.HadException())
1143 return;
1144
1145 setStart(ref_node->parentNode(), ref_node->NodeIndex() + 1, exception_state);
1146 }
1147
setEndBefore(Node * ref_node,ExceptionState & exception_state)1148 void Range::setEndBefore(Node* ref_node, ExceptionState& exception_state) {
1149 CheckNodeBA(ref_node, exception_state);
1150 if (exception_state.HadException())
1151 return;
1152
1153 setEnd(ref_node->parentNode(), ref_node->NodeIndex(), exception_state);
1154 }
1155
setEndAfter(Node * ref_node,ExceptionState & exception_state)1156 void Range::setEndAfter(Node* ref_node, ExceptionState& exception_state) {
1157 CheckNodeBA(ref_node, exception_state);
1158 if (exception_state.HadException())
1159 return;
1160
1161 setEnd(ref_node->parentNode(), ref_node->NodeIndex() + 1, exception_state);
1162 }
1163
selectNode(Node * ref_node,ExceptionState & exception_state)1164 void Range::selectNode(Node* ref_node, ExceptionState& exception_state) {
1165 if (!ref_node) {
1166 // FIXME: Generated bindings code never calls with null, and neither should
1167 // other callers!
1168 exception_state.ThrowTypeError("The node provided is null.");
1169 return;
1170 }
1171
1172 if (!ref_node->parentNode()) {
1173 exception_state.ThrowDOMException(DOMExceptionCode::kInvalidNodeTypeError,
1174 "the given Node has no parent.");
1175 return;
1176 }
1177
1178 switch (ref_node->getNodeType()) {
1179 case Node::kCdataSectionNode:
1180 case Node::kCommentNode:
1181 case Node::kDocumentTypeNode:
1182 case Node::kElementNode:
1183 case Node::kProcessingInstructionNode:
1184 case Node::kTextNode:
1185 break;
1186 case Node::kAttributeNode:
1187 case Node::kDocumentFragmentNode:
1188 case Node::kDocumentNode:
1189 exception_state.ThrowDOMException(
1190 DOMExceptionCode::kInvalidNodeTypeError,
1191 "The node provided is of type '" + ref_node->nodeName() + "'.");
1192 return;
1193 }
1194
1195 RangeUpdateScope scope(this);
1196 setStartBefore(ref_node);
1197 setEndAfter(ref_node);
1198 }
1199
selectNodeContents(Node * ref_node,ExceptionState & exception_state)1200 void Range::selectNodeContents(Node* ref_node,
1201 ExceptionState& exception_state) {
1202 if (!ref_node) {
1203 // FIXME: Generated bindings code never calls with null, and neither should
1204 // other callers!
1205 exception_state.ThrowTypeError("The node provided is null.");
1206 return;
1207 }
1208
1209 // InvalidNodeTypeError: Raised if refNode or an ancestor of refNode is an
1210 // Entity, Notation
1211 // or DocumentType node.
1212 for (Node* n = ref_node; n; n = n->parentNode()) {
1213 switch (n->getNodeType()) {
1214 case Node::kAttributeNode:
1215 case Node::kCdataSectionNode:
1216 case Node::kCommentNode:
1217 case Node::kDocumentFragmentNode:
1218 case Node::kDocumentNode:
1219 case Node::kElementNode:
1220 case Node::kProcessingInstructionNode:
1221 case Node::kTextNode:
1222 break;
1223 case Node::kDocumentTypeNode:
1224 exception_state.ThrowDOMException(
1225 DOMExceptionCode::kInvalidNodeTypeError,
1226 "The node provided is of type '" + ref_node->nodeName() + "'.");
1227 return;
1228 }
1229 }
1230
1231 RangeUpdateScope scope(this);
1232 if (owner_document_ != ref_node->GetDocument())
1233 SetDocument(ref_node->GetDocument());
1234
1235 start_.SetToStartOfNode(*ref_node);
1236 end_.SetToEndOfNode(*ref_node);
1237 }
1238
selectNodeContents(Node * ref_node,Position & start,Position & end)1239 bool Range::selectNodeContents(Node* ref_node, Position& start, Position& end) {
1240 if (!ref_node) {
1241 return false;
1242 }
1243
1244 for (Node* n = ref_node; n; n = n->parentNode()) {
1245 switch (n->getNodeType()) {
1246 case Node::kAttributeNode:
1247 case Node::kCdataSectionNode:
1248 case Node::kCommentNode:
1249 case Node::kDocumentFragmentNode:
1250 case Node::kDocumentNode:
1251 case Node::kElementNode:
1252 case Node::kProcessingInstructionNode:
1253 case Node::kTextNode:
1254 break;
1255 case Node::kDocumentTypeNode:
1256 return false;
1257 }
1258 }
1259
1260 RangeBoundaryPoint start_boundary_point(*ref_node);
1261 start_boundary_point.SetToStartOfNode(*ref_node);
1262 start = start_boundary_point.ToPosition();
1263 RangeBoundaryPoint end_boundary_point(*ref_node);
1264 end_boundary_point.SetToEndOfNode(*ref_node);
1265 end = end_boundary_point.ToPosition();
1266 return true;
1267 }
1268
1269 // https://dom.spec.whatwg.org/#dom-range-surroundcontents
surroundContents(Node * new_parent,ExceptionState & exception_state)1270 void Range::surroundContents(Node* new_parent,
1271 ExceptionState& exception_state) {
1272 if (!new_parent) {
1273 // FIXME: Generated bindings code never calls with null, and neither should
1274 // other callers!
1275 exception_state.ThrowTypeError("The node provided is null.");
1276 return;
1277 }
1278
1279 // 1. If a non-Text node is partially contained in the context object, then
1280 // throw an InvalidStateError.
1281 Node* start_non_text_container = &start_.Container();
1282 if (start_non_text_container->getNodeType() == Node::kTextNode)
1283 start_non_text_container = start_non_text_container->parentNode();
1284 Node* end_non_text_container = &end_.Container();
1285 if (end_non_text_container->getNodeType() == Node::kTextNode)
1286 end_non_text_container = end_non_text_container->parentNode();
1287 if (start_non_text_container != end_non_text_container) {
1288 exception_state.ThrowDOMException(
1289 DOMExceptionCode::kInvalidStateError,
1290 "The Range has partially selected a non-Text node.");
1291 return;
1292 }
1293
1294 // 2. If newParent is a Document, DocumentType, or DocumentFragment node, then
1295 // throw an InvalidNodeTypeError.
1296 switch (new_parent->getNodeType()) {
1297 case Node::kAttributeNode:
1298 case Node::kDocumentFragmentNode:
1299 case Node::kDocumentNode:
1300 case Node::kDocumentTypeNode:
1301 exception_state.ThrowDOMException(
1302 DOMExceptionCode::kInvalidNodeTypeError,
1303 "The node provided is of type '" + new_parent->nodeName() + "'.");
1304 return;
1305 case Node::kCdataSectionNode:
1306 case Node::kCommentNode:
1307 case Node::kElementNode:
1308 case Node::kProcessingInstructionNode:
1309 case Node::kTextNode:
1310 break;
1311 }
1312
1313 EventQueueScope scope;
1314
1315 // 3. Let fragment be the result of extracting context object.
1316 DocumentFragment* fragment = extractContents(exception_state);
1317 if (exception_state.HadException())
1318 return;
1319
1320 // 4. If newParent has children, replace all with null within newParent.
1321 while (Node* n = new_parent->firstChild()) {
1322 To<ContainerNode>(new_parent)->RemoveChild(n, exception_state);
1323 if (exception_state.HadException())
1324 return;
1325 }
1326
1327 // 5. If newParent has children, replace all with null within newParent.
1328 insertNode(new_parent, exception_state);
1329 if (exception_state.HadException())
1330 return;
1331
1332 // 6. Append fragment to newParent.
1333 new_parent->appendChild(fragment, exception_state);
1334 if (exception_state.HadException())
1335 return;
1336
1337 // 7. Select newParent within context object.
1338 selectNode(new_parent, exception_state);
1339 }
1340
setStartBefore(Node * ref_node,ExceptionState & exception_state)1341 void Range::setStartBefore(Node* ref_node, ExceptionState& exception_state) {
1342 CheckNodeBA(ref_node, exception_state);
1343 if (exception_state.HadException())
1344 return;
1345
1346 setStart(ref_node->parentNode(), ref_node->NodeIndex(), exception_state);
1347 }
1348
CheckExtractPrecondition(ExceptionState & exception_state)1349 void Range::CheckExtractPrecondition(ExceptionState& exception_state) {
1350 DCHECK(BoundaryPointsValid());
1351
1352 if (!commonAncestorContainer())
1353 return;
1354
1355 Node* past_last = PastLastNode();
1356 for (Node* n = FirstNode(); n != past_last; n = NodeTraversal::Next(*n)) {
1357 if (n->IsDocumentTypeNode()) {
1358 exception_state.ThrowDOMException(
1359 DOMExceptionCode::kHierarchyRequestError,
1360 "The Range contains a doctype node.");
1361 return;
1362 }
1363 }
1364 }
1365
FirstNode() const1366 Node* Range::FirstNode() const {
1367 return StartPosition().NodeAsRangeFirstNode();
1368 }
1369
PastLastNode() const1370 Node* Range::PastLastNode() const {
1371 return EndPosition().NodeAsRangePastLastNode();
1372 }
1373
BoundingBox() const1374 IntRect Range::BoundingBox() const {
1375 return ComputeTextRect(EphemeralRange(this));
1376 }
1377
AreRangesEqual(const Range * a,const Range * b)1378 bool AreRangesEqual(const Range* a, const Range* b) {
1379 if (a == b)
1380 return true;
1381 if (!a || !b)
1382 return false;
1383 return a->StartPosition() == b->StartPosition() &&
1384 a->EndPosition() == b->EndPosition();
1385 }
1386
BoundaryNodeChildrenWillBeRemoved(RangeBoundaryPoint & boundary,ContainerNode & container)1387 static inline void BoundaryNodeChildrenWillBeRemoved(
1388 RangeBoundaryPoint& boundary,
1389 ContainerNode& container) {
1390 for (Node* node_to_be_removed = container.firstChild(); node_to_be_removed;
1391 node_to_be_removed = node_to_be_removed->nextSibling()) {
1392 if (boundary.ChildBefore() == node_to_be_removed) {
1393 boundary.SetToStartOfNode(container);
1394 return;
1395 }
1396
1397 for (Node* n = &boundary.Container(); n; n = n->parentNode()) {
1398 if (n == node_to_be_removed) {
1399 boundary.SetToStartOfNode(container);
1400 return;
1401 }
1402 }
1403 }
1404 }
1405
BoundaryShadowNodeChildrenWillBeRemoved(RangeBoundaryPoint & boundary,ContainerNode & container)1406 static void BoundaryShadowNodeChildrenWillBeRemoved(
1407 RangeBoundaryPoint& boundary,
1408 ContainerNode& container) {
1409 for (Node* node_to_be_removed = container.firstChild(); node_to_be_removed;
1410 node_to_be_removed = node_to_be_removed->nextSibling()) {
1411 for (Node* n = &boundary.Container(); n;
1412 n = n->ParentOrShadowHostElement()) {
1413 if (n == node_to_be_removed) {
1414 boundary.SetToStartOfNode(container);
1415 return;
1416 }
1417 }
1418 }
1419 }
1420
NodeChildrenWillBeRemoved(ContainerNode & container)1421 void Range::NodeChildrenWillBeRemoved(ContainerNode& container) {
1422 DCHECK_EQ(container.GetDocument(), owner_document_);
1423 BoundaryNodeChildrenWillBeRemoved(start_, container);
1424 BoundaryNodeChildrenWillBeRemoved(end_, container);
1425 }
1426
FixupRemovedChildrenAcrossShadowBoundary(ContainerNode & container)1427 void Range::FixupRemovedChildrenAcrossShadowBoundary(ContainerNode& container) {
1428 DCHECK_EQ(container.GetDocument(), owner_document_);
1429 BoundaryShadowNodeChildrenWillBeRemoved(start_, container);
1430 BoundaryShadowNodeChildrenWillBeRemoved(end_, container);
1431 }
1432
BoundaryNodeWillBeRemoved(RangeBoundaryPoint & boundary,Node & node_to_be_removed)1433 static inline void BoundaryNodeWillBeRemoved(RangeBoundaryPoint& boundary,
1434 Node& node_to_be_removed) {
1435 if (boundary.ChildBefore() == node_to_be_removed) {
1436 boundary.ChildBeforeWillBeRemoved();
1437 return;
1438 }
1439
1440 for (Node* n = &boundary.Container(); n; n = n->parentNode()) {
1441 if (n == node_to_be_removed) {
1442 boundary.SetToBeforeChild(node_to_be_removed);
1443 return;
1444 }
1445 }
1446 }
1447
BoundaryShadowNodeWillBeRemoved(RangeBoundaryPoint & boundary,Node & node_to_be_removed)1448 static inline void BoundaryShadowNodeWillBeRemoved(RangeBoundaryPoint& boundary,
1449 Node& node_to_be_removed) {
1450 DCHECK_NE(boundary.ChildBefore(), node_to_be_removed);
1451
1452 for (Node* node = &boundary.Container(); node;
1453 node = node->ParentOrShadowHostElement()) {
1454 if (node == node_to_be_removed) {
1455 boundary.SetToBeforeChild(node_to_be_removed);
1456 return;
1457 }
1458 }
1459 }
1460
NodeWillBeRemoved(Node & node)1461 void Range::NodeWillBeRemoved(Node& node) {
1462 DCHECK_EQ(node.GetDocument(), owner_document_);
1463 DCHECK_NE(node, owner_document_.Get());
1464
1465 // FIXME: Once DOMNodeRemovedFromDocument mutation event removed, we
1466 // should change following if-statement to DCHECK(!node->parentNode).
1467 if (!node.parentNode())
1468 return;
1469 BoundaryNodeWillBeRemoved(start_, node);
1470 BoundaryNodeWillBeRemoved(end_, node);
1471 }
1472
FixupRemovedNodeAcrossShadowBoundary(Node & node)1473 void Range::FixupRemovedNodeAcrossShadowBoundary(Node& node) {
1474 BoundaryShadowNodeWillBeRemoved(start_, node);
1475 BoundaryShadowNodeWillBeRemoved(end_, node);
1476 }
1477
BoundaryTextInserted(RangeBoundaryPoint & boundary,const CharacterData & text,unsigned offset,unsigned length)1478 static inline void BoundaryTextInserted(RangeBoundaryPoint& boundary,
1479 const CharacterData& text,
1480 unsigned offset,
1481 unsigned length) {
1482 if (boundary.Container() != &text)
1483 return;
1484 boundary.MarkValid();
1485 unsigned boundary_offset = boundary.Offset();
1486 if (offset >= boundary_offset)
1487 return;
1488 boundary.SetOffset(boundary_offset + length);
1489 }
1490
DidInsertText(const CharacterData & text,unsigned offset,unsigned length)1491 void Range::DidInsertText(const CharacterData& text,
1492 unsigned offset,
1493 unsigned length) {
1494 DCHECK_EQ(text.GetDocument(), owner_document_);
1495 BoundaryTextInserted(start_, text, offset, length);
1496 BoundaryTextInserted(end_, text, offset, length);
1497 }
1498
BoundaryTextRemoved(RangeBoundaryPoint & boundary,const CharacterData & text,unsigned offset,unsigned length)1499 static inline void BoundaryTextRemoved(RangeBoundaryPoint& boundary,
1500 const CharacterData& text,
1501 unsigned offset,
1502 unsigned length) {
1503 if (boundary.Container() != &text)
1504 return;
1505 boundary.MarkValid();
1506 unsigned boundary_offset = boundary.Offset();
1507 if (offset >= boundary_offset)
1508 return;
1509 if (offset + length >= boundary_offset)
1510 boundary.SetOffset(offset);
1511 else
1512 boundary.SetOffset(boundary_offset - length);
1513 }
1514
DidRemoveText(const CharacterData & text,unsigned offset,unsigned length)1515 void Range::DidRemoveText(const CharacterData& text,
1516 unsigned offset,
1517 unsigned length) {
1518 DCHECK_EQ(text.GetDocument(), owner_document_);
1519 BoundaryTextRemoved(start_, text, offset, length);
1520 BoundaryTextRemoved(end_, text, offset, length);
1521 }
1522
BoundaryTextNodesMerged(RangeBoundaryPoint & boundary,const NodeWithIndex & old_node,unsigned offset)1523 static inline void BoundaryTextNodesMerged(RangeBoundaryPoint& boundary,
1524 const NodeWithIndex& old_node,
1525 unsigned offset) {
1526 if (boundary.Container() == old_node.GetNode()) {
1527 Node* const previous_sibling = old_node.GetNode().previousSibling();
1528 DCHECK(previous_sibling);
1529 boundary.Set(*previous_sibling, boundary.Offset() + offset, nullptr);
1530 } else if (boundary.Container() == old_node.GetNode().parentNode() &&
1531 boundary.Offset() == static_cast<unsigned>(old_node.Index())) {
1532 Node* const previous_sibling = old_node.GetNode().previousSibling();
1533 DCHECK(previous_sibling);
1534 boundary.Set(*previous_sibling, offset, nullptr);
1535 }
1536 }
1537
DidMergeTextNodes(const NodeWithIndex & old_node,unsigned offset)1538 void Range::DidMergeTextNodes(const NodeWithIndex& old_node, unsigned offset) {
1539 DCHECK_EQ(old_node.GetNode().GetDocument(), owner_document_);
1540 DCHECK(old_node.GetNode().parentNode());
1541 DCHECK(old_node.GetNode().IsTextNode());
1542 DCHECK(old_node.GetNode().previousSibling());
1543 DCHECK(old_node.GetNode().previousSibling()->IsTextNode());
1544 BoundaryTextNodesMerged(start_, old_node, offset);
1545 BoundaryTextNodesMerged(end_, old_node, offset);
1546 }
1547
UpdateOwnerDocumentIfNeeded()1548 void Range::UpdateOwnerDocumentIfNeeded() {
1549 Document& new_document = start_.Container().GetDocument();
1550 DCHECK_EQ(new_document, end_.Container().GetDocument());
1551 if (new_document == owner_document_)
1552 return;
1553 owner_document_->DetachRange(this);
1554 owner_document_ = &new_document;
1555 owner_document_->AttachRange(this);
1556 }
1557
BoundaryTextNodeSplit(RangeBoundaryPoint & boundary,const Text & old_node)1558 static inline void BoundaryTextNodeSplit(RangeBoundaryPoint& boundary,
1559 const Text& old_node) {
1560 unsigned boundary_offset = boundary.Offset();
1561 if (boundary.ChildBefore() == &old_node) {
1562 boundary.Set(boundary.Container(), boundary_offset + 1,
1563 old_node.nextSibling());
1564 } else if (boundary.Container() == &old_node &&
1565 boundary_offset > old_node.length()) {
1566 Node* const next_sibling = old_node.nextSibling();
1567 DCHECK(next_sibling);
1568 boundary.Set(*next_sibling, boundary_offset - old_node.length(), nullptr);
1569 }
1570 }
1571
DidSplitTextNode(const Text & old_node)1572 void Range::DidSplitTextNode(const Text& old_node) {
1573 DCHECK_EQ(old_node.GetDocument(), owner_document_);
1574 DCHECK(old_node.parentNode());
1575 DCHECK(old_node.nextSibling());
1576 DCHECK(old_node.nextSibling()->IsTextNode());
1577 BoundaryTextNodeSplit(start_, old_node);
1578 BoundaryTextNodeSplit(end_, old_node);
1579 DCHECK(BoundaryPointsValid());
1580 }
1581
expand(const String & unit,ExceptionState & exception_state)1582 void Range::expand(const String& unit, ExceptionState& exception_state) {
1583 if (!StartPosition().IsConnected() || !EndPosition().IsConnected())
1584 return;
1585 owner_document_->UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
1586 VisiblePosition start = CreateVisiblePosition(StartPosition());
1587 VisiblePosition end = CreateVisiblePosition(EndPosition());
1588 if (unit == "word") {
1589 start = CreateVisiblePosition(StartOfWordPosition(start.DeepEquivalent()));
1590 end = CreateVisiblePosition(EndOfWordPosition(end.DeepEquivalent()));
1591 } else if (unit == "sentence") {
1592 start =
1593 CreateVisiblePosition(StartOfSentencePosition(start.DeepEquivalent()));
1594 end = CreateVisiblePosition(EndOfSentence(end.DeepEquivalent()));
1595 } else if (unit == "block") {
1596 start = StartOfParagraph(start);
1597 end = EndOfParagraph(end);
1598 } else if (unit == "document") {
1599 start = StartOfDocument(start);
1600 end = EndOfDocument(end);
1601 } else {
1602 return;
1603 }
1604 setStart(start.DeepEquivalent().ComputeContainerNode(),
1605 start.DeepEquivalent().ComputeOffsetInContainerNode(),
1606 exception_state);
1607 setEnd(end.DeepEquivalent().ComputeContainerNode(),
1608 end.DeepEquivalent().ComputeOffsetInContainerNode(), exception_state);
1609 }
1610
getClientRects() const1611 DOMRectList* Range::getClientRects() const {
1612 owner_document_->UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
1613
1614 Vector<FloatQuad> quads;
1615 GetBorderAndTextQuads(quads);
1616
1617 return MakeGarbageCollected<DOMRectList>(quads);
1618 }
1619
getBoundingClientRect() const1620 DOMRect* Range::getBoundingClientRect() const {
1621 return DOMRect::FromFloatRect(BoundingRect());
1622 }
1623
1624 // TODO(editing-dev): We should make
1625 // |Document::AdjustFloatQuadsForScrollAndAbsoluteZoom()| as const function
1626 // and takes |const LayoutObject&|.
ComputeTextQuads(const Document & owner_document,const LayoutText & layout_text,unsigned start_offset,unsigned end_offset)1627 static Vector<FloatQuad> ComputeTextQuads(const Document& owner_document,
1628 const LayoutText& layout_text,
1629 unsigned start_offset,
1630 unsigned end_offset) {
1631 Vector<FloatQuad> text_quads;
1632 layout_text.AbsoluteQuadsForRange(text_quads, start_offset, end_offset);
1633 const_cast<Document&>(owner_document)
1634 .AdjustFloatQuadsForScrollAndAbsoluteZoom(
1635 text_quads, const_cast<LayoutText&>(layout_text));
1636 return text_quads;
1637 }
1638
1639 // https://www.w3.org/TR/cssom-view-1/#dom-range-getclientrects
GetBorderAndTextQuads(Vector<FloatQuad> & quads) const1640 void Range::GetBorderAndTextQuads(Vector<FloatQuad>& quads) const {
1641 Node* start_container = &start_.Container();
1642 Node* end_container = &end_.Container();
1643 Node* stop_node = PastLastNode();
1644
1645 // Stores the elements selected by the range.
1646 HeapHashSet<Member<const Node>> selected_elements;
1647 for (Node* node = FirstNode(); node != stop_node;
1648 node = NodeTraversal::Next(*node)) {
1649 if (!node->IsElementNode())
1650 continue;
1651 if (selected_elements.Contains(node->parentNode()) ||
1652 (!node->contains(start_container) && !node->contains(end_container))) {
1653 DCHECK_LE(StartPosition(), Position::BeforeNode(*node));
1654 DCHECK_GE(EndPosition(), Position::AfterNode(*node));
1655 selected_elements.insert(node);
1656 }
1657 }
1658
1659 for (const Node* node = FirstNode(); node != stop_node;
1660 node = NodeTraversal::Next(*node)) {
1661 auto* element_node = DynamicTo<Element>(node);
1662 if (element_node) {
1663 if (!selected_elements.Contains(node) ||
1664 selected_elements.Contains(node->parentNode()))
1665 continue;
1666 LayoutObject* const layout_object = element_node->GetLayoutObject();
1667 if (!layout_object)
1668 continue;
1669 Vector<FloatQuad> element_quads;
1670 layout_object->AbsoluteQuads(element_quads);
1671 owner_document_->AdjustFloatQuadsForScrollAndAbsoluteZoom(element_quads,
1672 *layout_object);
1673
1674 quads.AppendVector(element_quads);
1675 continue;
1676 }
1677
1678 auto* const text_node = DynamicTo<Text>(node);
1679 if (!text_node)
1680 continue;
1681 LayoutText* const layout_text = text_node->GetLayoutObject();
1682 if (!layout_text)
1683 continue;
1684
1685 // TODO(editing-dev): Offset in |LayoutText| doesn't match to DOM offset
1686 // when |text-transform| applied. We should map DOM offset to offset in
1687 // |LayouText| for |start_offset| and |end_offset|.
1688 const unsigned start_offset =
1689 (node == start_container) ? start_.Offset() : 0;
1690 const unsigned end_offset = (node == end_container)
1691 ? end_.Offset()
1692 : std::numeric_limits<unsigned>::max();
1693 if (!layout_text->IsTextFragment()) {
1694 quads.AppendVector(ComputeTextQuads(*owner_document_, *layout_text,
1695 start_offset, end_offset));
1696 continue;
1697 }
1698
1699 // Handle ::first-letter
1700 const LayoutTextFragment& first_letter_part =
1701 *ToLayoutTextFragment(AssociatedLayoutObjectOf(*node, 0));
1702 const bool overlaps_with_first_letter =
1703 start_offset < first_letter_part.FragmentLength() ||
1704 (start_offset == first_letter_part.FragmentLength() &&
1705 end_offset == start_offset);
1706 if (overlaps_with_first_letter) {
1707 const unsigned start_in_first_letter = start_offset;
1708 const unsigned end_in_first_letter =
1709 std::min(end_offset, first_letter_part.FragmentLength());
1710 quads.AppendVector(ComputeTextQuads(*owner_document_, first_letter_part,
1711 start_in_first_letter,
1712 end_in_first_letter));
1713 }
1714 const LayoutTextFragment& remaining_part =
1715 *ToLayoutTextFragment(layout_text);
1716 if (end_offset > remaining_part.Start()) {
1717 const unsigned start_in_remaining_part =
1718 std::max(start_offset, remaining_part.Start()) -
1719 remaining_part.Start();
1720 // TODO(editing-dev): As we previously set |end_offset == UINT_MAX| as a
1721 // hacky support for |text-transform|, we need the same hack here.
1722 const unsigned end_in_remaining_part =
1723 end_offset == UINT_MAX ? end_offset
1724 : end_offset - remaining_part.Start();
1725 quads.AppendVector(ComputeTextQuads(*owner_document_, remaining_part,
1726 start_in_remaining_part,
1727 end_in_remaining_part));
1728 }
1729 }
1730 }
1731
BoundingRect() const1732 FloatRect Range::BoundingRect() const {
1733 owner_document_->UpdateStyleAndLayout(DocumentUpdateReason::kJavaScript);
1734
1735 Vector<FloatQuad> quads;
1736 GetBorderAndTextQuads(quads);
1737
1738 FloatRect result;
1739 for (const FloatQuad& quad : quads)
1740 result.Unite(quad.BoundingBox()); // Skips empty rects.
1741
1742 // If all rects are empty, return the first rect.
1743 if (result.IsEmpty() && !quads.IsEmpty())
1744 return quads.front().BoundingBox();
1745
1746 return result;
1747 }
1748
UpdateSelectionIfAddedToSelection()1749 void Range::UpdateSelectionIfAddedToSelection() {
1750 if (!OwnerDocument().GetFrame())
1751 return;
1752 FrameSelection& selection = OwnerDocument().GetFrame()->Selection();
1753 if (this != selection.DocumentCachedRange())
1754 return;
1755 DCHECK(startContainer()->isConnected());
1756 DCHECK(startContainer()->GetDocument() == OwnerDocument());
1757 DCHECK(endContainer()->isConnected());
1758 DCHECK(endContainer()->GetDocument() == OwnerDocument());
1759 EventDispatchForbiddenScope no_events;
1760 selection.SetSelection(SelectionInDOMTree::Builder()
1761 .Collapse(StartPosition())
1762 .Extend(EndPosition())
1763 .Build(),
1764 SetSelectionOptions::Builder()
1765 .SetShouldCloseTyping(true)
1766 .SetShouldClearTypingStyle(true)
1767 .SetDoNotSetFocus(true)
1768 .Build());
1769 selection.CacheRangeOfDocument(this);
1770 }
1771
RemoveFromSelectionIfInDifferentRoot(Document & old_document)1772 void Range::RemoveFromSelectionIfInDifferentRoot(Document& old_document) {
1773 if (!old_document.GetFrame())
1774 return;
1775 FrameSelection& selection = old_document.GetFrame()->Selection();
1776 if (this != selection.DocumentCachedRange())
1777 return;
1778 if (OwnerDocument() == old_document && startContainer()->isConnected() &&
1779 endContainer()->isConnected())
1780 return;
1781 selection.Clear();
1782 selection.ClearDocumentCachedRange();
1783 }
1784
Trace(Visitor * visitor)1785 void Range::Trace(Visitor* visitor) {
1786 visitor->Trace(owner_document_);
1787 visitor->Trace(start_);
1788 visitor->Trace(end_);
1789 ScriptWrappable::Trace(visitor);
1790 }
1791
1792 } // namespace blink
1793
1794 #if DCHECK_IS_ON()
1795
showTree(const blink::Range * range)1796 void showTree(const blink::Range* range) {
1797 if (range && range->BoundaryPointsValid()) {
1798 LOG(INFO) << "\n"
1799 << range->startContainer()
1800 ->ToMarkedTreeString(range->startContainer(), "S",
1801 range->endContainer(), "E")
1802 .Utf8()
1803 << "start offset: " << range->startOffset()
1804 << ", end offset: " << range->endOffset();
1805 } else {
1806 LOG(INFO) << "Cannot show tree if range is null, or if boundary points are "
1807 "invalid.";
1808 }
1809 }
1810
1811 #endif
1812