1 /*
2 * Copyright (C) 2010 Apple Inc. All rights reserved.
3 * Portions Copyright (c) 2010 Motorola Mobility, Inc. All rights reserved.
4 * Copyright (C) 2011 Igalia S.L.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
19 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
25 * THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include "config.h"
29 #include "PageClientImpl.h"
30
31 #include "ChunkedUpdateDrawingAreaProxy.h"
32 #include "NativeWebKeyboardEvent.h"
33 #include "NativeWebMouseEvent.h"
34 #include "NotImplemented.h"
35 #include "WebContext.h"
36 #include "WebContextMenuProxy.h"
37 #include "WebEventFactory.h"
38 #include "WebKitWebViewBasePrivate.h"
39 #include "WebPageProxy.h"
40 #include <wtf/text/WTFString.h>
41
42 typedef HashMap<int, const char*> IntConstCharHashMap;
43
44 using namespace WebCore;
45
46 namespace WebKit {
47
backspaceCallback(GtkWidget * widget,PageClientImpl * client)48 static void backspaceCallback(GtkWidget* widget, PageClientImpl* client)
49 {
50 g_signal_stop_emission_by_name(widget, "backspace");
51 client->addPendingEditorCommand("DeleteBackward");
52 }
53
selectAllCallback(GtkWidget * widget,gboolean select,PageClientImpl * client)54 static void selectAllCallback(GtkWidget* widget, gboolean select, PageClientImpl* client)
55 {
56 g_signal_stop_emission_by_name(widget, "select-all");
57 client->addPendingEditorCommand(select ? "SelectAll" : "Unselect");
58 }
59
cutClipboardCallback(GtkWidget * widget,PageClientImpl * client)60 static void cutClipboardCallback(GtkWidget* widget, PageClientImpl* client)
61 {
62 g_signal_stop_emission_by_name(widget, "cut-clipboard");
63 client->addPendingEditorCommand("Cut");
64 }
65
copyClipboardCallback(GtkWidget * widget,PageClientImpl * client)66 static void copyClipboardCallback(GtkWidget* widget, PageClientImpl* client)
67 {
68 g_signal_stop_emission_by_name(widget, "copy-clipboard");
69 client->addPendingEditorCommand("Copy");
70 }
71
pasteClipboardCallback(GtkWidget * widget,PageClientImpl * client)72 static void pasteClipboardCallback(GtkWidget* widget, PageClientImpl* client)
73 {
74 g_signal_stop_emission_by_name(widget, "paste-clipboard");
75 client->addPendingEditorCommand("Paste");
76 }
77
toggleOverwriteCallback(GtkWidget * widget,EditorClient *)78 static void toggleOverwriteCallback(GtkWidget* widget, EditorClient*)
79 {
80 // We don't support toggling the overwrite mode, but the default callback expects
81 // the GtkTextView to have a layout, so we handle this signal just to stop it.
82 g_signal_stop_emission_by_name(widget, "toggle-overwrite");
83 }
84
85 // GTK+ will still send these signals to the web view. So we can safely stop signal
86 // emission without breaking accessibility.
popupMenuCallback(GtkWidget * widget,EditorClient *)87 static void popupMenuCallback(GtkWidget* widget, EditorClient*)
88 {
89 g_signal_stop_emission_by_name(widget, "popup-menu");
90 }
91
showHelpCallback(GtkWidget * widget,EditorClient *)92 static void showHelpCallback(GtkWidget* widget, EditorClient*)
93 {
94 g_signal_stop_emission_by_name(widget, "show-help");
95 }
96
97 static const char* const gtkDeleteCommands[][2] = {
98 { "DeleteBackward", "DeleteForward" }, // Characters
99 { "DeleteWordBackward", "DeleteWordForward" }, // Word ends
100 { "DeleteWordBackward", "DeleteWordForward" }, // Words
101 { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Lines
102 { "DeleteToBeginningOfLine", "DeleteToEndOfLine" }, // Line ends
103 { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraph ends
104 { "DeleteToBeginningOfParagraph", "DeleteToEndOfParagraph" }, // Paragraphs
105 { 0, 0 } // Whitespace (M-\ in Emacs)
106 };
107
deleteFromCursorCallback(GtkWidget * widget,GtkDeleteType deleteType,gint count,PageClientImpl * client)108 static void deleteFromCursorCallback(GtkWidget* widget, GtkDeleteType deleteType, gint count, PageClientImpl* client)
109 {
110 g_signal_stop_emission_by_name(widget, "delete-from-cursor");
111 int direction = count > 0 ? 1 : 0;
112
113 // Ensuring that deleteType <= G_N_ELEMENTS here results in a compiler warning
114 // that the condition is always true.
115
116 if (deleteType == GTK_DELETE_WORDS) {
117 if (!direction) {
118 client->addPendingEditorCommand("MoveWordForward");
119 client->addPendingEditorCommand("MoveWordBackward");
120 } else {
121 client->addPendingEditorCommand("MoveWordBackward");
122 client->addPendingEditorCommand("MoveWordForward");
123 }
124 } else if (deleteType == GTK_DELETE_DISPLAY_LINES) {
125 if (!direction)
126 client->addPendingEditorCommand("MoveToBeginningOfLine");
127 else
128 client->addPendingEditorCommand("MoveToEndOfLine");
129 } else if (deleteType == GTK_DELETE_PARAGRAPHS) {
130 if (!direction)
131 client->addPendingEditorCommand("MoveToBeginningOfParagraph");
132 else
133 client->addPendingEditorCommand("MoveToEndOfParagraph");
134 }
135
136 const char* rawCommand = gtkDeleteCommands[deleteType][direction];
137 if (!rawCommand)
138 return;
139
140 for (int i = 0; i < abs(count); i++)
141 client->addPendingEditorCommand(rawCommand);
142 }
143
144 static const char* const gtkMoveCommands[][4] = {
145 { "MoveBackward", "MoveForward",
146 "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Forward/backward grapheme
147 { "MoveLeft", "MoveRight",
148 "MoveBackwardAndModifySelection", "MoveForwardAndModifySelection" }, // Left/right grapheme
149 { "MoveWordBackward", "MoveWordForward",
150 "MoveWordBackwardAndModifySelection", "MoveWordForwardAndModifySelection" }, // Forward/backward word
151 { "MoveUp", "MoveDown",
152 "MoveUpAndModifySelection", "MoveDownAndModifySelection" }, // Up/down line
153 { "MoveToBeginningOfLine", "MoveToEndOfLine",
154 "MoveToBeginningOfLineAndModifySelection", "MoveToEndOfLineAndModifySelection" }, // Up/down line ends
155 { "MoveParagraphForward", "MoveParagraphBackward",
156 "MoveParagraphForwardAndModifySelection", "MoveParagraphBackwardAndModifySelection" }, // Up/down paragraphs
157 { "MoveToBeginningOfParagraph", "MoveToEndOfParagraph",
158 "MoveToBeginningOfParagraphAndModifySelection", "MoveToEndOfParagraphAndModifySelection" }, // Up/down paragraph ends.
159 { "MovePageUp", "MovePageDown",
160 "MovePageUpAndModifySelection", "MovePageDownAndModifySelection" }, // Up/down page
161 { "MoveToBeginningOfDocument", "MoveToEndOfDocument",
162 "MoveToBeginningOfDocumentAndModifySelection", "MoveToEndOfDocumentAndModifySelection" }, // Begin/end of buffer
163 { 0, 0,
164 0, 0 } // Horizontal page movement
165 };
166
moveCursorCallback(GtkWidget * widget,GtkMovementStep step,gint count,gboolean extendSelection,PageClientImpl * client)167 static void moveCursorCallback(GtkWidget* widget, GtkMovementStep step, gint count, gboolean extendSelection, PageClientImpl* client)
168 {
169 g_signal_stop_emission_by_name(widget, "move-cursor");
170 int direction = count > 0 ? 1 : 0;
171 if (extendSelection)
172 direction += 2;
173
174 if (static_cast<unsigned>(step) >= G_N_ELEMENTS(gtkMoveCommands))
175 return;
176
177 const char* rawCommand = gtkMoveCommands[step][direction];
178 if (!rawCommand)
179 return;
180
181 for (int i = 0; i < abs(count); i++)
182 client->addPendingEditorCommand(rawCommand);
183 }
184
185 static const unsigned CtrlKey = 1 << 0;
186 static const unsigned AltKey = 1 << 1;
187 static const unsigned ShiftKey = 1 << 2;
188
189 struct KeyDownEntry {
190 unsigned virtualKey;
191 unsigned modifiers;
192 const char* name;
193 };
194
195 struct KeyPressEntry {
196 unsigned charCode;
197 unsigned modifiers;
198 const char* name;
199 };
200
201 static const KeyDownEntry keyDownEntries[] = {
202 { 'B', CtrlKey, "ToggleBold" },
203 { 'I', CtrlKey, "ToggleItalic" },
204 { VK_ESCAPE, 0, "Cancel" },
205 { VK_OEM_PERIOD, CtrlKey, "Cancel" },
206 { VK_TAB, 0, "InsertTab" },
207 { VK_TAB, ShiftKey, "InsertBacktab" },
208 { VK_RETURN, 0, "InsertNewline" },
209 { VK_RETURN, CtrlKey, "InsertNewline" },
210 { VK_RETURN, AltKey, "InsertNewline" },
211 { VK_RETURN, AltKey | ShiftKey, "InsertNewline" },
212 };
213
214 static const KeyPressEntry keyPressEntries[] = {
215 { '\t', 0, "InsertTab" },
216 { '\t', ShiftKey, "InsertBacktab" },
217 { '\r', 0, "InsertNewline" },
218 { '\r', CtrlKey, "InsertNewline" },
219 { '\r', AltKey, "InsertNewline" },
220 { '\r', AltKey | ShiftKey, "InsertNewline" },
221 };
222
PageClientImpl(GtkWidget * viewWidget)223 PageClientImpl::PageClientImpl(GtkWidget* viewWidget)
224 : m_viewWidget(viewWidget)
225 , m_nativeWidget(gtk_text_view_new())
226 {
227 g_signal_connect(m_nativeWidget.get(), "backspace", G_CALLBACK(backspaceCallback), this);
228 g_signal_connect(m_nativeWidget.get(), "cut-clipboard", G_CALLBACK(cutClipboardCallback), this);
229 g_signal_connect(m_nativeWidget.get(), "copy-clipboard", G_CALLBACK(copyClipboardCallback), this);
230 g_signal_connect(m_nativeWidget.get(), "paste-clipboard", G_CALLBACK(pasteClipboardCallback), this);
231 g_signal_connect(m_nativeWidget.get(), "select-all", G_CALLBACK(selectAllCallback), this);
232 g_signal_connect(m_nativeWidget.get(), "move-cursor", G_CALLBACK(moveCursorCallback), this);
233 g_signal_connect(m_nativeWidget.get(), "delete-from-cursor", G_CALLBACK(deleteFromCursorCallback), this);
234 g_signal_connect(m_nativeWidget.get(), "toggle-overwrite", G_CALLBACK(toggleOverwriteCallback), this);
235 g_signal_connect(m_nativeWidget.get(), "popup-menu", G_CALLBACK(popupMenuCallback), this);
236 g_signal_connect(m_nativeWidget.get(), "show-help", G_CALLBACK(showHelpCallback), this);
237 }
238
~PageClientImpl()239 PageClientImpl::~PageClientImpl()
240 {
241 }
242
getEditorCommandsForKeyEvent(const NativeWebKeyboardEvent & event,Vector<WTF::String> & commandList)243 void PageClientImpl::getEditorCommandsForKeyEvent(const NativeWebKeyboardEvent& event, Vector<WTF::String>& commandList)
244 {
245 m_pendingEditorCommands.clear();
246
247 #ifdef GTK_API_VERSION_2
248 gtk_bindings_activate_event(GTK_OBJECT(m_nativeWidget.get()), const_cast<GdkEventKey*>(&event.nativeEvent()->key));
249 #else
250 gtk_bindings_activate_event(G_OBJECT(m_nativeWidget.get()), const_cast<GdkEventKey*>(&event.nativeEvent()->key));
251 #endif
252
253 if (m_pendingEditorCommands.isEmpty()) {
254 commandList.append(m_pendingEditorCommands);
255 return;
256 }
257
258 DEFINE_STATIC_LOCAL(IntConstCharHashMap, keyDownCommandsMap, ());
259 DEFINE_STATIC_LOCAL(IntConstCharHashMap, keyPressCommandsMap, ());
260
261 if (keyDownCommandsMap.isEmpty()) {
262 for (unsigned i = 0; i < G_N_ELEMENTS(keyDownEntries); i++)
263 keyDownCommandsMap.set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
264
265 for (unsigned i = 0; i < G_N_ELEMENTS(keyPressEntries); i++)
266 keyPressCommandsMap.set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
267 }
268
269 unsigned modifiers = 0;
270 if (event.shiftKey())
271 modifiers |= ShiftKey;
272 if (event.altKey())
273 modifiers |= AltKey;
274 if (event.controlKey())
275 modifiers |= CtrlKey;
276
277 // For keypress events, we want charCode(), but keyCode() does that.
278 int mapKey = modifiers << 16 | event.nativeVirtualKeyCode();
279 if (mapKey) {
280 HashMap<int, const char*>* commandMap = event.type() == WebEvent::KeyDown ?
281 &keyDownCommandsMap : &keyPressCommandsMap;
282 if (const char* commandString = commandMap->get(mapKey))
283 m_pendingEditorCommands.append(commandString);
284 }
285
286 commandList.append(m_pendingEditorCommands);
287 }
288
289 // PageClient's pure virtual functions
createDrawingAreaProxy()290 PassOwnPtr<DrawingAreaProxy> PageClientImpl::createDrawingAreaProxy()
291 {
292 WebKitWebViewBase* view = WEBKIT_WEB_VIEW_BASE(m_viewWidget);
293 return ChunkedUpdateDrawingAreaProxy::create(view, webkitWebViewBaseGetPage(view));
294 }
295
setViewNeedsDisplay(const WebCore::IntRect &)296 void PageClientImpl::setViewNeedsDisplay(const WebCore::IntRect&)
297 {
298 notImplemented();
299 }
300
displayView()301 void PageClientImpl::displayView()
302 {
303 notImplemented();
304 }
305
scrollView(const WebCore::IntRect & scrollRect,const WebCore::IntSize & scrollOffset)306 void PageClientImpl::scrollView(const WebCore::IntRect& scrollRect, const WebCore::IntSize& scrollOffset)
307 {
308 notImplemented();
309 }
310
viewSize()311 WebCore::IntSize PageClientImpl::viewSize()
312 {
313 GtkAllocation allocation;
314 gtk_widget_get_allocation(m_viewWidget, &allocation);
315 return IntSize(allocation.width, allocation.height);
316 }
317
isViewWindowActive()318 bool PageClientImpl::isViewWindowActive()
319 {
320 notImplemented();
321 return true;
322 }
323
isViewFocused()324 bool PageClientImpl::isViewFocused()
325 {
326 notImplemented();
327 return true;
328 }
329
isViewVisible()330 bool PageClientImpl::isViewVisible()
331 {
332 notImplemented();
333 return true;
334 }
335
isViewInWindow()336 bool PageClientImpl::isViewInWindow()
337 {
338 notImplemented();
339 return true;
340 }
341
processDidCrash()342 void PageClientImpl::PageClientImpl::processDidCrash()
343 {
344 notImplemented();
345 }
346
didRelaunchProcess()347 void PageClientImpl::didRelaunchProcess()
348 {
349 notImplemented();
350 }
351
takeFocus(bool)352 void PageClientImpl::takeFocus(bool)
353 {
354 notImplemented();
355 }
356
toolTipChanged(const String &,const String &)357 void PageClientImpl::toolTipChanged(const String&, const String&)
358 {
359 notImplemented();
360 }
361
setCursor(const Cursor & cursor)362 void PageClientImpl::setCursor(const Cursor& cursor)
363 {
364 // [GTK] Widget::setCursor() gets called frequently
365 // http://bugs.webkit.org/show_bug.cgi?id=16388
366 // Setting the cursor may be an expensive operation in some backends,
367 // so don't re-set the cursor if it's already set to the target value.
368 GdkWindow* window = gtk_widget_get_window(m_viewWidget);
369 GdkCursor* currentCursor = gdk_window_get_cursor(window);
370 GdkCursor* newCursor = cursor.platformCursor().get();
371 if (currentCursor != newCursor)
372 gdk_window_set_cursor(window, newCursor);
373 }
374
setViewportArguments(const WebCore::ViewportArguments &)375 void PageClientImpl::setViewportArguments(const WebCore::ViewportArguments&)
376 {
377 notImplemented();
378 }
379
registerEditCommand(PassRefPtr<WebEditCommandProxy>,WebPageProxy::UndoOrRedo)380 void PageClientImpl::registerEditCommand(PassRefPtr<WebEditCommandProxy>, WebPageProxy::UndoOrRedo)
381 {
382 notImplemented();
383 }
384
clearAllEditCommands()385 void PageClientImpl::clearAllEditCommands()
386 {
387 notImplemented();
388 }
389
canUndoRedo(WebPageProxy::UndoOrRedo)390 bool PageClientImpl::canUndoRedo(WebPageProxy::UndoOrRedo)
391 {
392 notImplemented();
393 return false;
394 }
395
executeUndoRedo(WebPageProxy::UndoOrRedo)396 void PageClientImpl::executeUndoRedo(WebPageProxy::UndoOrRedo)
397 {
398 notImplemented();
399 }
400
convertToDeviceSpace(const FloatRect & viewRect)401 FloatRect PageClientImpl::convertToDeviceSpace(const FloatRect& viewRect)
402 {
403 notImplemented();
404 return viewRect;
405 }
406
convertToUserSpace(const FloatRect & viewRect)407 FloatRect PageClientImpl::convertToUserSpace(const FloatRect& viewRect)
408 {
409 notImplemented();
410 return viewRect;
411 }
412
windowToScreen(const IntRect & rect)413 IntRect PageClientImpl::windowToScreen(const IntRect& rect)
414 {
415 notImplemented();
416 return IntRect();
417 }
418
doneWithKeyEvent(const NativeWebKeyboardEvent &,bool wasEventHandled)419 void PageClientImpl::doneWithKeyEvent(const NativeWebKeyboardEvent&, bool wasEventHandled)
420 {
421 notImplemented();
422 }
423
didNotHandleKeyEvent(const NativeWebKeyboardEvent & event)424 void PageClientImpl::didNotHandleKeyEvent(const NativeWebKeyboardEvent& event)
425 {
426 notImplemented();
427 }
428
createPopupMenuProxy(WebPageProxy *)429 PassRefPtr<WebPopupMenuProxy> PageClientImpl::createPopupMenuProxy(WebPageProxy*)
430 {
431 notImplemented();
432 return 0;
433 }
434
createContextMenuProxy(WebPageProxy *)435 PassRefPtr<WebContextMenuProxy> PageClientImpl::createContextMenuProxy(WebPageProxy*)
436 {
437 notImplemented();
438 return 0;
439 }
440
setFindIndicator(PassRefPtr<FindIndicator>,bool fadeOut)441 void PageClientImpl::setFindIndicator(PassRefPtr<FindIndicator>, bool fadeOut)
442 {
443 notImplemented();
444 }
445
446 #if USE(ACCELERATED_COMPOSITING)
pageDidEnterAcceleratedCompositing()447 void PageClientImpl::pageDidEnterAcceleratedCompositing()
448 {
449 notImplemented();
450 }
451
pageDidLeaveAcceleratedCompositing()452 void PageClientImpl::pageDidLeaveAcceleratedCompositing()
453 {
454 notImplemented();
455 }
456 #endif // USE(ACCELERATED_COMPOSITING)
457
didCommitLoadForMainFrame(bool useCustomRepresentation)458 void PageClientImpl::didCommitLoadForMainFrame(bool useCustomRepresentation)
459 {
460 }
461
didFinishLoadingDataForCustomRepresentation(const String & suggestedFilename,const CoreIPC::DataReference &)462 void PageClientImpl::didFinishLoadingDataForCustomRepresentation(const String& suggestedFilename, const CoreIPC::DataReference&)
463 {
464 }
465
customRepresentationZoomFactor()466 double PageClientImpl::customRepresentationZoomFactor()
467 {
468 notImplemented();
469 return 0;
470 }
471
setCustomRepresentationZoomFactor(double)472 void PageClientImpl::setCustomRepresentationZoomFactor(double)
473 {
474 notImplemented();
475 }
476
pageClosed()477 void PageClientImpl::pageClosed()
478 {
479 notImplemented();
480 }
481
didChangeScrollbarsForMainFrame() const482 void PageClientImpl::didChangeScrollbarsForMainFrame() const
483 {
484 }
485
flashBackingStoreUpdates(const Vector<IntRect> &)486 void PageClientImpl::flashBackingStoreUpdates(const Vector<IntRect>&)
487 {
488 notImplemented();
489 }
490
findStringInCustomRepresentation(const String &,FindOptions,unsigned)491 void PageClientImpl::findStringInCustomRepresentation(const String&, FindOptions, unsigned)
492 {
493 notImplemented();
494 }
495
countStringMatchesInCustomRepresentation(const String &,FindOptions,unsigned)496 void PageClientImpl::countStringMatchesInCustomRepresentation(const String&, FindOptions, unsigned)
497 {
498 notImplemented();
499 }
500
501 } // namespace WebKit
502