Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. | 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple 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 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 86 static inline LayoutUnit NoXPosForVerticalArrowNavigation() | 86 static inline LayoutUnit NoXPosForVerticalArrowNavigation() |
| 87 { | 87 { |
| 88 return LayoutUnit::min(); | 88 return LayoutUnit::min(); |
| 89 } | 89 } |
| 90 | 90 |
| 91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) | 91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) |
| 92 { | 92 { |
| 93 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirecti onal(); | 93 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirecti onal(); |
| 94 } | 94 } |
| 95 | 95 |
| 96 class GranularityStrategy { | |
|
yosin_UTC9
2015/04/17 04:24:50
Is it better to move XXXStrategy to another file?
mfomitchev
2015/04/17 14:41:49
It's your call. The upside is reducing the file si
yosin_UTC9
2015/04/20 01:25:55
We've moved classes to their own files for TextIte
| |
| 97 public: | |
| 98 virtual ~GranularityStrategy() { }; | |
| 99 virtual SelectionStrategy GetType() const; | |
|
yosin_UTC9
2015/04/17 04:24:50
As of link error, we want to put "= 0".
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 100 virtual void Clear(); | |
|
yosin_UTC9
2015/04/17 04:24:50
ditto
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 101 | |
| 102 // Calculates and returns the new selection based on the updated user select ion extent |extentPosition| and the granularity strategy. | |
| 103 virtual VisibleSelection updateExtent(const VisiblePosition& extentPosition, const VisibleSelection&); | |
|
yosin_UTC9
2015/04/17 04:24:50
ditto
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 104 protected: | |
| 105 GranularityStrategy() { }; | |
| 106 }; | |
| 107 | |
| 108 // Always uses character granularity. | |
| 109 class CharacterGranularityStrategy : public GranularityStrategy { | |
|
yosin_UTC9
2015/04/17 04:24:50
nit: "final"
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 110 public: | |
| 111 CharacterGranularityStrategy() { }; | |
| 112 ~CharacterGranularityStrategy() override { }; | |
| 113 | |
| 114 SelectionStrategy GetType() const override { return StrategyCharacter; } | |
| 115 void Clear() override { }; | |
| 116 VisibleSelection updateExtent(const VisiblePosition& extentPosition, const V isibleSelection& selection) override | |
| 117 { | |
| 118 return VisibleSelection(selection.visibleBase(), extentPosition); | |
| 119 }; | |
| 120 }; | |
| 121 | |
| 122 // "Expand by word, shrink by character" selection strategy. | |
| 123 // Uses character granularity when selection is shrinking. If the selection is e xpanding, | |
| 124 // granularity doesn't change until a word boundary is passed, after which the g ranularity | |
| 125 // switches to "word". | |
| 126 class DirectionGranularityStrategy : public GranularityStrategy { | |
|
yosin_UTC9
2015/04/17 04:24:50
nit: final
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 127 public: | |
| 128 DirectionGranularityStrategy(); | |
| 129 ~DirectionGranularityStrategy() override { }; | |
| 130 | |
| 131 SelectionStrategy GetType() const override { return StrategyDirection; } | |
| 132 void Clear() override; | |
| 133 VisibleSelection updateExtent(const VisiblePosition&, const VisibleSelection &) override; | |
| 134 private: | |
| 135 enum EWordBoundAdjust {CurrentPosIfOnBound = false, NextBoundIfOnBound = tru e}; | |
|
yosin_UTC9
2015/04/17 04:24:51
nit: we don't need to assign value for enum fields
yosin_UTC9
2015/04/17 04:24:51
nit: better to use |enum class|.
mfomitchev
2015/04/17 14:41:49
Done.
mfomitchev
2015/04/17 14:41:49
Neat! Thanks for the tip!
| |
| 136 enum ESearchDirection {SearchBackwards = 0, SearchForward = 1}; | |
|
yosin_UTC9
2015/04/17 04:24:50
nit: we don't need to assign value for enum fields
yosin_UTC9
2015/04/17 04:24:50
nit: better to use |enum class|.
mfomitchev
2015/04/17 14:41:49
Done.
mfomitchev
2015/04/17 14:41:49
Done.
| |
| 137 | |
| 138 // Returns the next word boundary starting from |pos|. |direction| specifies the direction | |
| 139 // in which to search for the next bound. nextIfOnBound controls whether |po s| or the next boundary | |
| 140 // is returned when |pos| is located exactly on word boundary. | |
| 141 VisiblePosition nextWordBound(const VisiblePosition& /*pos*/, ESearchDirecti on /*direction*/, EWordBoundAdjust /*nextIfOnBound*/); | |
| 142 | |
| 143 // Current selection granularity being used | |
| 144 TextGranularity m_granularity; | |
| 145 // Set to true if the selection was shrunk (without changing relative base/e xtent order) | |
| 146 // as a result of the most recent updateExtent call. | |
| 147 bool m_lastMoveShrunkSelection; | |
| 148 }; | |
| 149 | |
| 150 DirectionGranularityStrategy::DirectionGranularityStrategy() | |
| 151 : m_granularity(CharacterGranularity) | |
| 152 , m_lastMoveShrunkSelection(false) { } | |
| 153 | |
| 154 void DirectionGranularityStrategy::Clear() | |
| 155 { | |
| 156 m_granularity = CharacterGranularity; | |
| 157 m_lastMoveShrunkSelection = false; | |
| 158 } | |
| 159 | |
| 160 VisiblePosition DirectionGranularityStrategy::nextWordBound( | |
| 161 const VisiblePosition& pos, | |
| 162 ESearchDirection direction, | |
| 163 EWordBoundAdjust wordBoundAdjust) | |
| 164 { | |
| 165 if (direction == SearchForward) | |
| 166 return endOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? LeftWord IfOnBoundary : RightWordIfOnBoundary); | |
| 167 return startOfWord(pos, wordBoundAdjust == CurrentPosIfOnBound ? RightWordI fOnBoundary : LeftWordIfOnBoundary); | |
| 168 } | |
| 169 | |
| 170 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection) | |
| 171 { | |
| 172 if (extentPosition == selection.visibleExtent()) | |
| 173 return selection; | |
| 174 | |
| 175 const VisiblePosition base = selection.visibleBase(); | |
| 176 const VisiblePosition oldExtentWithGranularity = selection.isBaseFirst() ? s election.visibleEnd() : selection.visibleStart(); | |
| 177 | |
| 178 int extentBaseOrder = comparePositions(extentPosition, base); | |
| 179 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base); | |
| 180 | |
| 181 bool extentBaseOrderSwitched = (extentBaseOrder > 0 && oldExtentBaseOrder < 0) | |
| 182 || (extentBaseOrder < 0 && oldExtentBaseOrder > 0); | |
| 183 | |
| 184 // Determine the boundary of the 'current word', i.e. the boundary extending beyond which | |
| 185 // should change the granularity to WordGranularity. | |
| 186 // If the last move has shrunk the selection and is now exactly on the word boundary - | |
| 187 // we need to take the next bound as the bound of the "current word". | |
| 188 VisiblePosition currentWordBoundary = nextWordBound( | |
| 189 oldExtentWithGranularity, | |
| 190 oldExtentBaseOrder > 0 ? SearchForward : SearchBackwards, | |
| 191 m_lastMoveShrunkSelection ? NextBoundIfOnBound : CurrentPosIfOnBound); | |
| 192 | |
| 193 bool thisMoveShrunkSelection = (extentBaseOrder > 0 && comparePositions(exte ntPosition, selection.visibleExtent()) < 0) | |
| 194 || (extentBaseOrder < 0 && comparePositions(extentPosition, selection.vi sibleExtent()) > 0); | |
| 195 // If the extent-base order was switched, then the selection is now expandin g in a different | |
| 196 // direction than before. Therefore we need to calculate the boundary of the 'current word' | |
| 197 // in this new direction in order to be able to tell if the selection expand ed beyond it. | |
| 198 if (extentBaseOrderSwitched) { | |
| 199 currentWordBoundary = nextWordBound(base, extentBaseOrder > 0 ? SearchFo rward : SearchBackwards, NextBoundIfOnBound); | |
| 200 m_granularity = CharacterGranularity; | |
| 201 // When the base/extent order switches it doesn't count as shrinking sel ection. | |
| 202 thisMoveShrunkSelection = false; | |
| 203 } | |
| 204 | |
| 205 bool expandedBeyondWordBoundary; | |
| 206 if (extentBaseOrder > 0) | |
| 207 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) > 0; | |
| 208 else | |
| 209 expandedBeyondWordBoundary = comparePositions(extentPosition, currentWor dBoundary) < 0; | |
| 210 if (expandedBeyondWordBoundary) { | |
| 211 m_granularity = WordGranularity; | |
| 212 } else if (thisMoveShrunkSelection) { | |
| 213 m_granularity = CharacterGranularity; | |
| 214 m_lastMoveShrunkSelection = true; | |
| 215 } | |
| 216 | |
| 217 m_lastMoveShrunkSelection = thisMoveShrunkSelection; | |
| 218 VisibleSelection newSelection = selection; | |
| 219 newSelection.setExtent(extentPosition); | |
| 220 if (m_granularity == WordGranularity) { | |
| 221 if (extentBaseOrder > 0) | |
| 222 newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOn Boundary); | |
| 223 else | |
| 224 newSelection.setStartRespectingGranularity(m_granularity, RightWordI fOnBoundary); | |
| 225 } | |
| 226 | |
| 227 return newSelection; | |
| 228 } | |
| 229 | |
| 96 FrameSelection::FrameSelection(LocalFrame* frame) | 230 FrameSelection::FrameSelection(LocalFrame* frame) |
| 97 : m_frame(frame) | 231 : m_frame(frame) |
| 98 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) | 232 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) |
| 99 , m_observingVisibleSelection(false) | 233 , m_observingVisibleSelection(false) |
| 100 , m_granularity(CharacterGranularity) | 234 , m_granularity(CharacterGranularity) |
| 101 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) | 235 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) |
| 102 , m_caretRectDirty(true) | 236 , m_caretRectDirty(true) |
| 103 , m_shouldPaintCaret(true) | 237 , m_shouldPaintCaret(true) |
| 104 , m_isCaretBlinkingSuspended(false) | 238 , m_isCaretBlinkingSuspended(false) |
| 105 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame) | 239 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame) |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 215 | 349 |
| 216 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional | 350 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional |
| 217 if (m_selection == newSelection) | 351 if (m_selection == newSelection) |
| 218 return; | 352 return; |
| 219 | 353 |
| 220 setSelection(newSelection, granularity); | 354 setSelection(newSelection, granularity); |
| 221 } | 355 } |
| 222 | 356 |
| 223 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) | 357 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) |
| 224 { | 358 { |
| 359 if (m_granularityStrategy && (options & FrameSelection::DoNotClearStrategy) == 0) { | |
| 360 m_granularityStrategy->Clear(); | |
| 361 } | |
| 225 bool closeTyping = options & CloseTyping; | 362 bool closeTyping = options & CloseTyping; |
| 226 bool shouldClearTypingStyle = options & ClearTypingStyle; | 363 bool shouldClearTypingStyle = options & ClearTypingStyle; |
| 227 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | 364 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); |
| 228 | 365 |
| 229 VisibleSelection s = validateSelection(newSelection); | 366 VisibleSelection s = validateSelection(newSelection); |
| 367 | |
| 230 if (shouldAlwaysUseDirectionalSelection(m_frame)) | 368 if (shouldAlwaysUseDirectionalSelection(m_frame)) |
| 231 s.setIsDirectional(true); | 369 s.setIsDirectional(true); |
| 232 | 370 |
| 233 if (!m_frame) { | 371 if (!m_frame) { |
| 234 m_selection = s; | 372 m_selection = s; |
| 235 return; | 373 return; |
| 236 } | 374 } |
| 237 | 375 |
| 238 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection | 376 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection |
| 239 // if document->frame() == m_frame we can get into an infinite loop | 377 // if document->frame() == m_frame we can get into an infinite loop |
| (...skipping 926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1166 m_xPosForVerticalArrowNavigation = x; | 1304 m_xPosForVerticalArrowNavigation = x; |
| 1167 } else | 1305 } else |
| 1168 x = m_xPosForVerticalArrowNavigation; | 1306 x = m_xPosForVerticalArrowNavigation; |
| 1169 | 1307 |
| 1170 return x; | 1308 return x; |
| 1171 } | 1309 } |
| 1172 | 1310 |
| 1173 void FrameSelection::clear() | 1311 void FrameSelection::clear() |
| 1174 { | 1312 { |
| 1175 m_granularity = CharacterGranularity; | 1313 m_granularity = CharacterGranularity; |
| 1314 if (m_granularityStrategy) | |
| 1315 m_granularityStrategy->Clear(); | |
| 1176 setSelection(VisibleSelection()); | 1316 setSelection(VisibleSelection()); |
| 1177 } | 1317 } |
| 1178 | 1318 |
| 1179 void FrameSelection::prepareForDestruction() | 1319 void FrameSelection::prepareForDestruction() |
| 1180 { | 1320 { |
| 1181 m_granularity = CharacterGranularity; | 1321 m_granularity = CharacterGranularity; |
| 1182 | 1322 |
| 1183 m_caretBlinkTimer.stop(); | 1323 m_caretBlinkTimer.stop(); |
| 1184 | 1324 |
| 1185 LayoutView* view = m_frame->contentRenderer(); | 1325 LayoutView* view = m_frame->contentRenderer(); |
| (...skipping 720 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1906 String text = plainText(start.deepEquivalent(), end.deepEquivalent()); | 2046 String text = plainText(start.deepEquivalent(), end.deepEquivalent()); |
| 1907 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { | 2047 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { |
| 1908 setSelection(VisibleSelection(start, end), WordGranularity); | 2048 setSelection(VisibleSelection(start, end), WordGranularity); |
| 1909 return true; | 2049 return true; |
| 1910 } | 2050 } |
| 1911 } | 2051 } |
| 1912 | 2052 |
| 1913 return false; | 2053 return false; |
| 1914 } | 2054 } |
| 1915 | 2055 |
| 1916 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion, TextGranularity granularity) | 2056 GranularityStrategy* FrameSelection::granularityStrategy() |
| 1917 { | 2057 { |
| 1918 if (isNone()) | 2058 // We do lazy initalization for m_granularityStrategy, because if we initial ize it |
| 2059 // right in the constructor - the correct settings may not be set yet. | |
| 2060 SelectionStrategy strategyType = StrategyCharacter; | |
| 2061 Settings* settings = m_frame ? m_frame->settings() : 0; | |
| 2062 if (settings && settings->selectionStrategy() == StrategyDirection) | |
| 2063 strategyType = StrategyDirection; | |
| 2064 | |
| 2065 if (m_granularityStrategy && m_granularityStrategy->GetType() == strategyTyp e) | |
| 2066 return m_granularityStrategy.get(); | |
| 2067 | |
| 2068 if (strategyType == StrategyDirection) | |
| 2069 m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy()); | |
| 2070 else | |
| 2071 m_granularityStrategy = adoptPtr(new CharacterGranularityStrategy()); | |
| 2072 return m_granularityStrategy.get(); | |
| 2073 } | |
| 2074 | |
| 2075 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion) | |
| 2076 { | |
| 2077 const VisiblePosition base = m_selection.visibleBase(); | |
| 2078 | |
| 2079 if (isNone() || base == extentPosition) | |
| 1919 return; | 2080 return; |
| 1920 | 2081 |
| 1921 const VisiblePosition basePosition = m_selection.isBaseFirst() ? m_selection .visibleStart() : m_selection.visibleEnd(); | 2082 VisibleSelection newSelection = granularityStrategy()->updateExtent(extentPo sition, selection()); |
| 1922 VisibleSelection newSelection(basePosition, extentPosition); | 2083 setSelection( |
| 1923 if (newSelection.isBaseFirst()) | 2084 newSelection, |
| 1924 newSelection.setEndRespectingGranularity(granularity); | 2085 FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSe lection::DoNotClearStrategy | UserTriggered, |
| 1925 else | 2086 FrameSelection::AlignCursorOnScrollIfNeeded, |
| 1926 newSelection.setStartRespectingGranularity(granularity); | 2087 CharacterGranularity); |
| 1927 if (!newSelection.isRange()) | |
| 1928 return; | |
| 1929 | |
| 1930 setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::Cle arTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, gran ularity); | |
| 1931 } | 2088 } |
| 1932 | 2089 |
| 1933 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) | 2090 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) |
| 1934 { | 2091 { |
| 1935 VisibleSelection newSelection(basePosition, extentPosition); | 2092 VisibleSelection newSelection(basePosition, extentPosition); |
| 1936 newSelection.expandUsingGranularity(granularity); | 2093 newSelection.expandUsingGranularity(granularity); |
| 1937 | 2094 |
| 1938 if (newSelection.isNone()) | 2095 if (newSelection.isNone()) |
| 1939 return; | 2096 return; |
| 1940 | 2097 |
| 1941 setSelection(newSelection, granularity); | 2098 setSelection(newSelection, granularity); |
| 1942 } | 2099 } |
| 1943 | 2100 |
| 1944 } | 2101 } |
| 1945 | 2102 |
| 1946 #ifndef NDEBUG | 2103 #ifndef NDEBUG |
| 1947 | 2104 |
| 1948 void showTree(const blink::FrameSelection& sel) | 2105 void showTree(const blink::FrameSelection& sel) |
| 1949 { | 2106 { |
| 1950 sel.showTreeForThis(); | 2107 sel.showTreeForThis(); |
| 1951 } | 2108 } |
| 1952 | 2109 |
| 1953 void showTree(const blink::FrameSelection* sel) | 2110 void showTree(const blink::FrameSelection* sel) |
| 1954 { | 2111 { |
| 1955 if (sel) | 2112 if (sel) |
| 1956 sel->showTreeForThis(); | 2113 sel->showTreeForThis(); |
| 1957 } | 2114 } |
| 1958 | 2115 |
| 1959 #endif | 2116 #endif |
| OLD | NEW |