Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / editing / SelectionEditor.cpp
blob45f19913e1219e13fd00e5a5675947bfe534f0e8
1 /*
2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "config.h"
27 #include "core/editing/SelectionEditor.h"
29 #include "core/editing/EditingUtilities.h"
30 #include "core/editing/Editor.h"
31 #include "core/events/Event.h"
32 #include "core/frame/LocalFrame.h"
33 #include "core/frame/Settings.h"
34 #include "core/layout/LayoutBlock.h"
35 #include "core/layout/line/InlineTextBox.h"
36 #include "core/page/SpatialNavigation.h"
38 namespace blink {
40 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
42 return LayoutUnit::min();
45 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
47 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
50 SelectionEditor::SelectionEditor(FrameSelection& frameSelection)
51 : m_frameSelection(frameSelection)
52 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
53 , m_observingVisibleSelection(false)
57 SelectionEditor::~SelectionEditor()
59 #if !ENABLE(OILPAN)
60 // Oilpan: No need to clear out VisibleSelection observer;
61 // it is finalized as a part object of FrameSelection.
62 stopObservingVisibleSelectionChangeIfNecessary();
63 #endif
66 LocalFrame* SelectionEditor::frame() const
68 return m_frameSelection->frame();
71 void SelectionEditor::resetXPosForVerticalArrowNavigation()
73 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
76 void SelectionEditor::setIsDirectional(bool isDirectional)
78 m_selection.setIsDirectional(isDirectional);
81 void SelectionEditor::setWithoutValidation(const Position& start, const Position& end)
83 m_selection.setWithoutValidation(start, end);
86 TextDirection SelectionEditor::directionOfEnclosingBlock()
88 return blink::directionOfEnclosingBlock(m_selection.extent());
91 TextDirection SelectionEditor::directionOfSelection()
93 InlineBox* startBox = nullptr;
94 InlineBox* endBox = nullptr;
95 // Cache the VisiblePositions because visibleStart() and visibleEnd()
96 // can cause layout, which has the potential to invalidate lineboxes.
97 VisiblePosition startPosition = m_selection.visibleStart();
98 VisiblePosition endPosition = m_selection.visibleEnd();
99 if (startPosition.isNotNull())
100 startBox = computeInlineBoxPosition(startPosition).inlineBox;
101 if (endPosition.isNotNull())
102 endBox = computeInlineBoxPosition(endPosition).inlineBox;
103 if (startBox && endBox && startBox->direction() == endBox->direction())
104 return startBox->direction();
106 return directionOfEnclosingBlock();
109 void SelectionEditor::willBeModified(EAlteration alter, SelectionDirection direction)
111 if (alter != FrameSelection::AlterationExtend)
112 return;
114 Position start = m_selection.start();
115 Position end = m_selection.end();
117 bool baseIsStart = true;
119 if (m_selection.isDirectional()) {
120 // Make base and extent match start and end so we extend the user-visible selection.
121 // This only matters for cases where base and extend point to different positions than
122 // start and end (e.g. after a double-click to select a word).
123 if (m_selection.isBaseFirst())
124 baseIsStart = true;
125 else
126 baseIsStart = false;
127 } else {
128 switch (direction) {
129 case DirectionRight:
130 if (directionOfSelection() == LTR)
131 baseIsStart = true;
132 else
133 baseIsStart = false;
134 break;
135 case DirectionForward:
136 baseIsStart = true;
137 break;
138 case DirectionLeft:
139 if (directionOfSelection() == LTR)
140 baseIsStart = false;
141 else
142 baseIsStart = true;
143 break;
144 case DirectionBackward:
145 baseIsStart = false;
146 break;
149 if (baseIsStart) {
150 m_selection.setBase(start);
151 m_selection.setExtent(end);
152 } else {
153 m_selection.setBase(end);
154 m_selection.setExtent(start);
158 VisiblePosition SelectionEditor::positionForPlatform(bool isGetStart) const
160 Settings* settings = frame() ? frame()->settings() : nullptr;
161 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
162 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
163 // Linux and Windows always extend selections from the extent endpoint.
164 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
165 // base/extent always point to the same nodes as start/end, but which points
166 // to which depends on the value of isBaseFirst. Then this can be changed
167 // to just return m_sel.extent().
168 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
171 VisiblePosition SelectionEditor::startForPlatform() const
173 return positionForPlatform(true);
176 VisiblePosition SelectionEditor::endForPlatform() const
178 return positionForPlatform(false);
181 VisiblePosition SelectionEditor::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
183 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
185 if (frame() && frame()->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
186 // In order to skip spaces when moving right, we advance one
187 // word further and then move one word back. Given the
188 // semantics of previousWordPosition() this will put us at the
189 // beginning of the word following.
190 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
191 if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord.deepEquivalent() != positionAfterCurrentWord.deepEquivalent())
192 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
194 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord.deepEquivalent() == previousWordPosition(nextWordPosition(originalPosition)).deepEquivalent();
195 if (movingBackwardsMovedPositionToStartOfCurrentWord)
196 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
198 return positionAfterCurrentWord;
201 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
203 if (Node* rootUserSelectAll = EditingStrategy::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
204 pos = createVisiblePosition(isForward ? mostForwardCaretPosition(positionAfterNode(rootUserSelectAll), CanCrossEditingBoundary) : mostBackwardCaretPosition(positionBeforeNode(rootUserSelectAll), CanCrossEditingBoundary));
207 VisiblePosition SelectionEditor::modifyExtendingRight(TextGranularity granularity)
209 VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity());
211 // The difference between modifyExtendingRight and modifyExtendingForward is:
212 // modifyExtendingForward always extends forward logically.
213 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
214 // it extends forward logically if the enclosing block is LTR direction,
215 // but it extends backward logically if the enclosing block is RTL direction.
216 switch (granularity) {
217 case CharacterGranularity:
218 if (directionOfEnclosingBlock() == LTR)
219 pos = nextPositionOf(pos, CanSkipOverEditingBoundary);
220 else
221 pos = previousPositionOf(pos, CanSkipOverEditingBoundary);
222 break;
223 case WordGranularity:
224 if (directionOfEnclosingBlock() == LTR)
225 pos = nextWordPositionForPlatform(pos);
226 else
227 pos = previousWordPosition(pos);
228 break;
229 case LineBoundary:
230 if (directionOfEnclosingBlock() == LTR)
231 pos = modifyExtendingForward(granularity);
232 else
233 pos = modifyExtendingBackward(granularity);
234 break;
235 case SentenceGranularity:
236 case LineGranularity:
237 case ParagraphGranularity:
238 case SentenceBoundary:
239 case ParagraphBoundary:
240 case DocumentBoundary:
241 // FIXME: implement all of the above?
242 pos = modifyExtendingForward(granularity);
243 break;
245 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
246 return pos;
249 VisiblePosition SelectionEditor::modifyExtendingForward(TextGranularity granularity)
251 VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity());
252 switch (granularity) {
253 case CharacterGranularity:
254 pos = nextPositionOf(pos, CanSkipOverEditingBoundary);
255 break;
256 case WordGranularity:
257 pos = nextWordPositionForPlatform(pos);
258 break;
259 case SentenceGranularity:
260 pos = nextSentencePosition(pos);
261 break;
262 case LineGranularity:
263 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
264 break;
265 case ParagraphGranularity:
266 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
267 break;
268 case SentenceBoundary:
269 pos = endOfSentence(endForPlatform());
270 break;
271 case LineBoundary:
272 pos = logicalEndOfLine(endForPlatform());
273 break;
274 case ParagraphBoundary:
275 pos = endOfParagraph(endForPlatform());
276 break;
277 case DocumentBoundary:
278 pos = endForPlatform();
279 if (isEditablePosition(pos.deepEquivalent()))
280 pos = endOfEditableContent(pos);
281 else
282 pos = endOfDocument(pos);
283 break;
285 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
286 return pos;
289 VisiblePosition SelectionEditor::modifyMovingRight(TextGranularity granularity)
291 VisiblePosition pos;
292 switch (granularity) {
293 case CharacterGranularity:
294 if (m_selection.isRange()) {
295 if (directionOfSelection() == LTR)
296 pos = createVisiblePosition(m_selection.end(), m_selection.affinity());
297 else
298 pos = createVisiblePosition(m_selection.start(), m_selection.affinity());
299 } else {
300 pos = rightPositionOf(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
302 break;
303 case WordGranularity: {
304 bool skipsSpaceWhenMovingRight = frame() && frame()->editor().behavior().shouldSkipSpaceWhenMovingRight();
305 pos = rightWordPosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
306 break;
308 case SentenceGranularity:
309 case LineGranularity:
310 case ParagraphGranularity:
311 case SentenceBoundary:
312 case ParagraphBoundary:
313 case DocumentBoundary:
314 // FIXME: Implement all of the above.
315 pos = modifyMovingForward(granularity);
316 break;
317 case LineBoundary:
318 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
319 break;
321 return pos;
324 VisiblePosition SelectionEditor::modifyMovingForward(TextGranularity granularity)
326 VisiblePosition pos;
327 // FIXME: Stay in editable content for the less common granularities.
328 switch (granularity) {
329 case CharacterGranularity:
330 if (m_selection.isRange())
331 pos = createVisiblePosition(m_selection.end(), m_selection.affinity());
332 else
333 pos = nextPositionOf(createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary);
334 break;
335 case WordGranularity:
336 pos = nextWordPositionForPlatform(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
337 break;
338 case SentenceGranularity:
339 pos = nextSentencePosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
340 break;
341 case LineGranularity: {
342 // down-arrowing from a range selection that ends at the start of a line needs
343 // to leave the selection at that line start (no need to call nextLinePosition!)
344 pos = endForPlatform();
345 if (!m_selection.isRange() || !isStartOfLine(pos))
346 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
347 break;
349 case ParagraphGranularity:
350 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
351 break;
352 case SentenceBoundary:
353 pos = endOfSentence(endForPlatform());
354 break;
355 case LineBoundary:
356 pos = logicalEndOfLine(endForPlatform());
357 break;
358 case ParagraphBoundary:
359 pos = endOfParagraph(endForPlatform());
360 break;
361 case DocumentBoundary:
362 pos = endForPlatform();
363 if (isEditablePosition(pos.deepEquivalent()))
364 pos = endOfEditableContent(pos);
365 else
366 pos = endOfDocument(pos);
367 break;
369 return pos;
372 VisiblePosition SelectionEditor::modifyExtendingLeft(TextGranularity granularity)
374 VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity());
376 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
377 // modifyExtendingBackward always extends backward logically.
378 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
379 // it extends backward logically if the enclosing block is LTR direction,
380 // but it extends forward logically if the enclosing block is RTL direction.
381 switch (granularity) {
382 case CharacterGranularity:
383 if (directionOfEnclosingBlock() == LTR)
384 pos = previousPositionOf(pos, CanSkipOverEditingBoundary);
385 else
386 pos = nextPositionOf(pos, CanSkipOverEditingBoundary);
387 break;
388 case WordGranularity:
389 if (directionOfEnclosingBlock() == LTR)
390 pos = previousWordPosition(pos);
391 else
392 pos = nextWordPositionForPlatform(pos);
393 break;
394 case LineBoundary:
395 if (directionOfEnclosingBlock() == LTR)
396 pos = modifyExtendingBackward(granularity);
397 else
398 pos = modifyExtendingForward(granularity);
399 break;
400 case SentenceGranularity:
401 case LineGranularity:
402 case ParagraphGranularity:
403 case SentenceBoundary:
404 case ParagraphBoundary:
405 case DocumentBoundary:
406 pos = modifyExtendingBackward(granularity);
407 break;
409 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
410 return pos;
413 VisiblePosition SelectionEditor::modifyExtendingBackward(TextGranularity granularity)
415 VisiblePosition pos = createVisiblePosition(m_selection.extent(), m_selection.affinity());
417 // Extending a selection backward by word or character from just after a table selects
418 // the table. This "makes sense" from the user perspective, esp. when deleting.
419 // It was done here instead of in VisiblePosition because we want VPs to iterate
420 // over everything.
421 switch (granularity) {
422 case CharacterGranularity:
423 pos = previousPositionOf(pos, CanSkipOverEditingBoundary);
424 break;
425 case WordGranularity:
426 pos = previousWordPosition(pos);
427 break;
428 case SentenceGranularity:
429 pos = previousSentencePosition(pos);
430 break;
431 case LineGranularity:
432 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
433 break;
434 case ParagraphGranularity:
435 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
436 break;
437 case SentenceBoundary:
438 pos = startOfSentence(startForPlatform());
439 break;
440 case LineBoundary:
441 pos = logicalStartOfLine(startForPlatform());
442 break;
443 case ParagraphBoundary:
444 pos = startOfParagraph(startForPlatform());
445 break;
446 case DocumentBoundary:
447 pos = startForPlatform();
448 if (isEditablePosition(pos.deepEquivalent()))
449 pos = startOfEditableContent(pos);
450 else
451 pos = startOfDocument(pos);
452 break;
454 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
455 return pos;
458 VisiblePosition SelectionEditor::modifyMovingLeft(TextGranularity granularity)
460 VisiblePosition pos;
461 switch (granularity) {
462 case CharacterGranularity:
463 if (m_selection.isRange()) {
464 if (directionOfSelection() == LTR)
465 pos = createVisiblePosition(m_selection.start(), m_selection.affinity());
466 else
467 pos = createVisiblePosition(m_selection.end(), m_selection.affinity());
468 } else {
469 pos = leftPositionOf(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
471 break;
472 case WordGranularity: {
473 bool skipsSpaceWhenMovingRight = frame() && frame()->editor().behavior().shouldSkipSpaceWhenMovingRight();
474 pos = leftWordPosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
475 break;
477 case SentenceGranularity:
478 case LineGranularity:
479 case ParagraphGranularity:
480 case SentenceBoundary:
481 case ParagraphBoundary:
482 case DocumentBoundary:
483 // FIXME: Implement all of the above.
484 pos = modifyMovingBackward(granularity);
485 break;
486 case LineBoundary:
487 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
488 break;
490 return pos;
493 VisiblePosition SelectionEditor::modifyMovingBackward(TextGranularity granularity)
495 VisiblePosition pos;
496 switch (granularity) {
497 case CharacterGranularity:
498 if (m_selection.isRange())
499 pos = createVisiblePosition(m_selection.start(), m_selection.affinity());
500 else
501 pos = previousPositionOf(createVisiblePosition(m_selection.extent(), m_selection.affinity()), CanSkipOverEditingBoundary);
502 break;
503 case WordGranularity:
504 pos = previousWordPosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
505 break;
506 case SentenceGranularity:
507 pos = previousSentencePosition(createVisiblePosition(m_selection.extent(), m_selection.affinity()));
508 break;
509 case LineGranularity:
510 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
511 break;
512 case ParagraphGranularity:
513 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
514 break;
515 case SentenceBoundary:
516 pos = startOfSentence(startForPlatform());
517 break;
518 case LineBoundary:
519 pos = logicalStartOfLine(startForPlatform());
520 break;
521 case ParagraphBoundary:
522 pos = startOfParagraph(startForPlatform());
523 break;
524 case DocumentBoundary:
525 pos = startForPlatform();
526 if (isEditablePosition(pos.deepEquivalent()))
527 pos = startOfEditableContent(pos);
528 else
529 pos = startOfDocument(pos);
530 break;
532 return pos;
535 static bool isBoundary(TextGranularity granularity)
537 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
540 bool SelectionEditor::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
542 if (userTriggered == UserTriggered) {
543 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
544 trialFrameSelection->setSelection(m_selection);
545 trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered);
547 if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
548 return false;
551 willBeModified(alter, direction);
553 bool wasRange = m_selection.isRange();
554 VisiblePosition originalStartPosition = m_selection.visibleStart();
555 VisiblePosition position;
556 switch (direction) {
557 case DirectionRight:
558 if (alter == FrameSelection::AlterationMove)
559 position = modifyMovingRight(granularity);
560 else
561 position = modifyExtendingRight(granularity);
562 break;
563 case DirectionForward:
564 if (alter == FrameSelection::AlterationExtend)
565 position = modifyExtendingForward(granularity);
566 else
567 position = modifyMovingForward(granularity);
568 break;
569 case DirectionLeft:
570 if (alter == FrameSelection::AlterationMove)
571 position = modifyMovingLeft(granularity);
572 else
573 position = modifyExtendingLeft(granularity);
574 break;
575 case DirectionBackward:
576 if (alter == FrameSelection::AlterationExtend)
577 position = modifyExtendingBackward(granularity);
578 else
579 position = modifyMovingBackward(granularity);
580 break;
583 if (position.isNull())
584 return false;
586 if (isSpatialNavigationEnabled(frame())) {
587 if (!wasRange && alter == FrameSelection::AlterationMove && position.deepEquivalent() == originalStartPosition.deepEquivalent())
588 return false;
591 // Some of the above operations set an xPosForVerticalArrowNavigation.
592 // Setting a selection will clear it, so save it to possibly restore later.
593 // Note: the START position type is arbitrary because it is unused, it would be
594 // the requested position type if there were no xPosForVerticalArrowNavigation set.
595 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
596 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend);
598 switch (alter) {
599 case FrameSelection::AlterationMove:
600 m_frameSelection->moveTo(position, userTriggered);
601 break;
602 case FrameSelection::AlterationExtend:
604 if (!m_selection.isCaret()
605 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
606 && frame() && !frame()->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
607 // Don't let the selection go across the base position directly. Needed to match mac
608 // behavior when, for instance, word-selecting backwards starting with the caret in
609 // the middle of a word and then word-selecting forward, leaving the caret in the
610 // same place where it was, instead of directly selecting to the end of the word.
611 VisibleSelection newSelection = m_selection;
612 newSelection.setExtent(position);
613 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
614 position = m_selection.visibleBase();
617 // Standard Mac behavior when extending to a boundary is grow the
618 // selection rather than leaving the base in place and moving the
619 // extent. Matches NSTextView.
620 if (!frame() || !frame()->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary()
621 || m_selection.isCaret()
622 || !isBoundary(granularity)) {
623 m_frameSelection->setExtent(position, userTriggered);
624 } else {
625 TextDirection textDirection = directionOfEnclosingBlock();
626 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
627 m_frameSelection->setEnd(position, userTriggered);
628 else
629 m_frameSelection->setStart(position, userTriggered);
631 break;
634 if (granularity == LineGranularity || granularity == ParagraphGranularity)
635 m_xPosForVerticalArrowNavigation = x;
637 return true;
640 // FIXME: Maybe baseline would be better?
641 static bool absoluteCaretY(const VisiblePosition &c, int &y)
643 IntRect rect = absoluteCaretBoundsOf(c);
644 if (rect.isEmpty())
645 return false;
646 y = rect.y() + rect.height() / 2;
647 return true;
650 bool SelectionEditor::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
652 if (!verticalDistance)
653 return false;
655 if (userTriggered == UserTriggered) {
656 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
657 trialFrameSelection->setSelection(m_selection);
658 trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered);
661 willBeModified(alter, direction == FrameSelection::DirectionUp ? DirectionBackward : DirectionForward);
663 VisiblePosition pos;
664 LayoutUnit xPos = 0;
665 switch (alter) {
666 case FrameSelection::AlterationMove:
667 pos = createVisiblePosition(direction == FrameSelection::DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
668 xPos = lineDirectionPointForBlockDirectionNavigation(direction == FrameSelection::DirectionUp ? START : END);
669 m_selection.setAffinity(direction == FrameSelection::DirectionUp ? TextAffinity::Upstream : TextAffinity::Downstream);
670 break;
671 case FrameSelection::AlterationExtend:
672 pos = createVisiblePosition(m_selection.extent(), m_selection.affinity());
673 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
674 m_selection.setAffinity(TextAffinity::Downstream);
675 break;
678 int startY;
679 if (!absoluteCaretY(pos, startY))
680 return false;
681 if (direction == FrameSelection::DirectionUp)
682 startY = -startY;
683 int lastY = startY;
685 VisiblePosition result;
686 VisiblePosition next;
687 for (VisiblePosition p = pos; ; p = next) {
688 if (direction == FrameSelection::DirectionUp)
689 next = previousLinePosition(p, xPos);
690 else
691 next = nextLinePosition(p, xPos);
693 if (next.isNull() || next.deepEquivalent() == p.deepEquivalent())
694 break;
695 int nextY;
696 if (!absoluteCaretY(next, nextY))
697 break;
698 if (direction == FrameSelection::DirectionUp)
699 nextY = -nextY;
700 if (nextY - startY > static_cast<int>(verticalDistance))
701 break;
702 if (nextY >= lastY) {
703 lastY = nextY;
704 result = next;
708 if (result.isNull())
709 return false;
711 switch (alter) {
712 case FrameSelection::AlterationMove:
713 m_frameSelection->moveTo(result, userTriggered, align);
714 break;
715 case FrameSelection::AlterationExtend:
716 m_frameSelection->setExtent(result, userTriggered);
717 break;
720 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(frame()) || alter == FrameSelection::AlterationExtend);
722 return true;
725 // Abs x/y position of the caret ignoring transforms.
726 // TODO(yosin) navigation with transforms should be smarter.
727 static int lineDirectionPointForBlockDirectionNavigationOf(const VisiblePosition& visiblePosition)
729 if (visiblePosition.isNull())
730 return 0;
732 LayoutObject* layoutObject;
733 LayoutRect localRect = localCaretRectOfPosition(visiblePosition.toPositionWithAffinity(), layoutObject);
734 if (localRect.isEmpty() || !layoutObject)
735 return 0;
737 // This ignores transforms on purpose, for now. Vertical navigation is done
738 // without consulting transforms, so that 'up' in transformed text is 'up'
739 // relative to the text, not absolute 'up'.
740 FloatPoint caretPoint = layoutObject->localToAbsolute(FloatPoint(localRect.location()));
741 LayoutObject* containingBlock = layoutObject->containingBlock();
742 if (!containingBlock)
743 containingBlock = layoutObject; // Just use ourselves to determine the writing mode if we have no containing block.
744 return containingBlock->isHorizontalWritingMode() ? caretPoint.x() : caretPoint.y();
747 LayoutUnit SelectionEditor::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
749 LayoutUnit x = 0;
751 if (m_selection.isNone())
752 return x;
754 Position pos;
755 switch (type) {
756 case START:
757 pos = m_selection.start();
758 break;
759 case END:
760 pos = m_selection.end();
761 break;
762 case BASE:
763 pos = m_selection.base();
764 break;
765 case EXTENT:
766 pos = m_selection.extent();
767 break;
770 LocalFrame* frame = pos.document()->frame();
771 if (!frame)
772 return x;
774 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
775 VisiblePosition visiblePosition = createVisiblePosition(pos, m_selection.affinity());
776 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
777 // after the selection is created and before this function is called.
778 x = lineDirectionPointForBlockDirectionNavigationOf(visiblePosition);
779 m_xPosForVerticalArrowNavigation = x;
780 } else {
781 x = m_xPosForVerticalArrowNavigation;
784 return x;
787 bool SelectionEditor::setSelectedRange(const EphemeralRange& range, TextAffinity affinity, SelectionDirectionalMode directional, FrameSelection::SetSelectionOptions options)
789 if (range.isNull())
790 return false;
792 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
793 // they start at the beginning of the next line instead
794 m_logicalRange = nullptr;
795 stopObservingVisibleSelectionChangeIfNecessary();
797 // Since |FrameSeleciton::setSelection()| dispatches events and DOM tree
798 // can be modified by event handlers, we should create |Range| object before
799 // calling it.
800 m_logicalRange = createRange(range);
802 VisibleSelection newSelection(range.startPosition(), range.endPosition(), affinity, directional == SelectionDirectionalMode::Directional);
803 m_frameSelection->setSelection(newSelection, options);
804 startObservingVisibleSelectionChange();
805 return true;
808 PassRefPtrWillBeRawPtr<Range> SelectionEditor::firstRange() const
810 if (m_logicalRange)
811 return m_logicalRange->cloneRange();
812 return firstRangeOf(m_selection);
815 bool SelectionEditor::dispatchSelectStart()
817 Node* selectStartTarget = m_selection.extent().computeContainerNode();
818 if (!selectStartTarget)
819 return true;
821 return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
824 void SelectionEditor::didChangeVisibleSelection()
826 ASSERT(m_observingVisibleSelection);
827 // Invalidate the logical range when the underlying VisibleSelection has changed.
828 m_logicalRange = nullptr;
829 m_selection.clearChangeObserver();
830 m_observingVisibleSelection = false;
833 void SelectionEditor::startObservingVisibleSelectionChange()
835 ASSERT(!m_observingVisibleSelection);
836 m_selection.setChangeObserver(*this);
837 m_observingVisibleSelection = true;
840 void SelectionEditor::stopObservingVisibleSelectionChangeIfNecessary()
842 if (!m_observingVisibleSelection)
843 return;
844 m_selection.clearChangeObserver();
845 m_observingVisibleSelection = false;
848 DEFINE_TRACE(SelectionEditor)
850 visitor->trace(m_frameSelection);
851 visitor->trace(m_selection);
852 visitor->trace(m_logicalRange);
853 VisibleSelectionChangeObserver::trace(visitor);
856 } // namespace blink