[BlackBerry] Clear supression flag when caret changes aren't propogated through
[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         m_webPage->m_client->suppressCaretChangeNotification(true /*shouldClearState*/);
1708         return;
1709     }
1710
1711     // Scroll the field if necessary. This must be done even if we are processing
1712     // a change as the text change may have moved the caret. IMF doesn't require
1713     // the update, but the user needs to see the caret.
1714     if (m_shouldEnsureFocusTextElementVisibleOnSelectionChanged) {
1715         ensureFocusTextElementVisible(EdgeIfNeeded);
1716         m_shouldEnsureFocusTextElementVisibleOnSelectionChanged = false;
1717     }
1718
1719     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1720
1721     if (!m_shouldNotifyWebView)
1722         return;
1723
1724     int newSelectionStart = selectionStart();
1725     int newSelectionEnd = selectionEnd();
1726
1727     InputLog(Platform::LogLevelInfo,
1728         "InputHandler::selectionChanged selectionStart=%u, selectionEnd=%u",
1729         newSelectionStart, newSelectionEnd);
1730
1731     m_webPage->m_client->inputSelectionChanged(newSelectionStart, newSelectionEnd);
1732
1733     // Remove the attributed text markers as the previous call triggered an end to
1734     // the composition.
1735     removeAttributedTextMarker();
1736 }
1737
1738 bool InputHandler::setCursorPosition(int location)
1739 {
1740     return setSelection(location, location);
1741 }
1742
1743 bool InputHandler::setSelection(int start, int end, bool changeIsPartOfComposition)
1744 {
1745     if (!isActiveTextEdit())
1746         return false;
1747
1748     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1749
1750     ProcessingChangeGuard guard(this);
1751
1752     VisibleSelection newSelection = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end);
1753     m_currentFocusElement->document()->frame()->selection()->setSelection(newSelection, changeIsPartOfComposition ? 0 : FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
1754
1755     InputLog(Platform::LogLevelInfo,
1756         "InputHandler::setSelection selectionStart=%u, selectionEnd=%u",
1757         start, end);
1758
1759     return start == selectionStart() && end == selectionEnd();
1760 }
1761
1762 WebCore::IntRect InputHandler::rectForCaret(int index)
1763 {
1764     if (!isActiveTextEdit())
1765         return WebCore::IntRect();
1766
1767     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1768
1769     if (index < 0 || index > static_cast<int>(elementText().length())) {
1770         // Invalid request.
1771         return WebCore::IntRect();
1772     }
1773
1774     FrameSelection caretSelection;
1775     caretSelection.setSelection(DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), index, index).visibleStart());
1776     caretSelection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
1777     return caretSelection.selection().visibleStart().absoluteCaretBounds();
1778 }
1779
1780 void InputHandler::cancelSelection()
1781 {
1782     if (!isActiveTextEdit())
1783         return;
1784
1785     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1786
1787     int selectionStartPosition = selectionStart();
1788     ProcessingChangeGuard guard(this);
1789     setCursorPosition(selectionStartPosition);
1790 }
1791
1792 bool InputHandler::isNavigationKey(unsigned character) const
1793 {
1794     return character == KEYCODE_UP
1795         || character == KEYCODE_DOWN
1796         || character == KEYCODE_LEFT
1797         || character == KEYCODE_RIGHT;
1798 }
1799
1800 bool InputHandler::handleKeyboardInput(const Platform::KeyboardEvent& keyboardEvent, bool changeIsPartOfComposition)
1801 {
1802     InputLog(Platform::LogLevelInfo,
1803         "InputHandler::handleKeyboardInput received character='%c', type=%d",
1804         keyboardEvent.character(), keyboardEvent.type());
1805
1806     // Clearing the m_shouldNotifyWebView state on any KeyboardEvent.
1807     m_shouldNotifyWebView = true;
1808
1809     // Enable input mode if we are processing a key event.
1810     setInputModeEnabled();
1811
1812     Platform::KeyboardEvent::Type type = keyboardEvent.type();
1813     /*
1814      * IMF sends us an unadultered KeyUp for all key presses. This key event should be allowed to be processed at all times.
1815      * We bypass the check because the state of composition has no implication on this key event.
1816      * In order to ensure we allow the correct key event through, we keep track of key down events with m_expectedKeyUpChar.
1817     */
1818     if (type == Platform::KeyboardEvent::KeyUp) {
1819         // When IMF auto-capitalizes a KeyDown, say the first letter of a new sentence, our KeyUp will still be in lowercase.
1820         if (m_expectedKeyUpChar == keyboardEvent.character() || (isASCIIUpper(m_expectedKeyUpChar) && m_expectedKeyUpChar == toASCIIUpper(keyboardEvent.character()))) {
1821             m_expectedKeyUpChar = 0;
1822             changeIsPartOfComposition = true;
1823         }
1824     }
1825
1826     // If we aren't specifically part of a composition, fail, IMF should never send key input
1827     // while composing text. If IMF has failed, we should have already finished the
1828     // composition manually. There is a caveat for KeyUp which is explained above.
1829     if (!changeIsPartOfComposition && compositionActive()) {
1830         if (type == Platform::KeyboardEvent::KeyDown && isNavigationKey(keyboardEvent.character()))
1831             removeAttributedTextMarker();
1832         else
1833             return false;
1834     }
1835
1836     ProcessingChangeGuard guard(this);
1837
1838     unsigned adjustedModifiers = keyboardEvent.modifiers();
1839     if (WTF::isASCIIUpper(keyboardEvent.character()))
1840         adjustedModifiers |= KEYMOD_SHIFT;
1841
1842     ASSERT(m_webPage->m_page->focusController());
1843     bool keyboardEventHandled = false;
1844     if (Frame* focusedFrame = m_webPage->m_page->focusController()->focusedFrame()) {
1845         bool isKeyChar = type == Platform::KeyboardEvent::KeyChar;
1846
1847         // If this is a KeyChar type then we handle it as a keydown followed by a key up.
1848         if (isKeyChar)
1849             type = Platform::KeyboardEvent::KeyDown;
1850         else if (type == Platform::KeyboardEvent::KeyDown) {
1851             m_expectedKeyUpChar = keyboardEvent.character();
1852
1853             m_shouldNotifyWebView = shouldNotifyWebView(keyboardEvent);
1854         }
1855
1856         Platform::KeyboardEvent adjustedKeyboardEvent(keyboardEvent.character(), type, adjustedModifiers, keyboardEvent.keycode(), keyboardEvent.alternateCharacter(), keyboardEvent.sourceDevice());
1857         keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent));
1858
1859         m_shouldNotifyWebView = true;
1860
1861         if (isKeyChar) {
1862             type = Platform::KeyboardEvent::KeyUp;
1863             adjustedKeyboardEvent = Platform::KeyboardEvent(keyboardEvent.character(), type, adjustedModifiers, keyboardEvent.keycode(), keyboardEvent.alternateCharacter(), keyboardEvent.sourceDevice());
1864             keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent)) || keyboardEventHandled;
1865         }
1866
1867         if (!changeIsPartOfComposition && type == Platform::KeyboardEvent::KeyUp)
1868             ensureFocusTextElementVisible(EdgeIfNeeded);
1869     }
1870
1871     if (m_currentFocusElement && keyboardEventHandled)
1872         showTextInputTypeSuggestionBox();
1873
1874     return keyboardEventHandled;
1875 }
1876
1877 bool InputHandler::shouldNotifyWebView(const Platform::KeyboardEvent& keyboardEvent)
1878 {
1879     // If we receive the KeyDown of a Backspace or Enter key, set this flag to prevent sending unnecessary selection and caret changes to IMF.
1880     return !(keyboardEvent.character() == KEYCODE_BACKSPACE || keyboardEvent.character() == KEYCODE_RETURN || keyboardEvent.character() == KEYCODE_KP_ENTER);
1881 }
1882
1883 bool InputHandler::deleteSelection()
1884 {
1885     if (!isActiveTextEdit())
1886         return false;
1887
1888     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1889     Frame* frame = m_currentFocusElement->document()->frame();
1890
1891     if (frame->selection()->selectionType() != VisibleSelection::RangeSelection)
1892         return false;
1893
1894     ASSERT(frame->editor());
1895     return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), false /* changeIsPartOfComposition */);
1896 }
1897
1898 void InputHandler::insertText(const WTF::String& string)
1899 {
1900     if (!isActiveTextEdit())
1901         return;
1902
1903     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1904     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1905     editor->command("InsertText").execute(string);
1906 }
1907
1908 void InputHandler::clearField()
1909 {
1910     if (!isActiveTextEdit())
1911         return;
1912
1913     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1914     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1915
1916     editor->command("SelectAll").execute();
1917     editor->command("DeleteBackward").execute();
1918 }
1919
1920 bool InputHandler::executeTextEditCommand(const WTF::String& commandName)
1921 {
1922     ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->editor());
1923     Editor* editor = m_webPage->focusedOrMainFrame()->editor();
1924
1925     return editor->command(commandName).execute();
1926 }
1927
1928 void InputHandler::cut()
1929 {
1930     executeTextEditCommand("Cut");
1931 }
1932
1933 void InputHandler::copy()
1934 {
1935     executeTextEditCommand("Copy");
1936 }
1937
1938 void InputHandler::paste()
1939 {
1940     executeTextEditCommand("Paste");
1941 }
1942
1943 void InputHandler::selectAll()
1944 {
1945     executeTextEditCommand("SelectAll");
1946 }
1947
1948 void InputHandler::addAttributedTextMarker(int start, int end, const AttributeTextStyle& style)
1949 {
1950     if ((end - start) < 1 || end > static_cast<int>(elementText().length()))
1951         return;
1952
1953     RefPtr<Range> markerRange = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end).toNormalizedRange();
1954     m_currentFocusElement->document()->markers()->addMarker(markerRange.get(), DocumentMarker::AttributeText, WTF::String("Input Marker"), style);
1955 }
1956
1957 void InputHandler::removeAttributedTextMarker()
1958 {
1959     // Remove all attribute text markers.
1960     if (m_currentFocusElement && m_currentFocusElement->document())
1961         m_currentFocusElement->document()->markers()->removeMarkers(DocumentMarker::AttributeText);
1962
1963     m_composingTextStart = 0;
1964     m_composingTextEnd = 0;
1965 }
1966
1967 void InputHandler::clearCurrentFocusElement()
1968 {
1969     if (m_currentFocusElement)
1970         m_currentFocusElement->blur();
1971 }
1972
1973 bool InputHandler::willOpenPopupForNode(Node* node)
1974 {
1975     // This method must be kept synchronized with InputHandler::didNodeOpenPopup.
1976     if (!node)
1977         return false;
1978
1979     ASSERT(!node->isInShadowTree());
1980
1981     if (node->hasTagName(HTMLNames::selectTag) || node->hasTagName(HTMLNames::optionTag)) {
1982         // We open list popups for options and selects.
1983         return true;
1984     }
1985
1986     if (node->isElementNode()) {
1987         Element* element = toElement(node);
1988         if (DOMSupport::isPopupInputField(element))
1989             return true;
1990     }
1991
1992     return false;
1993 }
1994
1995 bool InputHandler::didNodeOpenPopup(Node* node)
1996 {
1997     // This method must be kept synchronized with InputHandler::willOpenPopupForNode.
1998     if (!node)
1999         return false;
2000
2001     ASSERT(!node->isInShadowTree());
2002
2003     if (node->hasTagName(HTMLNames::selectTag))
2004         return openSelectPopup(static_cast<HTMLSelectElement*>(node));
2005
2006     if (node->hasTagName(HTMLNames::optionTag)) {
2007         HTMLOptionElement* optionElement = static_cast<HTMLOptionElement*>(node);
2008         return openSelectPopup(optionElement->ownerSelectElement());
2009     }
2010
2011     if (HTMLInputElement* element = node->toInputElement()) {
2012         if (DOMSupport::isDateTimeInputField(element))
2013             return openDatePopup(element, elementType(element));
2014
2015         if (DOMSupport::isColorInputField(element))
2016             return openColorPopup(element);
2017     }
2018     return false;
2019 }
2020
2021 bool InputHandler::openSelectPopup(HTMLSelectElement* select)
2022 {
2023     if (!select || select->isDisabledFormControl())
2024         return false;
2025
2026     // If there's no view, do nothing and return.
2027     if (!select->document()->view())
2028         return false;
2029
2030     if (isActiveTextEdit())
2031         clearCurrentFocusElement();
2032
2033     m_currentFocusElement = select;
2034     m_currentFocusElementType = SelectPopup;
2035
2036     const WTF::Vector<HTMLElement*>& listItems = select->listItems();
2037     int size = listItems.size();
2038
2039     bool multiple = select->multiple();
2040     ScopeArray<BlackBerry::Platform::String> labels;
2041     labels.reset(new BlackBerry::Platform::String[size]);
2042
2043     bool* enableds = 0;
2044     int* itemTypes = 0;
2045     bool* selecteds = 0;
2046
2047     if (size) {
2048         enableds = new bool[size];
2049         itemTypes = new int[size];
2050         selecteds = new bool[size];
2051         for (int i = 0; i < size; i++) {
2052             if (listItems[i]->hasTagName(HTMLNames::optionTag)) {
2053                 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(listItems[i]);
2054                 labels[i] = option->textIndentedToRespectGroupLabel();
2055                 enableds[i] = option->isDisabledFormControl() ? 0 : 1;
2056                 selecteds[i] = option->selected();
2057                 itemTypes[i] = option->parentNode() && option->parentNode()->hasTagName(HTMLNames::optgroupTag) ? TypeOptionInGroup : TypeOption;
2058             } else if (listItems[i]->hasTagName(HTMLNames::optgroupTag)) {
2059                 HTMLOptGroupElement* optGroup = static_cast<HTMLOptGroupElement*>(listItems[i]);
2060                 labels[i] = optGroup->groupLabelText();
2061                 enableds[i] = optGroup->isDisabledFormControl() ? 0 : 1;
2062                 selecteds[i] = false;
2063                 itemTypes[i] = TypeGroup;
2064             } else if (listItems[i]->hasTagName(HTMLNames::hrTag)) {
2065                 enableds[i] = false;
2066                 selecteds[i] = false;
2067                 itemTypes[i] = TypeSeparator;
2068             }
2069         }
2070     }
2071
2072     SelectPopupClient* selectClient = new SelectPopupClient(multiple, size, labels, enableds, itemTypes, selecteds, m_webPage, select);
2073     WebCore::IntRect elementRectInRootView = select->document()->view()->contentsToRootView(enclosingIntRect(select->getRect()));
2074     // Fail to create HTML popup, use the old path
2075     if (!m_webPage->openPagePopup(selectClient, elementRectInRootView))
2076         m_webPage->m_client->openPopupList(multiple, size, labels, enableds, itemTypes, selecteds);
2077     delete[] enableds;
2078     delete[] itemTypes;
2079     delete[] selecteds;
2080     return true;
2081 }
2082
2083 void InputHandler::setPopupListIndex(int index)
2084 {
2085     if (index == -2) // Abandon
2086         return clearCurrentFocusElement();
2087
2088     if (!isActiveSelectPopup())
2089         return clearCurrentFocusElement();
2090
2091     RenderObject* renderer = m_currentFocusElement->renderer();
2092     if (renderer && renderer->isMenuList()) {
2093         RenderMenuList* renderMenu = toRenderMenuList(renderer);
2094         renderMenu->hidePopup();
2095     }
2096
2097     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
2098     int optionIndex = selectElement->listToOptionIndex(index);
2099     selectElement->optionSelectedByUser(optionIndex, true /* deselect = true */, true /* fireOnChangeNow = false */);
2100     clearCurrentFocusElement();
2101 }
2102
2103 void InputHandler::setPopupListIndexes(int size, const bool* selecteds)
2104 {
2105     if (!isActiveSelectPopup())
2106         return clearCurrentFocusElement();
2107
2108     if (size < 0)
2109         return;
2110
2111     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
2112     const WTF::Vector<HTMLElement*>& items = selectElement->listItems();
2113     if (items.size() != static_cast<unsigned>(size))
2114         return;
2115
2116     HTMLOptionElement* option;
2117     for (int i = 0; i < size; i++) {
2118         if (items[i]->hasTagName(HTMLNames::optionTag)) {
2119             option = static_cast<HTMLOptionElement*>(items[i]);
2120             option->setSelectedState(selecteds[i]);
2121         }
2122     }
2123
2124     // Force repaint because we do not send mouse events to the select element
2125     // and the element doesn't automatically repaint itself.
2126     selectElement->dispatchFormControlChangeEvent();
2127     selectElement->renderer()->repaint();
2128     clearCurrentFocusElement();
2129 }
2130
2131 bool InputHandler::setBatchEditingActive(bool active)
2132 {
2133     if (!isActiveTextEdit())
2134         return false;
2135
2136     ASSERT(m_currentFocusElement->document());
2137     ASSERT(m_currentFocusElement->document()->frame());
2138
2139     // FIXME switch this to m_currentFocusElement->document()->frame() when we have separate
2140     // backingstore for each frame.
2141     BackingStoreClient* backingStoreClient = m_webPage->backingStoreClient();
2142     ASSERT(backingStoreClient);
2143
2144     // Enable / Disable the backingstore to prevent visual updates.
2145     // FIXME: Do we really need to suspend/resume both backingstore and screen here?
2146     if (!active) {
2147         backingStoreClient->backingStore()->resumeBackingStoreUpdates();
2148         backingStoreClient->backingStore()->resumeScreenUpdates(BackingStore::RenderAndBlit);
2149     } else {
2150         backingStoreClient->backingStore()->suspendBackingStoreUpdates();
2151         backingStoreClient->backingStore()->suspendScreenUpdates();
2152     }
2153
2154     return true;
2155 }
2156
2157 bool InputHandler::isCaretAtEndOfText()
2158 {
2159     return caretPosition() == static_cast<int>(elementText().length());
2160 }
2161
2162 int InputHandler::caretPosition() const
2163 {
2164     if (!isActiveTextEdit())
2165         return -1;
2166
2167     // NOTE: This function is expected to return the start of the selection if
2168     // a selection is applied.
2169     return selectionStart();
2170 }
2171
2172 int relativeLeftOffset(int caretPosition, int leftOffset)
2173 {
2174     ASSERT(caretPosition >= 0);
2175
2176     return std::max(0, caretPosition - leftOffset);
2177 }
2178
2179 int relativeRightOffset(int caretPosition, unsigned totalLengthOfText, int rightOffset)
2180 {
2181     ASSERT(caretPosition >= 0);
2182     ASSERT(totalLengthOfText < INT_MAX);
2183
2184     return std::min(caretPosition + rightOffset, static_cast<int>(totalLengthOfText));
2185 }
2186
2187 bool InputHandler::deleteTextRelativeToCursor(int leftOffset, int rightOffset)
2188 {
2189     if (!isActiveTextEdit() || compositionActive())
2190         return false;
2191
2192     ProcessingChangeGuard guard(this);
2193
2194     InputLog(Platform::LogLevelInfo,
2195         "InputHandler::deleteTextRelativeToCursor left %d right %d",
2196         leftOffset, rightOffset);
2197
2198     int caretOffset = caretPosition();
2199     int start = relativeLeftOffset(caretOffset, leftOffset);
2200     int end = relativeRightOffset(caretOffset, elementText().length(), rightOffset);
2201
2202     // If we have backspace in a single character, send this to webkit as a KeyboardEvent. Otherwise, call deleteText.
2203     if (leftOffset == 1 && !rightOffset) {
2204         if (!handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */))
2205             return false;
2206     } else if (!deleteText(start, end))
2207         return false;
2208
2209     // Scroll the field if necessary. The automatic update is suppressed
2210     // by the processing change guard.
2211     ensureFocusTextElementVisible(EdgeIfNeeded);
2212
2213     return true;
2214 }
2215
2216 bool InputHandler::deleteText(int start, int end)
2217 {
2218     if (!isActiveTextEdit())
2219         return false;
2220
2221     ProcessingChangeGuard guard(this);
2222
2223     if (end - start == 1)
2224         return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2225
2226     if (!setSelection(start, end, true /*changeIsPartOfComposition*/))
2227         return false;
2228
2229     InputLog(Platform::LogLevelInfo, "InputHandler::deleteText start %d end %d", start, end);
2230
2231     return deleteSelection();
2232 }
2233
2234 spannable_string_t* InputHandler::spannableTextInRange(int start, int end, int32_t)
2235 {
2236     if (!isActiveTextEdit())
2237         return 0;
2238
2239     if (start >= end)
2240         return 0;
2241
2242     int length = end - start;
2243
2244     WTF::String textString = elementText().substring(start, length);
2245
2246     spannable_string_t* pst = (spannable_string_t*)fastMalloc(sizeof(spannable_string_t));
2247
2248     // Don't use fastMalloc in case the string is unreasonably long. fastMalloc will
2249     // crash immediately on failure.
2250     pst->str = (wchar_t*)malloc(sizeof(wchar_t) * (length + 1));
2251     if (!pst->str) {
2252         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::spannableTextInRange Cannot allocate memory for string.");
2253         free(pst);
2254         return 0;
2255     }
2256
2257     int stringLength = 0;
2258     if (!convertStringToWchar(textString, pst->str, length + 1, &stringLength)) {
2259         Platform::logAlways(Platform::LogLevelCritical, "InputHandler::spannableTextInRange failed to convert string.");
2260         free(pst->str);
2261         free(pst);
2262         return 0;
2263     }
2264
2265     pst->length = stringLength;
2266     pst->spans_count = 0;
2267     pst->spans = 0;
2268
2269     return pst;
2270 }
2271
2272 spannable_string_t* InputHandler::selectedText(int32_t flags)
2273 {
2274     if (!isActiveTextEdit())
2275         return 0;
2276
2277     return spannableTextInRange(selectionStart(), selectionEnd(), flags);
2278 }
2279
2280 spannable_string_t* InputHandler::textBeforeCursor(int32_t length, int32_t flags)
2281 {
2282     if (!isActiveTextEdit())
2283         return 0;
2284
2285     int caretOffset = caretPosition();
2286     int start = relativeLeftOffset(caretOffset, length);
2287     int end = caretOffset;
2288
2289     return spannableTextInRange(start, end, flags);
2290 }
2291
2292 spannable_string_t* InputHandler::textAfterCursor(int32_t length, int32_t flags)
2293 {
2294     if (!isActiveTextEdit())
2295         return 0;
2296
2297     int caretOffset = caretPosition();
2298     int start = caretOffset;
2299     int end = relativeRightOffset(caretOffset, elementText().length(), length);
2300
2301     return spannableTextInRange(start, end, flags);
2302 }
2303
2304 extracted_text_t* InputHandler::extractedTextRequest(extracted_text_request_t*, int32_t flags)
2305 {
2306     if (!isActiveTextEdit())
2307         return 0;
2308
2309     extracted_text_t* extractedText = (extracted_text_t *)fastMalloc(sizeof(extracted_text_t));
2310
2311     // 'flags' indicates whether the text is being monitored. This is not currently used.
2312
2313     // FIXME add smart limiting based on the hint sizes. Currently return all text.
2314
2315     extractedText->text = spannableTextInRange(0, elementText().length(), flags);
2316
2317     // FIXME confirm these should be 0 on this requested. Text changes will likely require
2318     // the end be the length.
2319     extractedText->partial_start_offset = 0;
2320     extractedText->partial_end_offset = 0;
2321     extractedText->start_offset = 0;
2322
2323     // Adjust selection values relative to the start offset, which may be a subset
2324     // of the text in the field.
2325     extractedText->selection_start = selectionStart() - extractedText->start_offset;
2326     extractedText->selection_end = selectionEnd() - extractedText->start_offset;
2327
2328     // selectionActive is not limited to inside the extracted text.
2329     bool selectionActive = extractedText->selection_start != extractedText->selection_end;
2330     bool singleLine = m_currentFocusElement->hasTagName(HTMLNames::inputTag);
2331
2332     // FIXME flags has two values in doc, enum not in header yet.
2333     extractedText->flags = selectionActive & singleLine;
2334
2335     return extractedText;
2336 }
2337
2338 static void addCompositionTextStyleToAttributeTextStyle(AttributeTextStyle& style)
2339 {
2340     style.setUnderline(AttributeTextStyle::StandardUnderline);
2341 }
2342
2343 static void addActiveTextStyleToAttributeTextStyle(AttributeTextStyle& style)
2344 {
2345     style.setBackgroundColor(Color("blue"));
2346     style.setTextColor(Color("white"));
2347 }
2348
2349 static AttributeTextStyle compositionTextStyle()
2350 {
2351     AttributeTextStyle style;
2352     addCompositionTextStyleToAttributeTextStyle(style);
2353     return style;
2354 }
2355
2356 static AttributeTextStyle textStyleFromMask(int64_t mask)
2357 {
2358     AttributeTextStyle style;
2359     if (mask & COMPOSED_TEXT_ATTRIB)
2360         addCompositionTextStyleToAttributeTextStyle(style);
2361
2362     if (mask & ACTIVE_REGION_ATTRIB)
2363         addActiveTextStyleToAttributeTextStyle(style);
2364
2365     return style;
2366 }
2367
2368 bool InputHandler::removeComposedText()
2369 {
2370     if (compositionActive()) {
2371         if (!deleteText(m_composingTextStart, m_composingTextEnd)) {
2372             // Could not remove the existing composition region.
2373             return false;
2374         }
2375         removeAttributedTextMarker();
2376     }
2377
2378     return true;
2379 }
2380
2381 int32_t InputHandler::setComposingRegion(int32_t start, int32_t end)
2382 {
2383     if (!isActiveTextEdit())
2384         return -1;
2385
2386     if (!removeComposedText()) {
2387         // Could not remove the existing composition region.
2388         return -1;
2389     }
2390
2391     m_composingTextStart = start;
2392     m_composingTextEnd = end;
2393
2394     if (compositionActive())
2395         addAttributedTextMarker(start, end, compositionTextStyle());
2396
2397     InputLog(Platform::LogLevelInfo, "InputHandler::setComposingRegion start %d end %d", start, end);
2398
2399     return 0;
2400 }
2401
2402 int32_t InputHandler::finishComposition()
2403 {
2404     if (!isActiveTextEdit())
2405         return -1;
2406
2407     // FIXME if no composition is active, should we return failure -1?
2408     if (!compositionActive())
2409         return 0;
2410
2411     // Remove all markers.
2412     removeAttributedTextMarker();
2413
2414     InputLog(Platform::LogLevelInfo, "InputHandler::finishComposition completed");
2415
2416     return 0;
2417 }
2418
2419 span_t* InputHandler::firstSpanInString(spannable_string_t* spannableString, SpannableStringAttribute attrib)
2420 {
2421     span_t* span = spannableString->spans;
2422     for (unsigned i = 0; i < spannableString->spans_count; i++) {
2423         if (span->attributes_mask & attrib)
2424             return span;
2425         span++;
2426     }
2427
2428     return 0;
2429 }
2430
2431 bool InputHandler::isTrailingSingleCharacter(span_t* span, unsigned stringLength, unsigned composingTextLength)
2432 {
2433     // Make sure the new string is one character larger than the old.
2434     if (composingTextLength != stringLength - 1)
2435         return false;
2436
2437     if (!span)
2438         return false;
2439
2440     // Has only 1 character changed?
2441     if (span->start == span->end) {
2442         // Is this character the last character in the string?
2443         if (span->start == stringLength - 1)
2444             return true;
2445     }
2446     // Return after the first changed_attrib is found.
2447     return false;
2448 }
2449
2450 bool InputHandler::setText(spannable_string_t* spannableString)
2451 {
2452     if (!isActiveTextEdit() || !spannableString)
2453         return false;
2454
2455     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
2456     Frame* frame = m_currentFocusElement->document()->frame();
2457
2458     Editor* editor = frame->editor();
2459     ASSERT(editor);
2460
2461     // Disable selectionHandler's active selection as we will be resetting and these
2462     // changes should not be handled as notification event.
2463     m_webPage->m_selectionHandler->setSelectionActive(false);
2464
2465     WTF::String textToInsert = convertSpannableStringToString(spannableString);
2466     int textLength = textToInsert.length();
2467
2468     InputLog(Platform::LogLevelInfo,
2469         "InputHandler::setText spannableString is '%s', of length %d",
2470         textToInsert.latin1().data(), textLength);
2471
2472     span_t* changedSpan = firstSpanInString(spannableString, CHANGED_ATTRIB);
2473     int composingTextStart = m_composingTextStart;
2474     int composingTextEnd = m_composingTextEnd;
2475     int composingTextLength = compositionLength();
2476     removeAttributedTextMarker();
2477
2478     if (isTrailingSingleCharacter(changedSpan, textLength, composingTextLength)) {
2479         // If the text is unconverted, do not allow JS processing as it is not a "real"
2480         // character in the field.
2481         if (firstSpanInString(spannableString, UNCONVERTED_TEXT_ATTRIB)) {
2482             InputLog(Platform::LogLevelInfo, "InputHandler::setText Single trailing character detected. Text is unconverted.");
2483             return editor->command("InsertText").execute(textToInsert.right(1));
2484         }
2485         InputLog(Platform::LogLevelInfo, "InputHandler::setText Single trailing character detected.");
2486         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[textLength - 1], Platform::KeyboardEvent::KeyDown, 0), false /* changeIsPartOfComposition */);
2487     }
2488
2489     // If no spans have changed, treat it as a delete operation.
2490     if (!changedSpan) {
2491         // If the composition length is the same as our string length, then we don't need to do anything.
2492         if (composingTextLength == textLength) {
2493             InputLog(Platform::LogLevelInfo, "InputHandler::setText No spans have changed. New text is the same length as the old. Nothing to do.");
2494             return true;
2495         }
2496
2497         if (composingTextLength - textLength == 1) {
2498             InputLog(Platform::LogLevelInfo, "InputHandler::setText No spans have changed. New text is one character shorter than the old. Treating as 'delete'.");
2499             return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2500         }
2501     }
2502
2503     if (composingTextLength && !setSelection(composingTextStart, composingTextEnd, true /* changeIsPartOfComposition */))
2504         return false;
2505
2506     // If there is no text to add just delete.
2507     if (!textLength) {
2508         if (selectionActive())
2509             return handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_BACKSPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2510
2511         // Nothing to do.
2512         return true;
2513     }
2514
2515     // Triggering an insert of the text with a space character trailing
2516     // causes new text to adopt the previous text style.
2517     // Remove it and apply it as a keypress later.
2518     // Upstream Webkit bug created https://bugs.webkit.org/show_bug.cgi?id=70823
2519     bool requiresSpaceKeyPress = false;
2520     if (textLength > 0 && textToInsert[textLength - 1] == KEYCODE_SPACE) {
2521         requiresSpaceKeyPress = true;
2522         textLength--;
2523         textToInsert.remove(textLength, 1);
2524     }
2525
2526     InputLog(Platform::LogLevelInfo,
2527         "InputHandler::setText Request being processed. Text before processing: '%s'",
2528         elementText().latin1().data());
2529
2530     if (textLength == 1 && !spannableString->spans_count) {
2531         // Handle single key non-attributed entry as key press rather than insert to allow
2532         // triggering of javascript events.
2533         InputLog(Platform::LogLevelInfo, "InputHandler::setText Single character entry treated as key-press in the absense of spans.");
2534         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[0], Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2535     }
2536
2537     // Perform the text change as a single command if there is one.
2538     if (!textToInsert.isEmpty() && !editor->command("InsertText").execute(textToInsert)) {
2539         InputLog(Platform::LogLevelWarn,
2540             "InputHandler::setText Failed to insert text '%s'",
2541             textToInsert.latin1().data());
2542         return false;
2543     }
2544
2545     if (requiresSpaceKeyPress)
2546         handleKeyboardInput(Platform::KeyboardEvent(KEYCODE_SPACE, Platform::KeyboardEvent::KeyDown, 0), true /* changeIsPartOfComposition */);
2547
2548     InputLog(Platform::LogLevelInfo,
2549         "InputHandler::setText Request being processed. Text after processing '%s'",
2550         elementText().latin1().data());
2551
2552     return true;
2553 }
2554
2555 bool InputHandler::setTextAttributes(int insertionPoint, spannable_string_t* spannableString)
2556 {
2557     // Apply the attributes to the field.
2558     span_t* span = spannableString->spans;
2559     for (unsigned i = 0; i < spannableString->spans_count; i++) {
2560         unsigned startPosition = insertionPoint + span->start;
2561         // The end point includes the character that it is before. Ie, 0, 0
2562         // applies to the first character as the end point includes the character
2563         // at the position. This means the endPosition is always +1.
2564         unsigned endPosition = insertionPoint + span->end + 1;
2565         if (endPosition < startPosition || endPosition > elementText().length())
2566             return false;
2567
2568         if (!span->attributes_mask)
2569             continue; // Nothing to do.
2570
2571         // MISSPELLED_WORD_ATTRIB is present as an option, but it is not currently
2572         // used by IMF. When they add support for on the fly spell checking we can
2573         // use it to apply spelling markers and disable continuous spell checking.
2574
2575         InputLog(Platform::LogLevelInfo,
2576             "InputHandler::setTextAttributes adding marker %d to %d - %llu",
2577             startPosition, endPosition, span->attributes_mask);
2578         addAttributedTextMarker(startPosition, endPosition, textStyleFromMask(span->attributes_mask));
2579
2580         span++;
2581     }
2582
2583     InputLog(Platform::LogLevelInfo, "InputHandler::setTextAttributes attribute count %d", spannableString->spans_count);
2584
2585     return true;
2586 }
2587
2588 bool InputHandler::setRelativeCursorPosition(int insertionPoint, int relativeCursorPosition)
2589 {
2590     if (!isActiveTextEdit())
2591         return false;
2592
2593     // 1 place cursor at end of insertion text.
2594     if (relativeCursorPosition == 1) {
2595         m_currentFocusElement->document()->frame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2596         return true;
2597     }
2598
2599     int cursorPosition = 0;
2600     if (relativeCursorPosition <= 0) {
2601         // Zero = insertionPoint
2602         // Negative value, move the cursor the requested number of characters before
2603         // the start of the inserted text.
2604         cursorPosition = insertionPoint + relativeCursorPosition;
2605     } else {
2606         // Positive value, move the cursor the requested number of characters after
2607         // the end of the inserted text minus 1.
2608         cursorPosition = caretPosition() + relativeCursorPosition - 1;
2609     }
2610
2611     if (cursorPosition < 0 || cursorPosition > (int)elementText().length())
2612         return false;
2613
2614     InputLog(Platform::LogLevelInfo,
2615         "InputHandler::setRelativeCursorPosition cursor position %d",
2616         cursorPosition);
2617
2618     return setCursorPosition(cursorPosition);
2619 }
2620
2621 bool InputHandler::setSpannableTextAndRelativeCursor(spannable_string_t* spannableString, int relativeCursorPosition, bool markTextAsComposing)
2622 {
2623     InputLog(Platform::LogLevelInfo,
2624         "InputHandler::setSpannableTextAndRelativeCursor(%d, %d, %d)",
2625         spannableString->length, relativeCursorPosition, markTextAsComposing);
2626
2627     int insertionPoint = compositionActive() ? m_composingTextStart : selectionStart();
2628
2629     ProcessingChangeGuard guard(this);
2630
2631     if (!setText(spannableString))
2632         return false;
2633
2634     if (!setTextAttributes(insertionPoint, spannableString))
2635         return false;
2636
2637     if (!setRelativeCursorPosition(insertionPoint, relativeCursorPosition))
2638         return false;
2639
2640     if (markTextAsComposing) {
2641         m_composingTextStart = insertionPoint;
2642         m_composingTextEnd = insertionPoint + spannableString->length;
2643     }
2644
2645     return true;
2646 }
2647
2648 int32_t InputHandler::setComposingText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2649 {
2650     if (!isActiveTextEdit())
2651         return -1;
2652
2653     if (!spannableString)
2654         return -1;
2655
2656     InputLog(Platform::LogLevelInfo,
2657         "InputHandler::setComposingText at relativeCursorPosition: %d",
2658         relativeCursorPosition);
2659
2660     // Enable input mode if we are processing a key event.
2661     setInputModeEnabled();
2662
2663     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, true /* markTextAsComposing */) ? 0 : -1;
2664 }
2665
2666 int32_t InputHandler::commitText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2667 {
2668     if (!isActiveTextEdit())
2669         return -1;
2670
2671     if (!spannableString)
2672         return -1;
2673
2674     InputLog(Platform::LogLevelInfo, "InputHandler::commitText");
2675
2676     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, false /* markTextAsComposing */) ? 0 : -1;
2677 }
2678
2679 void InputHandler::restoreViewState()
2680 {
2681     setInputModeEnabled();
2682 }
2683
2684 void InputHandler::showTextInputTypeSuggestionBox(bool allowEmptyPrefix)
2685 {
2686     if (!isActiveTextEdit())
2687         return;
2688
2689     HTMLInputElement* focusedInputElement = static_cast<HTMLInputElement*>(m_currentFocusElement->toInputElement());
2690     if (!focusedInputElement)
2691         return;
2692
2693     if (!m_suggestionDropdownBoxHandler)
2694         m_suggestionDropdownBoxHandler = SuggestionBoxHandler::create(focusedInputElement);
2695
2696     // If the focused input element isn't the same as the one inside the handler, reset and display.
2697     // If the focused element is the same, display the suggestions.
2698     if ((m_suggestionDropdownBoxHandler->focusedElement()) && m_suggestionDropdownBoxHandler->focusedElement() != focusedInputElement)
2699         m_suggestionDropdownBoxHandler->setInputElementAndUpdateDisplay(focusedInputElement);
2700     else
2701         m_suggestionDropdownBoxHandler->showDropdownBox(allowEmptyPrefix);
2702 }
2703
2704 void InputHandler::hideTextInputTypeSuggestionBox()
2705 {
2706     if (m_suggestionDropdownBoxHandler)
2707         m_suggestionDropdownBoxHandler->hideDropdownBox();
2708 }
2709
2710 void InputHandler::elementTouched(WebCore::Element* nonShadowElementUnderFatFinger)
2711 {
2712     // Attempt to show all suggestions when the input field is empty and a tap is registered when the element is focused.
2713     if (isActiveTextEdit() && nonShadowElementUnderFatFinger == m_currentFocusElement)
2714         showTextInputTypeSuggestionBox(true /* allowEmptyPrefix */);
2715
2716     m_elementTouchedIsCrossFrame = nonShadowElementUnderFatFinger
2717         && nonShadowElementUnderFatFinger->document()
2718         && nonShadowElementUnderFatFinger->document()->frame() != m_webPage->focusedOrMainFrame();
2719 }
2720
2721 }
2722 }