2 * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "core/editing/InputMethodController.h"
30 #include "core/events/CompositionEvent.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/Element.h"
33 #include "core/dom/Range.h"
34 #include "core/dom/Text.h"
35 #include "core/editing/Editor.h"
36 #include "core/editing/TypingCommand.h"
37 #include "core/html/HTMLTextAreaElement.h"
38 #include "core/page/EditorClient.h"
39 #include "core/page/EventHandler.h"
40 #include "core/frame/Frame.h"
41 #include "core/rendering/RenderObject.h"
45 InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodController* inputMethodController)
46 : m_inputMethodController(inputMethodController)
47 , m_offsets(inputMethodController->getSelectionOffsets())
51 InputMethodController::SelectionOffsetsScope::~SelectionOffsetsScope()
53 m_inputMethodController->setSelectionOffsets(m_offsets);
56 // ----------------------------
58 PassOwnPtr<InputMethodController> InputMethodController::create(Frame& frame)
60 return adoptPtr(new InputMethodController(frame));
63 InputMethodController::InputMethodController(Frame& frame)
65 , m_compositionStart(0)
70 InputMethodController::~InputMethodController()
74 bool InputMethodController::hasComposition() const
76 return m_compositionNode && m_compositionNode->isContentEditable();
79 inline Editor& InputMethodController::editor() const
81 return m_frame.editor();
84 inline EditorClient& InputMethodController::editorClient() const
86 return editor().client();
89 void InputMethodController::clear()
91 m_compositionNode = 0;
92 m_customCompositionUnderlines.clear();
95 bool InputMethodController::insertTextForConfirmedComposition(const String& text)
97 return m_frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition);
100 void InputMethodController::selectComposition() const
102 RefPtr<Range> range = compositionRange();
106 // The composition can start inside a composed character sequence, so we have to override checks.
107 // See <http://bugs.webkit.org/show_bug.cgi?id=15781>
108 VisibleSelection selection;
109 selection.setWithoutValidation(range->startPosition(), range->endPosition());
110 m_frame.selection().setSelection(selection, 0);
113 bool InputMethodController::confirmComposition()
115 if (!hasComposition())
117 return finishComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition);
120 bool InputMethodController::confirmComposition(const String& text)
122 return finishComposition(text, ConfirmComposition);
125 bool InputMethodController::confirmCompositionOrInsertText(const String& text, ConfirmCompositionBehavior confirmBehavior)
127 if (!hasComposition()) {
130 editor().insertText(text, 0);
135 confirmComposition(text);
139 if (confirmBehavior != KeepSelection)
140 return confirmComposition();
142 SelectionOffsetsScope selectionOffsetsScope(this);
143 return confirmComposition();
146 void InputMethodController::confirmCompositionAndResetState()
148 if (!hasComposition())
151 // EditorClient::willSetInputMethodState() resets input method and the composition string is committed.
152 editorClient().willSetInputMethodState();
155 void InputMethodController::cancelComposition()
157 finishComposition(emptyString(), CancelComposition);
160 void InputMethodController::cancelCompositionIfSelectionIsInvalid()
162 if (!hasComposition() || editor().preventRevealSelection())
165 // Check if selection start and selection end are valid.
166 Position start = m_frame.selection().start();
167 Position end = m_frame.selection().end();
168 if (start.containerNode() == m_compositionNode
169 && end.containerNode() == m_compositionNode
170 && static_cast<unsigned>(start.computeOffsetInContainerNode()) >= m_compositionStart
171 && static_cast<unsigned>(end.computeOffsetInContainerNode()) <= m_compositionEnd)
175 editorClient().didCancelCompositionOnSelectionChange();
178 bool InputMethodController::finishComposition(const String& text, FinishCompositionMode mode)
180 if (!hasComposition())
183 ASSERT(mode == ConfirmComposition || mode == CancelComposition);
185 Editor::RevealSelectionScope revealSelectionScope(&editor());
187 if (mode == CancelComposition)
188 ASSERT(text == emptyString());
192 if (m_frame.selection().isNone())
195 // Dispatch a compositionend event to the focused node.
196 // We should send this event before sending a TextEvent as written in Section 6.2.2 and 6.2.3 of
197 // the DOM Event specification.
198 if (Element* target = m_frame.document()->focusedElement()) {
199 RefPtr<CompositionEvent> event = CompositionEvent::create(EventTypeNames::compositionend, m_frame.domWindow(), text);
200 target->dispatchEvent(event, IGNORE_EXCEPTION);
203 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
204 // will delete the old composition with an optimized replace operation.
205 if (text.isEmpty() && mode != CancelComposition) {
206 ASSERT(m_frame.document());
207 TypingCommand::deleteSelection(*m_frame.document(), 0);
210 m_compositionNode = 0;
211 m_customCompositionUnderlines.clear();
213 insertTextForConfirmedComposition(text);
215 if (mode == CancelComposition) {
216 // An open typing command that disagrees about current selection would cause issues with typing later on.
217 TypingCommand::closeTyping(&m_frame);
223 void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
225 Editor::RevealSelectionScope revealSelectionScope(&editor());
227 // Updates styles before setting selection for composition to prevent
228 // inserting the previous composition text into text nodes oddly.
229 // See https://bugs.webkit.org/show_bug.cgi?id=46868
230 m_frame.document()->updateStyleIfNeeded();
234 if (m_frame.selection().isNone())
237 if (Element* target = m_frame.document()->focusedElement()) {
238 // Dispatch an appropriate composition event to the focused node.
239 // We check the composition status and choose an appropriate composition event since this
240 // function is used for three purposes:
241 // 1. Starting a new composition.
242 // Send a compositionstart and a compositionupdate event when this function creates
243 // a new composition node, i.e.
244 // m_compositionNode == 0 && !text.isEmpty().
245 // Sending a compositionupdate event at this time ensures that at least one
246 // compositionupdate event is dispatched.
247 // 2. Updating the existing composition node.
248 // Send a compositionupdate event when this function updates the existing composition
249 // node, i.e. m_compositionNode != 0 && !text.isEmpty().
250 // 3. Canceling the ongoing composition.
251 // Send a compositionend event when function deletes the existing composition node, i.e.
252 // m_compositionNode != 0 && test.isEmpty().
253 RefPtr<CompositionEvent> event;
254 if (!hasComposition()) {
255 // We should send a compositionstart event only when the given text is not empty because this
256 // function doesn't create a composition node when the text is empty.
257 if (!text.isEmpty()) {
258 target->dispatchEvent(CompositionEvent::create(EventTypeNames::compositionstart, m_frame.domWindow(), m_frame.selectedText()));
259 event = CompositionEvent::create(EventTypeNames::compositionupdate, m_frame.domWindow(), text);
263 event = CompositionEvent::create(EventTypeNames::compositionupdate, m_frame.domWindow(), text);
265 event = CompositionEvent::create(EventTypeNames::compositionend, m_frame.domWindow(), text);
268 target->dispatchEvent(event, IGNORE_EXCEPTION);
271 // If text is empty, then delete the old composition here. If text is non-empty, InsertTextCommand::input
272 // will delete the old composition with an optimized replace operation.
273 if (text.isEmpty()) {
274 ASSERT(m_frame.document());
275 TypingCommand::deleteSelection(*m_frame.document(), TypingCommand::PreventSpellChecking);
278 m_compositionNode = 0;
279 m_customCompositionUnderlines.clear();
281 if (!text.isEmpty()) {
282 ASSERT(m_frame.document());
283 TypingCommand::insertText(*m_frame.document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
285 // Find out what node has the composition now.
286 Position base = m_frame.selection().base().downstream();
287 Position extent = m_frame.selection().extent();
288 Node* baseNode = base.deprecatedNode();
289 unsigned baseOffset = base.deprecatedEditingOffset();
290 Node* extentNode = extent.deprecatedNode();
291 unsigned extentOffset = extent.deprecatedEditingOffset();
293 if (baseNode && baseNode == extentNode && baseNode->isTextNode() && baseOffset + text.length() == extentOffset) {
294 m_compositionNode = toText(baseNode);
295 m_compositionStart = baseOffset;
296 m_compositionEnd = extentOffset;
297 m_customCompositionUnderlines = underlines;
298 size_t numUnderlines = m_customCompositionUnderlines.size();
299 for (size_t i = 0; i < numUnderlines; ++i) {
300 m_customCompositionUnderlines[i].startOffset += baseOffset;
301 m_customCompositionUnderlines[i].endOffset += baseOffset;
303 if (baseNode->renderer())
304 baseNode->renderer()->repaint();
306 unsigned start = std::min(baseOffset + selectionStart, extentOffset);
307 unsigned end = std::min(std::max(start, baseOffset + selectionEnd), extentOffset);
308 RefPtr<Range> selectedRange = Range::create(baseNode->document(), baseNode, start, baseNode, end);
309 m_frame.selection().setSelectedRange(selectedRange.get(), DOWNSTREAM, false);
314 void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd)
316 Node* editable = m_frame.selection().rootEditableElement();
317 Position base = m_frame.selection().base().downstream();
318 Node* baseNode = base.anchorNode();
319 if (editable->firstChild() == baseNode && editable->lastChild() == baseNode && baseNode->isTextNode()) {
320 m_compositionNode = 0;
321 m_customCompositionUnderlines.clear();
323 if (base.anchorType() != Position::PositionIsOffsetInAnchor)
325 if (!baseNode || baseNode != m_frame.selection().extent().anchorNode())
328 m_compositionNode = toText(baseNode);
329 m_compositionStart = compositionStart;
330 m_compositionEnd = compositionEnd;
331 m_customCompositionUnderlines = underlines;
332 size_t numUnderlines = m_customCompositionUnderlines.size();
333 for (size_t i = 0; i < numUnderlines; ++i) {
334 m_customCompositionUnderlines[i].startOffset += compositionStart;
335 m_customCompositionUnderlines[i].endOffset += compositionStart;
337 if (baseNode->renderer())
338 baseNode->renderer()->repaint();
342 Editor::RevealSelectionScope revealSelectionScope(&editor());
343 SelectionOffsetsScope selectionOffsetsScope(this);
344 setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd));
345 setComposition(m_frame.selectedText(), underlines, 0, 0);
348 PassRefPtr<Range> InputMethodController::compositionRange() const
350 if (!hasComposition())
352 unsigned length = m_compositionNode->length();
353 unsigned start = std::min(m_compositionStart, length);
354 unsigned end = std::min(std::max(start, m_compositionEnd), length);
357 return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
360 PlainTextRange InputMethodController::getSelectionOffsets() const
362 RefPtr<Range> range = m_frame.selection().selection().firstRange();
364 return PlainTextRange();
365 Node* editable = m_frame.selection().rootEditableElementOrTreeScopeRootNode();
367 return PlainTextRange::create(*editable, *range.get());
370 bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionOffsets)
372 if (selectionOffsets.isNull())
374 Element* rootEditableElement = m_frame.selection().rootEditableElement();
375 if (!rootEditableElement)
378 RefPtr<Range> range = selectionOffsets.createRange(*rootEditableElement);
382 return m_frame.selection().setSelectedRange(range.get(), VP_DEFAULT_AFFINITY, true);
385 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& selectionOffsets)
387 if (!editor().canEdit())
389 return setSelectionOffsets(selectionOffsets);
392 void InputMethodController::extendSelectionAndDelete(int before, int after)
394 if (!editor().canEdit())
396 PlainTextRange selectionOffsets(getSelectionOffsets());
397 if (selectionOffsets.isNull())
399 setSelectionOffsets(PlainTextRange(std::max(static_cast<int>(selectionOffsets.start()) - before, 0), selectionOffsets.end() + after));
400 TypingCommand::deleteSelection(*m_frame.document());
403 } // namespace WebCore