Cancel composition when focused node was changed
[framework/web/webkit-efl.git] / Source / WebKit2 / WebProcess / WebCoreSupport / gtk / WebEditorClientGtk.cpp
1 /*
2  *  Copyright (C) 2011 Igalia S.L.
3  *
4  *  This library is free software; you can redistribute it and/or
5  *  modify it under the terms of the GNU Lesser General Public License
6  *  as published by the Free Software Foundation; either version 2 of
7  *  the License, or (at your option) any later version.
8  *
9  *  This library is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  *  Lesser General Public License for more details.
13  *
14  *  You should have received a copy of the GNU Lesser General Public
15  *  License along with this library; if not, write to the Free
16  *  Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  *  Boston, MA 02110-1301 USA
18  */
19
20 #include "config.h"
21 #include "WebEditorClient.h"
22
23 #include "Frame.h"
24 #include "FrameDestructionObserver.h"
25 #include "PlatformKeyboardEvent.h"
26 #include "WebPage.h"
27 #include "WebPageProxyMessages.h"
28 #include "WebProcess.h"
29 #include <WebCore/DataObjectGtk.h>
30 #include <WebCore/KeyboardEvent.h>
31 #include <WebCore/PasteboardHelper.h>
32 #include <WebCore/NotImplemented.h>
33
34 using namespace WebCore;
35
36 namespace WebKit {
37
38 void WebEditorClient::getEditorCommandsForKeyEvent(const KeyboardEvent* event, Vector<WTF::String>& pendingEditorCommands)
39 {
40     ASSERT(event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent);
41
42     /* First try to interpret the command in the UI and get the commands.
43        UI needs to receive event type because only knows current NativeWebKeyboardEvent.*/
44     WebProcess::shared().connection()->sendSync(Messages::WebPageProxy::GetEditorCommandsForKeyEvent(event->type()),
45                                                 Messages::WebPageProxy::GetEditorCommandsForKeyEvent::Reply(pendingEditorCommands),
46                                                 m_page->pageID(), CoreIPC::Connection::NoTimeout);
47 }
48
49 bool WebEditorClient::executePendingEditorCommands(Frame* frame, Vector<WTF::String> pendingEditorCommands, bool allowTextInsertion)
50 {
51     Vector<Editor::Command> commands;
52     for (size_t i = 0; i < pendingEditorCommands.size(); i++) {
53         Editor::Command command = frame->editor()->command(pendingEditorCommands.at(i).utf8().data());
54         if (command.isTextInsertion() && !allowTextInsertion)
55             return false;
56
57         commands.append(command);
58     }
59
60     for (size_t i = 0; i < commands.size(); i++) {
61         if (!commands.at(i).execute())
62             return false;
63     }
64
65     return true;
66 }
67
68 void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
69 {
70     Node* node = event->target()->toNode();
71     ASSERT(node);
72     Frame* frame = node->document()->frame();
73     ASSERT(frame);
74
75     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
76     if (!platformEvent)
77         return;
78
79     Vector<WTF::String> pendingEditorCommands;
80     getEditorCommandsForKeyEvent(event, pendingEditorCommands);
81     if (!pendingEditorCommands.isEmpty()) {
82
83         // During RawKeyDown events if an editor command will insert text, defer
84         // the insertion until the keypress event. We want keydown to bubble up
85         // through the DOM first.
86         if (platformEvent->type() == PlatformEvent::RawKeyDown) {
87             if (executePendingEditorCommands(frame, pendingEditorCommands, false))
88                 event->setDefaultHandled();
89
90             return;
91         }
92
93         // Only allow text insertion commands if the current node is editable.
94         if (executePendingEditorCommands(frame, pendingEditorCommands, frame->editor()->canEdit())) {
95             event->setDefaultHandled();
96             return;
97         }
98     }
99
100     // Don't allow text insertion for nodes that cannot edit.
101     if (!frame->editor()->canEdit())
102         return;
103
104     // This is just a normal text insertion, so wait to execute the insertion
105     // until a keypress event happens. This will ensure that the insertion will not
106     // be reflected in the contents of the field until the keyup DOM event.
107     if (event->type() == eventNames().keypressEvent) {
108
109         // FIXME: Add IM support
110         // https://bugs.webkit.org/show_bug.cgi?id=55946
111         frame->editor()->insertText(platformEvent->text(), event);
112         event->setDefaultHandled();
113
114     } else {
115         // Don't insert null or control characters as they can result in unexpected behaviour
116         if (event->charCode() < ' ')
117             return;
118
119         // Don't insert anything if a modifier is pressed
120         if (platformEvent->ctrlKey() || platformEvent->altKey())
121             return;
122
123         if (frame->editor()->insertText(platformEvent->text(), event))
124             event->setDefaultHandled();
125     }
126 }
127
128 void WebEditorClient::handleInputMethodKeydown(KeyboardEvent*)
129 {
130     notImplemented();
131 }
132
133 #if PLATFORM(X11)
134 class EditorClientFrameDestructionObserver : FrameDestructionObserver {
135 public:
136     EditorClientFrameDestructionObserver(Frame* frame, GClosure* closure)
137         : FrameDestructionObserver(frame)
138         , m_closure(closure)
139     {
140         g_closure_add_finalize_notifier(m_closure, this, destroyOnClosureFinalization);
141     }
142
143     void frameDestroyed()
144     {
145         g_closure_invalidate(m_closure);
146         FrameDestructionObserver::frameDestroyed();
147     }
148 private:
149     GClosure* m_closure;
150
151     static void destroyOnClosureFinalization(gpointer data, GClosure* closure)
152     {
153         // Calling delete void* will free the memory but won't invoke
154         // the destructor, something that is a must for us.
155         EditorClientFrameDestructionObserver* observer = static_cast<EditorClientFrameDestructionObserver*>(data);
156         delete observer;
157     }
158 };
159
160 static Frame* frameSettingClipboard;
161
162 static void collapseSelection(GtkClipboard* clipboard, Frame* frame)
163 {
164     if (frameSettingClipboard && frameSettingClipboard == frame)
165         return;
166
167     // Collapse the selection without clearing it.
168     ASSERT(frame);
169     frame->selection()->setBase(frame->selection()->extent(), frame->selection()->affinity());
170 }
171 #endif
172
173 void WebEditorClient::setSelectionPrimaryClipboardIfNeeded(Frame* frame)
174 {
175 #if PLATFORM(X11)
176     GtkClipboard* clipboard = PasteboardHelper::defaultPasteboardHelper()->getPrimarySelectionClipboard(frame);
177     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
178
179     if (!frame->selection()->isRange())
180         return;
181
182     dataObject->clearAll();
183     dataObject->setRange(frame->selection()->toNormalizedRange());
184
185     frameSettingClipboard = frame;
186     GClosure* callback = g_cclosure_new(G_CALLBACK(collapseSelection), frame, 0);
187     // This observer will be self-destroyed on closure finalization,
188     // that will happen either after closure execution or after
189     // closure invalidation.
190     new EditorClientFrameDestructionObserver(frame, callback);
191     g_closure_set_marshal(callback, g_cclosure_marshal_VOID__VOID);
192     PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(clipboard, PasteboardHelper::DoNotIncludeSmartPaste, callback);
193     frameSettingClipboard = 0;
194 #endif
195 }
196
197 bool WebEditorClient::shouldShowUnicodeMenu()
198 {
199     return true;
200 }
201
202 }