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 anchor.parentAnchoredEquivalent();
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 focus.parentAnchoredEquivalent();
86 }
87
basePosition(const VisibleSelection & selection)88 static Position basePosition(const VisibleSelection& selection)
89 {
90 return selection.base().parentAnchoredEquivalent();
91 }
92
extentPosition(const VisibleSelection & selection)93 static Position extentPosition(const VisibleSelection& selection)
94 {
95 return selection.extent().parentAnchoredEquivalent();
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->parentNodeGuaranteedHostFree();
104 return anchorPosition(visibleSelection()).containerNode();
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()).offsetInContainerNode();
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->parentNodeGuaranteedHostFree();
122 return focusPosition(visibleSelection()).containerNode();
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()).offsetInContainerNode();
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->parentNodeGuaranteedHostFree();
140 return basePosition(visibleSelection()).containerNode();
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()).offsetInContainerNode();
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->parentNodeGuaranteedHostFree();
158 return extentPosition(visibleSelection()).containerNode();
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()).offsetInContainerNode();
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
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
collapseToEnd(ExceptionCode & ec)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
collapseToStart(ExceptionCode & ec)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
empty()248 void DOMSelection::empty()
249 {
250 if (!m_frame)
251 return;
252 m_frame->selection()->clear();
253 }
254
setBaseAndExtent(Node * baseNode,int baseOffset,Node * extentNode,int extentOffset,ExceptionCode & ec)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
setPosition(Node * node,int offset,ExceptionCode & ec)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
modify(const String & alterString,const String & directionString,const String & granularityString)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
extend(Node * node,int offset,ExceptionCode & ec)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
getRangeAt(int index,ExceptionCode & ec)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
removeAllRanges()388 void DOMSelection::removeAllRanges()
389 {
390 if (!m_frame)
391 return;
392 m_frame->selection()->clear();
393 }
394
addRange(Range * r)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
deleteFromDocument()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
containsNode(const Node * n,bool allowPartial) const459 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), ec) >= 0 && !ec
478 && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec), ec) <= 0 && !ec;
479 ASSERT(!ec);
480 if (nodeFullySelected)
481 return true;
482
483 bool nodeFullyUnselected = (Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec), ec) > 0 && !ec)
484 || (Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec) < 0 && !ec);
485 ASSERT(!ec);
486 if (nodeFullyUnselected)
487 return false;
488
489 return allowPartial || n->isTextNode();
490 }
491
selectAllChildren(Node * n,ExceptionCode & ec)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
toString()501 String DOMSelection::toString()
502 {
503 if (!m_frame)
504 return String();
505
506 return plainText(m_frame->selection()->selection().toNormalizedRange().get());
507 }
508
isValidForPosition(Node * node) const509 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
518