This source file includes following definitions.
- m_offsets
- create
- m_compositionEnd
- hasComposition
- editor
- clear
- insertTextForConfirmedComposition
- selectComposition
- confirmComposition
- confirmComposition
- confirmCompositionOrInsertText
- confirmCompositionAndResetState
- cancelComposition
- cancelCompositionIfSelectionIsInvalid
- finishComposition
- setComposition
- setCompositionFromExistingText
- compositionRange
- getSelectionOffsets
- setSelectionOffsets
- setEditableSelectionOffsets
- extendSelectionAndDelete
#include "config.h"
#include "core/editing/InputMethodController.h"
#include "core/events/CompositionEvent.h"
#include "core/dom/Document.h"
#include "core/dom/Element.h"
#include "core/dom/Range.h"
#include "core/dom/Text.h"
#include "core/editing/Editor.h"
#include "core/editing/TypingCommand.h"
#include "core/frame/LocalFrame.h"
#include "core/html/HTMLTextAreaElement.h"
#include "core/page/Chrome.h"
#include "core/page/ChromeClient.h"
#include "core/page/EventHandler.h"
#include "core/rendering/RenderObject.h"
namespace WebCore {
InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodController* inputMethodController)
: m_inputMethodController(inputMethodController)
, m_offsets(inputMethodController->getSelectionOffsets())
{
}
InputMethodController::SelectionOffsetsScope::~SelectionOffsetsScope()
{
m_inputMethodController->setSelectionOffsets(m_offsets);
}
PassOwnPtr<InputMethodController> InputMethodController::create(LocalFrame& frame)
{
return adoptPtr(new InputMethodController(frame));
}
InputMethodController::InputMethodController(LocalFrame& frame)
: m_frame(frame)
, m_compositionStart(0)
, m_compositionEnd(0)
{
}
InputMethodController::~InputMethodController()
{
}
bool InputMethodController::hasComposition() const
{
return m_compositionNode && m_compositionNode->isContentEditable();
}
inline Editor& InputMethodController::editor() const
{
return m_frame.editor();
}
void InputMethodController::clear()
{
m_compositionNode = nullptr;
m_customCompositionUnderlines.clear();
}
bool InputMethodController::insertTextForConfirmedComposition(const String& text)
{
return m_frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition);
}
void InputMethodController::selectComposition() const
{
RefPtrWillBeRawPtr<Range> range = compositionRange();
if (!range)
return;
VisibleSelection selection;
selection.setWithoutValidation(range->startPosition(), range->endPosition());
m_frame.selection().setSelection(selection, 0);
}
bool InputMethodController::confirmComposition()
{
if (!hasComposition())
return false;
return finishComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition);
}
bool InputMethodController::confirmComposition(const String& text)
{
return finishComposition(text, ConfirmComposition);
}
bool InputMethodController::confirmCompositionOrInsertText(const String& text, ConfirmCompositionBehavior confirmBehavior)
{
if (!hasComposition()) {
if (!text.length())
return false;
editor().insertText(text, 0);
return true;
}
if (text.length()) {
confirmComposition(text);
return true;
}
if (confirmBehavior != KeepSelection)
return confirmComposition();
SelectionOffsetsScope selectionOffsetsScope(this);
return confirmComposition();
}
void InputMethodController::confirmCompositionAndResetState()
{
if (!hasComposition())
return;
m_frame.chromeClient().willSetInputMethodState();
}
void InputMethodController::cancelComposition()
{
finishComposition(emptyString(), CancelComposition);
}
void InputMethodController::cancelCompositionIfSelectionIsInvalid()
{
if (!hasComposition() || editor().preventRevealSelection())
return;
Position start = m_frame.selection().start();
Position end = m_frame.selection().end();
if (start.containerNode() == m_compositionNode
&& end.containerNode() == m_compositionNode
&& static_cast<unsigned>(start.computeOffsetInContainerNode()) >= m_compositionStart
&& static_cast<unsigned>(end.computeOffsetInContainerNode()) <= m_compositionEnd)
return;
cancelComposition();
m_frame.chromeClient().didCancelCompositionOnSelectionChange();
}
bool InputMethodController::finishComposition(const String& text, FinishCompositionMode mode)
{
if (!hasComposition())
return false;
ASSERT(mode == ConfirmComposition || mode == CancelComposition);
Editor::RevealSelectionScope revealSelectionScope(&editor());
if (mode == CancelComposition)
ASSERT(text == emptyString());
else
selectComposition();
if (m_frame.selection().isNone())
return false;
if (Element* target = m_frame.document()->focusedElement()) {
unsigned baseOffset = m_frame.selection().base().downstream().deprecatedEditingOffset();
Vector<CompositionUnderline> underlines;
for (size_t i = 0; i < m_customCompositionUnderlines.size(); ++i) {
CompositionUnderline underline = m_customCompositionUnderlines[i];
underline.startOffset -= baseOffset;
underline.endOffset -= baseOffset;
underlines.append(underline);
}
RefPtrWillBeRawPtr<CompositionEvent> event = CompositionEvent::create(EventTypeNames::compositionend, m_frame.domWindow(), text, underlines);
target->dispatchEvent(event, IGNORE_EXCEPTION);
}
if (text.isEmpty() && mode != CancelComposition) {
ASSERT(m_frame.document());
TypingCommand::deleteSelection(*m_frame.document(), 0);
}
m_compositionNode = nullptr;
m_customCompositionUnderlines.clear();
insertTextForConfirmedComposition(text);
if (mode == CancelComposition) {
TypingCommand::closeTyping(&m_frame);
}
return true;
}
void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
{
Editor::RevealSelectionScope revealSelectionScope(&editor());
m_frame.document()->updateRenderTreeIfNeeded();
selectComposition();
if (m_frame.selection().isNone())
return;
if (Element* target = m_frame.document()->focusedElement()) {
RefPtrWillBeRawPtr<CompositionEvent> event = nullptr;
if (!hasComposition()) {
if (!text.isEmpty()) {
target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, m_frame.domWindow(), m_frame.selectedText(), underlines));
event = CompositionEvent::create(EventTypeNames::compositionupdate, m_frame.domWindow(), text, underlines);
}
} else {
if (!text.isEmpty())
event = CompositionEvent::create(EventTypeNames::compositionupdate, m_frame.domWindow(), text, underlines);
else
event = CompositionEvent::create(EventTypeNames::compositionend, m_frame.domWindow(), text, underlines);
}
if (event.get())
target->dispatchEvent(event, IGNORE_EXCEPTION);
}
if (text.isEmpty()) {
ASSERT(m_frame.document());
TypingCommand::deleteSelection(*m_frame.document(), TypingCommand::PreventSpellChecking);
}
m_compositionNode = nullptr;
m_customCompositionUnderlines.clear();
if (!text.isEmpty()) {
ASSERT(m_frame.document());
TypingCommand::insertText(*m_frame.document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
Position base = m_frame.selection().base().downstream();
Position extent = m_frame.selection().extent();
Node* baseNode = base.deprecatedNode();
unsigned baseOffset = base.deprecatedEditingOffset();
Node* extentNode = extent.deprecatedNode();
unsigned extentOffset = extent.deprecatedEditingOffset();
if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
m_compositionNode = toText(baseNode);
m_compositionStart = baseOffset;
m_compositionEnd = extentOffset;
m_customCompositionUnderlines = underlines;
size_t numUnderlines = m_customCompositionUnderlines.size();
for (size_t i = 0; i < numUnderlines; ++i) {
m_customCompositionUnderlines[i].startOffset += baseOffset;
m_customCompositionUnderlines[i].endOffset += baseOffset;
}
if (baseNode->renderer())
baseNode->renderer()->repaint();
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);
m_frame.selection().setSelectedRange(selectedRange.get(), DOWNSTREAM, static_cast<FrameSelection::SetSelectionOption>(0));
}
}
}
void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd)
{
Node* editable = m_frame.selection().rootEditableElement();
Position base = m_frame.selection().base().downstream();
Node* baseNode = base.anchorNode();
if (editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) {
m_compositionNode = nullptr;
m_customCompositionUnderlines.clear();
if (base.anchorType() != Position::PositionIsOffsetInAnchor)
return;
if (!baseNode || baseNode != m_frame.selection().extent().anchorNode())
return;
m_compositionNode = toText(baseNode);
m_compositionStart = compositionStart;
m_compositionEnd = compositionEnd;
m_customCompositionUnderlines = underlines;
size_t numUnderlines = m_customCompositionUnderlines.size();
for (size_t i = 0; i < numUnderlines; ++i) {
m_customCompositionUnderlines[i].startOffset += compositionStart;
m_customCompositionUnderlines[i].endOffset += compositionStart;
}
if (baseNode->renderer())
baseNode->renderer()->repaint();
return;
}
Editor::RevealSelectionScope revealSelectionScope(&editor());
SelectionOffsetsScope selectionOffsetsScope(this);
setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd));
setComposition(m_frame.selectedText(), underlines, 0, 0);
}
PassRefPtrWillBeRawPtr<Range> InputMethodController::compositionRange() const
{
if (!hasComposition())
return nullptr;
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 nullptr;
return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
}
PlainTextRange InputMethodController::getSelectionOffsets() const
{
RefPtrWillBeRawPtr<Range> range = m_frame.selection().selection().firstRange();
if (!range)
return PlainTextRange();
Node* editable = m_frame.selection().rootEditableElementOrTreeScopeRootNode();
ASSERT(editable);
return PlainTextRange::create(*editable, *range.get());
}
bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionOffsets)
{
if (selectionOffsets.isNull())
return false;
Element* rootEditableElement = m_frame.selection().rootEditableElement();
if (!rootEditableElement)
return false;
RefPtrWillBeRawPtr<Range> range = selectionOffsets.createRange(*rootEditableElement);
if (!range)
return false;
return m_frame.selection().setSelectedRange(range.get(), VP_DEFAULT_AFFINITY, FrameSelection::CloseTyping);
}
bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& selectionOffsets)
{
if (!editor().canEdit())
return false;
return setSelectionOffsets(selectionOffsets);
}
void InputMethodController::extendSelectionAndDelete(int before, int after)
{
if (!editor().canEdit())
return;
PlainTextRange selectionOffsets(getSelectionOffsets());
if (selectionOffsets.isNull())
return;
setSelectionOffsets(PlainTextRange(std::max(static_cast<int>(selectionOffsets.start()) - before, 0), selectionOffsets.end() + after));
TypingCommand::deleteSelection(*m_frame.document());
}
}