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