- add third_party src.
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / editing / InputMethodController.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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.
25  */
26
27 #include "config.h"
28 #include "core/editing/InputMethodController.h"
29
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"
42
43 namespace WebCore {
44
45 InputMethodController::SelectionOffsetsScope::SelectionOffsetsScope(InputMethodController* inputMethodController)
46     : m_inputMethodController(inputMethodController)
47     , m_offsets(inputMethodController->getSelectionOffsets())
48 {
49 }
50
51 InputMethodController::SelectionOffsetsScope::~SelectionOffsetsScope()
52 {
53     m_inputMethodController->setSelectionOffsets(m_offsets);
54 }
55
56 // ----------------------------
57
58 PassOwnPtr<InputMethodController> InputMethodController::create(Frame& frame)
59 {
60     return adoptPtr(new InputMethodController(frame));
61 }
62
63 InputMethodController::InputMethodController(Frame& frame)
64     : m_frame(frame)
65     , m_compositionStart(0)
66     , m_compositionEnd(0)
67 {
68 }
69
70 InputMethodController::~InputMethodController()
71 {
72 }
73
74 bool InputMethodController::hasComposition() const
75 {
76     return m_compositionNode && m_compositionNode->isContentEditable();
77 }
78
79 inline Editor& InputMethodController::editor() const
80 {
81     return m_frame.editor();
82 }
83
84 inline EditorClient& InputMethodController::editorClient() const
85 {
86     return editor().client();
87 }
88
89 void InputMethodController::clear()
90 {
91     m_compositionNode = 0;
92     m_customCompositionUnderlines.clear();
93 }
94
95 bool InputMethodController::insertTextForConfirmedComposition(const String& text)
96 {
97     return m_frame.eventHandler().handleTextInputEvent(text, 0, TextEventInputComposition);
98 }
99
100 void InputMethodController::selectComposition() const
101 {
102     RefPtr<Range> range = compositionRange();
103     if (!range)
104         return;
105
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);
111 }
112
113 bool InputMethodController::confirmComposition()
114 {
115     if (!hasComposition())
116         return false;
117     return finishComposition(m_compositionNode->data().substring(m_compositionStart, m_compositionEnd - m_compositionStart), ConfirmComposition);
118 }
119
120 bool InputMethodController::confirmComposition(const String& text)
121 {
122     return finishComposition(text, ConfirmComposition);
123 }
124
125 bool InputMethodController::confirmCompositionOrInsertText(const String& text, ConfirmCompositionBehavior confirmBehavior)
126 {
127     if (!hasComposition()) {
128         if (!text.length())
129             return false;
130         editor().insertText(text, 0);
131         return true;
132     }
133
134     if (text.length()) {
135         confirmComposition(text);
136         return true;
137     }
138
139     if (confirmBehavior != KeepSelection)
140         return confirmComposition();
141
142     SelectionOffsetsScope selectionOffsetsScope(this);
143     return confirmComposition();
144 }
145
146 void InputMethodController::confirmCompositionAndResetState()
147 {
148     if (!hasComposition())
149         return;
150
151     // EditorClient::willSetInputMethodState() resets input method and the composition string is committed.
152     editorClient().willSetInputMethodState();
153 }
154
155 void InputMethodController::cancelComposition()
156 {
157     finishComposition(emptyString(), CancelComposition);
158 }
159
160 void InputMethodController::cancelCompositionIfSelectionIsInvalid()
161 {
162     if (!hasComposition() || editor().preventRevealSelection())
163         return;
164
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)
172         return;
173
174     cancelComposition();
175     editorClient().didCancelCompositionOnSelectionChange();
176 }
177
178 bool InputMethodController::finishComposition(const String& text, FinishCompositionMode mode)
179 {
180     if (!hasComposition())
181         return false;
182
183     ASSERT(mode == ConfirmComposition || mode == CancelComposition);
184
185     Editor::RevealSelectionScope revealSelectionScope(&editor());
186
187     if (mode == CancelComposition)
188         ASSERT(text == emptyString());
189     else
190         selectComposition();
191
192     if (m_frame.selection().isNone())
193         return false;
194
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);
201     }
202
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);
208     }
209
210     m_compositionNode = 0;
211     m_customCompositionUnderlines.clear();
212
213     insertTextForConfirmedComposition(text);
214
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);
218     }
219
220     return true;
221 }
222
223 void InputMethodController::setComposition(const String& text, const Vector<CompositionUnderline>& underlines, unsigned selectionStart, unsigned selectionEnd)
224 {
225     Editor::RevealSelectionScope revealSelectionScope(&editor());
226
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();
231
232     selectComposition();
233
234     if (m_frame.selection().isNone())
235         return;
236
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);
260             }
261         } else {
262             if (!text.isEmpty())
263                 event = CompositionEvent::create(EventTypeNames::compositionupdate, m_frame.domWindow(), text);
264             else
265                 event = CompositionEvent::create(EventTypeNames::compositionend, m_frame.domWindow(), text);
266         }
267         if (event.get())
268             target->dispatchEvent(event, IGNORE_EXCEPTION);
269     }
270
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);
276     }
277
278     m_compositionNode = 0;
279     m_customCompositionUnderlines.clear();
280
281     if (!text.isEmpty()) {
282         ASSERT(m_frame.document());
283         TypingCommand::insertText(*m_frame.document(), text, TypingCommand::SelectInsertedText | TypingCommand::PreventSpellChecking, TypingCommand::TextCompositionUpdate);
284
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();
292
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;
302             }
303             if (baseNode->renderer())
304                 baseNode->renderer()->repaint();
305
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);
310         }
311     }
312 }
313
314 void InputMethodController::setCompositionFromExistingText(const Vector<CompositionUnderline>& underlines, unsigned compositionStart, unsigned compositionEnd)
315 {
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();
322
323         if (base.anchorType() != Position::PositionIsOffsetInAnchor)
324             return;
325         if (!baseNode || baseNode != m_frame.selection().extent().anchorNode())
326             return;
327
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;
336         }
337         if (baseNode->renderer())
338             baseNode->renderer()->repaint();
339         return;
340     }
341
342     Editor::RevealSelectionScope revealSelectionScope(&editor());
343     SelectionOffsetsScope selectionOffsetsScope(this);
344     setSelectionOffsets(PlainTextRange(compositionStart, compositionEnd));
345     setComposition(m_frame.selectedText(), underlines, 0, 0);
346 }
347
348 PassRefPtr<Range> InputMethodController::compositionRange() const
349 {
350     if (!hasComposition())
351         return 0;
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);
355     if (start >= end)
356         return 0;
357     return Range::create(m_compositionNode->document(), m_compositionNode.get(), start, m_compositionNode.get(), end);
358 }
359
360 PlainTextRange InputMethodController::getSelectionOffsets() const
361 {
362     RefPtr<Range> range = m_frame.selection().selection().firstRange();
363     if (!range)
364         return PlainTextRange();
365     Node* editable = m_frame.selection().rootEditableElementOrTreeScopeRootNode();
366     ASSERT(editable);
367     return PlainTextRange::create(*editable, *range.get());
368 }
369
370 bool InputMethodController::setSelectionOffsets(const PlainTextRange& selectionOffsets)
371 {
372     if (selectionOffsets.isNull())
373         return false;
374     Element* rootEditableElement = m_frame.selection().rootEditableElement();
375     if (!rootEditableElement)
376         return false;
377
378     RefPtr<Range> range = selectionOffsets.createRange(*rootEditableElement);
379     if (!range)
380         return false;
381
382     return m_frame.selection().setSelectedRange(range.get(), VP_DEFAULT_AFFINITY, true);
383 }
384
385 bool InputMethodController::setEditableSelectionOffsets(const PlainTextRange& selectionOffsets)
386 {
387     if (!editor().canEdit())
388         return false;
389     return setSelectionOffsets(selectionOffsets);
390 }
391
392 void InputMethodController::extendSelectionAndDelete(int before, int after)
393 {
394     if (!editor().canEdit())
395         return;
396     PlainTextRange selectionOffsets(getSelectionOffsets());
397     if (selectionOffsets.isNull())
398         return;
399     setSelectionOffsets(PlainTextRange(std::max(static_cast<int>(selectionOffsets.start()) - before, 0), selectionOffsets.end() + after));
400     TypingCommand::deleteSelection(*m_frame.document());
401 }
402
403 } // namespace WebCore