e98dd763b83959a9b6e6be8bc3c3b82b73bbf5f4
[WebKit-https.git] / Source / WebCore / page / DOMSelection.cpp
1 /*
2  * Copyright (C) 2007, 2009, 2016 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30
31 #include "config.h"
32 #include "DOMSelection.h"
33
34 #include "Document.h"
35 #include "Editing.h"
36 #include "Frame.h"
37 #include "FrameSelection.h"
38 #include "Range.h"
39 #include "TextIterator.h"
40
41 namespace WebCore {
42
43 static Node* selectionShadowAncestor(Frame& frame)
44 {
45     auto* node = frame.selection().selection().base().anchorNode();
46     if (!node)
47         return nullptr;
48     if (!node->isInShadowTree())
49         return nullptr;
50     // FIXME: Unclear on why this needs to be the possibly null frame.document() instead of the never null node->document().
51     return frame.document()->ancestorNodeInThisScope(node);
52 }
53
54 DOMSelection::DOMSelection(Frame& frame)
55     : DOMWindowProperty(&frame)
56 {
57 }
58
59 const VisibleSelection& DOMSelection::visibleSelection() const
60 {
61     ASSERT(m_frame);
62     return m_frame->selection().selection();
63 }
64
65 static Position anchorPosition(const VisibleSelection& selection)
66 {
67     auto anchor = selection.isBaseFirst() ? selection.start() : selection.end();
68     return anchor.parentAnchoredEquivalent();
69 }
70
71 static Position focusPosition(const VisibleSelection& selection)
72 {
73     auto focus = selection.isBaseFirst() ? selection.end() : selection.start();
74     return focus.parentAnchoredEquivalent();
75 }
76
77 static Position basePosition(const VisibleSelection& selection)
78 {
79     return selection.base().parentAnchoredEquivalent();
80 }
81
82 static Position extentPosition(const VisibleSelection& selection)
83 {
84     return selection.extent().parentAnchoredEquivalent();
85 }
86
87 Node* DOMSelection::anchorNode() const
88 {
89     if (!m_frame)
90         return 0;
91     return shadowAdjustedNode(anchorPosition(visibleSelection()));
92 }
93
94 unsigned DOMSelection::anchorOffset() const
95 {
96     if (!m_frame)
97         return 0;
98     return shadowAdjustedOffset(anchorPosition(visibleSelection()));
99 }
100
101 Node* DOMSelection::focusNode() const
102 {
103     if (!m_frame)
104         return nullptr;
105     return shadowAdjustedNode(focusPosition(visibleSelection()));
106 }
107
108 unsigned DOMSelection::focusOffset() const
109 {
110     if (!m_frame)
111         return 0;
112     return shadowAdjustedOffset(focusPosition(visibleSelection()));
113 }
114
115 Node* DOMSelection::baseNode() const
116 {
117     if (!m_frame)
118         return 0;
119     return shadowAdjustedNode(basePosition(visibleSelection()));
120 }
121
122 unsigned DOMSelection::baseOffset() const
123 {
124     if (!m_frame)
125         return 0;
126     return shadowAdjustedOffset(basePosition(visibleSelection()));
127 }
128
129 Node* DOMSelection::extentNode() const
130 {
131     if (!m_frame)
132         return 0;
133     return shadowAdjustedNode(extentPosition(visibleSelection()));
134 }
135
136 unsigned DOMSelection::extentOffset() const
137 {
138     if (!m_frame)
139         return 0;
140     return shadowAdjustedOffset(extentPosition(visibleSelection()));
141 }
142
143 bool DOMSelection::isCollapsed() const
144 {
145     if (!m_frame || selectionShadowAncestor(*m_frame))
146         return true;
147     return !m_frame->selection().isRange();
148 }
149
150 String DOMSelection::type() const
151 {
152     if (!m_frame)
153         return "None"_s;
154     auto& selection = m_frame->selection();
155     if (selection.isNone())
156         return "None"_s;
157     if (selection.isCaret())
158         return "Caret"_s;
159     return "Range"_s;
160 }
161
162 unsigned DOMSelection::rangeCount() const
163 {
164     return !m_frame || m_frame->selection().isNone() ? 0 : 1;
165 }
166
167 void DOMSelection::collapse(Node* node, unsigned offset)
168 {
169     if (!isValidForPosition(node))
170         return;
171
172     Ref<Frame> protector(*m_frame);
173     m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM);
174 }
175
176 ExceptionOr<void> DOMSelection::collapseToEnd()
177 {
178     if (!m_frame)
179         return { };
180     auto& selection = m_frame->selection();
181     if (selection.isNone())
182         return Exception { InvalidStateError };
183
184     Ref<Frame> protector(*m_frame);
185     selection.moveTo(selection.selection().end(), DOWNSTREAM);
186     return { };
187 }
188
189 ExceptionOr<void> DOMSelection::collapseToStart()
190 {
191     if (!m_frame)
192         return { };
193     auto& selection = m_frame->selection();
194     if (selection.isNone())
195         return Exception { InvalidStateError };
196
197     Ref<Frame> protector(*m_frame);
198     selection.moveTo(selection.selection().start(), DOWNSTREAM);
199     return { };
200 }
201
202 void DOMSelection::empty()
203 {
204     if (!m_frame)
205         return;
206     m_frame->selection().clear();
207 }
208
209 void DOMSelection::setBaseAndExtent(Node* baseNode, unsigned baseOffset, Node* extentNode, unsigned extentOffset)
210 {
211     if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
212         return;
213
214     Ref<Frame> protector(*m_frame);
215     m_frame->selection().moveTo(createLegacyEditingPosition(baseNode, baseOffset), createLegacyEditingPosition(extentNode, extentOffset), DOWNSTREAM);
216 }
217
218 void DOMSelection::setPosition(Node* node, unsigned offset)
219 {
220     if (!isValidForPosition(node))
221         return;
222
223     Ref<Frame> protector(*m_frame);
224     m_frame->selection().moveTo(createLegacyEditingPosition(node, offset), DOWNSTREAM);
225 }
226
227 void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
228 {
229     if (!m_frame)
230         return;
231
232     FrameSelection::EAlteration alter;
233     if (equalLettersIgnoringASCIICase(alterString, "extend"))
234         alter = FrameSelection::AlterationExtend;
235     else if (equalLettersIgnoringASCIICase(alterString, "move"))
236         alter = FrameSelection::AlterationMove;
237     else
238         return;
239
240     SelectionDirection direction;
241     if (equalLettersIgnoringASCIICase(directionString, "forward"))
242         direction = DirectionForward;
243     else if (equalLettersIgnoringASCIICase(directionString, "backward"))
244         direction = DirectionBackward;
245     else if (equalLettersIgnoringASCIICase(directionString, "left"))
246         direction = DirectionLeft;
247     else if (equalLettersIgnoringASCIICase(directionString, "right"))
248         direction = DirectionRight;
249     else
250         return;
251
252     TextGranularity granularity;
253     if (equalLettersIgnoringASCIICase(granularityString, "character"))
254         granularity = CharacterGranularity;
255     else if (equalLettersIgnoringASCIICase(granularityString, "word"))
256         granularity = WordGranularity;
257     else if (equalLettersIgnoringASCIICase(granularityString, "sentence"))
258         granularity = SentenceGranularity;
259     else if (equalLettersIgnoringASCIICase(granularityString, "line"))
260         granularity = LineGranularity;
261     else if (equalLettersIgnoringASCIICase(granularityString, "paragraph"))
262         granularity = ParagraphGranularity;
263     else if (equalLettersIgnoringASCIICase(granularityString, "lineboundary"))
264         granularity = LineBoundary;
265     else if (equalLettersIgnoringASCIICase(granularityString, "sentenceboundary"))
266         granularity = SentenceBoundary;
267     else if (equalLettersIgnoringASCIICase(granularityString, "paragraphboundary"))
268         granularity = ParagraphBoundary;
269     else if (equalLettersIgnoringASCIICase(granularityString, "documentboundary"))
270         granularity = DocumentBoundary;
271     else
272         return;
273
274     Ref<Frame> protector(*m_frame);
275     m_frame->selection().modify(alter, direction, granularity);
276 }
277
278 ExceptionOr<void> DOMSelection::extend(Node& node, unsigned offset)
279 {
280     if (!m_frame)
281         return { };
282     if (offset > (node.offsetInCharacters() ? caretMaxOffset(node) : node.countChildNodes()))
283         return Exception { IndexSizeError };
284     if (!isValidForPosition(&node))
285         return { };
286
287     Ref<Frame> protector(*m_frame);
288     m_frame->selection().setExtent(createLegacyEditingPosition(&node, offset), DOWNSTREAM);
289     return { };
290 }
291
292 ExceptionOr<Ref<Range>> DOMSelection::getRangeAt(unsigned index)
293 {
294     if (index >= rangeCount())
295         return Exception { IndexSizeError };
296
297     // If you're hitting this, you've added broken multi-range selection support.
298     ASSERT(rangeCount() == 1);
299
300     if (auto* shadowAncestor = selectionShadowAncestor(*m_frame)) {
301         auto* container = shadowAncestor->parentNodeGuaranteedHostFree();
302         unsigned offset = shadowAncestor->computeNodeIndex();
303         return Range::create(shadowAncestor->document(), container, offset, container, offset);
304     }
305
306     auto firstRange = m_frame->selection().selection().firstRange();
307     ASSERT(firstRange);
308     if (!firstRange)
309         return Exception { IndexSizeError };
310     return firstRange.releaseNonNull();
311 }
312
313 void DOMSelection::removeAllRanges()
314 {
315     if (!m_frame)
316         return;
317     m_frame->selection().clear();
318 }
319
320 void DOMSelection::addRange(Range& range)
321 {
322     if (!m_frame)
323         return;
324
325     Ref<Frame> protector(*m_frame);
326
327     auto& selection = m_frame->selection();
328     if (selection.isNone()) {
329         selection.moveTo(&range);
330         return;
331     }
332
333     auto normalizedRange = selection.selection().toNormalizedRange();
334     if (!normalizedRange)
335         return;
336
337     auto result = range.compareBoundaryPoints(Range::START_TO_START, *normalizedRange);
338     if (!result.hasException() && result.releaseReturnValue() == -1) {
339         // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect.
340         result = range.compareBoundaryPoints(Range::START_TO_END, *normalizedRange);
341         if (!result.hasException() && result.releaseReturnValue() > -1) {
342             result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange);
343             if (!result.hasException() && result.releaseReturnValue() == -1) {
344                 // The ranges intersect.
345                 selection.moveTo(range.startPosition(), normalizedRange->endPosition(), DOWNSTREAM);
346             } else {
347                 // The new range contains the original range.
348                 selection.moveTo(&range);
349             }
350         }
351     } else {
352         // We don't support discontiguous selection. We don't do anything if the two ranges don't intersect.
353         result = range.compareBoundaryPoints(Range::END_TO_START, *normalizedRange);
354         if (!result.hasException() && result.releaseReturnValue() < 1) {
355             result = range.compareBoundaryPoints(Range::END_TO_END, *normalizedRange);
356             if (!result.hasException() && result.releaseReturnValue() == -1) {
357                 // The original range contains the new range.
358                 selection.moveTo(normalizedRange.get());
359             } else {
360                 // The ranges intersect.
361                 selection.moveTo(normalizedRange->startPosition(), range.endPosition(), DOWNSTREAM);
362             }
363         }
364     }
365 }
366
367 void DOMSelection::deleteFromDocument()
368 {
369     if (!m_frame)
370         return;
371
372     auto& selection = m_frame->selection();
373     if (selection.isNone())
374         return;
375
376     auto selectedRange = selection.selection().toNormalizedRange();
377     if (!selectedRange || selectedRange->shadowRoot())
378         return;
379
380     Ref<Frame> protector(*m_frame);
381     selectedRange->deleteContents();
382     setBaseAndExtent(&selectedRange->startContainer(), selectedRange->startOffset(), &selectedRange->startContainer(), selectedRange->startOffset());
383 }
384
385 bool DOMSelection::containsNode(Node& node, bool allowPartial) const
386 {
387     if (!m_frame)
388         return false;
389
390     auto& selection = m_frame->selection();
391     if (m_frame->document() != &node.document() || selection.isNone())
392         return false;
393
394     Ref<Node> protectedNode(node);
395     auto selectedRange = selection.selection().toNormalizedRange();
396     if (!selectedRange)
397         return false;
398
399     ContainerNode* parentNode = node.parentNode();
400     if (!parentNode || !parentNode->isConnected())
401         return false;
402     unsigned nodeIndex = node.computeNodeIndex();
403
404     auto startsResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->startContainer(), selectedRange->startOffset());
405     ASSERT(!startsResult.hasException());
406     auto endsResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->endContainer(), selectedRange->endOffset());
407     ASSERT(!endsResult.hasException());
408     bool isNodeFullySelected = !startsResult.hasException() && startsResult.releaseReturnValue() >= 0
409         && !endsResult.hasException() && endsResult.releaseReturnValue() <= 0;
410     if (isNodeFullySelected)
411         return true;
412
413     auto startEndResult = Range::compareBoundaryPoints(parentNode, nodeIndex, &selectedRange->endContainer(), selectedRange->endOffset());
414     ASSERT(!startEndResult.hasException());
415     auto endStartResult = Range::compareBoundaryPoints(parentNode, nodeIndex + 1, &selectedRange->startContainer(), selectedRange->startOffset());
416     ASSERT(!endStartResult.hasException());
417     bool isNodeFullyUnselected = (!startEndResult.hasException() && startEndResult.releaseReturnValue() > 0)
418         || (!endStartResult.hasException() && endStartResult.releaseReturnValue() < 0);
419     if (isNodeFullyUnselected)
420         return false;
421
422     return allowPartial || node.isTextNode();
423 }
424
425 void DOMSelection::selectAllChildren(Node& node)
426 {
427     // This doesn't (and shouldn't) select text node characters.
428     setBaseAndExtent(&node, 0, &node, node.countChildNodes());
429 }
430
431 String DOMSelection::toString()
432 {
433     if (!m_frame)
434         return String();
435     return plainText(m_frame->selection().selection().toNormalizedRange().get());
436 }
437
438 Node* DOMSelection::shadowAdjustedNode(const Position& position) const
439 {
440     if (position.isNull())
441         return nullptr;
442
443     auto* containerNode = position.containerNode();
444     auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode);
445     if (!adjustedNode)
446         return nullptr;
447
448     if (containerNode == adjustedNode)
449         return containerNode;
450
451     return adjustedNode->parentNodeGuaranteedHostFree();
452 }
453
454 unsigned DOMSelection::shadowAdjustedOffset(const Position& position) const
455 {
456     if (position.isNull())
457         return 0;
458
459     auto* containerNode = position.containerNode();
460     auto* adjustedNode = m_frame->document()->ancestorNodeInThisScope(containerNode);
461     if (!adjustedNode)
462         return 0;
463
464     if (containerNode == adjustedNode)
465         return position.computeOffsetInContainerNode();
466
467     return adjustedNode->computeNodeIndex();
468 }
469
470 bool DOMSelection::isValidForPosition(Node* node) const
471 {
472     if (!m_frame)
473         return false;
474     if (!node)
475         return true;
476     return &node->document() == m_frame->document();
477 }
478
479 } // namespace WebCore