Index: third_party/WebKit/Source/core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.cpp |
diff --git a/third_party/WebKit/Source/core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.cpp b/third_party/WebKit/Source/core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d63161ef1323546602dd63182141920a960aa429 |
--- /dev/null |
+++ b/third_party/WebKit/Source/core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.cpp |
@@ -0,0 +1,266 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "core/editing/state_machines/ForwardGraphemeBoundaryStateMachine.h" |
+ |
+#include "core/editing/state_machines/StateMachineUtil.h" |
+#include "core/editing/state_machines/TextSegmentationMachineState.h" |
+#include "platform/fonts/Character.h" |
+#include "wtf/text/Unicode.h" |
+#include <ostream> // NOLINT |
+ |
+namespace blink { |
+ |
+#define FOR_EACH_FORWARD_GRAPHEME_BOUNDARY_STATE(V) \ |
+ /* Counting preceding regional indicators. This is initial state. */ \ |
+ V(CountRIS) \ |
+ /* Waiting lead surrogate during counting regional indicators. */ \ |
+ V(CountRISWaitLeadSurrogate) \ |
+ /* Waiting first following code unit. */ \ |
+ V(StartForward) \ |
+ /* Waiting trail surrogate for the first following code point. */ \ |
+ V(StartForwardWaitTrailSurrgate) \ |
+ /* Searching grapheme boundary. */ \ |
+ V(Search) \ |
+ /* Waiting trail surrogate during searching grapheme boundary. */ \ |
+ V(SearchWaitTrailSurrogate) \ |
+ /* The state machine has stopped. */ \ |
+ V(Finished) |
+ |
+enum class ForwardGraphemeBoundaryStateMachine::InternalState { |
+#define V(name) name, |
+ FOR_EACH_FORWARD_GRAPHEME_BOUNDARY_STATE(V) |
+#undef V |
+}; |
+ |
+std::ostream& operator<<(std::ostream& os, |
+ ForwardGraphemeBoundaryStateMachine::InternalState state) |
+{ |
+ static const char* const texts[] = { |
+#define V(name) #name, |
+ FOR_EACH_FORWARD_GRAPHEME_BOUNDARY_STATE(V) |
+#undef V |
+ }; |
+ const auto& it = std::begin(texts) + static_cast<size_t>(state); |
+ DCHECK_GE(it, std::begin(texts)) << "Unknown state value"; |
+ DCHECK_LT(it, std::end(texts)) << "Unknown state value"; |
+ return os << *it; |
+} |
+ |
+ForwardGraphemeBoundaryStateMachine::ForwardGraphemeBoundaryStateMachine() |
+ : m_internalState(InternalState::CountRIS) |
+{ |
+} |
+ |
+TextSegmentationMachineState |
+ForwardGraphemeBoundaryStateMachine::feedPrecedingCodeUnit(UChar codeUnit) |
+{ |
+ DCHECK_EQ(m_prevCodePoint, 0); |
+ DCHECK_EQ(m_boundaryOffset, 0); |
+ switch (m_internalState) { |
+ case InternalState::CountRIS: |
+ DCHECK_EQ(m_pendingCodeUnit, 0); |
+ if (U16_IS_TRAIL(codeUnit)) { |
+ m_pendingCodeUnit = codeUnit; |
+ return moveToNextState(InternalState::CountRISWaitLeadSurrogate); |
+ } |
+ return moveToNextState(InternalState::StartForward); |
+ case InternalState::CountRISWaitLeadSurrogate: |
+ DCHECK_NE(m_pendingCodeUnit, 0); |
+ if (U16_IS_LEAD(codeUnit)) { |
+ const UChar32 codePoint = |
+ U16_GET_SUPPLEMENTARY(codeUnit, m_pendingCodeUnit); |
+ m_pendingCodeUnit = 0; |
+ if (Character::isRegionalIndicator(codePoint)) { |
+ ++m_precedingRISCount; |
+ return moveToNextState(InternalState::CountRIS); |
+ } |
+ } |
+ m_pendingCodeUnit = 0; |
+ return moveToNextState(InternalState::StartForward); |
+ case InternalState::StartForward: // Fallthrough |
+ case InternalState::StartForwardWaitTrailSurrgate: // Fallthrough |
+ case InternalState::Search: // Fallthrough |
+ case InternalState::SearchWaitTrailSurrogate: // Fallthrough |
+ NOTREACHED() << "Do not call feedPrecedingCodeUnit() once " |
+ << TextSegmentationMachineState::NeedFollowingCodeUnit |
+ << " is returned. InternalState: " << m_internalState; |
+ return finish(); |
+ case InternalState::Finished: |
+ NOTREACHED() << "Do not call feedPrecedingCodeUnit() once it finishes."; |
+ return finish(); |
+ } |
+ NOTREACHED() << "Unhandled state: " << m_internalState; |
+ return finish(); |
+} |
+ |
+TextSegmentationMachineState |
+ForwardGraphemeBoundaryStateMachine::feedFollowingCodeUnit(UChar codeUnit) |
+{ |
+ switch (m_internalState) { |
+ case InternalState::CountRIS: // Fallthrough |
+ case InternalState::CountRISWaitLeadSurrogate: |
+ NOTREACHED() << "Do not call feedFollowingCodeUnit() until " |
+ << TextSegmentationMachineState::NeedFollowingCodeUnit |
+ << " is returned. InternalState: " << m_internalState; |
+ return finish(); |
+ case InternalState::StartForward: |
+ DCHECK_EQ(m_prevCodePoint, 0); |
+ DCHECK_EQ(m_boundaryOffset, 0); |
+ DCHECK_EQ(m_pendingCodeUnit, 0); |
+ if (U16_IS_TRAIL(codeUnit)) { |
+ // Lonely trail surrogate. |
+ m_boundaryOffset = 1; |
+ return finish(); |
+ } |
+ if (U16_IS_LEAD(codeUnit)) { |
+ m_pendingCodeUnit = codeUnit; |
+ return moveToNextState( |
+ InternalState::StartForwardWaitTrailSurrgate); |
+ } |
+ m_prevCodePoint = codeUnit; |
+ m_boundaryOffset = 1; |
+ return moveToNextState(InternalState::Search); |
+ case InternalState::StartForwardWaitTrailSurrgate: |
+ DCHECK_EQ(m_prevCodePoint, 0); |
+ DCHECK_EQ(m_boundaryOffset, 0); |
+ DCHECK_NE(m_pendingCodeUnit, 0); |
+ if (U16_IS_TRAIL(codeUnit)) { |
+ m_prevCodePoint = |
+ U16_GET_SUPPLEMENTARY(m_pendingCodeUnit, codeUnit); |
+ m_boundaryOffset = 2; |
+ m_pendingCodeUnit = 0; |
+ return moveToNextState(InternalState::Search); |
+ } |
+ // Lonely lead surrogate. |
+ m_boundaryOffset = 1; |
+ return finish(); |
+ case InternalState::Search: |
+ DCHECK_NE(m_prevCodePoint, 0); |
+ DCHECK_NE(m_boundaryOffset, 0); |
+ DCHECK_EQ(m_pendingCodeUnit, 0); |
+ if (U16_IS_LEAD(codeUnit)) { |
+ m_pendingCodeUnit = codeUnit; |
+ return moveToNextState(InternalState::SearchWaitTrailSurrogate); |
+ } |
+ if (U16_IS_TRAIL(codeUnit)) |
+ return finish(); // Lonely trail surrogate. |
+ if (isGraphemeBreak(m_prevCodePoint, codeUnit)) |
+ return finish(); |
+ m_prevCodePoint = codeUnit; |
+ m_boundaryOffset += 1; |
+ return staySameState(); |
+ case InternalState::SearchWaitTrailSurrogate: |
+ DCHECK_NE(m_prevCodePoint, 0); |
+ DCHECK_NE(m_boundaryOffset, 0); |
+ DCHECK_NE(m_pendingCodeUnit, 0); |
+ if (!U16_IS_TRAIL(codeUnit)) |
+ return finish(); // Lonely lead surrogate. |
+ |
+ { |
+ const UChar32 codePoint = |
+ U16_GET_SUPPLEMENTARY(m_pendingCodeUnit, codeUnit); |
+ m_pendingCodeUnit = 0; |
+ if (Character::isRegionalIndicator(m_prevCodePoint) |
+ && Character::isRegionalIndicator(codePoint)) { |
+ if (m_precedingRISCount % 2 == 0) { |
+ // Odd numbered RI case, note that m_prevCodePoint is also |
+ // RI. |
+ m_boundaryOffset += 2; |
+ } |
+ return finish(); |
+ } |
+ if (isGraphemeBreak(m_prevCodePoint, codePoint)) |
+ return finish(); |
+ m_prevCodePoint = codePoint; |
+ m_boundaryOffset += 2; |
+ return moveToNextState(InternalState::Search); |
+ } |
+ case InternalState::Finished: |
+ NOTREACHED() << "Do not call feedFollowingCodeUnit() once it finishes."; |
+ return finish(); |
+ } |
+ NOTREACHED() << "Unhandled staet: " << m_internalState; |
+ return finish(); |
+} |
+ |
+TextSegmentationMachineState |
+ForwardGraphemeBoundaryStateMachine::tellEndOfPrecedingText() |
+{ |
+ DCHECK(m_internalState == InternalState::CountRIS |
+ || m_internalState == InternalState::CountRISWaitLeadSurrogate) |
+ << "Do not call tellEndOfPrecedingText() once " |
+ << TextSegmentationMachineState::NeedFollowingCodeUnit |
+ << " is returned. InternalState: " << m_internalState; |
+ |
+ // Clear pending code unit since preceding buffer may end with lonely trail |
+ // surrogate. We can just ignore it since preceding buffer is only used for |
+ // counting preceding regional indicators. |
+ m_pendingCodeUnit = 0; |
+ return moveToNextState(InternalState::StartForward); |
+} |
+ |
+int ForwardGraphemeBoundaryStateMachine::finalizeAndGetBoundaryOffset() |
+{ |
+ if (m_internalState != InternalState::Finished) |
+ finishWithEndOfText(); |
+ DCHECK_GE(m_boundaryOffset, 0); |
+ return m_boundaryOffset; |
+} |
+ |
+void ForwardGraphemeBoundaryStateMachine::reset() |
+{ |
+ m_pendingCodeUnit = 0; |
+ m_boundaryOffset = 0; |
+ m_precedingRISCount = 0; |
+ m_prevCodePoint = 0; |
+ m_internalState = InternalState::CountRIS; |
+} |
+ |
+TextSegmentationMachineState ForwardGraphemeBoundaryStateMachine::finish() |
+{ |
+ DCHECK_NE(m_internalState, InternalState::Finished); |
+ m_internalState = InternalState::Finished; |
+ return TextSegmentationMachineState::Finished; |
+} |
+ |
+TextSegmentationMachineState |
+ForwardGraphemeBoundaryStateMachine::moveToNextState(InternalState nextState) |
+{ |
+ DCHECK_NE(nextState, InternalState::Finished) << "Use finish() instead"; |
+ DCHECK_NE(nextState, m_internalState) << "Use staySameSatate() instead"; |
+ m_internalState = nextState; |
+ if (nextState == InternalState::StartForward) |
+ return TextSegmentationMachineState::NeedFollowingCodeUnit; |
+ return TextSegmentationMachineState::NeedMoreCodeUnit; |
+} |
+ |
+TextSegmentationMachineState |
+ForwardGraphemeBoundaryStateMachine::staySameState() |
+{ |
+ DCHECK_EQ(m_internalState, InternalState::Search) |
+ << "Only Search can stay the same state."; |
+ return TextSegmentationMachineState::NeedMoreCodeUnit; |
+} |
+ |
+void ForwardGraphemeBoundaryStateMachine::finishWithEndOfText() |
+{ |
+ switch (m_internalState) { |
+ case InternalState::CountRIS: // Fallthrough |
+ case InternalState::CountRISWaitLeadSurrogate: // Fallthrough |
+ case InternalState::StartForward: // Fallthrough |
+ return; // Haven't search anything to forward. Just finish. |
+ case InternalState::StartForwardWaitTrailSurrgate: |
+ // Lonely lead surrogate. |
+ m_boundaryOffset = 1; |
+ return; |
+ case InternalState::Search: // Fallthrough |
+ case InternalState::SearchWaitTrailSurrogate: // Fallthrough |
+ return; |
+ case InternalState::Finished: // Fallthrough |
+ NOTREACHED() << "Do not call finishWithEndOfText() once it finishes."; |
+ } |
+ NOTREACHED() << "Unhandled state: " << m_internalState; |
+} |
+} // namespace blink |