Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(345)

Side by Side Diff: third_party/WebKit/WebCore/editing/Selection.cpp

Issue 21184: WebKit merge 40722:40785 (part 1) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « third_party/WebKit/WebCore/editing/Selection.h ('k') | third_party/WebKit/WebCore/editing/SelectionController.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698