0e525643601d967dd941e1d8f445168b24d14d35
[WebKit-https.git] / Source / WebKit / gtk / WebCoreSupport / EditorClientGtk.cpp
1 /*
2  *  Copyright (C) 2007 Alp Toker <alp@atoker.com>
3  *  Copyright (C) 2008 Nuanti Ltd.
4  *  Copyright (C) 2009 Diego Escalante Urrelo <diegoe@gnome.org>
5  *  Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
6  *  Copyright (C) 2009, 2010 Igalia S.L.
7  *  Copyright (C) 2010, Martin Robinson <mrobinson@webkit.org>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #include "config.h"
25 #include "EditorClientGtk.h"
26
27 #include "DataObjectGtk.h"
28 #include "DumpRenderTreeSupportGtk.h"
29 #include "Editor.h"
30 #include "EventNames.h"
31 #include "FocusController.h"
32 #include "Frame.h"
33 #include <glib.h>
34 #include "KeyboardEvent.h"
35 #include "markup.h"
36 #include "NotImplemented.h"
37 #include "Page.h"
38 #include "PasteboardHelper.h"
39 #include "PlatformKeyboardEvent.h"
40 #include "UndoStep.h"
41 #include "WebKitDOMBinding.h"
42 #include "WebKitDOMCSSStyleDeclarationPrivate.h"
43 #include "WebKitDOMHTMLElementPrivate.h"
44 #include "WebKitDOMNodePrivate.h"
45 #include "WebKitDOMRangePrivate.h"
46 #include "WindowsKeyboardCodes.h"
47 #include "webkitglobals.h"
48 #include "webkitglobalsprivate.h"
49 #include "webkitmarshal.h"
50 #include "webkitspellchecker.h"
51 #include "webkitwebsettingsprivate.h"
52 #include "webkitwebviewprivate.h"
53 #include <wtf/text/CString.h>
54
55 // Arbitrary depth limit for the undo stack, to keep it from using
56 // unbounded memory.  This is the maximum number of distinct undoable
57 // actions -- unbroken stretches of typed characters are coalesced
58 // into a single action.
59 #define maximumUndoStackDepth 1000
60
61 using namespace WebCore;
62
63 namespace WebKit {
64
65 void EditorClient::willSetInputMethodState()
66 {
67 }
68
69 void EditorClient::setInputMethodState(bool active)
70 {
71     m_webView->priv->imFilter.setEnabled(active);
72 }
73
74 bool EditorClient::shouldShowUnicodeMenu()
75 {
76     if (gtk_major_version > 2 || (gtk_major_version == 2 && gtk_minor_version >= 10)) {
77         GtkSettings* settings = gtk_widget_get_settings(GTK_WIDGET(m_webView));
78         if (!settings)
79             return true;
80
81         gboolean enabled;
82         g_object_get(settings, "gtk-show-unicode-menu", &enabled, NULL);
83         return enabled;
84     }
85
86     return true;
87 }
88
89 bool EditorClient::shouldDeleteRange(Range* range)
90 {
91     gboolean accept = TRUE;
92     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
93     g_signal_emit_by_name(m_webView, "should-delete-range", kitRange.get(), &accept);
94     return accept;
95 }
96
97 bool EditorClient::shouldShowDeleteInterface(HTMLElement* element)
98 {
99     gboolean accept = TRUE;
100     GRefPtr<WebKitDOMHTMLElement> kitElement(adoptGRef(kit(element)));
101     g_signal_emit_by_name(m_webView, "should-show-delete-interface-for-element", kitElement.get(), &accept);
102     return accept;
103 }
104
105 bool EditorClient::isContinuousSpellCheckingEnabled()
106 {
107     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
108
109     gboolean enabled;
110     g_object_get(settings, "enable-spell-checking", &enabled, NULL);
111
112     return enabled;
113 }
114
115 bool EditorClient::isGrammarCheckingEnabled()
116 {
117     notImplemented();
118     return false;
119 }
120
121 int EditorClient::spellCheckerDocumentTag()
122 {
123     notImplemented();
124     return 0;
125 }
126
127 bool EditorClient::shouldBeginEditing(WebCore::Range* range)
128 {
129     gboolean accept = TRUE;
130     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
131     g_signal_emit_by_name(m_webView, "should-begin-editing", kitRange.get(), &accept);
132     return accept;
133 }
134
135 bool EditorClient::shouldEndEditing(WebCore::Range* range)
136 {
137     gboolean accept = TRUE;
138     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
139     g_signal_emit_by_name(m_webView, "should-end-editing", kitRange.get(), &accept);
140     return accept;
141 }
142
143 static WebKitInsertAction kit(EditorInsertAction action)
144 {
145     switch (action) {
146     case EditorInsertActionTyped:
147         return WEBKIT_INSERT_ACTION_TYPED;
148     case EditorInsertActionPasted:
149         return WEBKIT_INSERT_ACTION_PASTED;
150     case EditorInsertActionDropped:
151         return WEBKIT_INSERT_ACTION_DROPPED;
152     }
153     ASSERT_NOT_REACHED();
154     return WEBKIT_INSERT_ACTION_TYPED;
155 }
156
157 bool EditorClient::shouldInsertText(const String& string, Range* range, EditorInsertAction action)
158 {
159     gboolean accept = TRUE;
160     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
161     g_signal_emit_by_name(m_webView, "should-insert-text", string.utf8().data(), kitRange.get(), kit(action), &accept);
162     return accept;
163 }
164
165 static WebKitSelectionAffinity kit(EAffinity affinity)
166 {
167     switch (affinity) {
168     case UPSTREAM:
169         return WEBKIT_SELECTION_AFFINITY_UPSTREAM;
170     case DOWNSTREAM:
171         return WEBKIT_SELECTION_AFFINITY_DOWNSTREAM;
172     }
173     ASSERT_NOT_REACHED();
174     return WEBKIT_SELECTION_AFFINITY_UPSTREAM;
175 }
176
177 bool EditorClient::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity affinity, bool stillSelecting)
178 {
179     gboolean accept = TRUE;
180     GRefPtr<WebKitDOMRange> kitFromRange(fromRange ? adoptGRef(kit(fromRange)) : 0);
181     GRefPtr<WebKitDOMRange> kitToRange(toRange ? adoptGRef(kit(toRange)) : 0);
182     g_signal_emit_by_name(m_webView, "should-change-selected-range", kitFromRange.get(), kitToRange.get(),
183                           kit(affinity), stillSelecting, &accept);
184     return accept;
185 }
186
187 bool EditorClient::shouldApplyStyle(WebCore::StylePropertySet* set, WebCore::Range* range)
188 {
189     gboolean accept = TRUE;
190     GRefPtr<WebKitDOMCSSStyleDeclaration> kitDeclaration(kit(set->ensureCSSStyleDeclaration()));
191     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
192     g_signal_emit_by_name(m_webView, "should-apply-style", kitDeclaration.get(), kitRange.get(), &accept);
193     return accept;
194 }
195
196 bool EditorClient::shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*)
197 {
198     notImplemented();
199     return true;
200 }
201
202 void EditorClient::didBeginEditing()
203 {
204     g_signal_emit_by_name(m_webView, "editing-began");
205 }
206
207 void EditorClient::respondToChangedContents()
208 {
209     g_signal_emit_by_name(m_webView, "user-changed-contents");
210 }
211
212 static WebKitWebView* viewSettingClipboard = 0;
213 static void collapseSelection(GtkClipboard* clipboard, WebKitWebView* webView)
214 {
215     if (viewSettingClipboard && viewSettingClipboard == webView)
216         return;
217
218     WebCore::Page* corePage = core(webView);
219     if (!corePage || !corePage->focusController())
220         return;
221
222     Frame* frame = corePage->focusController()->focusedOrMainFrame();
223
224     // Collapse the selection without clearing it
225     ASSERT(frame);
226     frame->selection()->setBase(frame->selection()->extent(), frame->selection()->affinity());
227 }
228
229 #if PLATFORM(X11)
230 static void setSelectionPrimaryClipboardIfNeeded(WebKitWebView* webView)
231 {
232     if (!gtk_widget_has_screen(GTK_WIDGET(webView)))
233         return;
234
235     GtkClipboard* clipboard = gtk_widget_get_clipboard(GTK_WIDGET(webView), GDK_SELECTION_PRIMARY);
236     DataObjectGtk* dataObject = DataObjectGtk::forClipboard(clipboard);
237     WebCore::Page* corePage = core(webView);
238     Frame* targetFrame = corePage->focusController()->focusedOrMainFrame();
239
240     if (!targetFrame->selection()->isRange())
241         return;
242
243     dataObject->clearAll();
244     dataObject->setRange(targetFrame->selection()->toNormalizedRange());
245
246     viewSettingClipboard = webView;
247     GClosure* callback = g_cclosure_new_object(G_CALLBACK(collapseSelection), G_OBJECT(webView));
248     g_closure_set_marshal(callback, g_cclosure_marshal_VOID__VOID);
249     PasteboardHelper::defaultPasteboardHelper()->writeClipboardContents(clipboard, PasteboardHelper::DoNotIncludeSmartPaste, callback);
250     viewSettingClipboard = 0;
251 }
252 #endif
253
254 void EditorClient::respondToChangedSelection(Frame* frame)
255 {
256     g_signal_emit_by_name(m_webView, "selection-changed");
257
258     if (!frame)
259         return;
260
261 #if PLATFORM(X11)
262     setSelectionPrimaryClipboardIfNeeded(m_webView);
263 #endif
264
265     if (m_updatingComposition || !frame->editor()->hasComposition() || frame->editor()->ignoreCompositionSelectionChange())
266         return;
267
268     unsigned start;
269     unsigned end;
270     if (!frame->editor()->getCompositionSelection(start, end))
271         m_webView->priv->imFilter.resetContext();
272 }
273
274 void EditorClient::didEndEditing()
275 {
276     g_signal_emit_by_name(m_webView, "editing-ended");
277 }
278
279 void EditorClient::didWriteSelectionToPasteboard()
280 {
281     notImplemented();
282 }
283
284 void EditorClient::didSetSelectionTypesForPasteboard()
285 {
286     notImplemented();
287 }
288
289 void EditorClient::registerUndoStep(WTF::PassRefPtr<WebCore::UndoStep> step)
290 {
291     if (undoStack.size() == maximumUndoStackDepth)
292         undoStack.removeFirst();
293     if (!m_isInRedo)
294         redoStack.clear();
295     undoStack.append(step);
296 }
297
298 void EditorClient::registerRedoStep(WTF::PassRefPtr<WebCore::UndoStep> step)
299 {
300     redoStack.append(step);
301 }
302
303 void EditorClient::clearUndoRedoOperations()
304 {
305     undoStack.clear();
306     redoStack.clear();
307 }
308
309 bool EditorClient::canCopyCut(WebCore::Frame*, bool defaultValue) const
310 {
311     return defaultValue;
312 }
313
314 bool EditorClient::canPaste(WebCore::Frame*, bool defaultValue) const
315 {
316     return defaultValue;
317 }
318
319 bool EditorClient::canUndo() const
320 {
321     return !undoStack.isEmpty();
322 }
323
324 bool EditorClient::canRedo() const
325 {
326     return !redoStack.isEmpty();
327 }
328
329 void EditorClient::undo()
330 {
331     if (canUndo()) {
332         RefPtr<WebCore::UndoStep> step(*(--undoStack.end()));
333         undoStack.remove(--undoStack.end());
334         // unapply will call us back to push this command onto the redo stack.
335         step->unapply();
336     }
337 }
338
339 void EditorClient::redo()
340 {
341     if (canRedo()) {
342         RefPtr<WebCore::UndoStep> step(*(--redoStack.end()));
343         redoStack.remove(--redoStack.end());
344
345         ASSERT(!m_isInRedo);
346         m_isInRedo = true;
347         // reapply will call us back to push this command onto the undo stack.
348         step->reapply();
349         m_isInRedo = false;
350     }
351 }
352
353 bool EditorClient::shouldInsertNode(Node* node, Range* range, EditorInsertAction action)
354 {
355     gboolean accept = TRUE;
356     GRefPtr<WebKitDOMRange> kitRange(adoptGRef(kit(range)));
357     GRefPtr<WebKitDOMNode> kitNode(adoptGRef(kit(node)));
358     g_signal_emit_by_name(m_webView, "should-insert-node", kitNode.get(), kitRange.get(), kit(action), &accept);
359     return accept;
360 }
361
362 void EditorClient::pageDestroyed()
363 {
364     delete this;
365 }
366
367 void EditorClient::setSmartInsertDeleteEnabled(bool enabled)
368 {
369     m_smartInsertDeleteEnabled = enabled;
370 }
371
372 bool EditorClient::smartInsertDeleteEnabled()
373 {
374     return m_smartInsertDeleteEnabled;
375 }
376
377 bool EditorClient::isSelectTrailingWhitespaceEnabled()
378 {
379     if (!DumpRenderTreeSupportGtk::dumpRenderTreeModeEnabled())
380         return false;
381     return DumpRenderTreeSupportGtk::selectTrailingWhitespaceEnabled();
382 }
383
384 void EditorClient::toggleContinuousSpellChecking()
385 {
386     WebKitWebSettings* settings = webkit_web_view_get_settings(m_webView);
387
388     gboolean enabled;
389     g_object_get(settings, "enable-spell-checking", &enabled, NULL);
390
391     g_object_set(settings, "enable-spell-checking", !enabled, NULL);
392 }
393
394 void EditorClient::toggleGrammarChecking()
395 {
396 }
397
398 bool EditorClient::executePendingEditorCommands(Frame* frame, bool allowTextInsertion)
399 {
400     Vector<Editor::Command> commands;
401     for (size_t i = 0; i < m_pendingEditorCommands.size(); i++) {
402         Editor::Command command = frame->editor()->command(m_pendingEditorCommands.at(i).utf8().data());
403         if (command.isTextInsertion() && !allowTextInsertion)
404             return false;
405
406         commands.append(command);
407     }
408
409     bool success = true;
410     for (size_t i = 0; i < commands.size(); i++) {
411         if (!commands.at(i).execute()) {
412             success = false;
413             break;
414         }
415     }
416
417     m_pendingEditorCommands.clear();
418     return success;
419 }
420
421 bool EditorClient::handleInputMethodKeyboardEvent(KeyboardEvent* event)
422 {
423     if (event->type() != eventNames().keydownEvent)
424         return false;
425
426     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
427     if (!platformEvent)
428         return false;
429
430     Frame* frame = core(m_webView)->focusController()->focusedOrMainFrame();
431     if (!frame || !frame->editor()->canEdit())
432         return false;
433
434     const CompositionResults& compositionResults = platformEvent->compositionResults();
435     if (!compositionResults.compositionUpdated())
436         return false;
437
438     m_updatingComposition = true;
439
440     // PlatformKeyboardEvent returns an empty string when there are composition results.
441     // That prevents the delivery of keypress, which is the behavior we want for composition
442     // events. See EventHandler::keyEvent. 
443     ASSERT(platformEvent->text().isNull());
444
445     if (!compositionResults.confirmedComposition.isNull())
446         frame->editor()->confirmComposition(compositionResults.confirmedComposition);
447
448     String preedit = compositionResults.preedit;
449     if (!preedit.isNull()) {
450         Vector<CompositionUnderline> underlines;
451         underlines.append(CompositionUnderline(0, preedit.length(), Color(1, 1, 1), false));
452         frame->editor()->setComposition(preedit, underlines, compositionResults.preeditCursorOffset, compositionResults.preeditCursorOffset);
453     }
454
455     m_updatingComposition = false;
456     return true;
457 }
458
459 void EditorClient::handleKeyboardEvent(KeyboardEvent* event)
460 {
461     Node* node = event->target()->toNode();
462     ASSERT(node);
463     Frame* frame = node->document()->frame();
464     ASSERT(frame);
465
466     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
467     if (!platformEvent)
468         return;
469
470     if (handleInputMethodKeyboardEvent(event))
471         return;
472
473     KeyBindingTranslator::EventType type = event->type() == eventNames().keydownEvent ?
474         KeyBindingTranslator::KeyDown : KeyBindingTranslator::KeyPress;
475     m_keyBindingTranslator.getEditorCommandsForKeyEvent(platformEvent->gdkEventKey(), type, m_pendingEditorCommands);
476     if (m_pendingEditorCommands.size() > 0) {
477
478         // During RawKeyDown events if an editor command will insert text, defer
479         // the insertion until the keypress event. We want keydown to bubble up
480         // through the DOM first.
481         if (platformEvent->type() == PlatformEvent::RawKeyDown) {
482             if (executePendingEditorCommands(frame, false))
483                 event->setDefaultHandled();
484
485             return;
486         }
487
488         // Only allow text insertion commands if the current node is editable.
489         if (executePendingEditorCommands(frame, frame->editor()->canEdit())) {
490             event->setDefaultHandled();
491             return;
492         }
493     }
494
495     // Don't allow text insertion for nodes that cannot edit.
496     if (!frame->editor()->canEdit())
497         return;
498
499     // This is just a normal text insertion, so wait to execute the insertion
500     // until a keypress event happens. This will ensure that the insertion will not
501     // be reflected in the contents of the field until the keyup DOM event.
502     if (event->type() != eventNames().keypressEvent)
503         return;
504
505     // Don't insert null or control characters as they can result in unexpected behaviour
506     if (event->charCode() < ' ')
507         return;
508
509     // Don't insert anything if a modifier is pressed
510     if (platformEvent->ctrlKey() || platformEvent->altKey())
511         return;
512
513     if (frame->editor()->insertText(platformEvent->text(), event))
514         event->setDefaultHandled();
515 }
516
517 void EditorClient::handleInputMethodKeydown(KeyboardEvent* event)
518 {
519     // Input method results are handled in handleKeyboardEvent, so that we can wait
520     // to trigger composition updates until after the keydown event handler. This better
521     // matches other browsers.
522 }
523
524 EditorClient::EditorClient(WebKitWebView* webView)
525     : m_isInRedo(false)
526 #if ENABLE(SPELLCHECK)
527     , m_textCheckerClient(WEBKIT_SPELL_CHECKER(webkit_get_text_checker()))
528 #endif
529     , m_webView(webView)
530     , m_smartInsertDeleteEnabled(false)
531     , m_updatingComposition(false)
532 {
533 }
534
535 EditorClient::~EditorClient()
536 {
537 }
538
539 void EditorClient::textFieldDidBeginEditing(Element*)
540 {
541 }
542
543 void EditorClient::textFieldDidEndEditing(Element*)
544 {
545 }
546
547 void EditorClient::textDidChangeInTextField(Element*)
548 {
549 }
550
551 bool EditorClient::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
552 {
553     return false;
554 }
555
556 void EditorClient::textWillBeDeletedInTextField(Element*)
557 {
558     notImplemented();
559 }
560
561 void EditorClient::textDidChangeInTextArea(Element*)
562 {
563     notImplemented();
564 }
565
566 void EditorClient::updateSpellingUIWithGrammarString(const String&, const GrammarDetail&)
567 {
568     notImplemented();
569 }
570
571 void EditorClient::updateSpellingUIWithMisspelledWord(const String&)
572 {
573     notImplemented();
574 }
575
576 void EditorClient::showSpellingUI(bool)
577 {
578     notImplemented();
579 }
580
581 bool EditorClient::spellingUIIsShowing()
582 {
583     notImplemented();
584     return false;
585 }
586
587 }