• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
selectionShadowAncestor(Frame * frame)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 
DOMSelection(Frame * frame)55 DOMSelection::DOMSelection(Frame* frame)
56     : m_frame(frame)
57 {
58 }
59 
frame() const60 Frame* DOMSelection::frame() const
61 {
62     return m_frame;
63 }
64 
disconnectFrame()65 void DOMSelection::disconnectFrame()
66 {
67     m_frame = 0;
68 }
69 
visibleSelection() const70 const VisibleSelection& DOMSelection::visibleSelection() const
71 {
72     ASSERT(m_frame);
73     return m_frame->selection()->selection();
74 }
75 
anchorPosition(const VisibleSelection & selection)76 static Position anchorPosition(const VisibleSelection& selection)
77 {
78     Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
79     return rangeCompliantEquivalent(anchor);
80 }
81 
focusPosition(const VisibleSelection & selection)82 static Position focusPosition(const VisibleSelection& selection)
83 {
84     Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
85     return rangeCompliantEquivalent(focus);
86 }
87 
basePosition(const VisibleSelection & selection)88 static Position basePosition(const VisibleSelection& selection)
89 {
90     return rangeCompliantEquivalent(selection.base());
91 }
92 
extentPosition(const VisibleSelection & selection)93 static Position extentPosition(const VisibleSelection& selection)
94 {
95     return rangeCompliantEquivalent(selection.extent());
96 }
97 
anchorNode() const98 Node* DOMSelection::anchorNode() const
99 {
100     if (!m_frame)
101         return 0;
102     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
103         return shadowAncestor->parentNode();
104     return anchorPosition(visibleSelection()).node();
105 }
106 
anchorOffset() const107 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()).deprecatedEditingOffset();
114 }
115 
focusNode() const116 Node* DOMSelection::focusNode() const
117 {
118     if (!m_frame)
119         return 0;
120     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
121         return shadowAncestor->parentNode();
122     return focusPosition(visibleSelection()).node();
123 }
124 
focusOffset() const125 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()).deprecatedEditingOffset();
132 }
133 
baseNode() const134 Node* DOMSelection::baseNode() const
135 {
136     if (!m_frame)
137         return 0;
138     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
139         return shadowAncestor->parentNode();
140     return basePosition(visibleSelection()).node();
141 }
142 
baseOffset() const143 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()).deprecatedEditingOffset();
150 }
151 
extentNode() const152 Node* DOMSelection::extentNode() const
153 {
154     if (!m_frame)
155         return 0;
156     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
157         return shadowAncestor->parentNode();
158     return extentPosition(visibleSelection()).node();
159 }
160 
extentOffset() const161 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()).deprecatedEditingOffset();
168 }
169 
isCollapsed() const170 bool DOMSelection::isCollapsed() const
171 {
172     if (!m_frame || selectionShadowAncestor(m_frame))
173         return true;
174     return !m_frame->selection()->isRange();
175 }
176 
type() const177 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 
rangeCount() const194 int DOMSelection::rangeCount() const
195 {
196     if (!m_frame)
197         return 0;
198     return m_frame->selection()->isNone() ? 0 : 1;
199 }
200 
collapse(Node * node,int offset,ExceptionCode & ec)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     m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
211 }
212 
collapseToEnd()213 void DOMSelection::collapseToEnd()
214 {
215     if (!m_frame)
216         return;
217 
218     const VisibleSelection& selection = m_frame->selection()->selection();
219     m_frame->selection()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
220 }
221 
collapseToStart()222 void DOMSelection::collapseToStart()
223 {
224     if (!m_frame)
225         return;
226 
227     const VisibleSelection& selection = m_frame->selection()->selection();
228     m_frame->selection()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
229 }
230 
empty()231 void DOMSelection::empty()
232 {
233     if (!m_frame)
234         return;
235     m_frame->selection()->clear();
236 }
237 
setBaseAndExtent(Node * baseNode,int baseOffset,Node * extentNode,int extentOffset,ExceptionCode & ec)238 void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec)
239 {
240     if (!m_frame)
241         return;
242 
243     if (baseOffset < 0 || extentOffset < 0) {
244         ec = INDEX_SIZE_ERR;
245         return;
246     }
247     VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM);
248     VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM);
249 
250     m_frame->selection()->moveTo(visibleBase, visibleExtent);
251 }
252 
setPosition(Node * node,int offset,ExceptionCode & ec)253 void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec)
254 {
255     if (!m_frame)
256         return;
257     if (offset < 0) {
258         ec = INDEX_SIZE_ERR;
259         return;
260     }
261     m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
262 }
263 
modify(const String & alterString,const String & directionString,const String & granularityString)264 void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
265 {
266     if (!m_frame)
267         return;
268 
269     SelectionController::EAlteration alter;
270     if (equalIgnoringCase(alterString, "extend"))
271         alter = SelectionController::EXTEND;
272     else if (equalIgnoringCase(alterString, "move"))
273         alter = SelectionController::MOVE;
274     else
275         return;
276 
277     SelectionController::EDirection direction;
278     if (equalIgnoringCase(directionString, "forward"))
279         direction = SelectionController::FORWARD;
280     else if (equalIgnoringCase(directionString, "backward"))
281         direction = SelectionController::BACKWARD;
282     else if (equalIgnoringCase(directionString, "left"))
283         direction = SelectionController::LEFT;
284     else if (equalIgnoringCase(directionString, "right"))
285         direction = SelectionController::RIGHT;
286     else
287         return;
288 
289     TextGranularity granularity;
290     if (equalIgnoringCase(granularityString, "character"))
291         granularity = CharacterGranularity;
292     else if (equalIgnoringCase(granularityString, "word"))
293         granularity = WordGranularity;
294     else if (equalIgnoringCase(granularityString, "sentence"))
295         granularity = SentenceGranularity;
296     else if (equalIgnoringCase(granularityString, "line"))
297         granularity = LineGranularity;
298     else if (equalIgnoringCase(granularityString, "paragraph"))
299         granularity = ParagraphGranularity;
300     else if (equalIgnoringCase(granularityString, "lineboundary"))
301         granularity = LineBoundary;
302     else if (equalIgnoringCase(granularityString, "sentenceboundary"))
303         granularity = SentenceBoundary;
304     else if (equalIgnoringCase(granularityString, "paragraphboundary"))
305         granularity = ParagraphBoundary;
306     else if (equalIgnoringCase(granularityString, "documentboundary"))
307         granularity = DocumentBoundary;
308     else
309         return;
310 
311     m_frame->selection()->modify(alter, direction, granularity, false);
312 }
313 
extend(Node * node,int offset,ExceptionCode & ec)314 void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec)
315 {
316     if (!m_frame)
317         return;
318 
319     if (!node) {
320         ec = TYPE_MISMATCH_ERR;
321         return;
322     }
323     if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
324         ec = INDEX_SIZE_ERR;
325         return;
326     }
327 
328     SelectionController* selection = m_frame->selection();
329     selection->expandUsingGranularity(CharacterGranularity);
330     selection->setExtent(VisiblePosition(node, offset, DOWNSTREAM));
331 }
332 
getRangeAt(int index,ExceptionCode & ec)333 PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
334 {
335     if (!m_frame)
336         return 0;
337 
338     if (index < 0 || index >= rangeCount()) {
339         ec = INDEX_SIZE_ERR;
340         return 0;
341     }
342 
343     // If you're hitting this, you've added broken multi-range selection support
344     ASSERT(rangeCount() == 1);
345 
346     if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) {
347         Node* container = shadowAncestor->parentNode();
348         int offset = shadowAncestor->nodeIndex();
349         return Range::create(shadowAncestor->document(), container, offset, container, offset);
350     }
351 
352     const VisibleSelection& selection = m_frame->selection()->selection();
353     return selection.firstRange();
354 }
355 
removeAllRanges()356 void DOMSelection::removeAllRanges()
357 {
358     if (!m_frame)
359         return;
360     m_frame->selection()->clear();
361 }
362 
addRange(Range * r)363 void DOMSelection::addRange(Range* r)
364 {
365     if (!m_frame)
366         return;
367     if (!r)
368         return;
369 
370     SelectionController* selection = m_frame->selection();
371 
372     if (selection->isNone()) {
373         selection->setSelection(VisibleSelection(r));
374         return;
375     }
376 
377     RefPtr<Range> range = selection->selection().toNormalizedRange();
378     ExceptionCode ec = 0;
379     if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) {
380         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
381         if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) > -1) {
382             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
383                 // The original range and r intersect.
384                 selection->setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM));
385             else
386                 // r contains the original range.
387                 selection->setSelection(VisibleSelection(r));
388         }
389     } else {
390         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
391         if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1) {
392             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
393                 // The original range contains r.
394                 selection->setSelection(VisibleSelection(range.get()));
395             else
396                 // The original range and r intersect.
397                 selection->setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM));
398         }
399     }
400 }
401 
deleteFromDocument()402 void DOMSelection::deleteFromDocument()
403 {
404     if (!m_frame)
405         return;
406 
407     SelectionController* selection = m_frame->selection();
408 
409     if (selection->isNone())
410         return;
411 
412     if (isCollapsed())
413         selection->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity);
414 
415     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
416 
417     ExceptionCode ec = 0;
418     selectedRange->deleteContents(ec);
419     ASSERT(!ec);
420 
421     setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
422     ASSERT(!ec);
423 }
424 
containsNode(const Node * n,bool allowPartial) const425 bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
426 {
427     if (!m_frame)
428         return false;
429 
430     SelectionController* selection = m_frame->selection();
431 
432     if (!n || selection->isNone())
433         return false;
434 
435     Node* parentNode = n->parentNode();
436     unsigned nodeIndex = n->nodeIndex();
437     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
438 
439     if (!parentNode)
440         return false;
441 
442     ExceptionCode ec = 0;
443     bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) >= 0
444         && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) <= 0;
445     ASSERT(!ec);
446     if (nodeFullySelected)
447         return true;
448 
449     bool nodeFullyUnselected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) > 0
450         || Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) < 0;
451     ASSERT(!ec);
452     if (nodeFullyUnselected)
453         return false;
454 
455     return allowPartial || n->isTextNode();
456 }
457 
selectAllChildren(Node * n,ExceptionCode & ec)458 void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
459 {
460     if (!n)
461         return;
462 
463     // This doesn't (and shouldn't) select text node characters.
464     setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
465 }
466 
toString()467 String DOMSelection::toString()
468 {
469     if (!m_frame)
470         return String();
471 
472     return plainText(m_frame->selection()->selection().toNormalizedRange().get());
473 }
474 
475 } // namespace WebCore
476