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 { | |
| 97 public: | |
| 98 virtual ~GranularityStrategy() { }; | |
| 99 virtual void Clear(); | |
| 100 | |
| 101 // Calculates and returns the new selection based on the updated user select ion extent |extentPosition| and the granularity strategy. | |
| 102 virtual VisibleSelection updateExtent(const VisiblePosition& extentPosition, const VisibleSelection&); | |
| 103 protected: | |
| 104 GranularityStrategy() { }; | |
| 105 }; | |
| 106 | |
| 107 class DefaultGranularityStrategy : public GranularityStrategy { | |
| 108 public: | |
| 109 DefaultGranularityStrategy() { }; | |
| 110 ~DefaultGranularityStrategy() override { }; | |
| 111 | |
| 112 void Clear() override { }; | |
| 113 VisibleSelection updateExtent(const VisiblePosition& extentPosition, const V isibleSelection& selection) override | |
| 114 { | |
| 115 return VisibleSelection(selection.visibleBase(), extentPosition); | |
| 116 }; | |
| 117 }; | |
| 118 | |
| 119 // "Expand by word, shrink by character" selection strategy. | |
| 120 // Uses character granularity when selection is shrinking. If the selection is e xpanding, | |
| 121 // granularity doesn't change until a word boundary is passed, after which the g ranularity | |
| 122 // switches to "word". | |
| 123 class DirectionGranularityStrategy : public GranularityStrategy { | |
| 124 public: | |
| 125 DirectionGranularityStrategy(); | |
| 126 ~DirectionGranularityStrategy() override { }; | |
| 127 | |
| 128 void Clear() override; | |
| 129 VisibleSelection updateExtent(const VisiblePosition&, const VisibleSelection &) override; | |
| 130 private: | |
| 131 // Returns the next word boundary starting from |pos|. |direction| specifies the direction | |
| 132 // in which to search for the next bound. nextIfOnBound controls whether |po s| or the next boundary | |
| 133 // is returned when |pos| is located exactly on word boundary. | |
| 134 VisiblePosition nextWordBound(const VisiblePosition& /*pos*/, int /*directio n*/, bool /*nextIfOnBound*/); | |
|
yosin_UTC9
2015/04/16 04:11:26
nit: Could you use enum for |nextIfOnBound|? In Bl
yosin_UTC9
2015/04/16 04:11:26
nit: It is better to use |SelectionDirection|, def
mfomitchev1
2015/04/17 00:13:50
Done.
mfomitchev1
2015/04/17 00:13:51
I created a local ESearchDirection instead. Select
| |
| 135 | |
| 136 // Current selection granularity being used | |
| 137 TextGranularity m_granularity; | |
| 138 // Set to true if the selection was shrunk (without changing relative base/e xtent order) | |
| 139 // as a result of the most recent updateExtent call. | |
| 140 bool m_lastMoveShrunkSelection; | |
| 141 }; | |
| 142 | |
| 143 DirectionGranularityStrategy::DirectionGranularityStrategy() | |
| 144 : m_granularity(CharacterGranularity) | |
| 145 , m_lastMoveShrunkSelection(false) { } | |
| 146 | |
| 147 void DirectionGranularityStrategy::Clear() | |
| 148 { | |
| 149 m_granularity = CharacterGranularity; | |
| 150 m_lastMoveShrunkSelection = false; | |
| 151 } | |
| 152 | |
| 153 VisiblePosition DirectionGranularityStrategy::nextWordBound( | |
| 154 const VisiblePosition& pos, | |
| 155 int direction, | |
| 156 bool nextIfOnBound) | |
| 157 { | |
| 158 return direction > 0 ? endOfWord(pos, nextIfOnBound ? RightWordIfOnBoundary : LeftWordIfOnBoundary) | |
| 159 : startOfWord(pos, nextIfOnBound ? LeftWordIfOnBoundary : RightWordIfOnB oundary); | |
| 160 } | |
| 161 | |
| 162 VisibleSelection DirectionGranularityStrategy::updateExtent(const VisiblePositio n& extentPosition, const VisibleSelection& selection) | |
| 163 { | |
| 164 if (extentPosition == selection.visibleExtent()) | |
| 165 return selection; | |
| 166 | |
| 167 const VisiblePosition& base = selection.visibleBase(); | |
|
yosin_UTC9
2015/04/16 04:11:26
nit: It is better to use |VisiblePostion| as value
mfomitchev1
2015/04/17 00:13:51
Done.
| |
| 168 const VisiblePosition& oldExtentWithGranularity = selection.isBaseFirst() ? selection.visibleEnd() : selection.visibleStart(); | |
| 169 | |
| 170 int extentBaseOrder = comparePositions(extentPosition, base); | |
| 171 int oldExtentBaseOrder = comparePositions(oldExtentWithGranularity, base); | |
| 172 ASSERT(extentBaseOrder != 0); | |
|
yosin_UTC9
2015/04/16 04:11:26
I'm not sure this assertion is always satisfied.
mfomitchev1
2015/04/17 00:13:51
I think it is - we short-circuit in FrameSelection
| |
| 173 | |
| 174 bool extentBaseOrderSwitched = extentBaseOrder * oldExtentBaseOrder < 0; | |
|
yosin_UTC9
2015/04/16 04:11:26
This is too smart conditional expression. Could yo
mfomitchev1
2015/04/17 00:13:51
Done.
| |
| 175 | |
| 176 // Determine the boundary of the 'current word', i.e. the boundary extending beyond which | |
| 177 // should change the granularity to WordGranularity. | |
| 178 // If the last move has shrunk the selection and is now exactly on the word boundary - | |
| 179 // we need to take the next bound as the bound of the "current word". | |
| 180 VisiblePosition currentWordBoundary = | |
| 181 nextWordBound(oldExtentWithGranularity, oldExtentBaseOrder, m_lastMoveSh runkSelection); | |
| 182 | |
| 183 bool thisMoveShrunkSelection = extentBaseOrder * comparePositions(extentPosi tion, selection.visibleExtent()) < 0; | |
|
yosin_UTC9
2015/04/16 04:11:26
ditto as L174.
mfomitchev1
2015/04/17 00:13:50
Done.
| |
| 184 // If the extent-base order was switched, then the selection is now expandin g in a different | |
| 185 // direction than before. Therefore we need to calculate the boundary of the 'current word' | |
| 186 // in this new direction in order to be able to tell if the selection expand ed beyond it. | |
| 187 if (extentBaseOrderSwitched) { | |
| 188 currentWordBoundary = nextWordBound(base, extentBaseOrder, true); | |
| 189 m_granularity = CharacterGranularity; | |
| 190 // When the base/extent order switches it doesn't count as shrinking sel ection. | |
| 191 thisMoveShrunkSelection = false; | |
| 192 } | |
| 193 | |
| 194 bool expandedBeyondWordBoundary = extentBaseOrder > 0 ? comparePositions(ext entPosition, currentWordBoundary) > 0 | |
|
yosin_UTC9
2015/04/16 04:11:26
This conditional expression looks complex. It is b
mfomitchev1
2015/04/17 00:13:51
Done.
| |
| 195 : comparePositions(extentPosition, currentWordBoundary) < 0; | |
| 196 if (expandedBeyondWordBoundary) { | |
| 197 m_granularity = WordGranularity; | |
| 198 } else if (thisMoveShrunkSelection) { | |
| 199 m_granularity = CharacterGranularity; | |
| 200 m_lastMoveShrunkSelection = true; | |
| 201 } | |
| 202 | |
| 203 m_lastMoveShrunkSelection = thisMoveShrunkSelection; | |
| 204 VisibleSelection newSelection = selection; | |
| 205 newSelection.setExtent(extentPosition); | |
| 206 if (m_granularity == WordGranularity) { | |
| 207 if (extentBaseOrder > 0) | |
| 208 newSelection.setEndRespectingGranularity(m_granularity, LeftWordIfOn Boundary); | |
| 209 else | |
| 210 newSelection.setStartRespectingGranularity(m_granularity, RightWordI fOnBoundary); | |
| 211 } | |
| 212 | |
| 213 return newSelection; | |
| 214 } | |
| 215 | |
| 96 FrameSelection::FrameSelection(LocalFrame* frame) | 216 FrameSelection::FrameSelection(LocalFrame* frame) |
| 97 : m_frame(frame) | 217 : m_frame(frame) |
| 98 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) | 218 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) |
| 99 , m_observingVisibleSelection(false) | 219 , m_observingVisibleSelection(false) |
| 100 , m_granularity(CharacterGranularity) | 220 , m_granularity(CharacterGranularity) |
| 101 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) | 221 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) |
| 102 , m_caretRectDirty(true) | 222 , m_caretRectDirty(true) |
| 103 , m_shouldPaintCaret(true) | 223 , m_shouldPaintCaret(true) |
| 104 , m_isCaretBlinkingSuspended(false) | 224 , m_isCaretBlinkingSuspended(false) |
| 105 , m_focused(frame && frame->page() && frame->page()->focusController().focus edFrame() == frame) | 225 , 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 | 335 |
| 216 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional | 336 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent w ill make newSelection always directional |
| 217 if (m_selection == newSelection) | 337 if (m_selection == newSelection) |
| 218 return; | 338 return; |
| 219 | 339 |
| 220 setSelection(newSelection, granularity); | 340 setSelection(newSelection, granularity); |
| 221 } | 341 } |
| 222 | 342 |
| 223 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) | 343 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelec tionOptions options, CursorAlignOnScroll align, TextGranularity granularity) |
| 224 { | 344 { |
| 345 if ((options & FrameSelection::DoNotClearStrategy) == 0) | |
| 346 granularityStrategy()->Clear(); | |
| 225 bool closeTyping = options & CloseTyping; | 347 bool closeTyping = options & CloseTyping; |
| 226 bool shouldClearTypingStyle = options & ClearTypingStyle; | 348 bool shouldClearTypingStyle = options & ClearTypingStyle; |
| 227 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | 349 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); |
| 228 | 350 |
| 229 VisibleSelection s = validateSelection(newSelection); | 351 VisibleSelection s = validateSelection(newSelection); |
| 352 | |
| 230 if (shouldAlwaysUseDirectionalSelection(m_frame)) | 353 if (shouldAlwaysUseDirectionalSelection(m_frame)) |
| 231 s.setIsDirectional(true); | 354 s.setIsDirectional(true); |
| 232 | 355 |
| 233 if (!m_frame) { | 356 if (!m_frame) { |
| 234 m_selection = s; | 357 m_selection = s; |
| 235 return; | 358 return; |
| 236 } | 359 } |
| 237 | 360 |
| 238 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at Fra meSelection::setSelection | 361 // <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 | 362 // 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; | 1289 m_xPosForVerticalArrowNavigation = x; |
| 1167 } else | 1290 } else |
| 1168 x = m_xPosForVerticalArrowNavigation; | 1291 x = m_xPosForVerticalArrowNavigation; |
| 1169 | 1292 |
| 1170 return x; | 1293 return x; |
| 1171 } | 1294 } |
| 1172 | 1295 |
| 1173 void FrameSelection::clear() | 1296 void FrameSelection::clear() |
| 1174 { | 1297 { |
| 1175 m_granularity = CharacterGranularity; | 1298 m_granularity = CharacterGranularity; |
| 1299 granularityStrategy()->Clear(); | |
| 1176 setSelection(VisibleSelection()); | 1300 setSelection(VisibleSelection()); |
| 1177 } | 1301 } |
| 1178 | 1302 |
| 1179 void FrameSelection::prepareForDestruction() | 1303 void FrameSelection::prepareForDestruction() |
| 1180 { | 1304 { |
| 1181 m_granularity = CharacterGranularity; | 1305 m_granularity = CharacterGranularity; |
| 1182 | 1306 |
| 1183 m_caretBlinkTimer.stop(); | 1307 m_caretBlinkTimer.stop(); |
| 1184 | 1308 |
| 1185 LayoutView* view = m_frame->contentRenderer(); | 1309 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()); | 2030 String text = plainText(start.deepEquivalent(), end.deepEquivalent()); |
| 1907 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { | 2031 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { |
| 1908 setSelection(VisibleSelection(start, end), WordGranularity); | 2032 setSelection(VisibleSelection(start, end), WordGranularity); |
| 1909 return true; | 2033 return true; |
| 1910 } | 2034 } |
| 1911 } | 2035 } |
| 1912 | 2036 |
| 1913 return false; | 2037 return false; |
| 1914 } | 2038 } |
| 1915 | 2039 |
| 1916 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion, TextGranularity granularity) | 2040 GranularityStrategy* const FrameSelection::granularityStrategy() |
|
yosin_UTC9
2015/04/16 04:11:26
nit: omit |const|
mfomitchev1
2015/04/17 00:13:51
Done.
| |
| 1917 { | 2041 { |
| 1918 if (isNone()) | 2042 if (m_granularityStrategy) |
| 2043 return m_granularityStrategy.get(); | |
| 2044 // We do lazy initalization for m_granularityStrategy, because if we initial ize it | |
| 2045 // right in the constructor - the correct settings may not be set yet. | |
|
Rick Byers
2015/04/16 19:09:48
and you're sure granularityStrategy won't ever be
mfomitchev1
2015/04/16 19:33:00
Yeah, that's a good point. I don't really understa
Rick Byers
2015/04/16 19:35:10
Sure, sounds good.
mfomitchev1
2015/04/17 00:13:51
Done.
| |
| 2046 Settings* settings = m_frame ? m_frame->settings() : 0; | |
| 2047 if (settings && settings->selectionStrategy() == StrategyDirection) | |
| 2048 m_granularityStrategy = adoptPtr(new DirectionGranularityStrategy()); | |
| 2049 else | |
| 2050 m_granularityStrategy = adoptPtr(new DefaultGranularityStrategy()); | |
| 2051 return m_granularityStrategy.get(); | |
| 2052 } | |
| 2053 | |
| 2054 void FrameSelection::moveRangeSelectionExtent(const VisiblePosition& extentPosit ion) | |
| 2055 { | |
| 2056 const VisiblePosition base = m_selection.visibleBase(); | |
| 2057 | |
| 2058 if (isNone() || base == extentPosition) | |
| 1919 return; | 2059 return; |
| 1920 | 2060 |
| 1921 const VisiblePosition basePosition = m_selection.isBaseFirst() ? m_selection .visibleStart() : m_selection.visibleEnd(); | 2061 VisibleSelection newSelection = granularityStrategy()->updateExtent(extentPo sition, selection()); |
| 1922 VisibleSelection newSelection(basePosition, extentPosition); | 2062 setSelection( |
| 1923 if (newSelection.isBaseFirst()) | 2063 newSelection, |
| 1924 newSelection.setEndRespectingGranularity(granularity); | 2064 FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle | FrameSe lection::DoNotClearStrategy | UserTriggered, |
| 1925 else | 2065 FrameSelection::AlignCursorOnScrollIfNeeded, |
| 1926 newSelection.setStartRespectingGranularity(granularity); | 2066 CharacterGranularity); |
| 1927 if (!newSelection.isRange()) | |
| 1928 return; | |
| 1929 | |
| 1930 setSelection(newSelection, FrameSelection::CloseTyping | FrameSelection::Cle arTypingStyle | UserTriggered, FrameSelection::AlignCursorOnScrollIfNeeded, gran ularity); | |
| 1931 } | 2067 } |
| 1932 | 2068 |
| 1933 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) | 2069 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, con st VisiblePosition& extentPosition, TextGranularity granularity) |
| 1934 { | 2070 { |
| 1935 VisibleSelection newSelection(basePosition, extentPosition); | 2071 VisibleSelection newSelection(basePosition, extentPosition); |
| 1936 newSelection.expandUsingGranularity(granularity); | 2072 newSelection.expandUsingGranularity(granularity); |
| 1937 | 2073 |
| 1938 if (newSelection.isNone()) | 2074 if (newSelection.isNone()) |
| 1939 return; | 2075 return; |
| 1940 | 2076 |
| 1941 setSelection(newSelection, granularity); | 2077 setSelection(newSelection, granularity); |
| 1942 } | 2078 } |
| 1943 | 2079 |
| 1944 } | 2080 } |
| 1945 | 2081 |
| 1946 #ifndef NDEBUG | 2082 #ifndef NDEBUG |
| 1947 | 2083 |
| 1948 void showTree(const blink::FrameSelection& sel) | 2084 void showTree(const blink::FrameSelection& sel) |
| 1949 { | 2085 { |
| 1950 sel.showTreeForThis(); | 2086 sel.showTreeForThis(); |
| 1951 } | 2087 } |
| 1952 | 2088 |
| 1953 void showTree(const blink::FrameSelection* sel) | 2089 void showTree(const blink::FrameSelection* sel) |
| 1954 { | 2090 { |
| 1955 if (sel) | 2091 if (sel) |
| 1956 sel->showTreeForThis(); | 2092 sel->showTreeForThis(); |
| 1957 } | 2093 } |
| 1958 | 2094 |
| 1959 #endif | 2095 #endif |
| OLD | NEW |