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