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