Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(693)

Unified Diff: Source/core/editing/InputMethodController.cpp

Issue 1325563002: Avoid style clobbering in setCompositionFromExistingText. (2nd land) (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Fix end to refer to endPosition Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/editing/InputMethodController.cpp
diff --git a/Source/core/editing/InputMethodController.cpp b/Source/core/editing/InputMethodController.cpp
index 3c695f1e8270c7f1baf763f8e3825d031e9a2993..c91b92cf248149decd60eaa8dd1420acfcbedb41 100644
--- a/Source/core/editing/InputMethodController.cpp
+++ b/Source/core/editing/InputMethodController.cpp
@@ -29,8 +29,8 @@
#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/events/CompositionEvent.h"
@@ -62,8 +62,7 @@ PassOwnPtrWillBeRawPtr<InputMethodController> InputMethodController::create(Loca
InputMethodController::InputMethodController(LocalFrame& frame)
: m_frame(&frame)
- , m_compositionStart(0)
- , m_compositionEnd(0)
+ , m_isExistingText(true)
yosin_UTC9 2015/08/31 01:35:40 Can we use inverse logic? It is unclear why just i
aelias_OOO_until_Jul13 2015/09/04 04:03:24 Done.
{
}
@@ -73,7 +72,7 @@ InputMethodController::~InputMethodController()
bool InputMethodController::hasComposition() const
{
- return m_compositionNode && m_compositionNode->isContentEditable();
+ return m_compositionRange.get() && m_compositionRange->startContainer() && m_compositionRange->startContainer()->isContentEditable();
}
inline Editor& InputMethodController::editor() const
@@ -83,8 +82,9 @@ inline Editor& InputMethodController::editor() const
void InputMethodController::clear()
{
- m_compositionNode = nullptr;
+ m_compositionRange = nullptr;
m_customCompositionUnderlines.clear();
+ m_isExistingText = true;
}
bool InputMethodController::insertTextForConfirmedComposition(const String& text)
@@ -109,7 +109,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 +149,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()) {
+ Position start = selection.start();
+ Position end = selection.end();
+ if (start.anchorNode() && end.anchorNode()) {
+ if (m_compositionRange->isPointInRange(start.computeContainerNode(), start.computeOffsetInContainerNode(), IGNORE_EXCEPTION)
+ && m_compositionRange->isPointInRange(end.computeContainerNode(), end.computeOffsetInContainerNode(), IGNORE_EXCEPTION))
+ return;
+ }
+ }
cancelComposition();
frame().chromeClient().didCancelCompositionOnSelectionChange();
@@ -186,17 +189,19 @@ bool InputMethodController::finishComposition(const String& text, FinishComposit
target->dispatchEvent(event);
}
+ bool existingText = m_isExistingText;
+
// 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 && !existingText) {
ASSERT(frame().document());
TypingCommand::deleteSelection(*frame().document(), 0);
}
- m_compositionNode = nullptr;
- m_customCompositionUnderlines.clear();
+ clear();
- insertTextForConfirmedComposition(text);
+ if (!existingText)
+ insertTextForConfirmedComposition(text);
if (mode == CancelComposition) {
// An open typing command that disagrees about current selection would cause issues with typing later on.
@@ -227,15 +232,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 +266,9 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp
TypingCommand::deleteSelection(*frame().document(), TypingCommand::PreventSpellChecking);
}
- m_compositionNode = nullptr;
- m_customCompositionUnderlines.clear();
+ clear();
+
+ m_isExistingText = false;
if (text.isEmpty())
return;
@@ -285,17 +291,25 @@ 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) {
+ m_compositionRange = Range::create(baseNode->document(), baseNode, baseOffset, baseNode, extentOffset);
+ NodeUnderlinesMap customCompositionUnderlines;
+ customCompositionUnderlines[baseNode].first = baseNode;
+ customCompositionUnderlines[baseNode].second = underlines;
+ for (auto& underline : customCompositionUnderlines[baseNode].second) {
underline.startOffset += baseOffset;
underline.endOffset += baseOffset;
}
+
+ m_customCompositionUnderlines.swap(customCompositionUnderlines);
+
if (baseNode->layoutObject())
baseNode->layoutObject()->setShouldDoFullPaintInvalidation();
+ for (auto& nodeUnderlinesPair : customCompositionUnderlines) {
+ if (nodeUnderlinesPair.first->layoutObject() && nodeUnderlinesPair.first != baseNode)
+ nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvalidation();
+ }
+
unsigned start = std::min(baseOffset + selectionStart, extentOffset);
unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset);
RefPtrWillBeRawPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
@@ -305,56 +319,99 @@ void InputMethodController::setComposition(const String& text, const Vector<Comp
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;
+ 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;
+ Position start = range.startPosition();
+ if (!start.anchorNode() || start.anchorNode()->rootEditableElement() != 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();
+ Position end = range.endPosition();
+ if (!end.anchorNode() || end.anchorNode()->rootEditableElement() != editable)
return;
+
+ clear();
+
+ // customCompositionUnderlines has the same type as m_customCompositionUnderlines. So we can swap them later.
+ NodeUnderlinesMap customCompositionUnderlines;
+ CompositionUnderline underline;
+ EphemeralRange ephemeralLineRange;
+ RefPtrWillBeRawPtr<Range> underlineRange;
+ for (size_t i = 0; i < underlines.size(); ++i) {
+ // We separate each disjoint underline into node specific underlines and associate them with the node that they belong to.
+ ephemeralLineRange = PlainTextRange(compositionStart + underlines[i].startOffset, compositionStart + underlines[i].endOffset).createRange(*editable);
+ if (ephemeralLineRange.isNull())
+ continue;
+ underlineRange = Range::create(ephemeralLineRange.document(), ephemeralLineRange.startPosition(), ephemeralLineRange.endPosition());
+ Node* stopNode = underlineRange->pastLastNode();
+ for (Node* node = underlineRange->firstNode(); node && node != stopNode; node = NodeTraversal::next(*node)) {
+ if (node->isTextNode()) {
+ if (!customCompositionUnderlines[node].first)
+ customCompositionUnderlines[node].first = node;
+ underline = underlines[i];
+ underline.startOffset = node == underlineRange->startContainer() ? underlineRange->startOffset() : 0;
+ underline.endOffset = node == underlineRange->endContainer() ? underlineRange->endOffset() : toText(node)->length();
+ customCompositionUnderlines[node].second.append(underline);
+ }
+ }
}
- Editor::RevealSelectionScope revealSelectionScope(&editor());
- SelectionOffsetsScope selectionOffsetsScope(this);
- setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd));
- setComposition(frame().selectedText(), underlines, 0, 0);
+ m_compositionRange = Range::create(range.document(), range.startPosition(), range.endPosition());
+ m_customCompositionUnderlines.swap(customCompositionUnderlines);
+
+ // Invalidate affected composition nodes
+ for (auto& nodeUnderlinesPair : m_customCompositionUnderlines) {
+ if (nodeUnderlinesPair.first->layoutObject())
+ nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvalidation();
+ NodeUnderlinesMap::iterator it = customCompositionUnderlines.find(nodeUnderlinesPair.first);
+ if (it != customCompositionUnderlines.end())
+ customCompositionUnderlines.erase(it);
+ }
+
+ for (auto& nodeUnderlinesPair : customCompositionUnderlines) {
+ if (nodeUnderlinesPair.first->layoutObject())
+ nodeUnderlinesPair.first->layoutObject()->setShouldDoFullPaintInvalidation();
+ }
}
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->startPosition(), m_compositionRange->endPosition());
}
PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const
{
- return createRange(compositionEphemeralRange());
+ return hasComposition() ? m_compositionRange : nullptr;
+}
+
+bool InputMethodController::isCompositionNode(const Node* node)
+{
+ if (!node || m_customCompositionUnderlines.empty() || !node->isTextNode())
+ return false;
+
+ return m_customCompositionUnderlines.find(node) != m_customCompositionUnderlines.end();
+}
+
+const Vector<CompositionUnderline>& InputMethodController::customCompositionUnderlines() const
+{
+ CR_DEFINE_STATIC_LOCAL(Vector<CompositionUnderline>, emptyUnderline, ());
+ return m_customCompositionUnderlines.empty() ? emptyUnderline : m_customCompositionUnderlines.begin()->second.second;
+}
+
+const Vector<CompositionUnderline>* InputMethodController::customCompositionUnderlines(const Node* node) const
+{
+ if (!node || !node->isTextNode() || m_customCompositionUnderlines.empty())
+ return nullptr;
+
+ NodeUnderlinesMap::iterator it = m_customCompositionUnderlines.find(node);
+ return (it == m_customCompositionUnderlines.end() || m_customCompositionUnderlines[node].second.isEmpty()) ?
+ nullptr : &(m_customCompositionUnderlines[node].second);
}
PlainTextRange InputMethodController::getSelectionOffsets() const
@@ -422,7 +479,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

Powered by Google App Engine
This is Rietveld 408576698