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 rangeCompliantEquivalent(anchor); 80 } 81 82 static Position focusPosition(const VisibleSelection& selection) 83 { 84 Position focus = selection.isBaseFirst() ? selection.end() : selection.start(); 85 return rangeCompliantEquivalent(focus); 86 } 87 88 static Position basePosition(const VisibleSelection& selection) 89 { 90 return rangeCompliantEquivalent(selection.base()); 91 } 92 93 static Position extentPosition(const VisibleSelection& selection) 94 { 95 return rangeCompliantEquivalent(selection.extent()); 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->parentNode(); 104 return anchorPosition(visibleSelection()).node(); 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()).deprecatedEditingOffset(); 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->parentNode(); 122 return focusPosition(visibleSelection()).node(); 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()).deprecatedEditingOffset(); 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->parentNode(); 140 return basePosition(visibleSelection()).node(); 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()).deprecatedEditingOffset(); 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->parentNode(); 158 return extentPosition(visibleSelection()).node(); 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()).deprecatedEditingOffset(); 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 m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM)); 211 } 212 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 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 231 void DOMSelection::empty() 232 { 233 if (!m_frame) 234 return; 235 m_frame->selection()->clear(); 236 } 237 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 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 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 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 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 356 void DOMSelection::removeAllRanges() 357 { 358 if (!m_frame) 359 return; 360 m_frame->selection()->clear(); 361 } 362 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 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 425 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 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 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