Commit e4cd8706 authored by guohui@chromium.org's avatar guohui@chromium.org

Emit object replacement char to IME

Currently Clank could only delete (with the backspace key) nodes that are
visible to the android IME, and since a replaced element, e.g. <img>, is not
visible to the IME, thus it could not be easily deleted using the backspace key.
Another side effect is that if two text nodes are separated by an image element
alone, then they appear as a single composition to the IME.

To fix these issues, this CL exposes a replaced element as a single object
replacement char 0xFFFC to InputMethodController. For more details please refer
to the atatched bug.

BUG=311448

Review URL: https://codereview.chromium.org/297003003

git-svn-id: svn://svn.chromium.org/blink/trunk@175819 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent 353c750e
...@@ -379,7 +379,7 @@ bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionO ...@@ -379,7 +379,7 @@ bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionO
if (!rootEditableElement) if (!rootEditableElement)
return false; return false;
RefPtrWillBeRawPtr<Range> range = selectionOffsets.createRange(*rootEditableElement); RefPtrWillBeRawPtr<Range> range = selectionOffsets.createRangeForInputMethod(*rootEditableElement);
if (!range) if (!range)
return false; return false;
......
...@@ -67,6 +67,11 @@ PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeForSelection(const Cont ...@@ -67,6 +67,11 @@ PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeForSelection(const Cont
return createRangeFor(scope, ForSelection); return createRangeFor(scope, ForSelection);
} }
PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeForInputMethod(const ContainerNode& scope) const
{
return createRangeFor(scope, ForInputMethod);
}
PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode& scope, GetRangeFor getRangeFor) const
{ {
ASSERT(isNotNull()); ASSERT(isNotNull());
...@@ -78,7 +83,12 @@ PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode ...@@ -78,7 +83,12 @@ PassRefPtrWillBeRawPtr<Range> PlainTextRange::createRangeFor(const ContainerNode
RefPtrWillBeRawPtr<Range> textRunRange = nullptr; RefPtrWillBeRawPtr<Range> textRunRange = nullptr;
TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), getRangeFor == ForSelection ? TextIteratorEmitsCharactersBetweenAllVisiblePositions : TextIteratorDefaultBehavior); TextIteratorBehavior behaviorFlags = TextIteratorDefaultBehavior;
if (getRangeFor == ForSelection)
behaviorFlags = TextIteratorEmitsCharactersBetweenAllVisiblePositions;
else if (getRangeFor == ForInputMethod)
behaviorFlags = TextIteratorEmitsObjectReplacementCharacter;
TextIterator it(rangeOfContents(const_cast<ContainerNode*>(&scope)).get(), behaviorFlags);
// FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>. // FIXME: the atEnd() check shouldn't be necessary, workaround for <http://bugs.webkit.org/show_bug.cgi?id=6289>.
if (!start() && !length() && it.atEnd()) { if (!start() && !length() && it.atEnd()) {
......
...@@ -51,13 +51,14 @@ public: ...@@ -51,13 +51,14 @@ public:
PassRefPtrWillBeRawPtr<Range> createRange(const ContainerNode& scope) const; PassRefPtrWillBeRawPtr<Range> createRange(const ContainerNode& scope) const;
PassRefPtrWillBeRawPtr<Range> createRangeForSelection(const ContainerNode& scope) const; PassRefPtrWillBeRawPtr<Range> createRangeForSelection(const ContainerNode& scope) const;
PassRefPtrWillBeRawPtr<Range> createRangeForInputMethod(const ContainerNode& scope) const;
static PlainTextRange create(const Node& scope, const Range&); static PlainTextRange create(const Node& scope, const Range&);
private: private:
PlainTextRange& operator=(const PlainTextRange&) WTF_DELETED_FUNCTION; PlainTextRange& operator=(const PlainTextRange&) WTF_DELETED_FUNCTION;
enum GetRangeFor { ForGeneric, ForSelection }; enum GetRangeFor { ForGeneric, ForSelection, ForInputMethod };
PassRefPtrWillBeRawPtr<Range> createRangeFor(const ContainerNode& scope, GetRangeFor) const; PassRefPtrWillBeRawPtr<Range> createRangeFor(const ContainerNode& scope, GetRangeFor) const;
const size_t m_start; const size_t m_start;
......
...@@ -265,6 +265,7 @@ TextIterator::TextIterator(const Range* range, TextIteratorBehaviorFlags behavio ...@@ -265,6 +265,7 @@ TextIterator::TextIterator(const Range* range, TextIteratorBehaviorFlags behavio
, m_shouldStop(false) , m_shouldStop(false)
, m_emitsImageAltText(behavior & TextIteratorEmitsImageAltText) , m_emitsImageAltText(behavior & TextIteratorEmitsImageAltText)
, m_entersAuthorShadowRoots(behavior & TextIteratorEntersAuthorShadowRoots) , m_entersAuthorShadowRoots(behavior & TextIteratorEntersAuthorShadowRoots)
, m_emitsObjectReplacementCharacter(behavior & TextIteratorEmitsObjectReplacementCharacter)
{ {
if (range) if (range)
initialize(range->startPosition(), range->endPosition()); initialize(range->startPosition(), range->endPosition());
...@@ -295,6 +296,7 @@ TextIterator::TextIterator(const Position& start, const Position& end, TextItera ...@@ -295,6 +296,7 @@ TextIterator::TextIterator(const Position& start, const Position& end, TextItera
, m_shouldStop(false) , m_shouldStop(false)
, m_emitsImageAltText(behavior & TextIteratorEmitsImageAltText) , m_emitsImageAltText(behavior & TextIteratorEmitsImageAltText)
, m_entersAuthorShadowRoots(behavior & TextIteratorEntersAuthorShadowRoots) , m_entersAuthorShadowRoots(behavior & TextIteratorEntersAuthorShadowRoots)
, m_emitsObjectReplacementCharacter(behavior & TextIteratorEmitsObjectReplacementCharacter)
{ {
initialize(start, end); initialize(start, end);
} }
...@@ -782,6 +784,11 @@ bool TextIterator::handleReplacedElement() ...@@ -782,6 +784,11 @@ bool TextIterator::handleReplacedElement()
if (renderer->style()->visibility() != VISIBLE && !m_ignoresStyleVisibility) if (renderer->style()->visibility() != VISIBLE && !m_ignoresStyleVisibility)
return false; return false;
if (m_emitsObjectReplacementCharacter) {
emitCharacter(objectReplacementCharacter, m_node->parentNode(), m_node, 0, 1);
return true;
}
if (m_lastTextNodeEndedWithCollapsedSpace) { if (m_lastTextNodeEndedWithCollapsedSpace) {
emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1); emitCharacter(' ', m_lastTextNode->parentNode(), m_lastTextNode, 1, 1);
return false; return false;
......
...@@ -45,7 +45,8 @@ enum TextIteratorBehavior { ...@@ -45,7 +45,8 @@ enum TextIteratorBehavior {
TextIteratorEmitsOriginalText = 1 << 3, TextIteratorEmitsOriginalText = 1 << 3,
TextIteratorStopsOnFormControls = 1 << 4, TextIteratorStopsOnFormControls = 1 << 4,
TextIteratorEmitsImageAltText = 1 << 5, TextIteratorEmitsImageAltText = 1 << 5,
TextIteratorEntersAuthorShadowRoots = 1 << 6 TextIteratorEntersAuthorShadowRoots = 1 << 6,
TextIteratorEmitsObjectReplacementCharacter = 1 << 7
}; };
typedef unsigned TextIteratorBehaviorFlags; typedef unsigned TextIteratorBehaviorFlags;
...@@ -208,6 +209,8 @@ private: ...@@ -208,6 +209,8 @@ private:
bool m_emitsImageAltText; bool m_emitsImageAltText;
bool m_entersAuthorShadowRoots; bool m_entersAuthorShadowRoots;
bool m_emitsObjectReplacementCharacter;
}; };
// Iterates through the DOM range, returning all the text, and 0-length boundaries // Iterates through the DOM range, returning all the text, and 0-length boundaries
......
...@@ -517,4 +517,26 @@ TEST_F(TextIteratorTest, FindPlainTextInvalidTarget) ...@@ -517,4 +517,26 @@ TEST_F(TextIteratorTest, FindPlainTextInvalidTarget)
} }
} }
TEST_F(TextIteratorTest, EmitsReplacementCharForInput)
{
static const char* bodyContent =
"<div contenteditable=\"true\">"
"Before"
"<img src=\"foo.png\">"
"After"
"</div>";
// "Before".
static const UChar expectedRawString1[] = { 0x42, 0x65, 0x66, 0x6F, 0x72, 0x65, 0 };
// Object replacement char.
static const UChar expectedRawString2[] = { 0xFFFC, 0 };
// "After".
static const UChar expectedRawString3[] = { 0x41, 0x66, 0x74, 0x65, 0x72, 0 };
static const UChar* expectedRawStrings[] = { expectedRawString1, expectedRawString2, expectedRawString3 };
Vector<String> expectedTextChunks;
expectedTextChunks.append(expectedRawStrings, WTF_ARRAY_LENGTH(expectedRawStrings));
setBodyInnerHTML(bodyContent);
EXPECT_EQ(expectedTextChunks, iterate(TextIteratorEmitsObjectReplacementCharacter));
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment