| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 25 matching lines...) Expand all Loading... |
| 36 #include "visible_units.h" | 36 #include "visible_units.h" |
| 37 #include "Range.h" | 37 #include "Range.h" |
| 38 #include <wtf/Assertions.h> | 38 #include <wtf/Assertions.h> |
| 39 #include <stdio.h> | 39 #include <stdio.h> |
| 40 | 40 |
| 41 namespace WebCore { | 41 namespace WebCore { |
| 42 | 42 |
| 43 Selection::Selection() | 43 Selection::Selection() |
| 44 : m_affinity(DOWNSTREAM) | 44 : m_affinity(DOWNSTREAM) |
| 45 , m_granularity(CharacterGranularity) | 45 , m_granularity(CharacterGranularity) |
| 46 , m_state(NONE) | 46 , m_selectionType(NoSelection) |
| 47 , m_baseIsFirst(true) | 47 , m_baseIsFirst(true) |
| 48 { | 48 { |
| 49 } | 49 } |
| 50 | 50 |
| 51 Selection::Selection(const Position& pos, EAffinity affinity) | 51 Selection::Selection(const Position& pos, EAffinity affinity) |
| 52 : m_base(pos) | 52 : m_base(pos) |
| 53 , m_extent(pos) | 53 , m_extent(pos) |
| 54 , m_affinity(affinity) | 54 , m_affinity(affinity) |
| 55 , m_granularity(CharacterGranularity) | 55 , m_granularity(CharacterGranularity) |
| 56 { | 56 { |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 m_extent = position; | 115 m_extent = position; |
| 116 validate(); | 116 validate(); |
| 117 } | 117 } |
| 118 | 118 |
| 119 void Selection::setExtent(const VisiblePosition& visiblePosition) | 119 void Selection::setExtent(const VisiblePosition& visiblePosition) |
| 120 { | 120 { |
| 121 m_extent = visiblePosition.deepEquivalent(); | 121 m_extent = visiblePosition.deepEquivalent(); |
| 122 validate(); | 122 validate(); |
| 123 } | 123 } |
| 124 | 124 |
| 125 PassRefPtr<Range> Selection::toRange() const | 125 PassRefPtr<Range> Selection::firstRange() const |
| 126 { |
| 127 if (isNone()) |
| 128 return 0; |
| 129 Position start = rangeCompliantEquivalent(m_start); |
| 130 Position end = rangeCompliantEquivalent(m_end); |
| 131 return Range::create(start.node()->document(), start, end); |
| 132 } |
| 133 |
| 134 PassRefPtr<Range> Selection::toNormalizedRange() const |
| 126 { | 135 { |
| 127 if (isNone()) | 136 if (isNone()) |
| 128 return 0; | 137 return 0; |
| 129 | 138 |
| 130 // Make sure we have an updated layout since this function is called | 139 // Make sure we have an updated layout since this function is called |
| 131 // in the course of running edit commands which modify the DOM. | 140 // in the course of running edit commands which modify the DOM. |
| 132 // Failing to call this can result in equivalentXXXPosition calls returning | 141 // Failing to call this can result in equivalentXXXPosition calls returning |
| 133 // incorrect results. | 142 // incorrect results. |
| 134 m_start.node()->document()->updateLayout(); | 143 m_start.node()->document()->updateLayout(); |
| 135 | 144 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 163 // Make sure the start is before the end. | 172 // Make sure the start is before the end. |
| 164 // The end can wind up before the start if collapsed whitespace is t
he only thing selected. | 173 // The end can wind up before the start if collapsed whitespace is t
he only thing selected. |
| 165 Position tmp = s; | 174 Position tmp = s; |
| 166 s = e; | 175 s = e; |
| 167 e = tmp; | 176 e = tmp; |
| 168 } | 177 } |
| 169 s = rangeCompliantEquivalent(s); | 178 s = rangeCompliantEquivalent(s); |
| 170 e = rangeCompliantEquivalent(e); | 179 e = rangeCompliantEquivalent(e); |
| 171 } | 180 } |
| 172 | 181 |
| 173 ExceptionCode ec = 0; | 182 // Selections are supposed to always be valid. This constructor will ASSERT |
| 174 RefPtr<Range> result(Range::create(s.node()->document())); | 183 // if a valid range could not be created, which is fine for this callsite. |
| 175 result->setStart(s.node(), s.offset(), ec); | 184 return Range::create(s.node()->document(), s, e); |
| 176 if (ec) { | |
| 177 LOG_ERROR("Exception setting Range start from Selection: %d", ec); | |
| 178 return 0; | |
| 179 } | |
| 180 result->setEnd(e.node(), e.offset(), ec); | |
| 181 if (ec) { | |
| 182 LOG_ERROR("Exception setting Range end from Selection: %d", ec); | |
| 183 return 0; | |
| 184 } | |
| 185 return result.release(); | |
| 186 } | 185 } |
| 187 | 186 |
| 188 bool Selection::expandUsingGranularity(TextGranularity granularity) | 187 bool Selection::expandUsingGranularity(TextGranularity granularity) |
| 189 { | 188 { |
| 190 if (isNone()) | 189 if (isNone()) |
| 191 return false; | 190 return false; |
| 192 | 191 |
| 193 m_granularity = granularity; | 192 m_granularity = granularity; |
| 194 validate(); | 193 validate(); |
| 195 return true; | 194 return true; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 231 CharacterIterator charIt(searchRange.get(), true); | 230 CharacterIterator charIt(searchRange.get(), true); |
| 232 | 231 |
| 233 for (; charIt.length(); charIt.advance(1)) { | 232 for (; charIt.length(); charIt.advance(1)) { |
| 234 UChar c = charIt.characters()[0]; | 233 UChar c = charIt.characters()[0]; |
| 235 if (!isSpaceOrNewline(c) && c != noBreakSpace) | 234 if (!isSpaceOrNewline(c) && c != noBreakSpace) |
| 236 break; | 235 break; |
| 237 m_end = charIt.range()->endPosition(); | 236 m_end = charIt.range()->endPosition(); |
| 238 } | 237 } |
| 239 } | 238 } |
| 240 | 239 |
| 241 void Selection::validate() | 240 void Selection::setBaseAndExtentToDeepEquivalents() |
| 242 { | 241 { |
| 243 // Move the selection to rendered positions, if possible. | 242 // Move the selection to rendered positions, if possible. |
| 244 bool baseAndExtentEqual = m_base == m_extent; | 243 bool baseAndExtentEqual = m_base == m_extent; |
| 245 if (m_base.isNotNull()) { | 244 if (m_base.isNotNull()) { |
| 246 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); | 245 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); |
| 247 if (baseAndExtentEqual) | 246 if (baseAndExtentEqual) |
| 248 m_extent = m_base; | 247 m_extent = m_base; |
| 249 } | 248 } |
| 250 if (m_extent.isNotNull() && !baseAndExtentEqual) | 249 if (m_extent.isNotNull() && !baseAndExtentEqual) |
| 251 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); | 250 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); |
| 252 | 251 |
| 253 // Make sure we do not have a dangling base or extent. | 252 // Make sure we do not have a dangling base or extent. |
| 254 if (m_base.isNull() && m_extent.isNull()) | 253 if (m_base.isNull() && m_extent.isNull()) |
| 255 m_baseIsFirst = true; | 254 m_baseIsFirst = true; |
| 256 else if (m_base.isNull()) { | 255 else if (m_base.isNull()) { |
| 257 m_base = m_extent; | 256 m_base = m_extent; |
| 258 m_baseIsFirst = true; | 257 m_baseIsFirst = true; |
| 259 } else if (m_extent.isNull()) { | 258 } else if (m_extent.isNull()) { |
| 260 m_extent = m_base; | 259 m_extent = m_base; |
| 261 m_baseIsFirst = true; | 260 m_baseIsFirst = true; |
| 262 } else { | 261 } else |
| 263 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; | 262 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; |
| 264 } | 263 } |
| 265 | 264 |
| 265 void Selection::setStartAndEndFromBaseAndExtentRespectingGranularity() |
| 266 { |
| 266 if (m_baseIsFirst) { | 267 if (m_baseIsFirst) { |
| 267 m_start = m_base; | 268 m_start = m_base; |
| 268 m_end = m_extent; | 269 m_end = m_extent; |
| 269 } else { | 270 } else { |
| 270 m_start = m_extent; | 271 m_start = m_extent; |
| 271 m_end = m_base; | 272 m_end = m_base; |
| 272 } | 273 } |
| 273 | 274 |
| 274 // Expand the selection if requested. | |
| 275 switch (m_granularity) { | 275 switch (m_granularity) { |
| 276 case CharacterGranularity: | 276 case CharacterGranularity: |
| 277 // Don't do any expansion. | 277 // Don't do any expansion. |
| 278 break; | 278 break; |
| 279 case WordGranularity: { | 279 case WordGranularity: { |
| 280 // General case: Select the word the caret is positioned inside of,
or at the start of (RightWordIfOnBoundary). | 280 // General case: Select the word the caret is positioned inside of,
or at the start of (RightWordIfOnBoundary). |
| 281 // Edge case: If the caret is after the last word in a soft-wrapped
line or the last word in | 281 // Edge case: If the caret is after the last word in a soft-wrapped
line or the last word in |
| 282 // the document, select that last word (LeftWordIfOnBoundary). | 282 // the document, select that last word (LeftWordIfOnBoundary). |
| 283 // Edge case: If the caret is after the last word in a paragraph, se
lect from the the end of the | 283 // Edge case: If the caret is after the last word in a paragraph, se
lect from the the end of the |
| 284 // last word to the line break (also RightWordIfOnBoundary); | 284 // last word to the line break (also RightWordIfOnBoundary); |
| (...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deep
Equivalent(); | 378 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deep
Equivalent(); |
| 379 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquiva
lent(); | 379 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquiva
lent(); |
| 380 break; | 380 break; |
| 381 } | 381 } |
| 382 | 382 |
| 383 // Make sure we do not have a dangling start or end. | 383 // Make sure we do not have a dangling start or end. |
| 384 if (m_start.isNull()) | 384 if (m_start.isNull()) |
| 385 m_start = m_end; | 385 m_start = m_end; |
| 386 if (m_end.isNull()) | 386 if (m_end.isNull()) |
| 387 m_end = m_start; | 387 m_end = m_start; |
| 388 | 388 } |
| 389 adjustForEditableContent(); | 389 |
| 390 | 390 void Selection::updateSelectionType() |
| 391 // adjust the state | 391 { |
| 392 if (m_start.isNull()) { | 392 if (m_start.isNull()) { |
| 393 ASSERT(m_end.isNull()); | 393 ASSERT(m_end.isNull()); |
| 394 m_state = NONE; | 394 m_selectionType = NoSelection; |
| 395 | 395 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { |
| 396 // enforce downstream affinity if not caret, as affinity only | 396 m_selectionType = CaretSelection; |
| 397 // makes sense for caret | 397 } else |
| 398 m_selectionType = RangeSelection; |
| 399 |
| 400 // Affinity only makes sense for a caret |
| 401 if (m_selectionType != CaretSelection) |
| 398 m_affinity = DOWNSTREAM; | 402 m_affinity = DOWNSTREAM; |
| 399 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { | 403 } |
| 400 m_state = CARET; | 404 |
| 401 } else { | 405 void Selection::validate() |
| 402 m_state = RANGE; | 406 { |
| 403 | 407 setBaseAndExtentToDeepEquivalents(); |
| 404 // enforce downstream affinity if not caret, as affinity only | 408 setStartAndEndFromBaseAndExtentRespectingGranularity(); |
| 405 // makes sense for caret | 409 adjustSelectionToAvoidCrossingEditingBoundaries(); |
| 406 m_affinity = DOWNSTREAM; | 410 updateSelectionType(); |
| 407 | 411 |
| 412 if (selectionType() == RangeSelection) { |
| 408 // "Constrain" the selection to be the smallest equivalent range of node
s. | 413 // "Constrain" the selection to be the smallest equivalent range of node
s. |
| 409 // This is a somewhat arbitrary choice, but experience shows that it is | 414 // This is a somewhat arbitrary choice, but experience shows that it is |
| 410 // useful to make to make the selection "canonical" (if only for | 415 // useful to make to make the selection "canonical" (if only for |
| 411 // purposes of comparing selections). This is an ideal point of the code | 416 // purposes of comparing selections). This is an ideal point of the code |
| 412 // to do this operation, since all selection changes that result in a RA
NGE | 417 // to do this operation, since all selection changes that result in a RA
NGE |
| 413 // come through here before anyone uses it. | 418 // come through here before anyone uses it. |
| 414 // FIXME: Canonicalizing is good, but haven't we already done it (when w
e | 419 // FIXME: Canonicalizing is good, but haven't we already done it (when w
e |
| 415 // set these two positions to VisiblePosition deepEquivalent()s above)? | 420 // set these two positions to VisiblePosition deepEquivalent()s above)? |
| 416 m_start = m_start.downstream(); | 421 m_start = m_start.downstream(); |
| 417 m_end = m_end.upstream(); | 422 m_end = m_end.upstream(); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 434 m_base = base; | 439 m_base = base; |
| 435 m_extent = extent; | 440 m_extent = extent; |
| 436 m_baseIsFirst = comparePositions(base, extent) <= 0; | 441 m_baseIsFirst = comparePositions(base, extent) <= 0; |
| 437 if (m_baseIsFirst) { | 442 if (m_baseIsFirst) { |
| 438 m_start = base; | 443 m_start = base; |
| 439 m_end = extent; | 444 m_end = extent; |
| 440 } else { | 445 } else { |
| 441 m_start = extent; | 446 m_start = extent; |
| 442 m_end = base; | 447 m_end = base; |
| 443 } | 448 } |
| 444 m_state = RANGE; | 449 m_selectionType = RangeSelection; |
| 445 } | 450 } |
| 446 | 451 |
| 447 void Selection::adjustForEditableContent() | 452 void Selection::adjustSelectionToAvoidCrossingEditingBoundaries() |
| 448 { | 453 { |
| 449 if (m_base.isNull() || m_start.isNull() || m_end.isNull()) | 454 if (m_base.isNull() || m_start.isNull() || m_end.isNull()) |
| 450 return; | 455 return; |
| 451 | 456 |
| 452 Node* baseRoot = highestEditableRoot(m_base); | 457 Node* baseRoot = highestEditableRoot(m_base); |
| 453 Node* startRoot = highestEditableRoot(m_start); | 458 Node* startRoot = highestEditableRoot(m_start); |
| 454 Node* endRoot = highestEditableRoot(m_end); | 459 Node* endRoot = highestEditableRoot(m_end); |
| 455 | 460 |
| 456 Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); | 461 Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); |
| 457 | 462 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 498 if (p.isNull() && endRoot && (shadowAncestor != endRoot)) | 503 if (p.isNull() && endRoot && (shadowAncestor != endRoot)) |
| 499 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); | 504 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); |
| 500 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { | 505 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { |
| 501 Node* root = editableRootForPosition(p); | 506 Node* root = editableRootForPosition(p); |
| 502 shadowAncestor = root ? root->shadowAncestorNode() : 0; | 507 shadowAncestor = root ? root->shadowAncestorNode() : 0; |
| 503 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : prev
iousVisuallyDistinctCandidate(p); | 508 p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : prev
iousVisuallyDistinctCandidate(p); |
| 504 if (p.isNull() && (shadowAncestor != root)) | 509 if (p.isNull() && (shadowAncestor != root)) |
| 505 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); | 510 p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); |
| 506 } | 511 } |
| 507 VisiblePosition previous(p); | 512 VisiblePosition previous(p); |
| 508 | 513 |
| 509 if (previous.isNull()) { | 514 if (previous.isNull()) { |
| 515 // The selection crosses an Editing boundary. This is a |
| 516 // programmer error in the editing code. Happy debugging! |
| 510 ASSERT_NOT_REACHED(); | 517 ASSERT_NOT_REACHED(); |
| 511 m_base = Position(); | 518 m_base = Position(); |
| 512 m_extent = Position(); | 519 m_extent = Position(); |
| 513 validate(); | 520 validate(); |
| 514 return; | 521 return; |
| 515 } | 522 } |
| 516 m_end = previous.deepEquivalent(); | 523 m_end = previous.deepEquivalent(); |
| 517 } | 524 } |
| 518 | 525 |
| 519 // The selection starts in editable content or non-editable content insi
de a different editable ancestor, | 526 // The selection starts in editable content or non-editable content insi
de a different editable ancestor, |
| 520 // move forward until non-editable content inside the same lowest editab
le ancestor is reached. | 527 // move forward until non-editable content inside the same lowest editab
le ancestor is reached. |
| 521 Node* startEditableAncestor = lowestEditableAncestor(m_start.node());
| 528 Node* startEditableAncestor = lowestEditableAncestor(m_start.node());
|
| 522 if (startRoot || startEditableAncestor != baseEditableAncestor) { | 529 if (startRoot || startEditableAncestor != baseEditableAncestor) { |
| 523 Position p = nextVisuallyDistinctCandidate(m_start); | 530 Position p = nextVisuallyDistinctCandidate(m_start); |
| 524 Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() :
0; | 531 Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() :
0; |
| 525 if (p.isNull() && startRoot && (shadowAncestor != startRoot)) | 532 if (p.isNull() && startRoot && (shadowAncestor != startRoot)) |
| 526 p = Position(shadowAncestor, 0); | 533 p = Position(shadowAncestor, 0); |
| 527 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { | 534 while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEd
itableAncestor && !isEditablePosition(p))) { |
| 528 Node* root = editableRootForPosition(p); | 535 Node* root = editableRootForPosition(p); |
| 529 shadowAncestor = root ? root->shadowAncestorNode() : 0; | 536 shadowAncestor = root ? root->shadowAncestorNode() : 0; |
| 530 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextV
isuallyDistinctCandidate(p); | 537 p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextV
isuallyDistinctCandidate(p); |
| 531 if (p.isNull() && (shadowAncestor != root)) | 538 if (p.isNull() && (shadowAncestor != root)) |
| 532 p = Position(shadowAncestor, 0); | 539 p = Position(shadowAncestor, 0); |
| 533 } | 540 } |
| 534 VisiblePosition next(p); | 541 VisiblePosition next(p); |
| 535 | 542 |
| 536 if (next.isNull()) { | 543 if (next.isNull()) { |
| 544 // The selection crosses an Editing boundary. This is a |
| 545 // programmer error in the editing code. Happy debugging! |
| 537 ASSERT_NOT_REACHED(); | 546 ASSERT_NOT_REACHED(); |
| 538 m_base = Position(); | 547 m_base = Position(); |
| 539 m_extent = Position(); | 548 m_extent = Position(); |
| 540 validate(); | 549 validate(); |
| 541 return; | 550 return; |
| 542 } | 551 } |
| 543 m_start = next.deepEquivalent(); | 552 m_start = next.deepEquivalent(); |
| 544 } | 553 } |
| 545 } | 554 } |
| 546 | 555 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 633 sel.showTreeForThis(); | 642 sel.showTreeForThis(); |
| 634 } | 643 } |
| 635 | 644 |
| 636 void showTree(const WebCore::Selection* sel) | 645 void showTree(const WebCore::Selection* sel) |
| 637 { | 646 { |
| 638 if (sel) | 647 if (sel) |
| 639 sel->showTreeForThis(); | 648 sel->showTreeForThis(); |
| 640 } | 649 } |
| 641 | 650 |
| 642 #endif | 651 #endif |
| OLD | NEW |