69989f81371fe75079301042dd2571b92acaaec8
[WebKit-https.git] / Source / WebKit / blackberry / WebKitSupport / InputHandler.cpp
1 /*
2  * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "InputHandler.h"
21
22 #include "BackingStore.h"
23 #include "BackingStoreClient.h"
24 #include "CSSStyleDeclaration.h"
25 #include "Chrome.h"
26 #include "ColorPickerClient.h"
27 #include "DOMSupport.h"
28 #include "DatePickerClient.h"
29 #include "Document.h"
30 #include "DocumentLoader.h"
31 #include "DocumentMarkerController.h"
32 #include "EditorClientBlackBerry.h"
33 #include "FocusController.h"
34 #include "Frame.h"
35 #include "FrameView.h"
36 #include "HTMLFormElement.h"
37 #include "HTMLInputElement.h"
38 #include "HTMLNames.h"
39 #include "HTMLOptGroupElement.h"
40 #include "HTMLOptionElement.h"
41 #include "HTMLSelectElement.h"
42 #include "HTMLTextAreaElement.h"
43 #include "NotImplemented.h"
44 #include "Page.h"
45 #include "PlatformKeyboardEvent.h"
46 #include "PluginView.h"
47 #include "Range.h"
48 #include "RenderLayer.h"
49 #include "RenderMenuList.h"
50 #include "RenderPart.h"
51 #include "RenderText.h"
52 #include "RenderTextControl.h"
53 #include "RenderWidget.h"
54 #include "RenderedDocumentMarker.h"
55 #include "ScopePointer.h"
56 #include "SelectPopupClient.h"
57 #include "SelectionHandler.h"
58 #include "SpellChecker.h"
59 #include "SpellingHandler.h"
60 #include "SuggestionBoxHandler.h"
61 #include "TextCheckerClient.h"
62 #include "TextIterator.h"
63 #include "VisiblePosition.h"
64 #include "VisibleUnits.h"
65 #include "WebPageClient.h"
66 #include "WebPage_p.h"
67 #include "WebSettings.h"
68 #include "htmlediting.h"
69
70 #include <BlackBerryPlatformDeviceInfo.h>
71 #include <BlackBerryPlatformIMF.h>
72 #include <BlackBerryPlatformKeyboardEvent.h>
73 #include <BlackBerryPlatformLog.h>
74 #include <BlackBerryPlatformScreen.h>
75 #include <BlackBerryPlatformSettings.h>
76 #include <sys/keycodes.h>
77 #include <wtf/text/CString.h>
78
79 #define ENABLE_INPUT_LOG 0
80 #define ENABLE_FOCUS_LOG 0
81 #define ENABLE_SPELLING_LOG 0
82
83 using namespace BlackBerry::Platform;
84 using namespace WebCore;
85
86 #if ENABLE_INPUT_LOG
87 #define InputLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
88 #else
89 #define InputLog(severity, format, ...)
90 #endif // ENABLE_INPUT_LOG
91
92 #if ENABLE_FOCUS_LOG
93 #define FocusLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
94 #else
95 #define FocusLog(severity, format, ...)
96 #endif // ENABLE_FOCUS_LOG
97
98 #if ENABLE_SPELLING_LOG
99 #define SpellingLog(severity, format, ...) Platform::logAlways(severity, format, ## __VA_ARGS__)
100 #else
101 #define SpellingLog(severity, format, ...)
102 #endif // ENABLE_SPELLING_LOG
103
104 namespace BlackBerry {
105 namespace WebKit {
106
107 static const float zoomAnimationThreshold = 0.5;
108
109 class ProcessingChangeGuard {
110 public:
111     ProcessingChangeGuard(InputHandler* inputHandler)
112         : m_inputHandler(inputHandler)
113         , m_savedProcessingChange(false)
114     {
115         ASSERT(m_inputHandler);
116
117         m_savedProcessingChange = m_inputHandler->processingChange();
118         m_inputHandler->setProcessingChange(true);
119     }
120
121     ~ProcessingChangeGuard()
122     {
123         m_inputHandler->setProcessingChange(m_savedProcessingChange);
124     }
125
126 private:
127     InputHandler* m_inputHandler;
128     bool m_savedProcessingChange;
129 };
130
131 InputHandler::InputHandler(WebPagePrivate* page)
132     : m_webPage(page)
133     , m_currentFocusElement(0)
134     , m_previousFocusableTextElement(0)
135     , m_nextFocusableTextElement(0)
136     , m_hasSubmitButton(false)
137     , m_inputModeEnabled(false)
138     , m_processingChange(false)
139     , m_shouldEnsureFocusTextElementVisibleOnSelectionChanged(false)
140     , m_currentFocusElementType(TextEdit)
141     , m_currentFocusElementTextEditMask(DEFAULT_STYLE)
142     , m_composingTextStart(0)
143     , m_composingTextEnd(0)
144     , m_pendingKeyboardVisibilityChange(NoChange)
145     , m_delayKeyboardVisibilityChange(false)
146     , m_sendFormStateOnNextKeyboardRequest(false)
147     , m_request(0)
148     , m_processingTransactionId(-1)
149     , m_shouldNotifyWebView(true)
150     , m_expectedKeyUpChar(0)
151     , m_didSpellCheckWord(false)
152     , m_spellingHandler(new SpellingHandler(this))
153     , m_spellCheckStatusConfirmed(false)
154     , m_globalSpellCheckStatus(false)
155 {
156 }
157
158 InputHandler::~InputHandler()
159 {
160     delete m_spellingHandler;
161 }
162
163 static BlackBerryInputType convertInputType(const HTMLInputElement* inputElement)
164 {
165     if (inputElement->isPasswordField())
166         return InputTypePassword;
167     if (inputElement->isSearchField())
168         return InputTypeSearch;
169     if (inputElement->isEmailField())
170         return InputTypeEmail;
171     if (inputElement->isMonthControl())
172         return InputTypeMonth;
173     if (inputElement->isNumberField())
174         return InputTypeNumber;
175     if (inputElement->isTelephoneField())
176         return InputTypeTelephone;
177     if (inputElement->isURLField())
178         return InputTypeURL;
179 #if ENABLE(INPUT_TYPE_COLOR)
180     if (inputElement->isColorControl())
181         return InputTypeColor;
182 #endif
183     if (inputElement->isDateControl())
184         return InputTypeDate;
185     if (inputElement->isDateTimeControl())
186         return InputTypeDateTime;
187     if (inputElement->isDateTimeLocalControl())
188         return InputTypeDateTimeLocal;
189     if (inputElement->isTimeControl())
190         return InputTypeTime;
191     // FIXME: missing WEEK popup selector
192     if (DOMSupport::elementIdOrNameIndicatesEmail(inputElement))
193         return InputTypeEmail;
194     if (DOMSupport::elementIdOrNameIndicatesUrl(inputElement))
195         return InputTypeURL;
196     if (DOMSupport::elementPatternIndicatesNumber(inputElement))
197         return InputTypeNumber;
198     if (DOMSupport::elementPatternIndicatesHexadecimal(inputElement))
199         return InputTypeHexadecimal;
200
201     return InputTypeText;
202 }
203
204 static int64_t inputStyle(BlackBerryInputType type, const Element* element)
205 {
206     switch (type) {
207     case InputTypeEmail:
208     case InputTypeURL:
209     case InputTypeSearch:
210     case InputTypeText:
211     case InputTypeTextArea:
212         {
213             DOMSupport::AttributeState autoCompleteState = DOMSupport::elementSupportsAutocomplete(element);
214             DOMSupport::AttributeState autoCorrectState = DOMSupport::elementSupportsAutocorrect(element);
215
216             // Autocomplete disabled explicitly.
217             if (autoCompleteState == DOMSupport::Off) {
218                 if (autoCorrectState == DOMSupport::On)
219                     return NO_PREDICTION;
220                 return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
221             }
222
223             // Autocomplete enabled explicitly.
224             if (autoCompleteState == DOMSupport::On) {
225                 if (autoCorrectState == DOMSupport::Off)
226                     return NO_AUTO_TEXT | NO_AUTO_CORRECTION;
227                 return DEFAULT_STYLE;
228             }
229
230             // Check for reserved strings and known types.
231             if (type == InputTypeEmail || type == InputTypeURL || DOMSupport::elementIdOrNameIndicatesNoAutocomplete(element)) {
232                 if (autoCorrectState == DOMSupport::On)
233                     return NO_PREDICTION;
234                 return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
235             }
236
237             // Autocomplete state wasn't provided if it is a text area or autocorrect is on, apply support.
238             if (autoCorrectState == DOMSupport::On || (type == InputTypeTextArea && autoCorrectState != DOMSupport::Off))
239                 return DEFAULT_STYLE;
240
241             // Single line text input, without special features explicitly turned on or a textarea
242             // with autocorrect disabled explicitly. Only enable predictions.
243             return NO_AUTO_TEXT | NO_AUTO_CORRECTION;
244         }
245     case InputTypePassword:
246     case InputTypeNumber:
247     case InputTypeTelephone:
248     case InputTypeHexadecimal:
249         // Disable special handling.
250         return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
251     default:
252         break;
253     }
254     return DEFAULT_STYLE;
255 }
256
257 static VirtualKeyboardType convertInputTypeToVKBType(BlackBerryInputType inputType)
258 {
259     switch (inputType) {
260     case InputTypeURL:
261         return VKBTypeUrl;
262     case InputTypeEmail:
263         return VKBTypeEmail;
264     case InputTypeTelephone:
265         return VKBTypePhone;
266     case InputTypePassword:
267         return VKBTypePassword;
268     case InputTypeNumber:
269     case InputTypeHexadecimal:
270         return VKBTypePin;
271     default:
272         // All other types are text based use default keyboard.
273         return VKBTypeDefault;
274     }
275 }
276
277 static VirtualKeyboardType convertStringToKeyboardType(const AtomicString& string)
278 {
279     DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
280     DEFINE_STATIC_LOCAL(AtomicString, Url, ("url"));
281     DEFINE_STATIC_LOCAL(AtomicString, Email, ("email"));
282     DEFINE_STATIC_LOCAL(AtomicString, Password, ("password"));
283     DEFINE_STATIC_LOCAL(AtomicString, Web, ("web"));
284     DEFINE_STATIC_LOCAL(AtomicString, Number, ("number"));
285     DEFINE_STATIC_LOCAL(AtomicString, Symbol, ("symbol"));
286     DEFINE_STATIC_LOCAL(AtomicString, Phone, ("phone"));
287     DEFINE_STATIC_LOCAL(AtomicString, Pin, ("pin"));
288     DEFINE_STATIC_LOCAL(AtomicString, Hex, ("hexadecimal"));
289
290     if (string.isEmpty())
291         return VKBTypeNotSet;
292     if (equalIgnoringCase(string, Default))
293         return VKBTypeDefault;
294     if (equalIgnoringCase(string, Url))
295         return VKBTypeUrl;
296     if (equalIgnoringCase(string, Email))
297         return VKBTypeEmail;
298     if (equalIgnoringCase(string, Password))
299         return VKBTypePassword;
300     if (equalIgnoringCase(string, Web))
301         return VKBTypeWeb;
302     if (equalIgnoringCase(string, Number))
303         return VKBTypeNumPunc;
304     if (equalIgnoringCase(string, Symbol))
305         return VKBTypeSymbol;
306     if (equalIgnoringCase(string, Phone))
307         return VKBTypePhone;
308     if (equalIgnoringCase(string, Pin) || equalIgnoringCase(string, Hex))
309         return VKBTypePin;
310     return VKBTypeNotSet;
311 }
312
313 static VirtualKeyboardType keyboardTypeAttribute(const WebCore::Element* element)
314 {
315     DEFINE_STATIC_LOCAL(QualifiedName, keyboardTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-type", nullAtom));
316
317     if (element->fastHasAttribute(keyboardTypeAttr)) {
318         AtomicString attributeString = element->fastGetAttribute(keyboardTypeAttr);
319         return convertStringToKeyboardType(attributeString);
320     }
321
322     if (element->isFormControlElement()) {
323         const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
324         if (formElement->form() && formElement->form()->fastHasAttribute(keyboardTypeAttr)) {
325             AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardTypeAttr);
326             return convertStringToKeyboardType(attributeString);
327         }
328     }
329
330     return VKBTypeNotSet;
331 }
332
333 static VirtualKeyboardEnterKeyType convertStringToKeyboardEnterKeyType(const AtomicString& string)
334 {
335     DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
336     DEFINE_STATIC_LOCAL(AtomicString, Connect, ("connect"));
337     DEFINE_STATIC_LOCAL(AtomicString, Done, ("done"));
338     DEFINE_STATIC_LOCAL(AtomicString, Go, ("go"));
339     DEFINE_STATIC_LOCAL(AtomicString, Join, ("join"));
340     DEFINE_STATIC_LOCAL(AtomicString, Next, ("next"));
341     DEFINE_STATIC_LOCAL(AtomicString, Search, ("search"));
342     DEFINE_STATIC_LOCAL(AtomicString, Send, ("send"));
343     DEFINE_STATIC_LOCAL(AtomicString, Submit, ("submit"));
344
345     if (string.isEmpty())
346         return VKBEnterKeyNotSet;
347     if (equalIgnoringCase(string, Default))
348         return VKBEnterKeyDefault;
349     if (equalIgnoringCase(string, Connect))
350         return VKBEnterKeyConnect;
351     if (equalIgnoringCase(string, Done))
352         return VKBEnterKeyDone;
353     if (equalIgnoringCase(string, Go))
354         return VKBEnterKeyGo;
355     if (equalIgnoringCase(string, Join))
356         return VKBEnterKeyJoin;
357     if (equalIgnoringCase(string, Next))
358         return VKBEnterKeyNext;
359     if (equalIgnoringCase(string, Search))
360         return VKBEnterKeySearch;
361     if (equalIgnoringCase(string, Send))
362         return VKBEnterKeySend;
363     if (equalIgnoringCase(string, Submit))
364         return VKBEnterKeySubmit;
365     return VKBEnterKeyNotSet;
366 }
367
368 static VirtualKeyboardEnterKeyType keyboardEnterKeyTypeAttribute(const WebCore::Element* element)
369 {
370     DEFINE_STATIC_LOCAL(QualifiedName, keyboardEnterKeyTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-enter-key", nullAtom));
371
372     if (element->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
373         AtomicString attributeString = element->fastGetAttribute(keyboardEnterKeyTypeAttr);
374         return convertStringToKeyboardEnterKeyType(attributeString);
375     }
376
377     if (element->isFormControlElement()) {
378         const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
379         if (formElement->form() && formElement->form()->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
380             AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardEnterKeyTypeAttr);
381             return convertStringToKeyboardEnterKeyType(attributeString);
382         }
383     }
384
385     return VKBEnterKeyNotSet;
386 }
387
388 void InputHandler::setProcessingChange(bool processingChange)
389 {
390     if (processingChange == m_processingChange)
391         return;
392
393     m_processingChange = processingChange;
394
395     if (!m_processingChange)
396         m_webPage->m_selectionHandler->inputHandlerDidFinishProcessingChange();
397 }
398
399 WTF::String InputHandler::elementText()
400 {
401     if (!isActiveTextEdit())
402         return WTF::String();
403
404     return DOMSupport::inputElementText(m_currentFocusElement.get());
405 }
406
407 BlackBerryInputType InputHandler::elementType(Element* element) const
408 {
409     if (const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element->toInputElement()))
410         return convertInputType(inputElement);
411
412     if (element->hasTagName(HTMLNames::textareaTag))
413         return InputTypeTextArea;
414
415     // Default to InputTypeTextArea for content editable fields.
416     return InputTypeTextArea;
417 }
418
419 void InputHandler::focusedNodeChanged()
420 {
421     ASSERT(m_webPage->m_page->focusController());
422     Frame* frame = m_webPage->m_page->focusController()->focusedOrMainFrame();
423     if (!frame || !frame->document())
424         return;
425
426     Node* node = frame->document()->focusedNode();
427
428     if (isActiveTextEdit() && m_currentFocusElement == node) {
429         notifyClientOfKeyboardVisibilityChange(true);
430         return;
431     }
432
433     if (node && node->isElementNode()) {
434         Element* element = toElement(node);
435         if (DOMSupport::isElementTypePlugin(element)) {
436             setPluginFocused(element);
437             return;
438         }
439
440         if (DOMSupport::isTextBasedContentEditableElement(element)) {
441             // Focused node is a text based input field, textarea or content editable field.
442             setElementFocused(element);
443             return;
444         }
445     } else if (node && DOMSupport::isTextBasedContentEditableElement(node->parentElement())) {
446         setElementFocused(node->parentElement());
447         return;
448     }
449
450     if (isActiveTextEdit() && m_currentFocusElement->isContentEditable()) {
451         // This is a special handler for content editable fields. The focus node is the top most
452         // field that is content editable, however, by enabling / disabling designmode and
453         // content editable, it is possible through javascript or selection to trigger the focus node to
454         // change even while maintaining editing on the same selection point. If the focus element
455         // isn't contentEditable, but the current selection is, don't send a change notification.
456
457         // When processing changes selection changes occur that may reset the focus element
458         // and could potentially cause a crash as m_currentFocusElement should not be
459         // changed during processing of an EditorCommand.
460         if (processingChange())
461             return;
462
463         Frame* frame = m_currentFocusElement->document()->frame();
464         ASSERT(frame);
465
466         // Test the current selection to make sure that the content in focus is still content
467         // editable. This may mean Javascript triggered a focus change by modifying the
468         // top level parent of this object's content editable state without actually modifying
469         // this particular object.
470         // Example site: html5demos.com/contentEditable - blur event triggers focus change.
471         if (frame == m_webPage->focusedOrMainFrame() && frame->selection()->start().anchorNode()
472             && frame->selection()->start().anchorNode()->isContentEditable())
473                 return;
474     }
475
476     // No valid focus element found for handling.
477     setElementUnfocused();
478 }
479
480 void InputHandler::setPluginFocused(Element* element)
481 {
482     ASSERT(DOMSupport::isElementTypePlugin(element));
483
484     if (isActiveTextEdit())
485         setElementUnfocused();
486
487     m_currentFocusElementType = Plugin;
488     m_currentFocusElement = element;
489 }
490
491 static bool convertStringToWchar(const WTF::String& string, wchar_t* dest, int destCapacity, int* destLength)
492 {
493     ASSERT(dest);
494
495     int length = string.length();
496
497     if (!length) {
498         destLength = 0;
499         return true;
500     }
501
502     UErrorCode ec = U_ZERO_ERROR;
503
504     // wchar_t strings sent to IMF are 32 bit so casting to UChar32 is safe.
505     u_strToUTF32(reinterpret_cast<UChar32*>(dest), destCapacity, destLength, string.characters(), length, &ec);
506     if (ec) {
507         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::convertStringToWchar Error converting string ec (%d).", ec);
508         destLength = 0;
509         return false;
510     }
511     return true;
512 }
513
514 static bool convertStringToWcharVector(const WTF::String& string, WTF::Vector<wchar_t>& wcharString)
515 {
516     ASSERT(wcharString.isEmpty());
517
518     int length = string.length();
519     if (!length)
520         return true;
521
522     if (!wcharString.tryReserveCapacity(length + 1)) {
523         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::convertStringToWcharVector Cannot allocate memory for string.");
524         return false;
525     }
526
527     int destLength = 0;
528     if (!convertStringToWchar(string, wcharString.data(), length + 1, &destLength))
529         return false;
530
531     wcharString.resize(destLength);
532     return true;
533 }
534
535 static WTF::String convertSpannableStringToString(spannable_string_t* src)
536 {
537     if (!src || !src->str || !src->length)
538         return WTF::String();
539
540     WTF::Vector<UChar> dest;
541     int destCapacity = (src->length * 2) + 1;
542     if (!dest.tryReserveCapacity(destCapacity)) {
543         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::convertSpannableStringToString Cannot allocate memory for string.");
544         return WTF::String();
545     }
546
547     int destLength = 0;
548     UErrorCode ec = U_ZERO_ERROR;
549     // wchar_t strings sent from IMF are 32 bit so casting to UChar32 is safe.
550     u_strFromUTF32(dest.data(), destCapacity, &destLength, reinterpret_cast<UChar32*>(src->str), src->length, &ec);
551     if (ec) {
552         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::convertSpannableStringToString Error converting string ec (%d).", ec);
553         return WTF::String();
554     }
555     dest.resize(destLength);
556     return WTF::String(dest.data(), destLength);
557 }
558
559 void InputHandler::sendLearnTextDetails(const WTF::String& string)
560 {
561     Vector<wchar_t> wcharString;
562     if (!convertStringToWcharVector(string, wcharString) || wcharString.isEmpty())
563         return;
564
565     m_webPage->m_client->inputLearnText(wcharString.data(), wcharString.size());
566 }
567
568 void InputHandler::learnText()
569 {
570     if (!isActiveTextEdit())
571         return;
572
573     // Do not send (or calculate) the text when the field is NO_PREDICTION or NO_AUTO_TEXT.
574     if (m_currentFocusElementTextEditMask & NO_PREDICTION || m_currentFocusElementTextEditMask & NO_AUTO_TEXT)
575         return;
576
577     WTF::String textInField(elementText());
578     if (textInField.length() >= MaxLearnTextDataSize)
579         textInField = textInField.substring(std::max(0, static_cast<int>(caretPosition() - MaxLearnTextDataSize)), std::min(textInField.length(), MaxLearnTextDataSize));
580
581     textInField = textInField.stripWhiteSpace();
582
583     // Build up the 500 character strings in word chunks.
584     // Spec says 1000, but memory corruption has been observed.
585     ASSERT(textInField.length() <= MaxLearnTextDataSize);
586
587     if (textInField.isEmpty())
588         return;
589
590     InputLog(Platform::LogLevelInfo, "InputHandler::learnText '%s'", textInField.latin1().data());
591     sendLearnTextDetails(textInField);
592 }
593
594 void InputHandler::callRequestCheckingFor(PassRefPtr<WebCore::SpellCheckRequest> spellCheckRequest)
595 {
596     if (SpellChecker* spellChecker = getSpellChecker())
597         spellChecker->requestCheckingFor(spellCheckRequest);
598 }
599
600 void InputHandler::requestCheckingOfString(PassRefPtr<WebCore::TextCheckingRequest> textCheckingRequest)
601 {
602     m_request = textCheckingRequest;
603
604     InputLog(Platform::LogLevelInfo, "InputHandler::requestCheckingOfString '%s'", m_request->data().text().latin1().data());
605
606     if (!m_request) {
607         SpellingLog(Platform::LogLevelWarn, "InputHandler::requestCheckingOfString did not receive a valid request.");
608         return;
609     }
610
611     unsigned requestLength = m_request->data().text().length();
612
613     // Check if the field should be spellchecked.
614     if (!isActiveTextEdit() || !shouldSpellCheckElement(m_currentFocusElement.get()) || requestLength < 2) {
615         m_request->didCancel();
616         return;
617     }
618
619     if (requestLength >= MaxSpellCheckingStringLength) {
620         // Batch requests which are generally created by us on focus, should not exceed this limit. Check that this is in fact of Incremental type.
621         ASSERT(textCheckingRequest->processType() == TextCheckingProcessIncremental);
622
623         // Cancel this request and send it off in newly created chunks.
624         m_request->didCancel();
625         if (m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->selection()) {
626             VisiblePosition caretPosition = m_currentFocusElement->document()->frame()->selection()->start();
627             // Convert from position back to selection so we can expand the range to include the previous line. This should handle cases when the user hits
628             // enter to finish composing a word and create a new line. Account for word wrapping by jumping to the start of the previous line, then moving
629             // to the start of any word which might be there.
630             VisibleSelection visibleSelection = VisibleSelection(
631                 startOfWord(startOfLine(previousLinePosition(caretPosition, caretPosition.lineDirectionPointForBlockDirectionNavigation()))),
632                 endOfWord(endOfLine(caretPosition)));
633             m_spellingHandler->spellCheckTextBlock(visibleSelection, TextCheckingProcessIncremental);
634         }
635         return;
636     }
637
638     wchar_t* checkingString = (wchar_t*)malloc(sizeof(wchar_t) * (requestLength + 1));
639     if (!checkingString) {
640         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::requestCheckingOfString Cannot allocate memory for string.");
641         m_request->didCancel();
642         return;
643     }
644
645     int paragraphLength = 0;
646     if (!convertStringToWchar(m_request->data().text(), checkingString, requestLength + 1, &paragraphLength)) {
647         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::requestCheckingOfString Failed to convert String to wchar type.");
648         free(checkingString);
649         m_request->didCancel();
650         return;
651     }
652
653     m_processingTransactionId = m_webPage->m_client->checkSpellingOfStringAsync(checkingString, static_cast<unsigned>(paragraphLength));
654     free(checkingString);
655
656     // If the call to the input service did not go through, then cancel the request so we don't block endlessly.
657     // This should still take transactionId as a parameter to maintain the same behavior as if InputMethodSupport
658     // were to cancel a request during processing.
659     if (m_processingTransactionId == -1) { // Error before sending request to input service.
660         m_request->didCancel();
661         return;
662     }
663 }
664
665 void InputHandler::spellCheckingRequestCancelled(int32_t transactionId)
666 {
667     SpellingLog(Platform::LogLevelWarn,
668         "InputHandler::spellCheckingRequestCancelled Expected transaction id %d, received %d. %s",
669         transactionId,
670         m_processingTransactionId,
671         transactionId == m_processingTransactionId ? "" : "We are out of sync with input service.");
672
673     m_request->didCancel();
674     m_processingTransactionId = -1;
675 }
676
677 void InputHandler::spellCheckingRequestProcessed(int32_t transactionId, spannable_string_t* spannableString)
678 {
679     SpellingLog(Platform::LogLevelWarn,
680         "InputHandler::spellCheckingRequestProcessed Expected transaction id %d, received %d. %s",
681         transactionId,
682         m_processingTransactionId,
683         transactionId == m_processingTransactionId ? "" : "We are out of sync with input service.");
684
685     if (!spannableString || !isActiveTextEdit() || !DOMSupport::elementHasContinuousSpellCheckingEnabled(m_currentFocusElement) || !m_processingTransactionId) {
686         SpellingLog(Platform::LogLevelWarn, "InputHandler::spellCheckingRequestProcessed Cancelling request with transactionId %d.", transactionId);
687         m_request->didCancel();
688         m_processingTransactionId = -1;
689         return;
690     }
691
692     Vector<TextCheckingResult> results;
693
694     // Convert the spannableString to TextCheckingResult then append to results vector.
695     WTF::String replacement;
696     TextCheckingResult textCheckingResult;
697     textCheckingResult.type = TextCheckingTypeSpelling;
698     textCheckingResult.replacement = replacement;
699     textCheckingResult.location = 0;
700     textCheckingResult.length = 0;
701
702     span_t* span = spannableString->spans;
703     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
704         if (!span)
705             break;
706         if (span->end < span->start) {
707             m_request->didCancel();
708             return;
709         }
710         if (span->attributes_mask & MISSPELLED_WORD_ATTRIB) {
711             textCheckingResult.location = span->start;
712             // The end point includes the character that it is before. Ie, 0, 0
713             // applies to the first character as the end point includes the character
714             // at the position. This means the endPosition is always +1.
715             textCheckingResult.length = span->end - span->start + 1;
716             results.append(textCheckingResult);
717         }
718         span++;
719     }
720
721     // free data that we malloc'ed in InputMethodSupport
722     free(spannableString->spans);
723     free(spannableString);
724
725     m_request->didSucceed(results);
726 }
727
728 SpellChecker* InputHandler::getSpellChecker()
729 {
730     if (!m_currentFocusElement || !m_currentFocusElement->document())
731         return 0;
732
733     if (Frame* frame = m_currentFocusElement->document()->frame())
734         if (Editor* editor = frame->editor())
735             return editor->spellChecker();
736
737     return 0;
738 }
739
740 bool InputHandler::shouldRequestSpellCheckingOptionsForPoint(const Platform::IntPoint& documentContentPosition, const Element* touchedElement, imf_sp_text_t& spellCheckingOptionRequest)
741 {
742     if (!isActiveTextEdit())
743         return false;
744
745     Element* currentFocusElement = m_currentFocusElement.get();
746     if (!currentFocusElement || !currentFocusElement->isElementNode())
747         return false;
748
749     while (!currentFocusElement->isRootEditableElement()) {
750         Element* parentElement = currentFocusElement->parentElement();
751         if (!parentElement)
752             break;
753         currentFocusElement = parentElement;
754     }
755
756     if (touchedElement != currentFocusElement)
757         return false;
758
759     LayoutPoint contentPos(documentContentPosition);
760     contentPos = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), m_webPage->focusedOrMainFrame(), roundedIntPoint(contentPos));
761
762     Document* document = currentFocusElement->document();
763     ASSERT(document);
764
765     RenderedDocumentMarker* marker = document->markers()->renderedMarkerContainingPoint(contentPos, DocumentMarker::Spelling);
766     if (!marker)
767         return false;
768
769     m_didSpellCheckWord = true;
770
771     // Populate the marker details in preparation for the request as the marker is
772     // not guaranteed to be valid after the cursor is placed.
773     spellCheckingOptionRequest.startTextPosition = marker->startOffset();
774     spellCheckingOptionRequest.endTextPosition = marker->endOffset();
775
776     m_spellCheckingOptionsRequest.startTextPosition = 0;
777     m_spellCheckingOptionsRequest.endTextPosition = 0;
778
779     SpellingLog(Platform::LogLevelInfo,
780         "InputHandler::shouldRequestSpellCheckingOptionsForPoint Found spelling marker at point %s\nMarker start %d end %d",
781         documentContentPosition.toString().c_str(),
782         spellCheckingOptionRequest.startTextPosition,
783         spellCheckingOptionRequest.endTextPosition);
784
785     return true;
786 }
787
788 void InputHandler::requestSpellingCheckingOptions(imf_sp_text_t& spellCheckingOptionRequest, WebCore::IntSize& screenOffset, const bool shouldMoveDialog)
789 {
790     // If the caret is no longer active, no message should be sent.
791     if (m_webPage->focusedOrMainFrame()->selection()->selectionType() != VisibleSelection::CaretSelection)
792         return;
793
794     if (!m_currentFocusElement || !m_currentFocusElement->document() || !m_currentFocusElement->document()->frame())
795         return;
796
797     if (shouldMoveDialog || !(spellCheckingOptionRequest.startTextPosition || spellCheckingOptionRequest.startTextPosition)) {
798         if (m_spellCheckingOptionsRequest.startTextPosition || m_spellCheckingOptionsRequest.endTextPosition)
799             spellCheckingOptionRequest = m_spellCheckingOptionsRequest;
800     }
801
802     if (!shouldMoveDialog && spellCheckingOptionRequest.startTextPosition == spellCheckingOptionRequest.endTextPosition)
803         return;
804
805     if (screenOffset.width() == -1 && screenOffset.height() == -1) {
806         screenOffset.setWidth(m_screenOffset.width());
807         screenOffset.setHeight(m_screenOffset.height());
808     } else {
809         m_screenOffset.setWidth(screenOffset.width());
810         m_screenOffset.setHeight(screenOffset.height());
811     }
812
813     // imf_sp_text_t should be generated in pixel viewport coordinates.
814     // Caret is in document coordinates.
815     WebCore::IntRect caretRect = m_webPage->focusedOrMainFrame()->selection()->selection().visibleStart().absoluteCaretBounds();
816
817     // Shift from posible iFrame to root view/main frame.
818     caretRect = m_webPage->focusedOrMainFrame()->view()->contentsToRootView(caretRect);
819
820     // Shift to document scroll position.
821     const WebCore::IntPoint scrollPosition = m_webPage->mainFrame()->view()->scrollPosition();
822     caretRect.move(scrollPosition.x(), scrollPosition.y());
823
824     // If we are only moving the dialog, we don't need to provide startTextPosition and endTextPosition so this logic can be skipped.
825     if (!shouldMoveDialog) {
826         // Calculate the offset for contentEditable since the marker offsets are relative to the node.
827         // Get caret position. Though the spelling markers might no longer exist, if this method is called we can assume the caret was placed on top of a marker earlier.
828         VisiblePosition caretPosition = m_currentFocusElement->document()->frame()->selection()->selection().visibleStart();
829
830         if (HTMLTextFormControlElement* controlElement = DOMSupport::toTextControlElement(m_currentFocusElement.get())) {
831             spellCheckingOptionRequest.startTextPosition = controlElement->indexForVisiblePosition(startOfWord(caretPosition));
832             spellCheckingOptionRequest.endTextPosition = controlElement->indexForVisiblePosition(endOfWord(caretPosition));
833         } else {
834             unsigned location = 0;
835             unsigned length = 0;
836
837             // Create a range from the start to end of word.
838             RefPtr<Range> rangeSelection = VisibleSelection(startOfWord(caretPosition), endOfWord(caretPosition)).toNormalizedRange();
839             if (!rangeSelection)
840                 return;
841
842             TextIterator::getLocationAndLengthFromRange(m_currentFocusElement.get(), rangeSelection.get(), location, length);
843             spellCheckingOptionRequest.startTextPosition = location;
844             spellCheckingOptionRequest.endTextPosition = location + length;
845         }
846     }
847     m_spellCheckingOptionsRequest = spellCheckingOptionRequest;
848
849     InputLog(Platform::LogLevelInfo,
850         "InputHandler::requestSpellingCheckingOptions caretRect topLeft=%s, bottomRight=%s, startTextPosition=%d, endTextPosition=%d",
851         Platform::IntPoint(caretRect.minXMinYCorner()).toString().c_str(),
852         Platform::IntPoint(caretRect.maxXMaxYCorner()).toString().c_str(),
853         spellCheckingOptionRequest.startTextPosition,
854         spellCheckingOptionRequest.endTextPosition);
855
856     m_webPage->m_client->requestSpellingCheckingOptions(spellCheckingOptionRequest, caretRect, screenOffset, shouldMoveDialog);
857 }
858
859 void InputHandler::setElementUnfocused(bool refocusOccuring)
860 {
861     if (isActiveTextEdit() && m_currentFocusElement->attached() && m_currentFocusElement->document()->attached()) {
862         FocusLog(Platform::LogLevelInfo, "InputHandler::setElementUnfocused");
863
864         // Pass any text into the field to IMF to learn.
865         learnText();
866
867         // End any composition that is in progress.
868         finishComposition();
869
870         // Only hide the keyboard if we aren't refocusing on a new input field.
871         if (!refocusOccuring) {
872             notifyClientOfKeyboardVisibilityChange(false, true /* triggeredByFocusChange */);
873             m_webPage->m_client->showFormControls(false /* visible */);
874         }
875
876         m_webPage->m_client->inputFocusLost();
877
878         // Hide the suggestion box if it is visible.
879         hideTextInputTypeSuggestionBox();
880
881         // Repaint the element absent of the caret.
882         if (m_currentFocusElement->renderer())
883             m_currentFocusElement->renderer()->repaint();
884
885         // If the frame selection isn't focused, focus it.
886         FrameSelection* frameSelection = m_currentFocusElement->document()->frame()->selection();
887         if (frameSelection && !frameSelection->isFocused())
888             frameSelection->setFocused(true);
889     }
890
891     m_spellingHandler->setSpellCheckActive(false);
892     m_processingTransactionId = 0;
893
894     // Clear the node details.
895     m_currentFocusElement = 0;
896     m_currentFocusElementType = TextEdit;
897     m_previousFocusableTextElement = 0;
898     m_nextFocusableTextElement = 0;
899     m_hasSubmitButton = false;
900 }
901
902 bool InputHandler::isInputModeEnabled() const
903 {
904     // Input mode is enabled when set, or when dump render tree or always show keyboard setting is enabled.
905     return m_inputModeEnabled || m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus();
906 }
907
908 void InputHandler::setInputModeEnabled(bool active)
909 {
910     FocusLog(Platform::LogLevelInfo,
911         "InputHandler::setInputModeEnabled '%s', override is '%s'",
912         active ? "true" : "false",
913         m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus() ? "true" : "false");
914
915     m_inputModeEnabled = active;
916
917     // If the frame selection isn't focused, focus it.
918     if (isInputModeEnabled() && isActiveTextEdit() && !m_currentFocusElement->document()->frame()->selection()->isFocused())
919         m_currentFocusElement->document()->frame()->selection()->setFocused(true);
920 }
921
922 void InputHandler::updateFormState()
923 {
924     m_previousFocusableTextElement = 0;
925     m_nextFocusableTextElement = 0;
926     m_hasSubmitButton = false;
927
928     if (!m_currentFocusElement || !m_currentFocusElement->isFormControlElement())
929         return;
930
931     HTMLFormElement* formElement = static_cast<HTMLFormControlElement*>(m_currentFocusElement.get())->form();
932     if (!formElement)
933         return;
934
935     const Vector<FormAssociatedElement*> formElementList = formElement->associatedElements();
936     int formElementCount = formElementList.size();
937     if (formElementCount < 2)
938         return;
939
940     InputLog(Platform::LogLevelInfo, "InputHandler::updateFormState form has %d fields", formElementCount);
941
942     m_hasSubmitButton = true;
943     for (int focusElementId = 0; focusElementId < formElementCount; focusElementId++) {
944         if (toHTMLElement(formElementList[focusElementId]) != m_currentFocusElement)
945             continue;
946
947         // Found the focused element, get the next and previous elements if they exist.
948
949         // Previous
950         for (int previousElementId = focusElementId - 1; previousElementId >= 0; previousElementId--) {
951             Element* element = const_cast<HTMLElement*>(toHTMLElement(formElementList[previousElementId]));
952             if (DOMSupport::isTextBasedContentEditableElement(element)) {
953                 m_previousFocusableTextElement = element;
954                 InputLog(Platform::LogLevelInfo, "InputHandler::updateFormState found previous element");
955                 break;
956             }
957         }
958         // Next
959         for (int nextElementId = focusElementId + 1; nextElementId < formElementCount; nextElementId++) {
960             Element* element = const_cast<HTMLElement*>(toHTMLElement(formElementList[nextElementId]));
961             if (DOMSupport::isTextBasedContentEditableElement(element)) {
962                 m_nextFocusableTextElement = element;
963                 InputLog(Platform::LogLevelInfo, "InputHandler::updateFormState found next element");
964                 break;
965             }
966         }
967     }
968
969     if (!m_nextFocusableTextElement && !m_previousFocusableTextElement) {
970         m_hasSubmitButton = false;
971         InputLog(Platform::LogLevelInfo, "InputHandler::updateFormState no valid elements found, clearing state.");
972     }
973 }
974
975 void InputHandler::focusNextField()
976 {
977     if (!m_nextFocusableTextElement)
978         return;
979
980     m_nextFocusableTextElement->focus();
981 }
982
983 void InputHandler::focusPreviousField()
984 {
985     if (!m_previousFocusableTextElement)
986         return;
987
988     m_previousFocusableTextElement->focus();
989 }
990
991 void InputHandler::submitForm()
992 {
993     if (!m_hasSubmitButton)
994         return;
995
996     HTMLFormElement* formElement = static_cast<HTMLFormControlElement*>(m_currentFocusElement.get())->form();
997     if (!formElement)
998         return;
999
1000     InputLog(Platform::LogLevelInfo, "InputHandler::submitForm triggered");
1001     if (elementType(m_currentFocusElement.get()) == InputTypeTextArea)
1002         formElement->submit();
1003     else {
1004         handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_RETURN, Platform::KeyboardEvent::KeyChar, 0), false /* changeIsPartOfComposition */);
1005
1006         // Confirm that implicit submission was accepted.
1007         if (isActiveTextEdit())
1008             formElement->submit();
1009     }
1010 }
1011
1012 static void addInputStyleMaskForKeyboardType(int64_t& inputMask, VirtualKeyboardType keyboardType)
1013 {
1014     switch (keyboardType) {
1015     case VKBTypeUrl:
1016         inputMask |= IMF_URL_TYPE;
1017         break;
1018     case VKBTypePassword:
1019         inputMask |= IMF_PASSWORD_TYPE;
1020         break;
1021     case VKBTypePin:
1022         inputMask |= IMF_PIN_TYPE;
1023         break;
1024     case VKBTypePhone:
1025         inputMask |= IMF_PHONE_TYPE;
1026         break;
1027     case VKBTypeEmail:
1028         inputMask |= IMF_EMAIL_TYPE;
1029         break;
1030     default:
1031         break;
1032     }
1033 }
1034
1035 void InputHandler::setElementFocused(Element* element)
1036 {
1037     ASSERT(DOMSupport::isTextBasedContentEditableElement(element));
1038     ASSERT(element && element->document() && element->document()->frame());
1039
1040 #ifdef ENABLE_SPELLING_LOG
1041     BlackBerry::Platform::StopWatch timer;
1042     timer.start();
1043 #endif
1044
1045     if (!element || !(element->document()))
1046         return;
1047
1048     Frame* frame = element->document()->frame();
1049     if (!frame)
1050         return;
1051
1052     if (frame->selection()->isFocused() != isInputModeEnabled())
1053         frame->selection()->setFocused(isInputModeEnabled());
1054
1055     // Ensure visible when refocusing.
1056     // If device does not have physical keyboard, wait to ensure visible until VKB resizes viewport so that both animations are combined into one.
1057     m_shouldEnsureFocusTextElementVisibleOnSelectionChanged = isActiveTextEdit() || DeviceInfo::instance()->hasPhysicalKeyboard();
1058
1059     // Clear the existing focus node details.
1060     setElementUnfocused(true /*refocusOccuring*/);
1061
1062     // Mark this element as active and add to frame set.
1063     m_currentFocusElement = element;
1064     m_currentFocusElementType = TextEdit;
1065     updateFormState();
1066
1067     if (isInputModeEnabled() && !m_delayKeyboardVisibilityChange)
1068         m_webPage->m_client->showFormControls(m_hasSubmitButton /* visible */, m_previousFocusableTextElement, m_nextFocusableTextElement);
1069     else
1070         m_sendFormStateOnNextKeyboardRequest = true;
1071
1072     // Send details to the client about this element.
1073     BlackBerryInputType type = elementType(element);
1074     m_currentFocusElementTextEditMask = inputStyle(type, element);
1075
1076     VirtualKeyboardType keyboardType = keyboardTypeAttribute(element);
1077     if (keyboardType == VKBTypeNotSet)
1078         keyboardType = convertInputTypeToVKBType(type);
1079
1080     addInputStyleMaskForKeyboardType(m_currentFocusElementTextEditMask, keyboardType);
1081
1082     VirtualKeyboardEnterKeyType enterKeyType = keyboardEnterKeyTypeAttribute(element);
1083
1084     if (enterKeyType == VKBEnterKeyNotSet && type != InputTypeTextArea) {
1085         if (element->isFormControlElement()) {
1086             const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
1087             if (formElement->form() && formElement->form()->defaultButton())
1088                 enterKeyType = VKBEnterKeySubmit;
1089         }
1090     }
1091
1092     FocusLog(Platform::LogLevelInfo,
1093         "InputHandler::setElementFocused, Type=%d, Style=%lld, Keyboard Type=%d, Enter Key=%d",
1094         type, m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
1095
1096     m_webPage->m_client->inputFocusGained(m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
1097
1098     handleInputLocaleChanged(m_webPage->m_webSettings->isWritingDirectionRTL());
1099
1100     // update the suggestion box
1101     showTextInputTypeSuggestionBox();
1102
1103     if (!m_delayKeyboardVisibilityChange)
1104         notifyClientOfKeyboardVisibilityChange(true, true /* triggeredByFocusChange */);
1105
1106 #ifdef ENABLE_SPELLING_LOG
1107     SpellingLog(Platform::LogLevelInfo, "InputHandler::setElementFocused Focusing the field took %f seconds.", timer.elapsed());
1108 #endif
1109
1110     // Check if the field should be spellchecked.
1111     if (!shouldSpellCheckElement(element) || !isActiveTextEdit())
1112         return;
1113
1114     // Spellcheck the field in its entirety.
1115     const VisibleSelection visibleSelection = DOMSupport::visibleSelectionForFocusedBlock(element);
1116     m_spellingHandler->spellCheckTextBlock(visibleSelection, TextCheckingProcessBatch);
1117
1118 #ifdef ENABLE_SPELLING_LOG
1119     SpellingLog(Platform::LogLevelInfo, "InputHandler::setElementFocused Spellchecking the field increased the total time to focus to %f seconds.", timer.elapsed());
1120 #endif
1121 }
1122
1123 bool InputHandler::shouldSpellCheckElement(const Element* element) const
1124 {
1125     DOMSupport::AttributeState spellCheckAttr = DOMSupport::elementSupportsSpellCheck(element);
1126
1127     // Explicitly set to off.
1128     if (spellCheckAttr == DOMSupport::Off)
1129         return false;
1130
1131     // Undefined and part of a set of cases which we do not wish to check. This includes user names and email addresses, so we are piggybacking on NoAutocomplete cases.
1132     if (spellCheckAttr == DOMSupport::Default && (m_currentFocusElementTextEditMask & NO_AUTO_TEXT))
1133         return false;
1134
1135     // Check if the system spell check setting is off
1136     return m_spellCheckStatusConfirmed ? m_globalSpellCheckStatus : true;
1137 }
1138
1139 void InputHandler::stopPendingSpellCheckRequests()
1140 {
1141     m_spellingHandler->setSpellCheckActive(false);
1142 }
1143
1144 void InputHandler::redrawSpellCheckDialogIfRequired(const bool shouldMoveDialog)
1145 {
1146     if (didSpellCheckWord()) {
1147         imf_sp_text_t spellCheckingOptionRequest;
1148         spellCheckingOptionRequest.startTextPosition = 0;
1149         spellCheckingOptionRequest.endTextPosition = 0;
1150         WebCore::IntSize screenOffset(-1, -1);
1151         requestSpellingCheckingOptions(spellCheckingOptionRequest, screenOffset, shouldMoveDialog);
1152     }
1153 }
1154
1155 bool InputHandler::openDatePopup(HTMLInputElement* element, BlackBerryInputType type)
1156 {
1157     if (!element || element->isDisabledOrReadOnly() || !DOMSupport::isDateTimeInputField(element))
1158         return false;
1159
1160     if (isActiveTextEdit())
1161         clearCurrentFocusElement();
1162
1163     switch (type) {
1164     case BlackBerry::Platform::InputTypeDate:
1165     case BlackBerry::Platform::InputTypeTime:
1166     case BlackBerry::Platform::InputTypeDateTime:
1167     case BlackBerry::Platform::InputTypeDateTimeLocal:
1168     case BlackBerry::Platform::InputTypeMonth: {
1169         // Date input have button appearance, we hide caret when they get clicked.
1170         element->document()->frame()->selection()->setCaretVisible(false);
1171
1172         // Check if popup already exists, close it if does.
1173         m_webPage->m_page->chrome()->client()->closePagePopup(0);
1174         WTF::String value = element->value();
1175         WTF::String min = element->getAttribute(HTMLNames::minAttr).string();
1176         WTF::String max = element->getAttribute(HTMLNames::maxAttr).string();
1177         double step = element->getAttribute(HTMLNames::stepAttr).toDouble();
1178
1179         DatePickerClient* client = new DatePickerClient(type, value, min, max, step,  m_webPage, element);
1180         return m_webPage->m_page->chrome()->client()->openPagePopup(client,  WebCore::IntRect());
1181         }
1182     default: // Other types not supported
1183         return false;
1184     }
1185 }
1186
1187 bool InputHandler::openColorPopup(HTMLInputElement* element)
1188 {
1189     if (!element || element->isDisabledOrReadOnly() || !DOMSupport::isColorInputField(element))
1190         return false;
1191
1192     if (isActiveTextEdit())
1193         clearCurrentFocusElement();
1194
1195     m_currentFocusElement = element;
1196     m_currentFocusElementType = TextPopup;
1197
1198     // Check if popup already exists, close it if does.
1199     m_webPage->m_page->chrome()->client()->closePagePopup(0);
1200
1201     ColorPickerClient* client = new ColorPickerClient(element->value(), m_webPage, element);
1202     return m_webPage->m_page->chrome()->client()->openPagePopup(client,  WebCore::IntRect());
1203 }
1204
1205 void InputHandler::setInputValue(const WTF::String& value)
1206 {
1207     if (!isActiveTextPopup())
1208         return;
1209
1210     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(m_currentFocusElement.get());
1211     inputElement->setValue(value);
1212     clearCurrentFocusElement();
1213 }
1214
1215 void InputHandler::nodeTextChanged(const Node* node)
1216 {
1217     if (processingChange() || !node || node != m_currentFocusElement || !m_shouldNotifyWebView)
1218         return;
1219
1220     InputLog(Platform::LogLevelInfo, "InputHandler::nodeTextChanged");
1221
1222     m_webPage->m_client->inputTextChanged();
1223
1224     // Remove the attributed text markers as the previous call triggered an end to
1225     // the composition.
1226     removeAttributedTextMarker();
1227 }
1228
1229 WebCore::IntRect InputHandler::boundingBoxForInputField()
1230 {
1231     if (!isActiveTextEdit())
1232         return WebCore::IntRect();
1233
1234     if (!m_currentFocusElement->renderer())
1235         return WebCore::IntRect();
1236
1237     // type="search" can have a 'X', so take the inner block bounding box to not include it.
1238     if (HTMLInputElement* element = m_currentFocusElement->toInputElement()) {
1239         if (element->isSearchField())
1240             return element->innerBlockElement()->renderer()->absoluteBoundingBoxRect();
1241         return m_currentFocusElement->renderer()->absoluteBoundingBoxRect();
1242     }
1243
1244     if (m_currentFocusElement->hasTagName(HTMLNames::textareaTag))
1245         return m_currentFocusElement->renderer()->absoluteBoundingBoxRect();
1246
1247     // Content Editable can't rely on the bounding box since it isn't fixed.
1248     return WebCore::IntRect();
1249 }
1250
1251 void InputHandler::ensureFocusTextElementVisible(CaretScrollType scrollType)
1252 {
1253     if (!isActiveTextEdit() || !isInputModeEnabled() || !m_currentFocusElement->document())
1254         return;
1255
1256     if (!(Platform::Settings::instance()->allowedScrollAdjustmentForInputFields() & scrollType))
1257         return;
1258
1259     // Fixed position elements cannot be scrolled into view.
1260     if (DOMSupport::isFixedPositionOrHasFixedPositionAncestor(m_currentFocusElement->renderer()))
1261         return;
1262
1263     Frame* elementFrame = m_currentFocusElement->document()->frame();
1264     if (!elementFrame)
1265         return;
1266
1267     Frame* mainFrame = m_webPage->mainFrame();
1268     if (!mainFrame)
1269         return;
1270
1271     FrameView* mainFrameView = mainFrame->view();
1272     if (!mainFrameView)
1273         return;
1274
1275     WebCore::IntRect selectionFocusRect;
1276     switch (elementFrame->selection()->selectionType()) {
1277     case VisibleSelection::CaretSelection:
1278         selectionFocusRect = elementFrame->selection()->absoluteCaretBounds();
1279         break;
1280     case VisibleSelection::RangeSelection: {
1281         Position selectionPosition;
1282         if (m_webPage->m_selectionHandler->lastUpdatedEndPointIsValid())
1283             selectionPosition = elementFrame->selection()->end();
1284         else
1285             selectionPosition = elementFrame->selection()->start();
1286         selectionFocusRect = VisiblePosition(selectionPosition).absoluteCaretBounds();
1287         break;
1288     }
1289     case VisibleSelection::NoSelection:
1290         m_shouldEnsureFocusTextElementVisibleOnSelectionChanged = true;
1291         return;
1292     }
1293
1294     int fontHeight = selectionFocusRect.height();
1295
1296     // If the text is too small, zoom in to make it a minimum size.
1297     // The minimum size being defined as 3 mm is a good value based on my observations.
1298     static const int s_minimumTextHeightInPixels = Graphics::Screen::primaryScreen()->heightInMMToPixels(3);
1299
1300     double zoomScaleRequired;
1301     if (m_webPage->isUserScalable() && fontHeight && fontHeight * m_webPage->currentScale() < s_minimumTextHeightInPixels && !isRunningDrt())
1302         zoomScaleRequired = static_cast<double>(s_minimumTextHeightInPixels) / fontHeight;
1303     else
1304         zoomScaleRequired = m_webPage->currentScale(); // Don't scale.
1305
1306     // Zoom level difference must exceed the given threshold before we perform a zoom animation.
1307     if (abs(zoomScaleRequired - m_webPage->currentScale()) < zoomAnimationThreshold)
1308         zoomScaleRequired = m_webPage->currentScale(); // Don't scale.
1309
1310     // The scroll location we should go to given the zoom required, could be adjusted later.
1311     WebCore::FloatPoint offset(selectionFocusRect.location().x() - m_webPage->scrollPosition().x(), selectionFocusRect.location().y() - m_webPage->scrollPosition().y());
1312     double inverseScale = zoomScaleRequired / m_webPage->currentScale();
1313     WebCore::IntPoint destinationScrollLocation = WebCore::IntPoint(max(0, static_cast<int>(roundf(selectionFocusRect.location().x() - offset.x() / inverseScale))),
1314                                                                     max(0, static_cast<int>(roundf(selectionFocusRect.location().y() - offset.y() / inverseScale))));
1315
1316     if (elementFrame != mainFrame) { // Element is in a subframe.
1317         // Remove any scroll offset within the subframe to get the point relative to the main frame.
1318         selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());
1319
1320         // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
1321         if (elementFrame->ownerRenderer()) {
1322             WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
1323             selectionFocusRect.move(frameOffset.x(), frameOffset.y());
1324         }
1325     }
1326
1327     bool shouldConstrainScrollingToContentEdge = true;
1328     Position start = elementFrame->selection()->start();
1329     if (start.anchorNode() && start.anchorNode()->renderer()) {
1330         if (RenderLayer* layer = start.anchorNode()->renderer()->enclosingLayer()) {
1331             // Screen rect after the required zoom.
1332             WebCore::IntRect actualScreenRect = WebCore::IntRect(destinationScrollLocation.x(), destinationScrollLocation.y(), m_webPage->actualVisibleSize().width() / inverseScale, m_webPage->actualVisibleSize().height() / inverseScale);
1333
1334             ScrollAlignment horizontalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;
1335             ScrollAlignment verticalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;
1336
1337             if (scrollType != EdgeIfNeeded) {
1338                 // Align the selection rect if possible so that we show the field's
1339                 // outline if the caret is at the edge of the field.
1340                 if (RenderObject* focusedRenderer = m_currentFocusElement->renderer()) {
1341                     WebCore::IntRect nodeOutlineBounds = focusedRenderer->absoluteOutlineBounds();
1342                     WebCore::IntRect caretAtEdgeRect = rectForCaret(0);
1343                     int paddingX = abs(caretAtEdgeRect.x() - nodeOutlineBounds.x());
1344                     int paddingY = abs(caretAtEdgeRect.y() - nodeOutlineBounds.y());
1345
1346                     if (selectionFocusRect.x() - paddingX == nodeOutlineBounds.x())
1347                         selectionFocusRect.setX(nodeOutlineBounds.x());
1348                     else if (selectionFocusRect.maxX() + paddingX == nodeOutlineBounds.maxX())
1349                         selectionFocusRect.setX(nodeOutlineBounds.maxX() - selectionFocusRect.width());
1350                     if (selectionFocusRect.y() - paddingY == nodeOutlineBounds.y())
1351                         selectionFocusRect.setY(nodeOutlineBounds.y() - selectionFocusRect.height());
1352                     else if (selectionFocusRect.maxY() + paddingY == nodeOutlineBounds.maxY())
1353                         selectionFocusRect.setY(nodeOutlineBounds.maxY() - selectionFocusRect.height());
1354
1355                     // If the editing point is on the left hand side of the screen when the node's
1356                     // rect is edge aligned, edge align the node rect.
1357                     if (selectionFocusRect.x() - caretAtEdgeRect.x() < actualScreenRect.width() / 2)
1358                         selectionFocusRect.setX(nodeOutlineBounds.x());
1359                     else
1360                         horizontalScrollAlignment = ScrollAlignment::alignCenterIfNeeded;
1361
1362                 }
1363                 verticalScrollAlignment = (scrollType == CenterAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
1364             }
1365
1366             // Pad the rect to improve the visual appearance.
1367             // Convert the padding back from transformed to ensure a consistent padding regardless of
1368             // zoom level as controls do not zoom.
1369             static const int s_focusRectPaddingSize = Graphics::Screen::primaryScreen()->heightInMMToPixels(3);
1370             selectionFocusRect.inflate(m_webPage->mapFromTransformed(WebCore::IntSize(0, s_focusRectPaddingSize)).height());
1371
1372             WebCore::IntRect revealRect(layer->getRectToExpose(actualScreenRect, selectionFocusRect,
1373                                                                  horizontalScrollAlignment,
1374                                                                  verticalScrollAlignment));
1375
1376             // Don't constrain scroll position when animation finishes.
1377             shouldConstrainScrollingToContentEdge = false;
1378
1379             // In order to adjust the scroll position to ensure the focused input field is visible,
1380             // we allow overscrolling. However this overscroll has to be strictly allowed towards the
1381             // bottom of the page on the y axis only, where the virtual keyboard pops up from.
1382             destinationScrollLocation = revealRect.location();
1383             destinationScrollLocation.clampNegativeToZero();
1384             WebCore::IntPoint maximumScrollPosition = WebCore::IntPoint(mainFrameView->contentsWidth() - actualScreenRect.width(), mainFrameView->contentsHeight() - actualScreenRect.height());
1385             destinationScrollLocation = destinationScrollLocation.shrunkTo(maximumScrollPosition);
1386         }
1387     }
1388
1389     InputLog(Platform::LogLevelInfo,
1390         "InputHandler::ensureFocusTextElementVisible zooming in to %f from %f and scrolling to point %s from %s",
1391         zoomScaleRequired, m_webPage->currentScale(),
1392         Platform::IntPoint(destinationScrollLocation).toString().c_str(),
1393         Platform::IntPoint(mainFrameView->scrollPosition()).toString().c_str());
1394
1395     m_webPage->animateToScaleAndDocumentScrollPosition(zoomScaleRequired, WebCore::FloatPoint(destinationScrollLocation), shouldConstrainScrollingToContentEdge);
1396 }
1397
1398 void InputHandler::ensureFocusPluginElementVisible()
1399 {
1400     if (!isActivePlugin() || !m_currentFocusElement->document())
1401         return;
1402
1403     Frame* elementFrame = m_currentFocusElement->document()->frame();
1404     if (!elementFrame)
1405         return;
1406
1407     Frame* mainFrame = m_webPage->mainFrame();
1408     if (!mainFrame)
1409         return;
1410
1411     FrameView* mainFrameView = mainFrame->view();
1412     if (!mainFrameView)
1413         return;
1414
1415     WebCore::IntRect selectionFocusRect;
1416
1417     RenderWidget* renderWidget = static_cast<RenderWidget*>(m_currentFocusElement->renderer());
1418     if (renderWidget) {
1419         PluginView* pluginView = toPluginView(renderWidget->widget());
1420
1421         if (pluginView)
1422             selectionFocusRect = pluginView->ensureVisibleRect();
1423     }
1424
1425     if (selectionFocusRect.isEmpty())
1426         return;
1427
1428     // FIXME: We may need to scroll the subframe (recursively) in the future. Revisit this...
1429     if (elementFrame != mainFrame) { // Element is in a subframe.
1430         // Remove any scroll offset within the subframe to get the point relative to the main frame.
1431         selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());
1432
1433         // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
1434         if (elementFrame->ownerRenderer()) {
1435             WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
1436             selectionFocusRect.move(frameOffset.x(), frameOffset.y());
1437         }
1438     }
1439
1440     WebCore::IntRect actualScreenRect = WebCore::IntRect(mainFrameView->scrollPosition(), m_webPage->actualVisibleSize());
1441     if (actualScreenRect.contains(selectionFocusRect))
1442         return;
1443
1444     // Calculate a point such that the center of the requested rectangle
1445     // is at the center of the screen. FIXME: If the element was partially on screen
1446     // we might want to just bring the offscreen portion into view, someone needs
1447     // to decide if that's the behavior we want or not.
1448     WebCore::IntPoint pos(selectionFocusRect.center().x() - actualScreenRect.width() / 2,
1449                  selectionFocusRect.center().y() - actualScreenRect.height() / 2);
1450
1451     mainFrameView->setScrollPosition(pos);
1452 }
1453
1454 void InputHandler::ensureFocusElementVisible(bool centerInView)
1455 {
1456     if (isActivePlugin())
1457         ensureFocusPluginElementVisible();
1458     else
1459         ensureFocusTextElementVisible(centerInView ? CenterAlways : CenterIfNeeded);
1460 }
1461
1462 void InputHandler::frameUnloaded(const Frame* frame)
1463 {
1464     if (!isActiveTextEdit())
1465         return;
1466
1467     if (m_currentFocusElement->document()->frame() != frame)
1468         return;
1469
1470     FocusLog(Platform::LogLevelInfo, "InputHandler::frameUnloaded");
1471
1472     setElementUnfocused(false /*refocusOccuring*/);
1473 }
1474
1475 void InputHandler::setDelayKeyboardVisibilityChange(bool value)
1476 {
1477     m_delayKeyboardVisibilityChange = value;
1478     m_pendingKeyboardVisibilityChange = NoChange;
1479 }
1480
1481 void InputHandler::processPendingKeyboardVisibilityChange()
1482 {
1483     if (!m_delayKeyboardVisibilityChange) {
1484         ASSERT(m_pendingKeyboardVisibilityChange == NoChange);
1485         return;
1486     }
1487
1488     m_delayKeyboardVisibilityChange = false;
1489
1490     if (m_pendingKeyboardVisibilityChange == NoChange)
1491         return;
1492
1493     notifyClientOfKeyboardVisibilityChange(m_pendingKeyboardVisibilityChange == Visible);
1494     m_pendingKeyboardVisibilityChange = NoChange;
1495 }
1496
1497 void InputHandler::notifyClientOfKeyboardVisibilityChange(bool visible, bool triggeredByFocusChange)
1498 {
1499     // If we aren't ready for input, keyboard changes should be ignored.
1500     if (!isInputModeEnabled() && visible)
1501         return;
1502
1503     // If we are processing a change assume the keyboard is visbile to avoid
1504     // flooding the VKB with show requests.
1505     if (!triggeredByFocusChange && processingChange() && visible)
1506         return;
1507
1508     if (!m_delayKeyboardVisibilityChange) {
1509         if (m_sendFormStateOnNextKeyboardRequest) {
1510             m_webPage->m_client->showFormControls(m_hasSubmitButton /* visible */, m_previousFocusableTextElement, m_nextFocusableTextElement);
1511             m_sendFormStateOnNextKeyboardRequest = false;
1512         }
1513         m_webPage->showVirtualKeyboard(visible);
1514         return;
1515     }
1516
1517     m_pendingKeyboardVisibilityChange = visible ? Visible : NotVisible;
1518 }
1519
1520 bool InputHandler::selectionAtStartOfElement()
1521 {
1522     if (!isActiveTextEdit())
1523         return false;
1524
1525     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1526
1527     if (!selectionStart())
1528         return true;
1529
1530     return false;
1531 }
1532
1533 bool InputHandler::selectionAtEndOfElement()
1534 {
1535     if (!isActiveTextEdit())
1536         return false;
1537
1538     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1539
1540     return selectionStart() == static_cast<int>(elementText().length());
1541 }
1542
1543 int InputHandler::selectionStart() const
1544 {
1545     return selectionPosition(true);
1546 }
1547
1548 int InputHandler::selectionEnd() const
1549 {
1550     return selectionPosition(false);
1551 }
1552
1553 int InputHandler::selectionPosition(bool start) const
1554 {
1555     if (!m_currentFocusElement->document() || !m_currentFocusElement->document()->frame())
1556         return 0;
1557
1558     if (HTMLTextFormControlElement* controlElement = DOMSupport::toTextControlElement(m_currentFocusElement.get()))
1559         return start ? controlElement->selectionStart() : controlElement->selectionEnd();
1560
1561     FrameSelection caretSelection;
1562     caretSelection.setSelection(m_currentFocusElement->document()->frame()->selection()->selection());
1563     RefPtr<Range> rangeSelection = caretSelection.selection().toNormalizedRange();
1564     if (!rangeSelection)
1565         return 0;
1566
1567     int selectionPointInNode = start ? rangeSelection->startOffset() : rangeSelection->endOffset();
1568     Node* containerNode = start ? rangeSelection->startContainer() : rangeSelection->endContainer();
1569
1570     ExceptionCode ec;
1571     RefPtr<Range> rangeForNode = rangeOfContents(m_currentFocusElement.get());
1572     rangeForNode->setEnd(containerNode, selectionPointInNode, ec);
1573     ASSERT(!ec);
1574
1575     return TextIterator::rangeLength(rangeForNode.get());
1576 }
1577
1578 void InputHandler::selectionChanged()
1579 {
1580     // This method can get called during WebPage shutdown process.
1581     // If that is the case, just bail out since the client is not
1582     // in a safe state of trust to request anything else from it.
1583     if (!m_webPage->m_mainFrame)
1584         return;
1585
1586     if (!isActiveTextEdit())
1587         return;
1588
1589     if (processingChange())
1590         return;
1591
1592     // Scroll the field if necessary. This must be done even if we are processing
1593     // a change as the text change may have moved the caret. IMF doesn't require
1594     // the update, but the user needs to see the caret.
1595     if (m_shouldEnsureFocusTextElementVisibleOnSelectionChanged) {
1596         ensureFocusTextElementVisible(EdgeIfNeeded);
1597         m_shouldEnsureFocusTextElementVisibleOnSelectionChanged = false;
1598     }
1599
1600     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1601
1602     if (!m_shouldNotifyWebView)
1603         return;
1604
1605     int newSelectionStart = selectionStart();
1606     int newSelectionEnd = selectionEnd();
1607
1608     InputLog(Platform::LogLevelInfo,
1609         "InputHandler::selectionChanged selectionStart=%u, selectionEnd=%u",
1610         newSelectionStart, newSelectionEnd);
1611
1612     m_webPage->m_client->inputSelectionChanged(newSelectionStart, newSelectionEnd);
1613
1614     // Remove the attributed text markers as the previous call triggered an end to
1615     // the composition.
1616     removeAttributedTextMarker();
1617 }
1618
1619 bool InputHandler::setCursorPosition(int location)
1620 {
1621     return setSelection(location, location);
1622 }
1623
1624 bool InputHandler::setSelection(int start, int end, bool changeIsPartOfComposition)
1625 {
1626     if (!isActiveTextEdit())
1627         return false;
1628
1629     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1630
1631     ProcessingChangeGuard guard(this);
1632
1633     VisibleSelection newSelection = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end);
1634     m_currentFocusElement->document()->frame()->selection()->setSelection(newSelection, changeIsPartOfComposition ? 0 : FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
1635
1636     InputLog(Platform::LogLevelInfo,
1637         "InputHandler::setSelection selectionStart=%u, selectionEnd=%u",
1638         start, end);
1639
1640     return start == selectionStart() && end == selectionEnd();
1641 }
1642
1643 WebCore::IntRect InputHandler::rectForCaret(int index)
1644 {
1645     if (!isActiveTextEdit())
1646         return WebCore::IntRect();
1647
1648     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1649
1650     if (index < 0 || index > static_cast<int>(elementText().length())) {
1651         // Invalid request.
1652         return WebCore::IntRect();
1653     }
1654
1655     FrameSelection caretSelection;
1656     caretSelection.setSelection(DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), index, index).visibleStart());
1657     caretSelection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
1658     return caretSelection.selection().visibleStart().absoluteCaretBounds();
1659 }
1660
1661 void InputHandler::cancelSelection()
1662 {
1663     if (!isActiveTextEdit())
1664         return;
1665
1666     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1667
1668     int selectionStartPosition = selectionStart();
1669     ProcessingChangeGuard guard(this);
1670     setCursorPosition(selectionStartPosition);
1671 }
1672
1673 bool InputHandler::isNavigationKey(unsigned character) const
1674 {
1675     return character == KEYCODE_UP
1676         || character == KEYCODE_DOWN
1677         || character == KEYCODE_LEFT
1678         || character == KEYCODE_RIGHT;
1679 }
1680
1681 bool InputHandler::handleKeyboardInput(const Platform::KeyboardEvent& keyboardEvent, bool changeIsPartOfComposition)
1682 {
1683     InputLog(Platform::LogLevelInfo,
1684         "InputHandler::handleKeyboardInput received character='%lc', type=%d",
1685         keyboardEvent.character(), keyboardEvent.type());
1686
1687     // Clearing the m_shouldNotifyWebView state on any KeyboardEvent.
1688     m_shouldNotifyWebView = true;
1689
1690     // Enable input mode if we are processing a key event.
1691     setInputModeEnabled();
1692
1693     Platform::KeyboardEvent::Type type = keyboardEvent.type();
1694     /*
1695      * IMF sends us an unadultered KeyUp for all key presses. This key event should be allowed to be processed at all times.
1696      * We bypass the check because the state of composition has no implication on this key event.
1697      * In order to ensure we allow the correct key event through, we keep track of key down events with m_expectedKeyUpChar.
1698     */
1699     if (type == Platform::KeyboardEvent::KeyUp) {
1700         // When IMF auto-capitalizes a KeyDown, say the first letter of a new sentence, our KeyUp will still be in lowercase.
1701         if (m_expectedKeyUpChar == keyboardEvent.character() || (isASCIIUpper(m_expectedKeyUpChar) && m_expectedKeyUpChar == toASCIIUpper(keyboardEvent.character()))) {
1702             m_expectedKeyUpChar = 0;
1703             changeIsPartOfComposition = true;
1704         }
1705     }
1706
1707     // If we aren't specifically part of a composition, fail, IMF should never send key input
1708     // while composing text. If IMF has failed, we should have already finished the
1709     // composition manually. There is a caveat for KeyUp which is explained above.
1710     if (!changeIsPartOfComposition && compositionActive()) {
1711         if (type == Platform::KeyboardEvent::KeyDown && isNavigationKey(keyboardEvent.character()))
1712             removeAttributedTextMarker();
1713         else
1714             return false;
1715     }
1716
1717     ProcessingChangeGuard guard(this);
1718
1719     unsigned adjustedModifiers = keyboardEvent.modifiers();
1720     if (WTF::isASCIIUpper(keyboardEvent.character()))
1721         adjustedModifiers |= KEYMOD_SHIFT;
1722
1723     ASSERT(m_webPage->m_page->focusController());
1724     bool keyboardEventHandled = false;
1725     if (Frame* focusedFrame = m_webPage->m_page->focusController()->focusedFrame()) {
1726         bool isKeyChar = type == Platform::KeyboardEvent::KeyChar;
1727
1728         // If this is a KeyChar type then we handle it as a keydown followed by a key up.
1729         if (isKeyChar)
1730             type = Platform::KeyboardEvent::KeyDown;
1731         else if (type == Platform::KeyboardEvent::KeyDown) {
1732             m_expectedKeyUpChar = keyboardEvent.character();
1733
1734             m_shouldNotifyWebView = shouldNotifyWebView(keyboardEvent);
1735         }
1736
1737         Platform::KeyboardEvent adjustedKeyboardEvent(keyboardEvent.character(), type, adjustedModifiers, keyboardEvent.keycode(), keyboardEvent.alternateCharacter(), keyboardEvent.sourceDevice());
1738         keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent));
1739
1740         m_shouldNotifyWebView = true;
1741
1742         if (isKeyChar) {
1743             type = Platform::KeyboardEvent::KeyUp;
1744             adjustedKeyboardEvent = Platform::KeyboardEvent(keyboardEvent.character(), type, adjustedModifiers, keyboardEvent.keycode(), keyboardEvent.alternateCharacter(), keyboardEvent.sourceDevice());
1745             keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent)) || keyboardEventHandled;
1746         }
1747
1748         if (!changeIsPartOfComposition && type == Platform::KeyboardEvent::KeyUp)
1749             ensureFocusTextElementVisible(EdgeIfNeeded);
1750     }
1751
1752     if (m_currentFocusElement && keyboardEventHandled)
1753         showTextInputTypeSuggestionBox();
1754
1755     return keyboardEventHandled;
1756 }
1757
1758 bool InputHandler::shouldNotifyWebView(const Platform::KeyboardEvent& keyboardEvent)
1759 {
1760     // If we receive the KeyDown of a Backspace or Enter key, set this flag to prevent sending unnecessary selection and caret changes to IMF.
1761     return !(keyboardEvent.character() == KEYCODE_BACKSPACE || keyboardEvent.character() == KEYCODE_RETURN || keyboardEvent.character() == KEYCODE_KP_ENTER);
1762 }
1763
1764 bool InputHandler::deleteSelection()
1765 {
1766     if (!isActiveTextEdit())
1767         return false;
1768
1769     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1770     Frame* frame = m_currentFocusElement->document()->frame();
1771
1772     if (frame->selection()->selectionType() != VisibleSelection::RangeSelection)
1773         return false;
1774
1775     ASSERT(frame->editor());
1776     return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), false /* changeIsPartOfComposition */);
1777 }
1778
1779 void InputHandler::insertText(const WTF::String& string)
1780 {
1781     if (!isActiveTextEdit())
1782         return;
1783
1784     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1785     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1786     editor->command("InsertText").execute(string);
1787 }
1788
1789 void InputHandler::clearField()
1790 {
1791     if (!isActiveTextEdit())
1792         return;
1793
1794     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1795     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1796
1797     editor->command("SelectAll").execute();
1798     editor->command("DeleteBackward").execute();
1799 }
1800
1801 bool InputHandler::executeTextEditCommand(const WTF::String& commandName)
1802 {
1803     ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->editor());
1804     Editor* editor = m_webPage->focusedOrMainFrame()->editor();
1805
1806     return editor->command(commandName).execute();
1807 }
1808
1809 void InputHandler::cut()
1810 {
1811     executeTextEditCommand("Cut");
1812 }
1813
1814 void InputHandler::copy()
1815 {
1816     executeTextEditCommand("Copy");
1817 }
1818
1819 void InputHandler::paste()
1820 {
1821     executeTextEditCommand("Paste");
1822 }
1823
1824 void InputHandler::selectAll()
1825 {
1826     executeTextEditCommand("SelectAll");
1827 }
1828
1829 void InputHandler::addAttributedTextMarker(int start, int end, const AttributeTextStyle& style)
1830 {
1831     if ((end - start) < 1 || end > static_cast<int>(elementText().length()))
1832         return;
1833
1834     RefPtr<Range> markerRange = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end).toNormalizedRange();
1835     m_currentFocusElement->document()->markers()->addMarker(markerRange.get(), DocumentMarker::AttributeText, WTF::String("Input Marker"), style);
1836 }
1837
1838 void InputHandler::removeAttributedTextMarker()
1839 {
1840     // Remove all attribute text markers.
1841     if (m_currentFocusElement && m_currentFocusElement->document())
1842         m_currentFocusElement->document()->markers()->removeMarkers(DocumentMarker::AttributeText);
1843
1844     m_composingTextStart = 0;
1845     m_composingTextEnd = 0;
1846 }
1847
1848 void InputHandler::clearCurrentFocusElement()
1849 {
1850     if (m_currentFocusElement)
1851         m_currentFocusElement->blur();
1852 }
1853
1854 bool InputHandler::willOpenPopupForNode(Node* node)
1855 {
1856     // This method must be kept synchronized with InputHandler::didNodeOpenPopup.
1857     if (!node)
1858         return false;
1859
1860     ASSERT(!node->isInShadowTree());
1861
1862     if (node->hasTagName(HTMLNames::selectTag) || node->hasTagName(HTMLNames::optionTag)) {
1863         // We open list popups for options and selects.
1864         return true;
1865     }
1866
1867     if (node->isElementNode()) {
1868         Element* element = toElement(node);
1869         if (DOMSupport::isPopupInputField(element))
1870             return true;
1871     }
1872
1873     return false;
1874 }
1875
1876 bool InputHandler::didNodeOpenPopup(Node* node)
1877 {
1878     // This method must be kept synchronized with InputHandler::willOpenPopupForNode.
1879     if (!node)
1880         return false;
1881
1882     ASSERT(!node->isInShadowTree());
1883
1884     if (node->hasTagName(HTMLNames::selectTag))
1885         return openSelectPopup(static_cast<HTMLSelectElement*>(node));
1886
1887     if (node->hasTagName(HTMLNames::optionTag)) {
1888         HTMLOptionElement* optionElement = static_cast<HTMLOptionElement*>(node);
1889         return openSelectPopup(optionElement->ownerSelectElement());
1890     }
1891
1892     if (HTMLInputElement* element = node->toInputElement()) {
1893         if (DOMSupport::isDateTimeInputField(element))
1894             return openDatePopup(element, elementType(element));
1895
1896         if (DOMSupport::isColorInputField(element))
1897             return openColorPopup(element);
1898     }
1899     return false;
1900 }
1901
1902 bool InputHandler::openSelectPopup(HTMLSelectElement* select)
1903 {
1904     if (!select || select->disabled())
1905         return false;
1906
1907     // If there's no view, do nothing and return.
1908     if (!select->document()->view())
1909         return false;
1910
1911     if (isActiveTextEdit())
1912         clearCurrentFocusElement();
1913
1914     m_currentFocusElement = select;
1915     m_currentFocusElementType = SelectPopup;
1916
1917     const WTF::Vector<HTMLElement*>& listItems = select->listItems();
1918     int size = listItems.size();
1919
1920     bool multiple = select->multiple();
1921     ScopeArray<BlackBerry::Platform::String> labels;
1922     labels.reset(new BlackBerry::Platform::String[size]);
1923
1924     // Check if popup already exists, close it if does.
1925     m_webPage->m_page->chrome()->client()->closePagePopup(0);
1926
1927     bool* enableds = 0;
1928     int* itemTypes = 0;
1929     bool* selecteds = 0;
1930
1931     if (size) {
1932         enableds = new bool[size];
1933         itemTypes = new int[size];
1934         selecteds = new bool[size];
1935         for (int i = 0; i < size; i++) {
1936             if (listItems[i]->hasTagName(HTMLNames::optionTag)) {
1937                 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(listItems[i]);
1938                 labels[i] = option->textIndentedToRespectGroupLabel();
1939                 enableds[i] = option->disabled() ? 0 : 1;
1940                 selecteds[i] = option->selected();
1941                 itemTypes[i] = option->parentNode() && option->parentNode()->hasTagName(HTMLNames::optgroupTag) ? TypeOptionInGroup : TypeOption;
1942             } else if (listItems[i]->hasTagName(HTMLNames::optgroupTag)) {
1943                 HTMLOptGroupElement* optGroup = static_cast<HTMLOptGroupElement*>(listItems[i]);
1944                 labels[i] = optGroup->groupLabelText();
1945                 enableds[i] = optGroup->disabled() ? 0 : 1;
1946                 selecteds[i] = false;
1947                 itemTypes[i] = TypeGroup;
1948             } else if (listItems[i]->hasTagName(HTMLNames::hrTag)) {
1949                 enableds[i] = false;
1950                 selecteds[i] = false;
1951                 itemTypes[i] = TypeSeparator;
1952             }
1953         }
1954     }
1955
1956     SelectPopupClient* selectClient = new SelectPopupClient(multiple, size, labels, enableds, itemTypes, selecteds, m_webPage, select);
1957     WebCore::IntRect elementRectInRootView = select->document()->view()->contentsToRootView(enclosingIntRect(select->getRect()));
1958     // Fail to create HTML popup, use the old path
1959     if (!m_webPage->m_page->chrome()->client()->openPagePopup(selectClient, elementRectInRootView))
1960         m_webPage->m_client->openPopupList(multiple, size, labels, enableds, itemTypes, selecteds);
1961     delete[] enableds;
1962     delete[] itemTypes;
1963     delete[] selecteds;
1964     return true;
1965 }
1966
1967 void InputHandler::setPopupListIndex(int index)
1968 {
1969     if (index == -2) // Abandon
1970         return clearCurrentFocusElement();
1971
1972     if (!isActiveSelectPopup())
1973         return clearCurrentFocusElement();
1974
1975     RenderObject* renderer = m_currentFocusElement->renderer();
1976     if (renderer && renderer->isMenuList()) {
1977         RenderMenuList* renderMenu = toRenderMenuList(renderer);
1978         renderMenu->hidePopup();
1979     }
1980
1981     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
1982     int optionIndex = selectElement->listToOptionIndex(index);
1983     selectElement->optionSelectedByUser(optionIndex, true /* deselect = true */, true /* fireOnChangeNow = false */);
1984     clearCurrentFocusElement();
1985 }
1986
1987 void InputHandler::setPopupListIndexes(int size, const bool* selecteds)
1988 {
1989     if (!isActiveSelectPopup())
1990         return clearCurrentFocusElement();
1991
1992     if (size < 0)
1993         return;
1994
1995     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
1996     const WTF::Vector<HTMLElement*>& items = selectElement->listItems();
1997     if (items.size() != static_cast<unsigned int>(size))
1998         return;
1999
2000     HTMLOptionElement* option;
2001     for (int i = 0; i < size; i++) {
2002         if (items[i]->hasTagName(HTMLNames::optionTag)) {
2003             option = static_cast<HTMLOptionElement*>(items[i]);
2004             option->setSelectedState(selecteds[i]);
2005         }
2006     }
2007
2008     // Force repaint because we do not send mouse events to the select element
2009     // and the element doesn't automatically repaint itself.
2010     selectElement->dispatchFormControlChangeEvent();
2011     selectElement->renderer()->repaint();
2012     clearCurrentFocusElement();
2013 }
2014
2015 bool InputHandler::setBatchEditingActive(bool active)
2016 {
2017     if (!isActiveTextEdit())
2018         return false;
2019
2020     ASSERT(m_currentFocusElement->document());
2021     ASSERT(m_currentFocusElement->document()->frame());
2022
2023     // FIXME switch this to m_currentFocusElement->document()->frame() when we have separate
2024     // backingstore for each frame.
2025     BackingStoreClient* backingStoreClient = m_webPage->backingStoreClient();
2026     ASSERT(backingStoreClient);
2027
2028     // Enable / Disable the backingstore to prevent visual updates.
2029     // FIXME: Do we really need to suspend/resume both backingstore and screen here?
2030     if (!active) {
2031         backingStoreClient->backingStore()->resumeBackingStoreUpdates();
2032         backingStoreClient->backingStore()->resumeScreenUpdates(BackingStore::RenderAndBlit);
2033     } else {
2034         backingStoreClient->backingStore()->suspendBackingStoreUpdates();
2035         backingStoreClient->backingStore()->suspendScreenUpdates();
2036     }
2037
2038     return true;
2039 }
2040
2041 bool InputHandler::isCaretAtEndOfText()
2042 {
2043     return caretPosition() == static_cast<int>(elementText().length());
2044 }
2045
2046 int InputHandler::caretPosition() const
2047 {
2048     if (!isActiveTextEdit())
2049         return -1;
2050
2051     // NOTE: This function is expected to return the start of the selection if
2052     // a selection is applied.
2053     return selectionStart();
2054 }
2055
2056 int relativeLeftOffset(int caretPosition, int leftOffset)
2057 {
2058     ASSERT(caretPosition >= 0);
2059
2060     return std::max(0, caretPosition - leftOffset);
2061 }
2062
2063 int relativeRightOffset(int caretPosition, unsigned totalLengthOfText, int rightOffset)
2064 {
2065     ASSERT(caretPosition >= 0);
2066     ASSERT(totalLengthOfText < INT_MAX);
2067
2068     return std::min(caretPosition + rightOffset, static_cast<int>(totalLengthOfText));
2069 }
2070
2071 bool InputHandler::deleteTextRelativeToCursor(int leftOffset, int rightOffset)
2072 {
2073     if (!isActiveTextEdit() || compositionActive())
2074         return false;
2075
2076     ProcessingChangeGuard guard(this);
2077
2078     InputLog(Platform::LogLevelInfo,
2079         "InputHandler::deleteTextRelativeToCursor left %d right %d",
2080         leftOffset, rightOffset);
2081
2082     int caretOffset = caretPosition();
2083     int start = relativeLeftOffset(caretOffset, leftOffset);
2084     int end = relativeRightOffset(caretOffset, elementText().length(), rightOffset);
2085
2086     // If we have backspace in a single character, send this to webkit as a KeyboardEvent. Otherwise, call deleteText.
2087     if (leftOffset == 1 && !rightOffset) {
2088         if (!handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */))
2089             return false;
2090     } else if (!deleteText(start, end))
2091         return false;
2092
2093     // Scroll the field if necessary. The automatic update is suppressed
2094     // by the processing change guard.
2095     ensureFocusTextElementVisible(EdgeIfNeeded);
2096
2097     return true;
2098 }
2099
2100 bool InputHandler::deleteText(int start, int end)
2101 {
2102     if (!isActiveTextEdit())
2103         return false;
2104
2105     ProcessingChangeGuard guard(this);
2106
2107     if (end - start == 1)
2108         return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2109
2110     if (!setSelection(start, end, true /*changeIsPartOfComposition*/))
2111         return false;
2112
2113     InputLog(Platform::LogLevelInfo, "InputHandler::deleteText start %d end %d", start, end);
2114
2115     return deleteSelection();
2116 }
2117
2118 spannable_string_t* InputHandler::spannableTextInRange(int start, int end, int32_t flags)
2119 {
2120     if (!isActiveTextEdit())
2121         return 0;
2122
2123     if (start >= end)
2124         return 0;
2125
2126     int length = end - start;
2127
2128     WTF::String textString = elementText().substring(start, length);
2129
2130     spannable_string_t* pst = (spannable_string_t*)fastMalloc(sizeof(spannable_string_t));
2131
2132     // Don't use fastMalloc in case the string is unreasonably long. fastMalloc will
2133     // crash immediately on failure.
2134     pst->str = (wchar_t*)malloc(sizeof(wchar_t) * (length + 1));
2135     if (!pst->str) {
2136         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::spannableTextInRange Cannot allocate memory for string.");
2137         free(pst);
2138         return 0;
2139     }
2140
2141     int stringLength = 0;
2142     if (!convertStringToWchar(textString, pst->str, length + 1, &stringLength)) {
2143         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::spannableTextInRange failed to convert string.");
2144         free(pst->str);
2145         free(pst);
2146         return 0;
2147     }
2148
2149     pst->length = stringLength;
2150     pst->spans_count = 0;
2151     pst->spans = 0;
2152
2153     return pst;
2154 }
2155
2156 spannable_string_t* InputHandler::selectedText(int32_t flags)
2157 {
2158     if (!isActiveTextEdit())
2159         return 0;
2160
2161     return spannableTextInRange(selectionStart(), selectionEnd(), flags);
2162 }
2163
2164 spannable_string_t* InputHandler::textBeforeCursor(int32_t length, int32_t flags)
2165 {
2166     if (!isActiveTextEdit())
2167         return 0;
2168
2169     int caretOffset = caretPosition();
2170     int start = relativeLeftOffset(caretOffset, length);
2171     int end = caretOffset;
2172
2173     return spannableTextInRange(start, end, flags);
2174 }
2175
2176 spannable_string_t* InputHandler::textAfterCursor(int32_t length, int32_t flags)
2177 {
2178     if (!isActiveTextEdit())
2179         return 0;
2180
2181     int caretOffset = caretPosition();
2182     int start = caretOffset;
2183     int end = relativeRightOffset(caretOffset, elementText().length(), length);
2184
2185     return spannableTextInRange(start, end, flags);
2186 }
2187
2188 extracted_text_t* InputHandler::extractedTextRequest(extracted_text_request_t* request, int32_t flags)
2189 {
2190     if (!isActiveTextEdit())
2191         return 0;
2192
2193     extracted_text_t* extractedText = (extracted_text_t *)fastMalloc(sizeof(extracted_text_t));
2194
2195     // 'flags' indicates whether the text is being monitored. This is not currently used.
2196
2197     // FIXME add smart limiting based on the hint sizes. Currently return all text.
2198
2199     extractedText->text = spannableTextInRange(0, elementText().length(), flags);
2200
2201     // FIXME confirm these should be 0 on this requested. Text changes will likely require
2202     // the end be the length.
2203     extractedText->partial_start_offset = 0;
2204     extractedText->partial_end_offset = 0;
2205     extractedText->start_offset = 0;
2206
2207     // Adjust selection values relative to the start offset, which may be a subset
2208     // of the text in the field.
2209     extractedText->selection_start = selectionStart() - extractedText->start_offset;
2210     extractedText->selection_end = selectionEnd() - extractedText->start_offset;
2211
2212     // selectionActive is not limited to inside the extracted text.
2213     bool selectionActive = extractedText->selection_start != extractedText->selection_end;
2214     bool singleLine = m_currentFocusElement->hasTagName(HTMLNames::inputTag);
2215
2216     // FIXME flags has two values in doc, enum not in header yet.
2217     extractedText->flags = selectionActive & singleLine;
2218
2219     return extractedText;
2220 }
2221
2222 static void addCompositionTextStyleToAttributeTextStyle(AttributeTextStyle& style)
2223 {
2224     style.setUnderline(AttributeTextStyle::StandardUnderline);
2225 }
2226
2227 static void addActiveTextStyleToAttributeTextStyle(AttributeTextStyle& style)
2228 {
2229     style.setBackgroundColor(Color("blue"));
2230     style.setTextColor(Color("white"));
2231 }
2232
2233 static AttributeTextStyle compositionTextStyle()
2234 {
2235     AttributeTextStyle style;
2236     addCompositionTextStyleToAttributeTextStyle(style);
2237     return style;
2238 }
2239
2240 static AttributeTextStyle textStyleFromMask(int64_t mask)
2241 {
2242     AttributeTextStyle style;
2243     if (mask & COMPOSED_TEXT_ATTRIB)
2244         addCompositionTextStyleToAttributeTextStyle(style);
2245
2246     if (mask & ACTIVE_REGION_ATTRIB)
2247         addActiveTextStyleToAttributeTextStyle(style);
2248
2249     return style;
2250 }
2251
2252 bool InputHandler::removeComposedText()
2253 {
2254     if (compositionActive()) {
2255         if (!deleteText(m_composingTextStart, m_composingTextEnd)) {
2256             // Could not remove the existing composition region.
2257             return false;
2258         }
2259         removeAttributedTextMarker();
2260     }
2261
2262     return true;
2263 }
2264
2265 int32_t InputHandler::setComposingRegion(int32_t start, int32_t end)
2266 {
2267     if (!isActiveTextEdit())
2268         return -1;
2269
2270     if (!removeComposedText()) {
2271         // Could not remove the existing composition region.
2272         return -1;
2273     }
2274
2275     m_composingTextStart = start;
2276     m_composingTextEnd = end;
2277
2278     if (compositionActive())
2279         addAttributedTextMarker(start, end, compositionTextStyle());
2280
2281     InputLog(Platform::LogLevelInfo, "InputHandler::setComposingRegion start %d end %d", start, end);
2282
2283     return 0;
2284 }
2285
2286 int32_t InputHandler::finishComposition()
2287 {
2288     if (!isActiveTextEdit())
2289         return -1;
2290
2291     // FIXME if no composition is active, should we return failure -1?
2292     if (!compositionActive())
2293         return 0;
2294
2295     // Remove all markers.
2296     removeAttributedTextMarker();
2297
2298     InputLog(Platform::LogLevelInfo, "InputHandler::finishComposition completed");
2299
2300     return 0;
2301 }
2302
2303 span_t* InputHandler::firstSpanInString(spannable_string_t* spannableString, SpannableStringAttribute attrib)
2304 {
2305     span_t* span = spannableString->spans;
2306     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
2307         if (span->attributes_mask & attrib)
2308             return span;
2309         span++;
2310     }
2311
2312     return 0;
2313 }
2314
2315 bool InputHandler::isTrailingSingleCharacter(span_t* span, unsigned stringLength, unsigned composingTextLength)
2316 {
2317     // Make sure the new string is one character larger than the old.
2318     if (composingTextLength != stringLength - 1)
2319         return false;
2320
2321     if (!span)
2322         return false;
2323
2324     // Has only 1 character changed?
2325     if (span->start == span->end) {
2326         // Is this character the last character in the string?
2327         if (span->start == stringLength - 1)
2328             return true;
2329     }
2330     // Return after the first changed_attrib is found.
2331     return false;
2332 }
2333
2334 bool InputHandler::setText(spannable_string_t* spannableString)
2335 {
2336     if (!isActiveTextEdit() || !spannableString)
2337         return false;
2338
2339     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
2340     Frame* frame = m_currentFocusElement->document()->frame();
2341
2342     Editor* editor = frame->editor();
2343     ASSERT(editor);
2344
2345     // Disable selectionHandler's active selection as we will be resetting and these
2346     // changes should not be handled as notification event.
2347     m_webPage->m_selectionHandler->setSelectionActive(false);
2348
2349     WTF::String textToInsert = convertSpannableStringToString(spannableString);
2350     int textLength = textToInsert.length();
2351
2352     InputLog(Platform::LogLevelInfo,
2353         "InputHandler::setText spannableString is '%s', of length %d",
2354         textToInsert.latin1().data(), textLength);
2355
2356     span_t* changedSpan = firstSpanInString(spannableString, CHANGED_ATTRIB);
2357     int composingTextStart = m_composingTextStart;
2358     int composingTextEnd = m_composingTextEnd;
2359     int composingTextLength = compositionLength();
2360     removeAttributedTextMarker();
2361
2362     if (isTrailingSingleCharacter(changedSpan, textLength, composingTextLength)) {
2363         // If the text is unconverted, do not allow JS processing as it is not a "real"
2364         // character in the field.
2365         if (firstSpanInString(spannableString, UNCONVERTED_TEXT_ATTRIB)) {
2366             InputLog(Platform::LogLevelInfo, "InputHandler::setText Single trailing character detected. Text is unconverted.");
2367             return editor->command("InsertText").execute(textToInsert.right(1));
2368         }
2369         InputLog(Platform::LogLevelInfo, "InputHandler::setText Single trailing character detected.");
2370         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[textLength - 1], Platform::KeyboardEvent::KeyDown, 0), false /* changeIsPartOfComposition */);
2371     }
2372
2373     // If no spans have changed, treat it as a delete operation.
2374     if (!changedSpan) {
2375         // If the composition length is the same as our string length, then we don't need to do anything.
2376         if (composingTextLength == textLength) {
2377             InputLog(Platform::LogLevelInfo, "InputHandler::setText No spans have changed. New text is the same length as the old. Nothing to do.");
2378             return true;
2379         }
2380
2381         if (composingTextLength - textLength == 1) {
2382             InputLog(Platform::LogLevelInfo, "InputHandler::setText No spans have changed. New text is one character shorter than the old. Treating as 'delete'.");
2383             return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2384         }
2385     }
2386
2387     if (composingTextLength && !setSelection(composingTextStart, composingTextEnd, true /* changeIsPartOfComposition */))
2388         return false;
2389
2390     // If there is no text to add just delete.
2391     if (!textLength) {
2392         if (selectionActive())
2393             return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2394
2395         // Nothing to do.
2396         return true;
2397     }
2398
2399     // Triggering an insert of the text with a space character trailing
2400     // causes new text to adopt the previous text style.
2401     // Remove it and apply it as a keypress later.
2402     // Upstream Webkit bug created https://bugs.webkit.org/show_bug.cgi?id=70823
2403     bool requiresSpaceKeyPress = false;
2404     if (textLength > 0 && textToInsert[textLength - 1] == KEYCODE_SPACE) {
2405         requiresSpaceKeyPress = true;
2406         textLength--;
2407         textToInsert.remove(textLength, 1);
2408     }
2409
2410     InputLog(Platform::LogLevelInfo,
2411         "InputHandler::setText Request being processed. Text before processing: '%s'",
2412         elementText().latin1().data());
2413
2414     if (textLength == 1 && !spannableString->spans_count) {
2415         // Handle single key non-attributed entry as key press rather than insert to allow
2416         // triggering of javascript events.
2417         InputLog(Platform::LogLevelInfo, "InputHandler::setText Single character entry treated as key-press in the absense of spans.");
2418         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[0], Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2419     }
2420
2421     // Perform the text change as a single command if there is one.
2422     if (!textToInsert.isEmpty() && !editor->command("InsertText").execute(textToInsert)) {
2423         InputLog(Platform::LogLevelWarn,
2424             "InputHandler::setText Failed to insert text '%s'",
2425             textToInsert.latin1().data());
2426         return false;
2427     }
2428
2429     if (requiresSpaceKeyPress)
2430         handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_SPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2431
2432     InputLog(Platform::LogLevelInfo,
2433         "InputHandler::setText Request being processed. Text after processing '%s'",
2434         elementText().latin1().data());
2435
2436     return true;
2437 }
2438
2439 bool InputHandler::setTextAttributes(int insertionPoint, spannable_string_t* spannableString)
2440 {
2441     // Apply the attributes to the field.
2442     span_t* span = spannableString->spans;
2443     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
2444         unsigned int startPosition = insertionPoint + span->start;
2445         // The end point includes the character that it is before. Ie, 0, 0
2446         // applies to the first character as the end point includes the character
2447         // at the position. This means the endPosition is always +1.
2448         unsigned int endPosition = insertionPoint + span->end + 1;
2449         if (endPosition < startPosition || endPosition > elementText().length())
2450             return false;
2451
2452         if (!span->attributes_mask)
2453             continue; // Nothing to do.
2454
2455         // MISSPELLED_WORD_ATTRIB is present as an option, but it is not currently
2456         // used by IMF. When they add support for on the fly spell checking we can
2457         // use it to apply spelling markers and disable continuous spell checking.
2458
2459         InputLog(Platform::LogLevelInfo,
2460             "InputHandler::setTextAttributes adding marker %d to %d - %llu",
2461             startPosition, endPosition, span->attributes_mask);
2462         addAttributedTextMarker(startPosition, endPosition, textStyleFromMask(span->attributes_mask));
2463
2464         span++;
2465     }
2466
2467     InputLog(Platform::LogLevelInfo, "InputHandler::setTextAttributes attribute count %d", spannableString->spans_count);
2468
2469     return true;
2470 }
2471
2472 bool InputHandler::setRelativeCursorPosition(int insertionPoint, int relativeCursorPosition)
2473 {
2474     if (!isActiveTextEdit())
2475         return false;
2476
2477     // 1 place cursor at end of insertion text.
2478     if (relativeCursorPosition == 1) {
2479         m_currentFocusElement->document()->frame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2480         return true;
2481     }
2482
2483     int cursorPosition = 0;
2484     if (relativeCursorPosition <= 0) {
2485         // Zero = insertionPoint
2486         // Negative value, move the cursor the requested number of characters before
2487         // the start of the inserted text.
2488         cursorPosition = insertionPoint + relativeCursorPosition;
2489     } else {
2490         // Positive value, move the cursor the requested number of characters after
2491         // the end of the inserted text minus 1.
2492         cursorPosition = caretPosition() + relativeCursorPosition - 1;
2493     }
2494
2495     if (cursorPosition < 0 || cursorPosition > (int)elementText().length())
2496         return false;
2497
2498     InputLog(Platform::LogLevelInfo,
2499         "InputHandler::setRelativeCursorPosition cursor position %d",
2500         cursorPosition);
2501
2502     return setCursorPosition(cursorPosition);
2503 }
2504
2505 bool InputHandler::setSpannableTextAndRelativeCursor(spannable_string_t* spannableString, int relativeCursorPosition, bool markTextAsComposing)
2506 {
2507     InputLog(Platform::LogLevelInfo,
2508         "InputHandler::setSpannableTextAndRelativeCursor(%d, %d, %d)",
2509         spannableString->length, relativeCursorPosition, markTextAsComposing);
2510
2511     int insertionPoint = compositionActive() ? m_composingTextStart : selectionStart();
2512
2513     ProcessingChangeGuard guard(this);
2514
2515     if (!setText(spannableString))
2516         return false;
2517
2518     if (!setTextAttributes(insertionPoint, spannableString))
2519         return false;
2520
2521     if (!setRelativeCursorPosition(insertionPoint, relativeCursorPosition))
2522         return false;
2523
2524     if (markTextAsComposing) {
2525         m_composingTextStart = insertionPoint;
2526         m_composingTextEnd = insertionPoint + spannableString->length;
2527     }
2528
2529     return true;
2530 }
2531
2532 int32_t InputHandler::setComposingText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2533 {
2534     if (!isActiveTextEdit())
2535         return -1;
2536
2537     if (!spannableString)
2538         return -1;
2539
2540     InputLog(Platform::LogLevelInfo,
2541         "InputHandler::setComposingText at relativeCursorPosition: %d",
2542         relativeCursorPosition);
2543
2544     // Enable input mode if we are processing a key event.
2545     setInputModeEnabled();
2546
2547     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, true /* markTextAsComposing */) ? 0 : -1;
2548 }
2549
2550 int32_t InputHandler::commitText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2551 {
2552     if (!isActiveTextEdit())
2553         return -1;
2554
2555     if (!spannableString)
2556         return -1;
2557
2558     InputLog(Platform::LogLevelInfo, "InputHandler::commitText");
2559
2560     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, false /* markTextAsComposing */) ? 0 : -1;
2561 }
2562
2563 void InputHandler::restoreViewState()
2564 {
2565     setInputModeEnabled();
2566 }
2567
2568 void InputHandler::showTextInputTypeSuggestionBox(bool allowEmptyPrefix)
2569 {
2570     HTMLInputElement* focusedInputElement = static_cast<HTMLInputElement*>(m_currentFocusElement->toInputElement());
2571     if (!focusedInputElement || !focusedInputElement->isTextField())
2572         return;
2573
2574     if (!focusedInputElement)
2575         return;
2576
2577     if (!m_suggestionDropdownBoxHandler)
2578         m_suggestionDropdownBoxHandler = SuggestionBoxHandler::create(focusedInputElement);
2579
2580     // If the focused input element isn't the same as the one inside the handler, reset and display.
2581     // If the focused element is the same, display the suggestions.
2582     if ((m_suggestionDropdownBoxHandler->focusedElement()) && m_suggestionDropdownBoxHandler->focusedElement() != focusedInputElement)
2583         m_suggestionDropdownBoxHandler->setInputElementAndUpdateDisplay(focusedInputElement);
2584     else
2585         m_suggestionDropdownBoxHandler->showDropdownBox(allowEmptyPrefix);
2586 }
2587
2588 void InputHandler::hideTextInputTypeSuggestionBox()
2589 {
2590     if (m_suggestionDropdownBoxHandler)
2591         m_suggestionDropdownBoxHandler->hideDropdownBox();
2592 }
2593
2594 void InputHandler::elementTouched(WebCore::Element* nonShadowElementUnderFatFinger)
2595 {
2596     // Attempt to show all suggestions when the input field is empty and a tap is registered when the element is focused.
2597     if (isActiveTextEdit() && nonShadowElementUnderFatFinger == m_currentFocusElement)
2598         showTextInputTypeSuggestionBox(true /* allowEmptyPrefix */);
2599 }
2600
2601 }
2602 }