Index: Source/core/editing/InputMethodController.cpp |
diff --git a/Source/core/editing/InputMethodController.cpp b/Source/core/editing/InputMethodController.cpp |
index 3c695f1e8270c7f1baf763f8e3825d031e9a2993..22ce021d249bc6918b339ea0bcaa83c57ccb096c 100644 |
--- a/Source/core/editing/InputMethodController.cpp |
+++ b/Source/core/editing/InputMethodController.cpp |
@@ -29,15 +29,17 @@ |
#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 { |
@@ -62,8 +64,8 @@ PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca |
InputMethodController::InputMethodController(LocalFrame& frame) |
: m_frame(&frame) |
- , m_compositionStart(0) |
- , m_compositionEnd(0) |
+ , m_isDirty(false) |
+ , m_hasComposition(false) |
{ |
} |
@@ -73,7 +75,7 @@ InputMethodController::~InputMethodController() |
bool InputMethodController::hasComposition() const |
{ |
- return m_compositionNode && m_compositionNode->isContentEditable(); |
+ return m_hasComposition; |
} |
inline Editor& InputMethodController::editor() const |
@@ -83,8 +85,13 @@ inline Editor& InputMethodController::editor() const |
void InputMethodController::clear() |
{ |
- m_compositionNode = nullptr; |
- m_customCompositionUnderlines.clear(); |
+ m_hasComposition = false; |
+ if (m_compositionRange) { |
+ m_compositionRange->setStart(frame().document(), 0); |
+ m_compositionRange->setEnd(frame().document(), 0); |
yosin_UTC9
2015/09/08 06:34:05
nit: m_compositionRange->collapse(true);
|
+ } |
+ frame().document()->markers().removeMarkers(DocumentMarker::Composition); |
+ m_isDirty = false; |
} |
bool InputMethodController::insertTextForConfirmedComposition(const String& text) |
@@ -109,7 +116,7 @@ bool InputMethodController::confirmComposition() |
{ |
if (!hasComposition()) |
return false; |
- return finishComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition); |
+ return confirmComposition(plainText(compositionEphemeralRange())); |
} |
bool InputMethodController::confirmComposition(const String& text) |
@@ -149,13 +156,16 @@ void InputMethodController::cancelCompositionIfSelectionIsInvalid() |
return; |
// Check if selection start and selection end are valid. |
- 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; |
+ FrameSelection& selection = frame().selection(); |
+ if (!selection.isNone() && !m_compositionRange->collapsed()) { |
+ Position start = selection.start(); |
+ Position end = selection.end(); |
+ if (start.anchorNode() && end.anchorNode()) { |
yosin_UTC9
2015/09/08 06:34:04
We don't need to have this if-statement, if |!sele
|
+ if (m_compositionRange->isPointInRange(start.computeContainerNode(), start.computeOffsetInContainerNode(), IGNORE_EXCEPTION) |
yosin_UTC9
2015/09/08 06:34:05
How about,
selection.start().compareTo(m_composit
|
+ && m_compositionRange->isPointInRange(end.computeContainerNode(), end.computeOffsetInContainerNode(), IGNORE_EXCEPTION)) |
+ return; |
+ } |
+ } |
cancelComposition(); |
frame().chromeClient().didCancelCompositionOnSelectionChange(); |
@@ -186,17 +196,19 @@ bool InputMethodController::finishComposition(const String& text, FinishComposit |
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) { |
+ if (text.isEmpty() && mode != CancelComposition && dirty) { |
ASSERT(frame().document()); |
TypingCommand::deleteSelection(*frame().document(), 0); |
} |
- m_compositionNode = nullptr; |
- m_customCompositionUnderlines.clear(); |
+ clear(); |
- insertTextForConfirmedComposition(text); |
+ if (dirty) |
+ insertTextForConfirmedComposition(text); |
if (mode == CancelComposition) { |
// An open typing command that disagrees about current selection would cause issues with typing later on. |
@@ -227,15 +239,15 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp |
// 1. Starting a new composition. |
// Send a compositionstart and a compositionupdate event when this function creates |
// a new composition node, i.e. |
- // m_compositionNode == 0 && !text.isEmpty(). |
+ // !hasComposition() && !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. m_compositionNode != 0 && !text.isEmpty(). |
+ // node, i.e. hasComposition() && !text.isEmpty(). |
// 3. Canceling the ongoing composition. |
// Send a compositionend event when function deletes the existing composition node, i.e. |
- // m_compositionNode != 0 && test.isEmpty(). |
+ // !hasComposition() && test.isEmpty(). |
RefPtrWillBeRawPtr<CompositionEvent> event = nullptr; |
if (!hasComposition()) { |
// We should send a compositionstart event only when the given text is not empty because this |
@@ -261,8 +273,7 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp |
TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking); |
} |
- m_compositionNode = nullptr; |
- m_customCompositionUnderlines.clear(); |
+ clear(); |
if (text.isEmpty()) |
return; |
@@ -285,14 +296,13 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp |
if (baseOffset + text.length() != extentOffset) |
return; |
- m_compositionNode = toText(baseNode); |
- m_compositionStart = baseOffset; |
- m_compositionEnd = extentOffset; |
- m_customCompositionUnderlines = underlines; |
- for (auto& underline : m_customCompositionUnderlines) { |
- underline.startOffset += baseOffset; |
- underline.endOffset += baseOffset; |
- } |
+ 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); |
+ |
if (baseNode->layoutObject()) |
baseNode->layoutObject()->setShouldDoFullPaintInvalidation(); |
@@ -300,61 +310,68 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp |
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.size()) { |
yosin_UTC9
2015/09/08 06:34:04
nit: Let's use underlines.isEmpty() and swap then
|
+ for (size_t i = 0; i < underlines.size(); ++i) { |
yosin_UTC9
2015/09/08 06:34:05
nit: Let's use range-for, for (const auto& underli
|
+ unsigned underlineStart = baseOffset + underlines[i].startOffset; |
+ unsigned underlineEnd = baseOffset + underlines[i].endOffset; |
+ EphemeralRange ephemeralLineRange = EphemeralRange(Position(baseNode, underlineStart), Position(baseNode, underlineEnd)); |
+ if (ephemeralLineRange.isNull()) |
+ continue; |
+ frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underlines[i].color, underlines[i].thick, underlines[i].backgroundColor); |
+ } |
+ } else { |
+ frame().document()->markers().addCompositionMarker(m_compositionRange->startPosition(), m_compositionRange->endPosition(), Color::black, false, LayoutTheme::theme().platformDefaultCompositionBackgroundColor()); |
+ } |
+ |
} |
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd) |
{ |
Element* editable = frame().selection().rootEditableElement(); |
- 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 (!editable) |
+ return; |
- if (!base.isOffsetInAnchor()) |
- return; |
- if (baseNode != frame().selection().extent().anchorNode()) |
- return; |
+ const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); |
+ if (range.isNull()) |
+ return; |
- m_compositionNode = toText(baseNode); |
- const EphemeralRange range = PlainTextRange(compositionStart, compositionEnd).createRange(*editable); |
- if (range.isNull()) |
- return; |
+ const Position start = range.startPosition(); |
+ if (!start.anchorNode() || start.anchorNode()->rootEditableElement() != editable) |
yosin_UTC9
2015/09/08 06:34:05
nit: editableRootForPosition(start) != editable
|
+ 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(); |
+ const Position end = range.endPosition(); |
+ if (!end.anchorNode() || end.anchorNode()->rootEditableElement() != editable) |
yosin_UTC9
2015/09/08 06:34:05
nit: editableRootForPosition(end) != editable
|
return; |
+ |
+ clear(); |
+ |
+ for (size_t i = 0; i < underlines.size(); ++i) { |
yosin_UTC9
2015/09/08 06:34:05
nit: Let's use range-for.
|
+ unsigned underlineStart = compositionStart + underlines[i].startOffset; |
+ unsigned underlineEnd = compositionStart + underlines[i].endOffset; |
+ EphemeralRange ephemeralLineRange = PlainTextRange(underlineStart, underlineEnd).createRange(*editable); |
+ if (ephemeralLineRange.isNull()) |
+ continue; |
+ frame().document()->markers().addCompositionMarker(ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition(), underlines[i].color, underlines[i].thick, underlines[i].backgroundColor); |
} |
- Editor::RevealSelectionScope revealSelectionScope(&editor()); |
- SelectionOffsetsScope selectionOffsetsScope(this); |
- setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd)); |
- setComposition(frame().selectedText(), underlines, 0, 0); |
+ m_hasComposition = true; |
+ if (!m_compositionRange) |
+ m_compositionRange = Range::create(range.document()); |
+ m_compositionRange->setStart(range.startPosition()); |
+ m_compositionRange->setEnd(range.endPosition()); |
} |
EphemeralRange InputMethodController::compositionEphemeralRange() const |
{ |
if (!hasComposition()) |
return EphemeralRange(); |
- 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)); |
+ return EphemeralRange(m_compositionRange.get()); |
} |
PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const |
{ |
- return createRange(compositionEphemeralRange()); |
+ return hasComposition() ? m_compositionRange : nullptr; |
} |
PlainTextRange InputMethodController::getSelectionOffsets() const |
@@ -422,7 +439,7 @@ void InputMethodController::extendSelectionAndDelete(int before, int after) |
DEFINE_TRACE(InputMethodController) |
{ |
visitor->trace(m_frame); |
- visitor->trace(m_compositionNode); |
+ visitor->trace(m_compositionRange); |
} |
} // namespace blink |