Index: Source/core/editing/InputMethodController.cpp |
diff --git a/Source/core/editing/InputMethodController.cpp b/Source/core/editing/InputMethodController.cpp |
index f1d4018dbf735e082b90f8c3fc7339dfd64f377c..3c695f1e8270c7f1baf763f8e3825d031e9a2993 100644 |
--- a/Source/core/editing/InputMethodController.cpp |
+++ b/Source/core/editing/InputMethodController.cpp |
@@ -29,17 +29,15 @@ |
#include "core/dom/Document.h" |
#include "core/dom/Element.h" |
+#include "core/dom/Range.h" |
#include "core/dom/Text.h" |
-#include "core/editing/EditingUtilities.h" |
#include "core/editing/Editor.h" |
#include "core/editing/commands/TypingCommand.h" |
-#include "core/editing/markers/DocumentMarkerController.h" |
#include "core/events/CompositionEvent.h" |
#include "core/frame/LocalFrame.h" |
#include "core/html/HTMLTextAreaElement.h" |
#include "core/input/EventHandler.h" |
#include "core/layout/LayoutObject.h" |
-#include "core/layout/LayoutTheme.h" |
#include "core/page/ChromeClient.h" |
namespace blink { |
@@ -64,8 +62,8 @@ |
InputMethodController::InputMethodController(LocalFrame& frame) |
: m_frame(&frame) |
- , m_isDirty(false) |
- , m_hasComposition(false) |
+ , m_compositionStart(0) |
+ , m_compositionEnd(0) |
{ |
} |
@@ -75,7 +73,7 @@ |
bool InputMethodController::hasComposition() const |
{ |
- return m_hasComposition; |
+ return m_compositionNode && m_compositionNode->isContentEditable(); |
} |
inline Editor& InputMethodController::editor() const |
@@ -85,13 +83,8 @@ |
void InputMethodController::clear() |
{ |
- m_hasComposition = false; |
- if (m_compositionRange) { |
- m_compositionRange->setStart(frame().document(), 0); |
- m_compositionRange->collapse(true); |
- } |
- frame().document()->markers().removeMarkers(DocumentMarker::Composition); |
- m_isDirty = false; |
+ m_compositionNode = nullptr; |
+ m_customCompositionUnderlines.clear(); |
} |
bool InputMethodController::insertTextForConfirmedComposition(const String& text) |
@@ -116,7 +109,7 @@ |
{ |
if (!hasComposition()) |
return false; |
- return confirmComposition(plainText(compositionEphemeralRange())); |
+ return finishComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition); |
} |
bool InputMethodController::confirmComposition(const String& text) |
@@ -156,14 +149,13 @@ |
return; |
// Check if selection start and selection end are valid. |
- FrameSelection& selection = frame().selection(); |
- if (!selection.isNone() && !m_compositionRange->collapsed()) { |
- Position start = selection.start(); |
- Position end = selection.end(); |
- if (selection.start().compareTo(m_compositionRange->startPosition()) >= 0 |
- && selection.end().compareTo(m_compositionRange->endPosition()) <= 0) |
- return; |
- } |
+ Position start = frame().selection().start(); |
+ Position end = frame().selection().end(); |
+ if (start.computeContainerNode() == m_compositionNode |
+ && end.computeContainerNode() == m_compositionNode |
+ && static_cast<unsigned>(start.computeOffsetInContainerNode()) >= m_compositionStart |
+ && static_cast<unsigned>(end.computeOffsetInContainerNode()) <= m_compositionEnd) |
+ return; |
cancelComposition(); |
frame().chromeClient().didCancelCompositionOnSelectionChange(); |
@@ -194,19 +186,17 @@ |
target->dispatchEvent(event); |
} |
- bool dirty = m_isDirty || plainText(compositionEphemeralRange()) != text; |
- |
// If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input |
// will delete the old composition with an optimized replace operation. |
- if (text.isEmpty() && mode != CancelComposition && dirty) { |
+ if (text.isEmpty() && mode != CancelComposition) { |
ASSERT(frame().document()); |
TypingCommand::deleteSelection(*frame().document(), 0); |
} |
- clear(); |
- |
- if (dirty) |
- insertTextForConfirmedComposition(text); |
+ m_compositionNode = nullptr; |
+ m_customCompositionUnderlines.clear(); |
+ |
+ insertTextForConfirmedComposition(text); |
if (mode == CancelComposition) { |
// An open typing command that disagrees about current selection would cause issues with typing later on. |
@@ -237,15 +227,15 @@ |
// 1. Starting a new composition. |
// Send a compositionstart and a compositionupdate event when this function creates |
// a new composition node, i.e. |
- // !hasComposition() && !text.isEmpty(). |
+ // m_compositionNode == 0 && !text.isEmpty(). |
// Sending a compositionupdate event at this time ensures that at least one |
// compositionupdate event is dispatched. |
// 2. Updating the existing composition node. |
// Send a compositionupdate event when this function updates the existing composition |
- // node, i.e. hasComposition() && !text.isEmpty(). |
+ // node, i.e. m_compositionNode != 0 && !text.isEmpty(). |
// 3. Canceling the ongoing composition. |
// Send a compositionend event when function deletes the existing composition node, i.e. |
- // !hasComposition() && test.isEmpty(). |
+ // m_compositionNode != 0 && test.isEmpty(). |
RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; |
if (!hasComposition()) { |
// We should send a compositionstart event only when the given text is not empty because this |
@@ -271,7 +261,8 @@ |
TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking); |
} |
- clear(); |
+ m_compositionNode = nullptr; |
+ m_customCompositionUnderlines.clear(); |
if (text.isEmpty()) |
return; |
@@ -294,13 +285,14 @@ |
if (baseOffset + text.length() != extentOffset) |
return; |
- m_isDirty = true; |
- m_hasComposition = true; |
- if (!m_compositionRange) |
- m_compositionRange = Range::create(baseNode->document()); |
- m_compositionRange->setStart(baseNode, baseOffset); |
- m_compositionRange->setEnd(baseNode, extentOffset); |
- |
+ m_compositionNode = toText(baseNode); |
+ m_compositionStart = baseOffset; |
+ m_compositionEnd = extentOffset; |
+ m_customCompositionUnderlines = underlines; |
+ for (auto& underline : m_customCompositionUnderlines) { |
+ underline.startOffset += baseOffset; |
+ underline.endOffset += baseOffset; |
+ } |
if (baseNode->layoutObject()) |
baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
@@ -308,67 +300,61 @@ |
unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset); |
RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end); |
frame().selection().setSelectedRange(selectedRange.get(), TextAffinity::Downstream, FrameSelection::NonDirectional, NotUserTriggered); |
- |
- if (underlines.isEmpty()) { |
- frame().document()->markers().addCompositionMarker(m_compositionRange->startPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTheme::theme().platformDefaultCompositionBackgroundColor()); |
- return; |
- } |
- for (const auto& underline : underlines) { |
- unsigned underlineStart = baseOffset + underline.startOffset; |
- unsigned underlineEnd = baseOffset + underline.endOffset; |
- EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); |
- if (ephemeralLineRange.isNull()) |
- continue; |
- frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thick, underline.backgroundColor); |
- } |
} |
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) |
{ |
Element* editable = frame().selection().rootEditableElement(); |
- if (!editable) |
- return; |
- |
- const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); |
- if (range.isNull()) |
- return; |
- |
- const Position start = range.startPosition(); |
- if (editableRootForPosition(start) != editable) |
- return; |
- |
- const Position end = range.endPosition(); |
- if (editableRootForPosition(end) != editable) |
- return; |
- |
- clear(); |
- |
- for (const auto& underline : underlines) { |
- unsigned underlineStart = compositionStart + underline.startOffset; |
- unsigned underlineEnd = compositionStart + underline.endOffset; |
- EphemeralRange ephemeralLineRange = PlainTextRange(underlineStart, underlineEnd).createRange(*editable); |
- if (ephemeralLineRange.isNull()) |
- continue; |
- frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underline.color, underline.thick, underline.backgroundColor); |
- } |
- |
- m_hasComposition = true; |
- if (!m_compositionRange) |
- m_compositionRange = Range::create(range.document()); |
- m_compositionRange->setStart(range.startPosition()); |
- m_compositionRange->setEnd(range.endPosition()); |
+ Position base = mostForwardCaretPosition(frame().selection().base()); |
+ Node* baseNode = base.anchorNode(); |
+ if (baseNode && editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) { |
+ m_compositionNode = nullptr; |
+ m_customCompositionUnderlines.clear(); |
+ |
+ if (!base.isOffsetInAnchor()) |
+ return; |
+ if (baseNode != frame().selection().extent().anchorNode()) |
+ return; |
+ |
+ m_compositionNode = toText(baseNode); |
+ const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); |
+ if (range.isNull()) |
+ return; |
+ |
+ m_compositionStart = range.startPosition().computeOffsetInContainerNode(); |
+ m_compositionEnd = range.endPosition().computeOffsetInContainerNode(); |
+ m_customCompositionUnderlines = underlines; |
+ size_t numUnderlines = m_customCompositionUnderlines.size(); |
+ for (size_t i = 0; i < numUnderlines; ++i) { |
+ m_customCompositionUnderlines[i].startOffset += m_compositionStart; |
+ m_customCompositionUnderlines[i].endOffset += m_compositionStart; |
+ } |
+ if (baseNode->layoutObject()) |
+ baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
+ return; |
+ } |
+ |
+ Editor::RevealSelectionScope revealSelectionScope(&editor()); |
+ SelectionOffsetsScope selectionOffsetsScope(this); |
+ setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); |
+ setComposition(frame().selectedText(), underlines, 0, 0); |
} |
EphemeralRange InputMethodController::compositionEphemeralRange() const |
{ |
if (!hasComposition()) |
return EphemeralRange(); |
- return EphemeralRange(m_compositionRange.get()); |
+ unsigned length = m_compositionNode->length(); |
+ unsigned start = std::min(m_compositionStart, length); |
+ unsigned end = std::min(std::max(start, m_compositionEnd), length); |
+ if (start >= end) |
+ return EphemeralRange(); |
+ return EphemeralRange(Position(m_compositionNode.get(), start), Position(m_compositionNode.get(), end)); |
} |
PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const |
{ |
- return hasComposition() ? m_compositionRange : nullptr; |
+ return createRange(compositionEphemeralRange()); |
} |
PlainTextRange InputMethodController::getSelectionOffsets() const |
@@ -436,7 +422,7 @@ |
DEFINE_TRACE(InputMethodController) |
{ |
visitor->trace(m_frame); |
- visitor->trace(m_compositionRange); |
+ visitor->trace(m_compositionNode); |
} |
} // namespace blink |