[Refactoring] Move platform-specific code in Editor::respondToChangedSelection to...
[WebKit-https.git] / Source / WebKit / blackberry / WebCoreSupport / EditorClientBlackBerry.cpp
1 /*
2  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
3  * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #include "config.h"
21 #include "EditorClientBlackBerry.h"
22
23 #include "AutofillManager.h"
24 #include "DOMSupport.h"
25 #include "DumpRenderTreeClient.h"
26 #include "EditCommand.h"
27 #include "Frame.h"
28 #include "HTMLInputElement.h"
29 #include "HTMLNames.h"
30 #include "InputHandler.h"
31 #include "KeyboardEvent.h"
32 #include "NotImplemented.h"
33 #include "Page.h"
34 #include "PlatformKeyboardEvent.h"
35 #include "SelectionHandler.h"
36 #include "WebPage_p.h"
37 #include "WindowsKeyboardCodes.h"
38
39 using namespace BlackBerry::WebKit;
40
41 namespace WebCore {
42
43 // Arbitrary depth limit for the undo stack, to keep it from using
44 // unbounded memory. This is the maximum number of distinct undoable
45 // actions -- unbroken stretches of typed characters are coalesced
46 // into a single action only when not interrupted by string replacements
47 // triggered by replaceText calls.
48 static const size_t maximumUndoStackDepth = 1000;
49
50 EditorClientBlackBerry::EditorClientBlackBerry(WebPagePrivate* webPagePrivate)
51     : m_webPagePrivate(webPagePrivate)
52     , m_waitingForCursorFocus(false)
53     , m_spellCheckState(SpellCheckDefault)
54     , m_inRedo(false)
55 {
56 }
57
58 void EditorClientBlackBerry::pageDestroyed()
59 {
60     delete this;
61 }
62
63 bool EditorClientBlackBerry::shouldDeleteRange(Range* range)
64 {
65     if (m_webPagePrivate->m_dumpRenderTree)
66         return m_webPagePrivate->m_dumpRenderTree->shouldDeleteDOMRange(range);
67     return true;
68 }
69
70 bool EditorClientBlackBerry::shouldShowDeleteInterface(HTMLElement*)
71 {
72     notImplemented();
73     return false;
74 }
75
76 bool EditorClientBlackBerry::smartInsertDeleteEnabled()
77 {
78     notImplemented();
79     return false;
80 }
81
82 bool EditorClientBlackBerry::isSelectTrailingWhitespaceEnabled()
83 {
84     if (m_webPagePrivate->m_dumpRenderTree)
85         return m_webPagePrivate->m_dumpRenderTree->isSelectTrailingWhitespaceEnabled();
86     return false;
87 }
88
89 void EditorClientBlackBerry::enableSpellChecking(bool enable)
90 {
91     m_spellCheckState = enable ? SpellCheckDefault : SpellCheckOff;
92 }
93
94 bool EditorClientBlackBerry::shouldSpellCheckFocusedField()
95 {
96     const Frame* frame = m_webPagePrivate->focusedOrMainFrame();
97     if (!frame || !frame->document() || !frame->editor())
98         return false;
99
100     const Node* node = frame->document()->focusedNode();
101     // NOTE: This logic is taken from EditorClientImpl::shouldSpellcheckByDefault
102     // If |node| is null, we default to allowing spellchecking. This is done in
103     // order to mitigate the issue when the user clicks outside the textbox, as a
104     // result of which |node| becomes null, resulting in all the spell check
105     // markers being deleted. Also, the Frame will decide not to do spellchecking
106     // if the user can't edit - so returning true here will not cause any problems
107     // to the Frame's behavior.
108     if (!node)
109         return true;
110
111     // If the field does not support autocomplete, do not do spellchecking.
112     if (node->isElementNode()) {
113         const Element* element = static_cast<const Element*>(node);
114         if (element->hasTagName(HTMLNames::inputTag) && !DOMSupport::elementSupportsAutocomplete(element))
115             return false;
116     }
117
118     // Check if the node disables spell checking directly.
119     return frame->editor()->isSpellCheckingEnabledInFocusedNode();
120 }
121
122 bool EditorClientBlackBerry::isContinuousSpellCheckingEnabled()
123 {
124     if (m_spellCheckState == SpellCheckOff)
125         return false;
126     if (m_spellCheckState == SpellCheckOn)
127         return true;
128     return shouldSpellCheckFocusedField();
129 }
130
131 void EditorClientBlackBerry::toggleContinuousSpellChecking()
132 {
133     // Use the current state to determine how to toggle, if it hasn't
134     // been explicitly set, it will toggle based on the field type.
135     if (isContinuousSpellCheckingEnabled())
136         m_spellCheckState = SpellCheckOff;
137     else
138         m_spellCheckState = SpellCheckOn;
139 }
140
141 bool EditorClientBlackBerry::isGrammarCheckingEnabled()
142 {
143     notImplemented();
144     return false;
145 }
146
147 void EditorClientBlackBerry::toggleGrammarChecking()
148 {
149     notImplemented();
150 }
151
152 int EditorClientBlackBerry::spellCheckerDocumentTag()
153 {
154     notImplemented();
155     return 0;
156 }
157
158 bool EditorClientBlackBerry::shouldBeginEditing(Range* range)
159 {
160     if (m_webPagePrivate->m_dumpRenderTree)
161         return m_webPagePrivate->m_dumpRenderTree->shouldBeginEditingInDOMRange(range);
162
163     return true;
164 }
165
166 bool EditorClientBlackBerry::shouldEndEditing(Range* range)
167 {
168     if (m_webPagePrivate->m_dumpRenderTree)
169         return m_webPagePrivate->m_dumpRenderTree->shouldEndEditingInDOMRange(range);
170     return true;
171 }
172
173 bool EditorClientBlackBerry::shouldInsertNode(Node* node, Range* range, EditorInsertAction insertAction)
174 {
175     if (m_webPagePrivate->m_dumpRenderTree)
176         return m_webPagePrivate->m_dumpRenderTree->shouldInsertNode(node, range, static_cast<int>(insertAction));
177     return true;
178 }
179
180 bool EditorClientBlackBerry::shouldInsertText(const WTF::String& text, Range* range, EditorInsertAction insertAction)
181 {
182     if (m_webPagePrivate->m_dumpRenderTree)
183         return m_webPagePrivate->m_dumpRenderTree->shouldInsertText(text, range, static_cast<int>(insertAction));
184     return true;
185 }
186
187 bool EditorClientBlackBerry::shouldChangeSelectedRange(Range* fromRange, Range* toRange, EAffinity affinity, bool stillSelecting)
188 {
189     if (m_webPagePrivate->m_dumpRenderTree)
190         return m_webPagePrivate->m_dumpRenderTree->shouldChangeSelectedDOMRangeToDOMRangeAffinityStillSelecting(fromRange, toRange, static_cast<int>(affinity), stillSelecting);
191
192     Frame* frame = m_webPagePrivate->focusedOrMainFrame();
193     if (frame && frame->document()) {
194         if (frame->document()->focusedNode() && frame->document()->focusedNode()->hasTagName(HTMLNames::selectTag))
195             return false;
196
197         // Check if this change does not represent a focus change and input is active and if so ensure the keyboard is visible.
198         if (m_webPagePrivate->m_inputHandler->isInputMode() && fromRange && toRange && (fromRange->startContainer() == toRange->startContainer()))
199             m_webPagePrivate->m_inputHandler->notifyClientOfKeyboardVisibilityChange(true);
200     }
201
202     return true;
203 }
204
205 bool EditorClientBlackBerry::shouldApplyStyle(StylePropertySet*, Range*)
206 {
207     notImplemented();
208     return true;
209 }
210
211 bool EditorClientBlackBerry::shouldMoveRangeAfterDelete(Range*, Range*)
212 {
213     notImplemented();
214     return true;
215 }
216
217 void EditorClientBlackBerry::didBeginEditing()
218 {
219     if (m_webPagePrivate->m_dumpRenderTree)
220         m_webPagePrivate->m_dumpRenderTree->didBeginEditing();
221 }
222
223 void EditorClientBlackBerry::respondToChangedContents()
224 {
225     if (m_webPagePrivate->m_dumpRenderTree)
226         m_webPagePrivate->m_dumpRenderTree->didChange();
227 }
228
229 void EditorClientBlackBerry::respondToChangedSelection(Frame* frame)
230 {
231     if (m_waitingForCursorFocus)
232         m_waitingForCursorFocus = false;
233     else
234         m_webPagePrivate->selectionChanged(frame);
235
236     if (m_webPagePrivate->m_dumpRenderTree)
237         m_webPagePrivate->m_dumpRenderTree->didChangeSelection();
238 }
239
240 void EditorClientBlackBerry::didEndEditing()
241 {
242     if (m_webPagePrivate->m_dumpRenderTree)
243         m_webPagePrivate->m_dumpRenderTree->didEndEditing();
244 }
245
246 void EditorClientBlackBerry::respondToSelectionAppearanceChange()
247 {
248     m_webPagePrivate->m_selectionHandler->selectionPositionChanged();
249 }
250
251 void EditorClientBlackBerry::didWriteSelectionToPasteboard()
252 {
253     notImplemented();
254 }
255
256 void EditorClientBlackBerry::didSetSelectionTypesForPasteboard()
257 {
258     notImplemented();
259 }
260
261 void EditorClientBlackBerry::registerUndoStep(PassRefPtr<UndoStep> step)
262 {
263     // Remove the oldest item if we've reached the maximum capacity for the stack.
264     if (m_undoStack.size() == maximumUndoStackDepth)
265         m_undoStack.removeFirst();
266
267     if (!m_inRedo)
268         m_redoStack.clear();
269
270     m_undoStack.append(step);
271 }
272
273 void EditorClientBlackBerry::registerRedoStep(PassRefPtr<UndoStep> step)
274 {
275     m_redoStack.append(step);
276 }
277
278 void EditorClientBlackBerry::clearUndoRedoOperations()
279 {
280     m_undoStack.clear();
281     m_redoStack.clear();
282 }
283
284 bool EditorClientBlackBerry::canUndo() const
285 {
286     return !m_undoStack.isEmpty();
287 }
288
289 bool EditorClientBlackBerry::canRedo() const
290 {
291     return !m_redoStack.isEmpty();
292 }
293
294 bool EditorClientBlackBerry::canCopyCut(Frame*, bool defaultValue) const
295 {
296     return defaultValue;
297 }
298
299 bool EditorClientBlackBerry::canPaste(Frame*, bool defaultValue) const
300 {
301     return defaultValue;
302 }
303
304 void EditorClientBlackBerry::undo()
305 {
306     if (canUndo()) {
307         EditCommandStack::iterator back = --m_undoStack.end();
308         RefPtr<UndoStep> command(*back);
309         m_undoStack.remove(back);
310
311         // Unapply will call us back to push this command onto the redo stack.
312         command->unapply();
313     }
314 }
315
316 void EditorClientBlackBerry::redo()
317 {
318     if (canRedo()) {
319         EditCommandStack::iterator back = --m_redoStack.end();
320         RefPtr<UndoStep> command(*back);
321         m_redoStack.remove(back);
322
323         ASSERT(!m_inRedo);
324         m_inRedo = true;
325
326         // Reapply will call us back to push this command onto the undo stack.
327         command->reapply();
328         m_inRedo = false;
329     }
330 }
331
332 static const unsigned CtrlKey = 1 << 0;
333 static const unsigned AltKey = 1 << 1;
334 static const unsigned ShiftKey = 1 << 2;
335
336 struct KeyDownEntry {
337     unsigned virtualKey;
338     unsigned modifiers;
339     const char* name;
340 };
341
342 struct KeyPressEntry {
343     unsigned charCode;
344     unsigned modifiers;
345     const char* name;
346 };
347
348 static const KeyDownEntry keyDownEntries[] = {
349     { VK_LEFT,   0,                  "MoveLeft"                                    },
350     { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"                  },
351     { VK_LEFT,   CtrlKey,            "MoveWordLeft"                                },
352     { VK_LEFT,   CtrlKey | ShiftKey, "MoveWordLeftAndModifySelection"              },
353     { VK_RIGHT,  0,                  "MoveRight"                                   },
354     { VK_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"                 },
355     { VK_RIGHT,  CtrlKey,            "MoveWordRight"                               },
356     { VK_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"             },
357     { VK_UP,     0,                  "MoveUp"                                      },
358     { VK_UP,     ShiftKey,           "MoveUpAndModifySelection"                    },
359     { VK_DOWN,   0,                  "MoveDown"                                    },
360     { VK_DOWN,   ShiftKey,           "MoveDownAndModifySelection"                  },
361     { VK_PRIOR,  0,                  "MovePageUp"                                  },
362     { VK_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"                },
363     { VK_NEXT,   0,                  "MovePageDown"                                },
364     { VK_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"              },
365     { VK_HOME,   0,                  "MoveToBeginningOfLine"                       },
366     { VK_HOME,   ShiftKey,           "MoveToBeginningOfLineAndModifySelection"     },
367     { VK_HOME,   CtrlKey,            "MoveToBeginningOfDocument"                   },
368     { VK_HOME,   CtrlKey | ShiftKey, "MoveToBeginningOfDocumentAndModifySelection" },
369     { VK_END,    0,                  "MoveToEndOfLine"                             },
370     { VK_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"           },
371     { VK_END,    CtrlKey,            "MoveToEndOfDocument"                         },
372     { VK_END,    CtrlKey | ShiftKey, "MoveToEndOfDocumentAndModifySelection"       },
373
374     { 'B',       CtrlKey,            "ToggleBold"                                  },
375     { 'I',       CtrlKey,            "ToggleItalic"                                },
376     { 'U',       CtrlKey,            "ToggleUnderline"                             },
377
378     { VK_BACK,   0,                  "DeleteBackward"                              },
379     { VK_BACK,   ShiftKey,           "DeleteBackward"                              },
380     { VK_DELETE, 0,                  "DeleteForward"                               },
381     { VK_BACK,   CtrlKey,            "DeleteWordBackward"                          },
382     { VK_DELETE, CtrlKey,            "DeleteWordForward"                           },
383
384     { 'C',       CtrlKey,            "Copy"                                        },
385     { 'V',       CtrlKey,            "Paste"                                       },
386     { 'V',       CtrlKey | ShiftKey, "PasteAndMatchStyle"                          },
387     { 'X',       CtrlKey,            "Cut"                                         },
388     { VK_INSERT, CtrlKey,            "Copy"                                        },
389     { VK_DELETE, ShiftKey,           "Cut"                                         },
390     { VK_INSERT, ShiftKey,           "Paste"                                       },
391
392     { 'A',       CtrlKey,            "SelectAll"                                   },
393     { 'Z',       CtrlKey,            "Undo"                                        },
394     { 'Z',       CtrlKey | ShiftKey, "Redo"                                        },
395     { 'Y',       CtrlKey,            "Redo"                                        },
396
397     { VK_TAB,    0,                  "InsertTab"                                   },
398     { VK_TAB,    ShiftKey,           "InsertBacktab"                               },
399     { VK_RETURN, 0,                  "InsertNewline"                               },
400     { VK_RETURN, CtrlKey,            "InsertNewline"                               },
401     { VK_RETURN, AltKey,             "InsertNewline"                               },
402     { VK_RETURN, ShiftKey,           "InsertLineBreak"                             },
403     { VK_RETURN, AltKey | ShiftKey,  "InsertNewline"                               },
404
405 };
406
407 static const KeyPressEntry keyPressEntries[] = {
408     { '\t',   0,                  "InsertTab"                                   },
409     { '\t',   ShiftKey,           "InsertBacktab"                               },
410     { '\r',   0,                  "InsertNewline"                               },
411     { '\r',   CtrlKey,            "InsertNewline"                               },
412     { '\r',   AltKey,             "InsertNewline"                               },
413     { '\r',   ShiftKey,           "InsertLineBreak"                             },
414     { '\r',   AltKey | ShiftKey,  "InsertNewline"                               },
415 };
416
417
418 const char* EditorClientBlackBerry::interpretKeyEvent(const KeyboardEvent* event)
419 {
420     ASSERT(event->type() == eventNames().keydownEvent || event->type() == eventNames().keypressEvent);
421
422     static HashMap<int, const char*>* keyDownCommandsMap = 0;
423     static HashMap<int, const char*>* keyPressCommandsMap = 0;
424
425     if (!keyDownCommandsMap) {
426         keyDownCommandsMap = new HashMap<int, const char*>;
427         keyPressCommandsMap = new HashMap<int, const char*>;
428
429         for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyDownEntries); ++i)
430             keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
431
432         for (size_t i = 0; i < WTF_ARRAY_LENGTH(keyPressEntries); ++i)
433             keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
434     }
435
436     unsigned modifiers = 0;
437     if (event->shiftKey())
438         modifiers |= ShiftKey;
439     if (event->altKey())
440         modifiers |= AltKey;
441     if (event->ctrlKey())
442         modifiers |= CtrlKey;
443
444     if (event->type() == eventNames().keydownEvent) {
445         int mapKey = modifiers << 16 | event->keyCode();
446         return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
447     }
448
449     int mapKey = modifiers << 16 | event->charCode();
450     return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
451 }
452
453 void EditorClientBlackBerry::handleKeyboardEvent(KeyboardEvent* event)
454 {
455     ASSERT(event);
456
457     const PlatformKeyboardEvent* platformEvent = event->keyEvent();
458     if (!platformEvent)
459         return;
460
461     ASSERT(event->target()->toNode());
462     Frame* frame = event->target()->toNode()->document()->frame();
463     ASSERT(frame);
464
465     String commandName = interpretKeyEvent(event);
466
467     if (!commandName.isEmpty()) {
468         // Hot key handling. Cancel processing mode.
469         m_webPagePrivate->m_inputHandler->setProcessingChange(false);
470         if (frame->editor()->command(commandName).execute())
471             event->setDefaultHandled();
472         return;
473     }
474
475     if (!frame->editor()->canEdit())
476         return;
477
478     // Text insertion commands should only be triggered from keypressEvent.
479     // There is an assert guaranteeing this in
480     // EventHandler::handleTextInputEvent. Note that windowsVirtualKeyCode
481     // is not set for keypressEvent: special keys should have been already
482     // handled in keydownEvent, which is called first.
483     if (event->type() != eventNames().keypressEvent)
484         return;
485
486     // Don't insert null or control characters as they can result in unexpected behaviour.
487     if (event->charCode() < ' ')
488         return;
489
490     // Don't insert anything if a modifier is pressed.
491     if (event->ctrlKey() || event->altKey())
492         return;
493
494     if (!platformEvent->text().isEmpty()) {
495         if (frame->editor()->insertText(platformEvent->text(), event))
496             event->setDefaultHandled();
497     }
498 }
499
500 void EditorClientBlackBerry::handleInputMethodKeydown(KeyboardEvent*)
501 {
502     notImplemented();
503 }
504
505 void EditorClientBlackBerry::textFieldDidBeginEditing(Element*)
506 {
507     notImplemented();
508 }
509
510 void EditorClientBlackBerry::textFieldDidEndEditing(Element*)
511 {
512     notImplemented();
513 }
514
515 void EditorClientBlackBerry::textDidChangeInTextField(Element* element)
516 {
517     if (HTMLInputElement* inputElement = element->toInputElement())
518         m_webPagePrivate->m_autofillManager->didChangeInTextField(inputElement);
519 }
520
521 bool EditorClientBlackBerry::doTextFieldCommandFromEvent(Element*, KeyboardEvent*)
522 {
523     notImplemented();
524     return false;
525 }
526
527 void EditorClientBlackBerry::textWillBeDeletedInTextField(Element*)
528 {
529     notImplemented();
530 }
531
532 void EditorClientBlackBerry::textDidChangeInTextArea(Element*)
533 {
534     notImplemented();
535 }
536
537 bool EditorClientBlackBerry::shouldEraseMarkersAfterChangeSelection(TextCheckingType) const
538 {
539     return true;
540 }
541
542 void EditorClientBlackBerry::ignoreWordInSpellDocument(const WTF::String&)
543 {
544     notImplemented();
545 }
546
547 void EditorClientBlackBerry::learnWord(const WTF::String&)
548 {
549     notImplemented();
550 }
551
552 void EditorClientBlackBerry::checkSpellingOfString(const UChar* text, int textLength, int* misspellLocation, int* misspellLength)
553 {
554     m_webPagePrivate->m_client->checkSpellingOfString(text, textLength, *misspellLocation, *misspellLength);
555 }
556
557 WTF::String EditorClientBlackBerry::getAutoCorrectSuggestionForMisspelledWord(const WTF::String& misspelledWord)
558 {
559     notImplemented();
560     return WTF::String();
561 }
562
563 void EditorClientBlackBerry::checkGrammarOfString(const UChar*, int, WTF::Vector<GrammarDetail, 0u>&, int*, int*)
564 {
565     notImplemented();
566 }
567
568 void EditorClientBlackBerry::requestCheckingOfString(SpellChecker*, const TextCheckingRequest&)
569 {
570     notImplemented();
571 }
572
573 TextCheckerClient* EditorClientBlackBerry::textChecker()
574 {
575     return this;
576 }
577
578 void EditorClientBlackBerry::updateSpellingUIWithGrammarString(const WTF::String&, const GrammarDetail&)
579 {
580     notImplemented();
581 }
582
583 void EditorClientBlackBerry::updateSpellingUIWithMisspelledWord(const WTF::String&)
584 {
585     notImplemented();
586 }
587
588 void EditorClientBlackBerry::showSpellingUI(bool)
589 {
590     notImplemented();
591 }
592
593 bool EditorClientBlackBerry::spellingUIIsShowing()
594 {
595     notImplemented();
596     return false;
597 }
598
599 void EditorClientBlackBerry::getGuessesForWord(const WTF::String&, WTF::Vector<WTF::String, 0u>&)
600 {
601     notImplemented();
602 }
603
604 void EditorClientBlackBerry::getGuessesForWord(const String&, const String&, Vector<String>&)
605 {
606     notImplemented();
607 }
608
609 void EditorClientBlackBerry::willSetInputMethodState()
610 {
611     notImplemented();
612 }
613
614 void EditorClientBlackBerry::setInputMethodState(bool)
615 {
616     m_webPagePrivate->m_inputHandler->focusedNodeChanged();
617 }
618
619 } // namespace WebCore