2011-03-30 Xiaomei Ji <xji@chromium.org>
[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 "ExceptionCode.h"
34 #include "Frame.h"
35 #include "Node.h"
36 #include "PlatformString.h"
37 #include "Range.h"
38 #include "SelectionController.h"
39 #include "TextIterator.h"
40 #include "htmlediting.h"
41
42 namespace WebCore {
43
44 static Node* selectionShadowAncestor(Frame* frame)
45 {
46     Node* node = frame->selection()->selection().base().anchorNode();
47     if (!node)
48         return 0;
49     Node* shadowAncestor = node->shadowAncestorNode();
50     if (shadowAncestor == node)
51         return 0;
52     return shadowAncestor;
53 }
54
55 DOMSelection::DOMSelection(Frame* frame)
56     : m_frame(frame)
57 {
58 }
59
60 Frame* DOMSelection::frame() const
61 {
62     return m_frame;
63 }
64
65 void DOMSelection::disconnectFrame()
66 {
67     m_frame = 0;
68 }
69
70 const VisibleSelection& DOMSelection::visibleSelection() const
71 {
72     ASSERT(m_frame);
73     return m_frame->selection()->selection();
74 }
75
76 static Position anchorPosition(const VisibleSelection& selection)
77 {
78     Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
79     return anchor.parentAnchoredEquivalent();
80 }
81
82 static Position focusPosition(const VisibleSelection& selection)
83 {
84     Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
85     return focus.parentAnchoredEquivalent();
86 }
87
88 static Position basePosition(const VisibleSelection& selection)
89 {
90     return selection.base().parentAnchoredEquivalent();
91 }
92
93 static Position extentPosition(const VisibleSelection& selection)
94 {
95     return selection.extent().parentAnchoredEquivalent();
96 }
97
98 Node* DOMSelection::anchorNode() const
99 {
100     if (!m_frame)
101         return 0;
102     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
103         return shadowAncestor->parentNodeGuaranteedHostFree();
104     return anchorPosition(visibleSelection()).containerNode();
105 }
106
107 int DOMSelection::anchorOffset() const
108 {
109     if (!m_frame)
110         return 0;
111     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
112         return shadowAncestor->nodeIndex();
113     return anchorPosition(visibleSelection()).offsetInContainerNode();
114 }
115
116 Node* DOMSelection::focusNode() const
117 {
118     if (!m_frame)
119         return 0;
120     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
121         return shadowAncestor->parentNodeGuaranteedHostFree();
122     return focusPosition(visibleSelection()).containerNode();
123 }
124
125 int DOMSelection::focusOffset() const
126 {
127     if (!m_frame)
128         return 0;
129     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
130         return shadowAncestor->nodeIndex();
131     return focusPosition(visibleSelection()).offsetInContainerNode();
132 }
133
134 Node* DOMSelection::baseNode() const
135 {
136     if (!m_frame)
137         return 0;
138     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
139         return shadowAncestor->parentNodeGuaranteedHostFree();
140     return basePosition(visibleSelection()).containerNode();
141 }
142
143 int DOMSelection::baseOffset() const
144 {
145     if (!m_frame)
146         return 0;
147     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
148         return shadowAncestor->nodeIndex();
149     return basePosition(visibleSelection()).offsetInContainerNode();
150 }
151
152 Node* DOMSelection::extentNode() const
153 {
154     if (!m_frame)
155         return 0;
156     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
157         return shadowAncestor->parentNodeGuaranteedHostFree();
158     return extentPosition(visibleSelection()).containerNode();
159 }
160
161 int DOMSelection::extentOffset() const
162 {
163     if (!m_frame)
164         return 0;
165     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
166         return shadowAncestor->nodeIndex();
167     return extentPosition(visibleSelection()).offsetInContainerNode();
168 }
169
170 bool DOMSelection::isCollapsed() const
171 {
172     if (!m_frame || selectionShadowAncestor(m_frame))
173         return true;
174     return !m_frame->selection()->isRange();
175 }
176
177 String DOMSelection::type() const
178 {
179     if (!m_frame)
180         return String();
181
182     SelectionController* selection = m_frame->selection();
183
184     // This is a WebKit DOM extension, incompatible with an IE extension
185     // IE has this same attribute, but returns "none", "text" and "control"
186     // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx
187     if (selection->isNone())
188         return "None";
189     if (selection->isCaret())
190         return "Caret";
191     return "Range";
192 }
193
194 int DOMSelection::rangeCount() const
195 {
196     if (!m_frame)
197         return 0;
198     return m_frame->selection()->isNone() ? 0 : 1;
199 }
200
201 void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec)
202 {
203     if (!m_frame)
204         return;
205
206     if (offset < 0) {
207         ec = INDEX_SIZE_ERR;
208         return;
209     }
210
211     if (!isValidForPosition(node))
212         return;
213
214     // FIXME: Eliminate legacy editing positions
215     m_frame->selection()->moveTo(VisiblePosition(Position(node, offset), DOWNSTREAM));
216 }
217
218 void DOMSelection::collapseToEnd(ExceptionCode& ec)
219 {
220     if (!m_frame)
221         return;
222
223     const VisibleSelection& selection = m_frame->selection()->selection();
224
225     if (selection.isNone()) {
226         ec = INVALID_STATE_ERR;
227         return;
228     }
229
230     m_frame->selection()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
231 }
232
233 void DOMSelection::collapseToStart(ExceptionCode& ec)
234 {
235     if (!m_frame)
236         return;
237
238     const VisibleSelection& selection = m_frame->selection()->selection();
239
240     if (selection.isNone()) {
241         ec = INVALID_STATE_ERR;
242         return;
243     }
244
245     m_frame->selection()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
246 }
247
248 void DOMSelection::empty()
249 {
250     if (!m_frame)
251         return;
252     m_frame->selection()->clear();
253 }
254
255 void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec)
256 {
257     if (!m_frame)
258         return;
259
260     if (baseOffset < 0 || extentOffset < 0) {
261         ec = INDEX_SIZE_ERR;
262         return;
263     }
264
265     if (!isValidForPosition(baseNode) || !isValidForPosition(extentNode))
266         return;
267
268     // FIXME: Eliminate legacy editing positions
269     VisiblePosition visibleBase = VisiblePosition(Position(baseNode, baseOffset), DOWNSTREAM);
270     VisiblePosition visibleExtent = VisiblePosition(Position(extentNode, extentOffset), DOWNSTREAM);
271
272     m_frame->selection()->moveTo(visibleBase, visibleExtent);
273 }
274
275 void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec)
276 {
277     if (!m_frame)
278         return;
279     if (offset < 0) {
280         ec = INDEX_SIZE_ERR;
281         return;
282     }
283
284     if (!isValidForPosition(node))
285         return;
286
287     // FIXME: Eliminate legacy editing positions
288     m_frame->selection()->moveTo(VisiblePosition(Position(node, offset), DOWNSTREAM));
289 }
290
291 void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
292 {
293     if (!m_frame)
294         return;
295
296     SelectionController::EAlteration alter;
297     if (equalIgnoringCase(alterString, "extend"))
298         alter = SelectionController::AlterationExtend;
299     else if (equalIgnoringCase(alterString, "move"))
300         alter = SelectionController::AlterationMove;
301     else
302         return;
303
304     SelectionDirection direction;
305     if (equalIgnoringCase(directionString, "forward"))
306         direction = DirectionForward;
307     else if (equalIgnoringCase(directionString, "backward"))
308         direction = DirectionBackward;
309     else if (equalIgnoringCase(directionString, "left"))
310         direction = DirectionLeft;
311     else if (equalIgnoringCase(directionString, "right"))
312         direction = DirectionRight;
313     else
314         return;
315
316     TextGranularity granularity;
317     if (equalIgnoringCase(granularityString, "character"))
318         granularity = CharacterGranularity;
319     else if (equalIgnoringCase(granularityString, "word"))
320         granularity = WordGranularity;
321     else if (equalIgnoringCase(granularityString, "sentence"))
322         granularity = SentenceGranularity;
323     else if (equalIgnoringCase(granularityString, "line"))
324         granularity = LineGranularity;
325     else if (equalIgnoringCase(granularityString, "paragraph"))
326         granularity = ParagraphGranularity;
327     else if (equalIgnoringCase(granularityString, "lineboundary"))
328         granularity = LineBoundary;
329     else if (equalIgnoringCase(granularityString, "sentenceboundary"))
330         granularity = SentenceBoundary;
331     else if (equalIgnoringCase(granularityString, "paragraphboundary"))
332         granularity = ParagraphBoundary;
333     else if (equalIgnoringCase(granularityString, "documentboundary"))
334         granularity = DocumentBoundary;
335     else if (equalIgnoringCase(granularityString, "-webkit-visual-word"))
336         granularity = WebKitVisualWordGranularity;
337     else
338         return;
339
340     m_frame->selection()->modify(alter, direction, granularity, false);
341 }
342
343 void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec)
344 {
345     if (!m_frame)
346         return;
347
348     if (!node) {
349         ec = TYPE_MISMATCH_ERR;
350         return;
351     }
352
353     if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
354         ec = INDEX_SIZE_ERR;
355         return;
356     }
357
358     if (!isValidForPosition(node))
359         return;
360
361     // FIXME: Eliminate legacy editing positions
362     m_frame->selection()->setExtent(VisiblePosition(Position(node, offset), DOWNSTREAM));
363 }
364
365 PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
366 {
367     if (!m_frame)
368         return 0;
369
370     if (index < 0 || index >= rangeCount()) {
371         ec = INDEX_SIZE_ERR;
372         return 0;
373     }
374
375     // If you're hitting this, you've added broken multi-range selection support
376     ASSERT(rangeCount() == 1);
377
378     if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) {
379         ContainerNode* container = shadowAncestor->parentNodeGuaranteedHostFree();
380         int offset = shadowAncestor->nodeIndex();
381         return Range::create(shadowAncestor->document(), container, offset, container, offset);
382     }
383
384     const VisibleSelection& selection = m_frame->selection()->selection();
385     return selection.firstRange();
386 }
387
388 void DOMSelection::removeAllRanges()
389 {
390     if (!m_frame)
391         return;
392     m_frame->selection()->clear();
393 }
394
395 void DOMSelection::addRange(Range* r)
396 {
397     if (!m_frame)
398         return;
399     if (!r)
400         return;
401
402     SelectionController* selection = m_frame->selection();
403
404     if (selection->isNone()) {
405         selection->setSelection(VisibleSelection(r));
406         return;
407     }
408
409     RefPtr<Range> range = selection->selection().toNormalizedRange();
410     ExceptionCode ec = 0;
411     if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) {
412         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
413         if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) > -1) {
414             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
415                 // The original range and r intersect.
416                 selection->setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM));
417             else
418                 // r contains the original range.
419                 selection->setSelection(VisibleSelection(r));
420         }
421     } else {
422         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
423         if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1) {
424             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
425                 // The original range contains r.
426                 selection->setSelection(VisibleSelection(range.get()));
427             else
428                 // The original range and r intersect.
429                 selection->setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM));
430         }
431     }
432 }
433
434 void DOMSelection::deleteFromDocument()
435 {
436     if (!m_frame)
437         return;
438
439     SelectionController* selection = m_frame->selection();
440
441     if (selection->isNone())
442         return;
443
444     if (isCollapsed())
445         selection->modify(SelectionController::AlterationExtend, DirectionBackward, CharacterGranularity);
446
447     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
448     if (!selectedRange)
449         return;
450
451     ExceptionCode ec = 0;
452     selectedRange->deleteContents(ec);
453     ASSERT(!ec);
454
455     setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
456     ASSERT(!ec);
457 }
458
459 bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
460 {
461     if (!m_frame)
462         return false;
463
464     SelectionController* selection = m_frame->selection();
465
466     if (!n || m_frame->document() != n->document() || selection->isNone())
467         return false;
468
469     ContainerNode* parentNode = n->parentNode();
470     unsigned nodeIndex = n->nodeIndex();
471     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
472
473     if (!parentNode)
474         return false;
475
476     ExceptionCode ec = 0;
477     bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) >= 0
478         && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) <= 0;
479     ASSERT(!ec);
480     if (nodeFullySelected)
481         return true;
482
483     bool nodeFullyUnselected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) > 0
484         || Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) < 0;
485     ASSERT(!ec);
486     if (nodeFullyUnselected)
487         return false;
488
489     return allowPartial || n->isTextNode();
490 }
491
492 void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
493 {
494     if (!n)
495         return;
496
497     // This doesn't (and shouldn't) select text node characters.
498     setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
499 }
500
501 String DOMSelection::toString()
502 {
503     if (!m_frame)
504         return String();
505
506     return plainText(m_frame->selection()->selection().toNormalizedRange().get());
507 }
508
509 bool DOMSelection::isValidForPosition(Node* node) const
510 {
511     ASSERT(m_frame);
512     if (!node)
513         return true;
514     return node->document() == m_frame->document();
515 }
516
517 } // namespace WebCore