2 * Copyright (C) 2011 Igalia S.L.
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.
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.
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
21 #include "WebEditorClient.h"
24 #include "FrameDestructionObserver.h"
25 #include "PlatformKeyboardEvent.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>
34 using namespace WebCore;
38 void WebEditorClient::getEditorCommandsForKeyEvent(const KeyboardEvent* event, Vector<WTF::String>& pendingEditorCommands)
40 ASSERT(event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent);
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);
49 bool WebEditorClient::executePendingEditorCommands(Frame* frame, Vector<WTF::String> pendingEditorCommands, bool allowTextInsertion)
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)
57 commands.append(command);
60 for (size_t i = 0; i < commands.size(); i++) {
61 if (!commands.at(i).execute())
68 void WebEditorClient::handleKeyboardEvent(KeyboardEvent* event)
70 Node* node = event->target()->toNode();
72 Frame* frame = node->document()->frame();
75 const PlatformKeyboardEvent* platformEvent = event->keyEvent();
79 Vector<WTF::String> pendingEditorCommands;
80 getEditorCommandsForKeyEvent(event, pendingEditorCommands);
81 if (!pendingEditorCommands.isEmpty()) {
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();
93 // Only allow text insertion commands if the current node is editable.
94 if (executePendingEditorCommands(frame, pendingEditorCommands, frame->editor()->canEdit())) {
95 event->setDefaultHandled();
100 // Don't allow text insertion for nodes that cannot edit.
101 if (!frame->editor()->canEdit())
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) {
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();
115 // Don't insert null or control characters as they can result in unexpected behaviour
116 if (event->charCode() < ' ')
119 // Don't insert anything if a modifier is pressed
120 if (platformEvent->ctrlKey() || platformEvent->altKey())
123 if (frame->editor()->insertText(platformEvent->text(), event))
124 event->setDefaultHandled();
128 void WebEditorClient::handleInputMethodKeydown(KeyboardEvent*)
134 class EditorClientFrameDestructionObserver : FrameDestructionObserver {
136 EditorClientFrameDestructionObserver(Frame* frame, GClosure* closure)
137 : FrameDestructionObserver(frame)
140 g_closure_add_finalize_notifier(m_closure, this, destroyOnClosureFinalization);
143 void frameDestroyed()
145 g_closure_invalidate(m_closure);
146 FrameDestructionObserver::frameDestroyed();
151 static void destroyOnClosureFinalization(gpointer data, GClosure* closure)
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);
160 static Frame* frameSettingClipboard;
162 static void collapseSelection(GtkClipboard* clipboard, Frame* frame)
164 if (frameSettingClipboard && frameSettingClipboard == frame)
167 // Collapse the selection without clearing it.
169 frame->selection()->setBase(frame->selection()->extent(), frame->selection()->affinity());
173 void WebEditorClient::setSelectionPrimaryClipboardIfNeeded(Frame* frame)
176 GtkClipboard* clipboard = PasteboardHelper::defaultPasteboardHelper()->getPrimarySelectionClipboard(frame);
177 DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
179 if (!frame->selection()->isRange())
182 dataObject->clearAll();
183 dataObject->setRange(frame->selection()->toNormalizedRange());
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;
197 bool WebEditorClient::shouldShowUnicodeMenu()