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