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