[BlackBerry] Queue spellcheck requests with char-count limitations
[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 "CString.h"
26 #include "Chrome.h"
27 #include "DOMSupport.h"
28 #include "DatePickerClient.h"
29 #include "Document.h"
30 #include "DocumentLoader.h"
31 #include "DocumentMarkerController.h"
32 #include "FocusController.h"
33 #include "Frame.h"
34 #include "FrameView.h"
35 #include "HTMLFormElement.h"
36 #include "HTMLInputElement.h"
37 #include "HTMLNames.h"
38 #include "HTMLOptGroupElement.h"
39 #include "HTMLOptionElement.h"
40 #include "HTMLSelectElement.h"
41 #include "HTMLTextAreaElement.h"
42 #include "NotImplemented.h"
43 #include "Page.h"
44 #include "PlatformKeyboardEvent.h"
45 #include "PluginView.h"
46 #include "Range.h"
47 #include "RenderLayer.h"
48 #include "RenderMenuList.h"
49 #include "RenderPart.h"
50 #include "RenderText.h"
51 #include "RenderTextControl.h"
52 #include "RenderWidget.h"
53 #include "RenderedDocumentMarker.h"
54 #include "ScopePointer.h"
55 #include "SelectPopupClient.h"
56 #include "SelectionHandler.h"
57 #include "SpellChecker.h"
58 #include "TextCheckerClient.h"
59 #include "TextIterator.h"
60 #include "VisiblePosition.h"
61 #include "WebPageClient.h"
62 #include "WebPage_p.h"
63 #include "WebSettings.h"
64 #include "visible_units.h"
65
66 #include <BlackBerryPlatformKeyboardEvent.h>
67 #include <BlackBerryPlatformLog.h>
68 #include <BlackBerryPlatformMisc.h>
69 #include <BlackBerryPlatformSettings.h>
70 #include <sys/keycodes.h>
71
72 #define ENABLE_INPUT_LOG 0
73 #define ENABLE_FOCUS_LOG 0
74 #define ENABLE_SPELLING_LOG 0
75
76 static const unsigned MaxLearnTextDataSize = 500;
77 static const unsigned MaxSpellCheckingStringLength = 250;
78
79 using namespace BlackBerry::Platform;
80 using namespace WebCore;
81
82 #if ENABLE_INPUT_LOG
83 #define InputLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
84 #else
85 #define InputLog(severity, format, ...)
86 #endif // ENABLE_INPUT_LOG
87
88 #if ENABLE_FOCUS_LOG
89 #define FocusLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
90 #else
91 #define FocusLog(severity, format, ...)
92 #endif // ENABLE_FOCUS_LOG
93
94 #if ENABLE_SPELLING_LOG
95 #define SpellingLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
96 #else
97 #define SpellingLog(severity, format, ...)
98 #endif // ENABLE_SPELLING_LOG
99
100 namespace BlackBerry {
101 namespace WebKit {
102
103 class ProcessingChangeGuard {
104 public:
105     ProcessingChangeGuard(InputHandler* inputHandler)
106         : m_inputHandler(inputHandler)
107         , m_savedProcessingChange(false)
108     {
109         ASSERT(m_inputHandler);
110
111         m_savedProcessingChange = m_inputHandler->processingChange();
112         m_inputHandler->setProcessingChange(true);
113     }
114
115     ~ProcessingChangeGuard()
116     {
117         m_inputHandler->setProcessingChange(m_savedProcessingChange);
118     }
119
120 private:
121     InputHandler* m_inputHandler;
122     bool m_savedProcessingChange;
123 };
124
125 InputHandler::InputHandler(WebPagePrivate* page)
126     : m_webPage(page)
127     , m_currentFocusElement(0)
128     , m_inputModeEnabled(false)
129     , m_processingChange(false)
130     , m_changingFocus(false)
131     , m_currentFocusElementType(TextEdit)
132     , m_currentFocusElementTextEditMask(DEFAULT_STYLE)
133     , m_composingTextStart(0)
134     , m_composingTextEnd(0)
135     , m_pendingKeyboardVisibilityChange(NoChange)
136     , m_delayKeyboardVisibilityChange(false)
137 {
138 }
139
140 InputHandler::~InputHandler()
141 {
142 }
143
144 static BlackBerryInputType convertInputType(const HTMLInputElement* inputElement)
145 {
146     if (inputElement->isPasswordField())
147         return InputTypePassword;
148     if (inputElement->isSearchField())
149         return InputTypeSearch;
150     if (inputElement->isEmailField())
151         return InputTypeEmail;
152     if (inputElement->isMonthControl())
153         return InputTypeMonth;
154     if (inputElement->isNumberField())
155         return InputTypeNumber;
156     if (inputElement->isTelephoneField())
157         return InputTypeTelephone;
158     if (inputElement->isURLField())
159         return InputTypeURL;
160 #if ENABLE(INPUT_TYPE_COLOR)
161     if (inputElement->isColorControl())
162         return InputTypeColor;
163 #endif
164     if (inputElement->isDateControl())
165         return InputTypeDate;
166     if (inputElement->isDateTimeControl())
167         return InputTypeDateTime;
168     if (inputElement->isDateTimeLocalControl())
169         return InputTypeDateTimeLocal;
170     if (inputElement->isTimeControl())
171         return InputTypeTime;
172     // FIXME: missing WEEK popup selector
173     if (DOMSupport::elementIdOrNameIndicatesEmail(inputElement))
174         return InputTypeEmail;
175     if (DOMSupport::elementIdOrNameIndicatesUrl(inputElement))
176         return InputTypeURL;
177     if (DOMSupport::elementPatternIndicatesNumber(inputElement))
178         return InputTypeNumber;
179     if (DOMSupport::elementPatternIndicatesHexadecimal(inputElement))
180         return InputTypeHexadecimal;
181
182     return InputTypeText;
183 }
184
185 static int inputStyle(BlackBerryInputType type, const Element* element)
186 {
187     switch (type) {
188     case InputTypeEmail:
189     case InputTypeURL:
190     case InputTypeSearch:
191     case InputTypeText:
192     case InputTypeTextArea:
193         {
194             // Regular input mode, disable help if autocomplete is off.
195             int imfMask = 0;
196             DOMSupport::AttributeState autoCompleteState = DOMSupport::elementSupportsAutocomplete(element);
197             if (autoCompleteState == DOMSupport::Off)
198                 imfMask = NO_AUTO_TEXT | NO_PREDICTION;
199             else if (autoCompleteState != DOMSupport::On
200                      && DOMSupport::elementIdOrNameIndicatesNoAutocomplete(element))
201                 imfMask = NO_AUTO_TEXT | NO_PREDICTION;
202
203             // Disable autocorrection if it's specifically off, of if it is in default mode
204             // and we have disabled auto text and prediction.
205             if (DOMSupport::elementSupportsAutocorrect(element) == DOMSupport::Off
206                 || (imfMask && DOMSupport::elementSupportsAutocorrect(element) == DOMSupport::Default))
207                 imfMask |= NO_AUTO_CORRECTION;
208
209             if (imfMask)
210                 return imfMask;
211             if ((type == InputTypeEmail || type == InputTypeURL) && autoCompleteState != DOMSupport::On)
212                 return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
213             break;
214         }
215     case InputTypeIsIndex:
216     case InputTypePassword:
217     case InputTypeNumber:
218     case InputTypeTelephone:
219     case InputTypeHexadecimal:
220         // Disable special handling.
221         return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
222     default:
223         break;
224     }
225     return DEFAULT_STYLE;
226 }
227
228 static VirtualKeyboardType convertStringToKeyboardType(const AtomicString& string)
229 {
230     DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
231     DEFINE_STATIC_LOCAL(AtomicString, Url, ("url"));
232     DEFINE_STATIC_LOCAL(AtomicString, Email, ("email"));
233     DEFINE_STATIC_LOCAL(AtomicString, Password, ("password"));
234     DEFINE_STATIC_LOCAL(AtomicString, Web, ("web"));
235     DEFINE_STATIC_LOCAL(AtomicString, Number, ("number"));
236     DEFINE_STATIC_LOCAL(AtomicString, Symbol, ("symbol"));
237     DEFINE_STATIC_LOCAL(AtomicString, Phone, ("phone"));
238     DEFINE_STATIC_LOCAL(AtomicString, Pin, ("pin"));
239     DEFINE_STATIC_LOCAL(AtomicString, Hex, ("hexadecimal"));
240
241     if (string.isEmpty())
242         return VKBTypeNotSet;
243     if (equalIgnoringCase(string, Default))
244         return VKBTypeDefault;
245     if (equalIgnoringCase(string, Url))
246         return VKBTypeUrl;
247     if (equalIgnoringCase(string, Email))
248         return VKBTypeEmail;
249     if (equalIgnoringCase(string, Password))
250         return VKBTypePassword;
251     if (equalIgnoringCase(string, Web))
252         return VKBTypeWeb;
253     if (equalIgnoringCase(string, Number))
254         return VKBTypeNumPunc;
255     if (equalIgnoringCase(string, Symbol))
256         return VKBTypeSymbol;
257     if (equalIgnoringCase(string, Phone))
258         return VKBTypePhone;
259     if (equalIgnoringCase(string, Pin) || equalIgnoringCase(string, Hex))
260         return VKBTypePin;
261     return VKBTypeNotSet;
262 }
263
264 static VirtualKeyboardType keyboardTypeAttribute(const WebCore::Element* element)
265 {
266     DEFINE_STATIC_LOCAL(QualifiedName, keyboardTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-type", nullAtom));
267
268     if (element->fastHasAttribute(keyboardTypeAttr)) {
269         AtomicString attributeString = element->fastGetAttribute(keyboardTypeAttr);
270         return convertStringToKeyboardType(attributeString);
271     }
272
273     if (element->isFormControlElement()) {
274         const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
275         if (formElement->form() && formElement->form()->fastHasAttribute(keyboardTypeAttr)) {
276             AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardTypeAttr);
277             return convertStringToKeyboardType(attributeString);
278         }
279     }
280
281     return VKBTypeNotSet;
282 }
283
284 static VirtualKeyboardEnterKeyType convertStringToKeyboardEnterKeyType(const AtomicString& string)
285 {
286     DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
287     DEFINE_STATIC_LOCAL(AtomicString, Connect, ("connect"));
288     DEFINE_STATIC_LOCAL(AtomicString, Done, ("done"));
289     DEFINE_STATIC_LOCAL(AtomicString, Go, ("go"));
290     DEFINE_STATIC_LOCAL(AtomicString, Join, ("join"));
291     DEFINE_STATIC_LOCAL(AtomicString, Next, ("next"));
292     DEFINE_STATIC_LOCAL(AtomicString, Search, ("search"));
293     DEFINE_STATIC_LOCAL(AtomicString, Send, ("send"));
294     DEFINE_STATIC_LOCAL(AtomicString, Submit, ("submit"));
295
296     if (string.isEmpty())
297         return VKBEnterKeyNotSet;
298     if (equalIgnoringCase(string, Default))
299         return VKBEnterKeyDefault;
300     if (equalIgnoringCase(string, Connect))
301         return VKBEnterKeyConnect;
302     if (equalIgnoringCase(string, Done))
303         return VKBEnterKeyDone;
304     if (equalIgnoringCase(string, Go))
305         return VKBEnterKeyGo;
306     if (equalIgnoringCase(string, Join))
307         return VKBEnterKeyJoin;
308     if (equalIgnoringCase(string, Next))
309         return VKBEnterKeyNext;
310     if (equalIgnoringCase(string, Search))
311         return VKBEnterKeySearch;
312     if (equalIgnoringCase(string, Send))
313         return VKBEnterKeySend;
314     if (equalIgnoringCase(string, Submit))
315         return VKBEnterKeySubmit;
316     return VKBEnterKeyNotSet;
317 }
318
319 static VirtualKeyboardEnterKeyType keyboardEnterKeyTypeAttribute(const WebCore::Element* element)
320 {
321     DEFINE_STATIC_LOCAL(QualifiedName, keyboardEnterKeyTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-enter-key", nullAtom));
322
323     if (element->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
324         AtomicString attributeString = element->fastGetAttribute(keyboardEnterKeyTypeAttr);
325         return convertStringToKeyboardEnterKeyType(attributeString);
326     }
327
328     if (element->isFormControlElement()) {
329         const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
330         if (formElement->form() && formElement->form()->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
331             AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardEnterKeyTypeAttr);
332             return convertStringToKeyboardEnterKeyType(attributeString);
333         }
334     }
335
336     return VKBEnterKeyNotSet;
337 }
338
339 WTF::String InputHandler::elementText()
340 {
341     if (!isActiveTextEdit())
342         return WTF::String();
343
344     return DOMSupport::inputElementText(m_currentFocusElement.get());
345 }
346
347 BlackBerryInputType InputHandler::elementType(Element* element) const
348 {
349     // <isIndex> is bundled with input so we need to check the formControlName
350     // first to differentiate it from input which is essentially the same as
351     // isIndex has been deprecated.
352     if (element->formControlName() == HTMLNames::isindexTag)
353         return InputTypeIsIndex;
354
355     if (const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element->toInputElement()))
356         return convertInputType(inputElement);
357
358     if (element->hasTagName(HTMLNames::textareaTag))
359         return InputTypeTextArea;
360
361     // Default to InputTypeTextArea for content editable fields.
362     return InputTypeTextArea;
363 }
364
365 void InputHandler::focusedNodeChanged()
366 {
367     ASSERT(m_webPage->m_page->focusController());
368     Frame* frame = m_webPage->m_page->focusController()->focusedOrMainFrame();
369     if (!frame || !frame->document())
370         return;
371
372     Node* node = frame->document()->focusedNode();
373
374     if (isActiveTextEdit() && m_currentFocusElement == node) {
375         notifyClientOfKeyboardVisibilityChange(true);
376         return;
377     }
378
379     if (node && node->isElementNode()) {
380         Element* element = static_cast<Element*>(node);
381         if (DOMSupport::isElementTypePlugin(element)) {
382             setPluginFocused(element);
383             return;
384         }
385
386         if (DOMSupport::isTextBasedContentEditableElement(element)) {
387             // Focused node is a text based input field, textarea or content editable field.
388             setElementFocused(element);
389             return;
390         }
391     }
392
393     if (isActiveTextEdit() && m_currentFocusElement->isContentEditable()) {
394         // This is a special handler for content editable fields. The focus node is the top most
395         // field that is content editable, however, by enabling / disabling designmode and
396         // content editable, it is possible through javascript or selection to trigger the focus node to
397         // change even while maintaining editing on the same selection point. If the focus element
398         // isn't contentEditable, but the current selection is, don't send a change notification.
399
400         // When processing changes selection changes occur that may reset the focus element
401         // and could potentially cause a crash as m_currentFocusElement should not be
402         // changed during processing of an EditorCommand.
403         if (processingChange())
404             return;
405
406         Frame* frame = m_currentFocusElement->document()->frame();
407         ASSERT(frame);
408
409         // Test the current selection to make sure that the content in focus is still content
410         // editable. This may mean Javascript triggered a focus change by modifying the
411         // top level parent of this object's content editable state without actually modifying
412         // this particular object.
413         // Example site: html5demos.com/contentEditable - blur event triggers focus change.
414         if (frame == m_webPage->focusedOrMainFrame() && frame->selection()->start().anchorNode()
415             && frame->selection()->start().anchorNode()->isContentEditable())
416                 return;
417     }
418
419     // No valid focus element found for handling.
420     setElementUnfocused();
421 }
422
423 void InputHandler::setPluginFocused(Element* element)
424 {
425     ASSERT(DOMSupport::isElementTypePlugin(element));
426
427     if (isActiveTextEdit())
428         setElementUnfocused();
429
430     m_currentFocusElementType = Plugin;
431     m_currentFocusElement = element;
432 }
433
434 static bool convertStringToWchar(const String& string, wchar_t* dest, int destCapacity, int* destLength)
435 {
436     ASSERT(dest);
437
438     if (!string.length()) {
439         destLength = 0;
440         return true;
441     }
442
443     UErrorCode ec = U_ZERO_ERROR;
444     // wchar_t strings sent to IMF are 32 bit so casting to UChar32 is safe.
445     u_strToUTF32(reinterpret_cast<UChar32*>(dest), destCapacity, destLength, string.characters(), string.length(), &ec);
446     if (ec) {
447         logAlways(LogLevelCritical, "InputHandler::convertStringToWchar Error converting string ec (%d).", ec);
448         destLength = 0;
449         return false;
450     }
451     return true;
452 }
453
454 static bool convertStringToWcharVector(const String& string, WTF::Vector<wchar_t>& wcharString)
455 {
456     ASSERT(wcharString.isEmpty());
457
458     int length = string.length();
459     if (!length)
460         return true;
461
462     if (!wcharString.tryReserveCapacity(length + 1)) {
463         logAlways(LogLevelCritical, "InputHandler::convertStringToWcharVector Cannot allocate memory for string.\n");
464         return false;
465     }
466
467     int destLength = 0;
468     if (!convertStringToWchar(string, wcharString.data(), length + 1, &destLength))
469         return false;
470
471     wcharString.resize(destLength);
472     return true;
473 }
474
475 static String convertSpannableStringToString(spannable_string_t* src)
476 {
477     if (!src || !src->str || !src->length)
478         return String();
479
480     WTF::Vector<UChar> dest;
481     int destCapacity = (src->length * 2) + 1;
482     if (!dest.tryReserveCapacity(destCapacity)) {
483         logAlways(LogLevelCritical, "InputHandler::convertSpannableStringToString Cannot allocate memory for string.\n");
484         return String();
485     }
486
487     int destLength = 0;
488     UErrorCode ec = U_ZERO_ERROR;
489     // wchar_t strings sent from IMF are 32 bit so casting to UChar32 is safe.
490     u_strFromUTF32(dest.data(), destCapacity, &destLength, reinterpret_cast<UChar32*>(src->str), src->length, &ec);
491     if (ec) {
492         logAlways(LogLevelCritical, "InputHandler::convertSpannableStringToString Error converting string ec (%d).", ec);
493         return String();
494     }
495     dest.resize(destLength);
496     return String(dest.data(), destLength);
497 }
498
499 void InputHandler::sendLearnTextDetails(const WTF::String& string)
500 {
501     Vector<wchar_t> wcharString;
502     if (!convertStringToWcharVector(string, wcharString) || wcharString.isEmpty())
503         return;
504
505     m_webPage->m_client->inputLearnText(wcharString.data(), wcharString.size());
506 }
507
508 void InputHandler::learnText()
509 {
510     if (!isActiveTextEdit())
511         return;
512
513     // Do not send (or calculate) the text when the field is NO_PREDICTION or NO_AUTO_TEXT.
514     if (m_currentFocusElementTextEditMask & NO_PREDICTION || m_currentFocusElementTextEditMask & NO_AUTO_TEXT)
515         return;
516
517     String textInField(elementText());
518     textInField = textInField.substring(std::max(0, static_cast<int>(textInField.length() - MaxLearnTextDataSize)), textInField.length());
519     textInField.remove(0, textInField.find(" "));
520
521     // Build up the 500 character strings in word chunks.
522     // Spec says 1000, but memory corruption has been observed.
523     ASSERT(textInField.length() <= MaxLearnTextDataSize);
524
525     if (textInField.isEmpty())
526         return;
527
528     InputLog(LogLevelInfo, "InputHandler::learnText '%s'", textInField.latin1().data());
529     sendLearnTextDetails(textInField);
530 }
531
532 void InputHandler::requestCheckingOfString(PassRefPtr<WebCore::TextCheckingRequest> textCheckingRequest)
533 {
534     RefPtr<WebCore::TextCheckingRequest> request = textCheckingRequest;
535
536     int32_t sequenceId = request->sequence();
537
538     // Check if field explicitly asked for spellchecking.
539     if (DOMSupport::elementSupportsSpellCheck(m_currentFocusElement.get()) != DOMSupport::On) {
540         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
541         return;
542     }
543
544     unsigned requestLength = request->text().length();
545
546     if (requestLength < 2) {
547         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
548         return;
549     }
550
551     if (requestLength > MaxSpellCheckingStringLength) {
552         // Cancel this request and send it off in newly created chunks.
553         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
554         if (m_currentFocusElement && m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->selection()) {
555             // Convert from position back to selection so we can expand the range to include the previous line. This should handle cases when the user hits
556             // enter to finish composing a word and create a new line.
557             VisiblePosition caretPosition = m_currentFocusElement->document()->frame()->selection()->start();
558             VisibleSelection visibleSelection = VisibleSelection(previousLinePosition(caretPosition, caretPosition.lineDirectionPointForBlockDirectionNavigation()), caretPosition);
559             spellCheckBlock(visibleSelection, TextCheckingProcessIncremental);
560         }
561         return;
562     }
563
564     wchar_t* checkingString = (wchar_t*)malloc(sizeof(wchar_t) * (requestLength + 1));
565     if (!checkingString) {
566         logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Cannot allocate memory for string.");
567         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
568         return;
569     }
570
571     int paragraphLength = 0;
572     if (!convertStringToWchar(request->text(), checkingString, requestLength + 1, &paragraphLength)) {
573         logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Failed to convert String to wchar type.");
574         free(checkingString);
575         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
576         return;
577     }
578
579     int32_t transactionId = m_webPage->m_client->checkSpellingOfStringAsync(checkingString, paragraphLength);
580     free(checkingString);
581
582     // If the call to the input service did not go through, then cancel the request so we don't block endlessly.
583     // This should still take transactionId as a parameter to maintain the same behavior as if InputMethodSupport
584     // were to cancel a request during processing.
585     if (transactionId == -1) { // Error before sending request to input service.
586         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
587         return;
588     }
589
590     // map sequenceId to transactionId.
591     m_sequenceMap[transactionId] = sequenceId;
592 }
593
594 int32_t InputHandler::convertTransactionIdToSequenceId(int32_t transactionId)
595 {
596     std::map<int32_t, int32_t>::iterator it = m_sequenceMap.find(transactionId);
597
598     if (it == m_sequenceMap.end())
599         return 0;
600
601     int32_t sequenceId = it->second;
602     // We only convert this value when we have received its response, so its safe to remove it from the map.
603     m_sequenceMap.erase(it);
604
605     return sequenceId;
606 }
607
608 void InputHandler::spellCheckingRequestProcessed(int32_t transactionId, spannable_string_t* spannableString)
609 {
610     if (!spannableString || !isActiveTextEdit()) {
611         spellCheckingRequestCancelled(transactionId, false /* isSequenceId */);
612         return;
613     }
614
615     Vector<TextCheckingResult> results;
616
617     // Convert the spannableString to TextCheckingResult then append to results vector.
618     String replacement;
619     TextCheckingResult textCheckingResult;
620     textCheckingResult.type = TextCheckingTypeSpelling;
621     textCheckingResult.replacement = replacement;
622     textCheckingResult.location = 0;
623     textCheckingResult.length = 0;
624
625     span_t* span = spannableString->spans;
626     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
627         if (!span)
628             break;
629         if (span->end < span->start) {
630             spellCheckingRequestCancelled(transactionId, false /* isSequenceId */);
631             return;
632         }
633         if (span->attributes_mask & MISSPELLED_WORD_ATTRIB) {
634             textCheckingResult.location = span->start;
635             // The end point includes the character that it is before. Ie, 0, 0
636             // applies to the first character as the end point includes the character
637             // at the position. This means the endPosition is always +1.
638             textCheckingResult.length = span->end - span->start + 1;
639             results.append(textCheckingResult);
640         }
641         span++;
642     }
643
644     // transactionId here is for use with the input service. We need to translate this to sequenceId used with SpellChecker.
645     int32_t sequenceId = convertTransactionIdToSequenceId(transactionId);
646
647     SpellChecker* spellChecker = getSpellChecker();
648     if (!spellChecker || !sequenceId) {
649         SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestProcessed Failed to process the request with sequenceId %d", sequenceId);
650         spellCheckingRequestCancelled(sequenceId, true /* isSequenceId */);
651         return;
652     }
653     spellChecker->didCheckSucceeded(sequenceId, results);
654 }
655
656 void InputHandler::cancelAllSpellCheckingRequests()
657 {
658     for (std::map<int32_t, int32_t>::iterator it = m_sequenceMap.begin(); it != m_sequenceMap.end(); ++it)
659         spellCheckingRequestCancelled(it->second, true /* isSequenceId */);
660     m_sequenceMap.clear();
661 }
662
663 void InputHandler::spellCheckingRequestCancelled(int32_t id, bool isSequenceId)
664 {
665     if (!isActiveTextEdit())
666         return;
667
668     int32_t sequenceId = isSequenceId ? id : convertTransactionIdToSequenceId(id);
669     SpellChecker* spellChecker = getSpellChecker();
670     if (!spellChecker) {
671         SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestCancelled failed to cancel the request with sequenceId %d", sequenceId);
672         return;
673     }
674     spellChecker->didCheckCanceled(sequenceId);
675 }
676
677 SpellChecker* InputHandler::getSpellChecker()
678 {
679     ASSERT(m_currentFocusElement);
680     ASSERT(m_currentFocusElement->document());
681
682     if (Frame* frame = m_currentFocusElement->document()->frame())
683         if (Editor* editor = frame->editor())
684             return editor->spellChecker();
685
686     return 0;
687 }
688
689 bool InputHandler::shouldRequestSpellCheckingOptionsForPoint(Platform::IntPoint& point, const Element* touchedElement, imf_sp_text_t& spellCheckingOptionRequest)
690 {
691     if (!isActiveTextEdit() || touchedElement != m_currentFocusElement)
692         return false;
693
694     LayoutPoint contentPos(m_webPage->mapFromViewportToContents(point));
695     contentPos = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), m_webPage->focusedOrMainFrame(), roundedIntPoint(contentPos));
696
697     Document* document = m_currentFocusElement->document();
698     ASSERT(document);
699
700     RenderedDocumentMarker* marker = document->markers()->renderedMarkerContainingPoint(contentPos, DocumentMarker::Spelling);
701     if (!marker)
702         return false;
703
704     SpellingLog(LogLevelInfo, "InputHandler::shouldRequestSpellCheckingOptionsForPoint Found spelling marker at point %d, %d", point.x(), point.y());
705
706     // imf_sp_text_t should be generated in pixel viewport coordinates.
707     WebCore::IntRect rect = m_webPage->mapToTransformed(m_webPage->focusedOrMainFrame()->view()->contentsToWindow(enclosingIntRect(marker->renderedRect())));
708     m_webPage->clipToTransformedContentsRect(rect);
709
710     // TODO use the actual caret position after it is placed.
711     spellCheckingOptionRequest.caret_rect.caret_top_x = point.x();
712     spellCheckingOptionRequest.caret_rect.caret_top_y = rect.y();
713     spellCheckingOptionRequest.caret_rect.caret_bottom_x = point.x();
714     spellCheckingOptionRequest.caret_rect.caret_bottom_y = rect.y() + rect.height();
715     spellCheckingOptionRequest.startTextPosition = marker->startOffset();
716     spellCheckingOptionRequest.endTextPosition = marker->endOffset();
717
718     return true;
719 }
720
721 void InputHandler::requestSpellingCheckingOptions(imf_sp_text_t& spellCheckingOptionRequest)
722 {
723     SpellingLog(LogLevelInfo, "InputHandler::requestSpellingCheckingOptions Sending request:\ncaret_rect.caret_top_x = %d\ncaret_rect.caret_top_y = %d" \
724                               "\ncaret_rect.caret_bottom_x = %d\ncaret_rect.caret_bottom_y = %d\nstartTextPosition = %d\nendTextPosition = %d",
725                               spellCheckingOptionRequest.caret_rect.caret_top_x, spellCheckingOptionRequest.caret_rect.caret_top_y,
726                               spellCheckingOptionRequest.caret_rect.caret_bottom_x, spellCheckingOptionRequest.caret_rect.caret_bottom_y,
727                               spellCheckingOptionRequest.startTextPosition, spellCheckingOptionRequest.endTextPosition);
728
729     if (spellCheckingOptionRequest.startTextPosition || spellCheckingOptionRequest.endTextPosition)
730         m_webPage->m_client->requestSpellingCheckingOptions(spellCheckingOptionRequest);
731 }
732
733 void InputHandler::setElementUnfocused(bool refocusOccuring)
734 {
735     if (isActiveTextEdit()) {
736         FocusLog(LogLevelInfo, "InputHandler::setElementUnfocused");
737
738         // Pass any text into the field to IMF to learn.
739         learnText();
740
741         // End any composition that is in progress.
742         finishComposition();
743
744         // Only hide the keyboard if we aren't refocusing on a new input field.
745         if (!refocusOccuring)
746             notifyClientOfKeyboardVisibilityChange(false);
747
748         m_webPage->m_client->inputFocusLost();
749
750         // If the frame selection isn't focused, focus it.
751         if (!m_currentFocusElement->document()->frame()->selection()->isFocused())
752             m_currentFocusElement->document()->frame()->selection()->setFocused(true);
753
754         // Cancel any spellcheck requests that might be ongoing.
755         cancelAllSpellCheckingRequests();
756     }
757
758     // Clear the node details.
759     m_currentFocusElement = 0;
760     m_currentFocusElementType = TextEdit;
761 }
762
763 bool InputHandler::isInputModeEnabled() const
764 {
765     // Input mode is enabled when set, or when dump render tree or always show keyboard setting is enabled.
766     return m_inputModeEnabled || m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus();
767 }
768
769 void InputHandler::setInputModeEnabled(bool active)
770 {
771     FocusLog(LogLevelInfo, "InputHandler::setInputModeEnabled '%s', override is '%s'"
772              , active ? "true" : "false"
773              , m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus() ? "true" : "false");
774
775     m_inputModeEnabled = active;
776
777     // If the frame selection isn't focused, focus it.
778     if (isInputModeEnabled() && isActiveTextEdit() && !m_currentFocusElement->document()->frame()->selection()->isFocused())
779         m_currentFocusElement->document()->frame()->selection()->setFocused(true);
780 }
781
782 void InputHandler::setElementFocused(Element* element)
783 {
784     ASSERT(DOMSupport::isTextBasedContentEditableElement(element));
785     ASSERT(element && element->document() && element->document()->frame());
786
787 #ifdef ENABLE_SPELLING_LOG
788     BlackBerry::Platform::StopWatch timer;
789     timer.start();
790 #endif
791
792     if (!element || !(element->document()))
793         return;
794
795     Frame* frame = element->document()->frame();
796     if (!frame)
797         return;
798
799     if (frame->selection()->isFocused() != isInputModeEnabled())
800         frame->selection()->setFocused(isInputModeEnabled());
801
802     // Clear the existing focus node details.
803     setElementUnfocused(true /*refocusOccuring*/);
804
805     // Mark this element as active and add to frame set.
806     m_currentFocusElement = element;
807     m_currentFocusElementType = TextEdit;
808
809     // Send details to the client about this element.
810     BlackBerryInputType type = elementType(element);
811     m_currentFocusElementTextEditMask = inputStyle(type, element);
812
813     VirtualKeyboardType keyboardType = keyboardTypeAttribute(element);
814     VirtualKeyboardEnterKeyType enterKeyType = keyboardEnterKeyTypeAttribute(element);
815
816     FocusLog(LogLevelInfo, "InputHandler::setElementFocused, Type=%d, Style=%d, Keyboard Type=%d, Enter Key=%d", type, m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
817     m_webPage->m_client->inputFocusGained(type, m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
818
819     handleInputLocaleChanged(m_webPage->m_webSettings->isWritingDirectionRTL());
820
821     if (!m_delayKeyboardVisibilityChange)
822         notifyClientOfKeyboardVisibilityChange(true);
823
824 #ifdef ENABLE_SPELLING_LOG
825     SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Focusing the field took %f seconds.", timer.elapsed());
826 #endif
827
828     // Check if the field explicitly asks for spellchecking.
829     if (DOMSupport::elementSupportsSpellCheck(element) != DOMSupport::On)
830         return;
831
832     // Spellcheck the field in its entirety.
833     VisibleSelection focusedBlock = DOMSupport::visibleSelectionForInputElement(element);
834     spellCheckBlock(focusedBlock, TextCheckingProcessBatch);
835
836 #ifdef ENABLE_SPELLING_LOG
837     SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Spellchecking the field increased the total time to focus to %f seconds.", timer.elapsed());
838 #endif
839 }
840
841 void InputHandler::spellCheckBlock(VisibleSelection& visibleSelection, TextCheckingProcessType textCheckingProcessType)
842 {
843     if (!isActiveTextEdit())
844         return;
845
846     SpellChecker* spellChecker = getSpellChecker();
847     if (!spellChecker) {
848         SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Failed to spellcheck the current focused element.");
849         return;
850     }
851
852     RefPtr<Range> rangeForSpellChecking = visibleSelection.toNormalizedRange();
853
854     // If we have a batch request, try to send off the entire block.
855     if (textCheckingProcessType == TextCheckingProcessBatch) {
856         // If total block text is under the limited amount, send the entire chunk.
857         if (rangeForSpellChecking->text().length() < MaxSpellCheckingStringLength) {
858             spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
859             return;
860         }
861     }
862
863     // Since we couldn't check the entire block at once, set up starting and ending markers to fire incrementally.
864     VisiblePosition startPos = visibleSelection.visibleStart();
865     VisiblePosition startOfCurrentLine = startOfLine(startPos);
866     VisiblePosition endOfCurrentLine = endOfLine(startPos);
867
868     while (startOfCurrentLine != endOfCurrentLine) {
869         // Create a selection with the start and end points of the line, and convert to Range to create a SpellCheckRequest.
870         rangeForSpellChecking = VisibleSelection(startOfCurrentLine, endOfCurrentLine).toNormalizedRange();
871
872         if (rangeForSpellChecking->text().length() >= MaxSpellCheckingStringLength) {
873             // Iterate through words from the start of the line to the end.
874             rangeForSpellChecking = getRangeForSpellCheckWithFineGranularity(startOfCurrentLine, endOfCurrentLine);
875             if (!rangeForSpellChecking) {
876                 SpellingLog(LogLevelWarn, "InputHandler::spellCheckBlock Failed to set text range for spellchecking");
877                 return;
878             }
879             startOfCurrentLine = VisiblePosition(rangeForSpellChecking->endPosition());
880         } else {
881             startOfCurrentLine = nextLinePosition(startOfCurrentLine, startOfCurrentLine.lineDirectionPointForBlockDirectionNavigation());
882             endOfCurrentLine = endOfLine(startOfCurrentLine);
883             // If we are at the last line, nextLinePosition will return the position at the end of the line. If we're not at the end, wrap with a call to startOfLine to be safe.
884             if (startOfCurrentLine != endOfCurrentLine)
885                 startOfCurrentLine = startOfLine(startOfCurrentLine);
886         }
887
888         SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Substring text is '%s', of size %d", rangeForSpellChecking->text().latin1().data(), rangeForSpellChecking->text().length());
889
890         // Call spellcheck with substring.
891         spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
892     }
893 }
894
895 PassRefPtr<Range> InputHandler::getRangeForSpellCheckWithFineGranularity(VisiblePosition startPosition, VisiblePosition endPosition)
896 {
897     VisiblePosition endOfCurrentWord = endOfWord(startPosition);
898     RefPtr<Range> rangeForSpellChecking;
899     while (endOfCurrentWord != endPosition) {
900         rangeForSpellChecking = VisibleSelection(startPosition, endOfCurrentWord).toNormalizedRange();
901         // If we exceed the MaxSpellCheckingStringLength limit, then go back one word and return this range.
902         if (rangeForSpellChecking->text().length() >= MaxSpellCheckingStringLength)
903             return VisibleSelection(startPosition, endOfWord(previousWordPosition(endOfCurrentWord))).toNormalizedRange();
904
905         endOfCurrentWord = endOfWord(nextWordPosition(endOfCurrentWord));
906     }
907     return 0;
908 }
909
910 bool InputHandler::openDatePopup(HTMLInputElement* element, BlackBerryInputType type)
911 {
912     if (!element || element->disabled() || !DOMSupport::isDateTimeInputField(element))
913         return false;
914
915     if (isActiveTextEdit())
916         clearCurrentFocusElement();
917
918     switch (type) {
919     case BlackBerry::Platform::InputTypeDate:
920     case BlackBerry::Platform::InputTypeTime:
921     case BlackBerry::Platform::InputTypeDateTime:
922     case BlackBerry::Platform::InputTypeDateTimeLocal: {
923         // Check if popup already exists, close it if does.
924         m_webPage->m_page->chrome()->client()->closePagePopup(0);
925         String value = element->value();
926         String min = element->getAttribute(HTMLNames::minAttr).string();
927         String max = element->getAttribute(HTMLNames::maxAttr).string();
928         double step = element->getAttribute(HTMLNames::stepAttr).toDouble();
929
930         DatePickerClient* client = new DatePickerClient(type, value, min, max, step,  m_webPage, element);
931         // Fail to create HTML popup, use the old path
932         if (!m_webPage->m_page->chrome()->client()->openPagePopup(client,  WebCore::IntRect()))
933             m_webPage->m_client->openDateTimePopup(type, value, min, max, step);
934
935         return true;
936         }
937     default: // Other types not supported
938         return false;
939     }
940 }
941
942 bool InputHandler::openColorPopup(HTMLInputElement* element)
943 {
944     if (!element || element->disabled() || !DOMSupport::isColorInputField(element))
945         return false;
946
947     if (isActiveTextEdit())
948         clearCurrentFocusElement();
949
950     m_currentFocusElement = element;
951     m_currentFocusElementType = TextPopup;
952
953     m_webPage->m_client->openColorPopup(element->value());
954     return true;
955 }
956
957 void InputHandler::setInputValue(const WTF::String& value)
958 {
959     if (!isActiveTextPopup())
960         return;
961
962     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(m_currentFocusElement.get());
963     inputElement->setValue(value);
964     clearCurrentFocusElement();
965 }
966
967 void InputHandler::nodeTextChanged(const Node* node)
968 {
969     if (processingChange() || !node)
970         return;
971
972     if (node != m_currentFocusElement)
973         return;
974
975     InputLog(LogLevelInfo, "InputHandler::nodeTextChanged");
976
977     m_webPage->m_client->inputTextChanged();
978
979     // Remove the attributed text markers as the previous call triggered an end to
980     // the composition.
981     removeAttributedTextMarker();
982 }
983
984 WebCore::IntRect InputHandler::boundingBoxForInputField()
985 {
986     if (!isActiveTextEdit())
987         return WebCore::IntRect();
988
989     if (!m_currentFocusElement->renderer())
990         return WebCore::IntRect();
991
992     return m_currentFocusElement->renderer()->absoluteBoundingBoxRect();
993 }
994
995 void InputHandler::ensureFocusTextElementVisible(CaretScrollType scrollType)
996 {
997     if (!isActiveTextEdit() || !isInputModeEnabled() || !m_currentFocusElement->document())
998         return;
999
1000     if (!Platform::Settings::instance()->allowCenterScrollAdjustmentForInputFields() && scrollType != EdgeIfNeeded)
1001         return;
1002
1003     Frame* elementFrame = m_currentFocusElement->document()->frame();
1004     if (!elementFrame)
1005         return;
1006
1007     Frame* mainFrame = m_webPage->mainFrame();
1008     if (!mainFrame)
1009         return;
1010
1011     FrameView* mainFrameView = mainFrame->view();
1012     if (!mainFrameView)
1013         return;
1014
1015     WebCore::IntRect selectionFocusRect;
1016     switch (elementFrame->selection()->selectionType()) {
1017     case VisibleSelection::CaretSelection:
1018         selectionFocusRect = elementFrame->selection()->absoluteCaretBounds();
1019         break;
1020     case VisibleSelection::RangeSelection: {
1021         Position selectionPosition;
1022         if (m_webPage->m_selectionHandler->lastUpdatedEndPointIsValid())
1023             selectionPosition = elementFrame->selection()->end();
1024         else
1025             selectionPosition = elementFrame->selection()->start();
1026         selectionFocusRect = VisiblePosition(selectionPosition).absoluteCaretBounds();
1027         break;
1028     }
1029     case VisibleSelection::NoSelection:
1030         return;
1031     }
1032
1033     int fontHeight = selectionFocusRect.height();
1034
1035     if (elementFrame != mainFrame) { // Element is in a subframe.
1036         // Remove any scroll offset within the subframe to get the point relative to the main frame.
1037         selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());
1038
1039         // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
1040         if (elementFrame->ownerRenderer()) {
1041             WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
1042             selectionFocusRect.move(frameOffset.x(), frameOffset.y());
1043         }
1044     }
1045
1046     Position start = elementFrame->selection()->start();
1047     if (start.anchorNode() && start.anchorNode()->renderer()) {
1048         if (RenderLayer* layer = start.anchorNode()->renderer()->enclosingLayer()) {
1049             WebCore::IntRect actualScreenRect = WebCore::IntRect(mainFrameView->scrollPosition(), m_webPage->actualVisibleSize());
1050             ScrollAlignment horizontalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;
1051             ScrollAlignment verticalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;
1052
1053             if (scrollType != EdgeIfNeeded) {
1054                 // Align the selection rect if possible so that we show the field's
1055                 // outline if the caret is at the edge of the field.
1056                 if (RenderObject* focusedRenderer = m_currentFocusElement->renderer()) {
1057                     WebCore::IntRect nodeOutlineBounds = focusedRenderer->absoluteOutlineBounds();
1058                     WebCore::IntRect caretAtEdgeRect = rectForCaret(0);
1059                     int paddingX = abs(caretAtEdgeRect.x() - nodeOutlineBounds.x());
1060                     int paddingY = abs(caretAtEdgeRect.y() - nodeOutlineBounds.y());
1061
1062                     if (selectionFocusRect.x() - paddingX == nodeOutlineBounds.x())
1063                         selectionFocusRect.setX(nodeOutlineBounds.x());
1064                     else if (selectionFocusRect.maxX() + paddingX == nodeOutlineBounds.maxX())
1065                         selectionFocusRect.setX(nodeOutlineBounds.maxX() - selectionFocusRect.width());
1066                     if (selectionFocusRect.y() - paddingY == nodeOutlineBounds.y())
1067                         selectionFocusRect.setY(nodeOutlineBounds.y() - selectionFocusRect.height());
1068                     else if (selectionFocusRect.maxY() + paddingY == nodeOutlineBounds.maxY())
1069                         selectionFocusRect.setY(nodeOutlineBounds.maxY() - selectionFocusRect.height());
1070
1071                     // If the editing point is on the left hand side of the screen when the node's
1072                     // rect is edge aligned, edge align the node rect.
1073                     if (selectionFocusRect.x() - caretAtEdgeRect.x() < actualScreenRect.width() / 2)
1074                         selectionFocusRect.setX(nodeOutlineBounds.x());
1075                     else
1076                         horizontalScrollAlignment = ScrollAlignment::alignCenterIfNeeded;
1077
1078                 }
1079                 verticalScrollAlignment = (scrollType == CenterAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
1080             }
1081
1082             // Pad the rect to improve the visual appearance.
1083             // Padding must be large enough to expose the selection / FCC should they exist. Dragging the handle offscreen and releasing
1084             // will not trigger an automatic scroll. Using a padding of 40 will fully exposing the width of the current handle and half of
1085             // the height making it usable.
1086             // FIXME: This will need to be updated when the graphics change.
1087             // FIXME: The value of 40 should be calculated as a unit of measure using Graphics::Screen::primaryScreen()->heightInMMToPixels
1088             // using a relative value to the size of the handle. We should also consider expanding different amounts horizontally vs vertically.
1089             selectionFocusRect.inflate(40 /* padding in pixels */);
1090             WebCore::IntRect revealRect = layer->getRectToExpose(actualScreenRect, selectionFocusRect,
1091                                                                  horizontalScrollAlignment,
1092                                                                  verticalScrollAlignment);
1093
1094             mainFrameView->setConstrainsScrollingToContentEdge(false);
1095             // In order to adjust the scroll position to ensure the focused input field is visible,
1096             // we allow overscrolling. However this overscroll has to be strictly allowed towards the
1097             // bottom of the page on the y axis only, where the virtual keyboard pops up from.
1098             WebCore::IntPoint scrollLocation = revealRect.location();
1099             scrollLocation.clampNegativeToZero();
1100             WebCore::IntPoint maximumScrollPosition = WebCore::IntPoint(mainFrameView->contentsWidth() - actualScreenRect.width(), mainFrameView->contentsHeight() - actualScreenRect.height());
1101             scrollLocation = scrollLocation.shrunkTo(maximumScrollPosition);
1102             mainFrameView->setScrollPosition(scrollLocation);
1103             mainFrameView->setConstrainsScrollingToContentEdge(true);
1104         }
1105     }
1106
1107     // If the text is too small, zoom in to make it a minimum size.
1108     static const int s_minimumTextHeightInPixels = 6;
1109     if (fontHeight && fontHeight < s_minimumTextHeightInPixels)
1110         m_webPage->zoomAboutPoint(s_minimumTextHeightInPixels / fontHeight, m_webPage->centerOfVisibleContentsRect());
1111 }
1112
1113 void InputHandler::ensureFocusPluginElementVisible()
1114 {
1115     if (!isActivePlugin() || !m_currentFocusElement->document())
1116         return;
1117
1118     Frame* elementFrame = m_currentFocusElement->document()->frame();
1119     if (!elementFrame)
1120         return;
1121
1122     Frame* mainFrame = m_webPage->mainFrame();
1123     if (!mainFrame)
1124         return;
1125
1126     FrameView* mainFrameView = mainFrame->view();
1127     if (!mainFrameView)
1128         return;
1129
1130     WebCore::IntRect selectionFocusRect;
1131
1132     RenderWidget* renderWidget = static_cast<RenderWidget*>(m_currentFocusElement->renderer());
1133     if (renderWidget) {
1134         PluginView* pluginView = static_cast<PluginView*>(renderWidget->widget());
1135
1136         if (pluginView)
1137             selectionFocusRect = pluginView->ensureVisibleRect();
1138     }
1139
1140     if (selectionFocusRect.isEmpty())
1141         return;
1142
1143     // FIXME: We may need to scroll the subframe (recursively) in the future. Revisit this...
1144     if (elementFrame != mainFrame) { // Element is in a subframe.
1145         // Remove any scroll offset within the subframe to get the point relative to the main frame.
1146         selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());
1147
1148         // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
1149         if (elementFrame->ownerRenderer()) {
1150             WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
1151             selectionFocusRect.move(frameOffset.x(), frameOffset.y());
1152         }
1153     }
1154
1155     WebCore::IntRect actualScreenRect = WebCore::IntRect(mainFrameView->scrollPosition(), m_webPage->actualVisibleSize());
1156     if (actualScreenRect.contains(selectionFocusRect))
1157         return;
1158
1159     // Calculate a point such that the center of the requested rectangle
1160     // is at the center of the screen. FIXME: If the element was partially on screen
1161     // we might want to just bring the offscreen portion into view, someone needs
1162     // to decide if that's the behavior we want or not.
1163     WebCore::IntPoint pos(selectionFocusRect.center().x() - actualScreenRect.width() / 2,
1164                  selectionFocusRect.center().y() - actualScreenRect.height() / 2);
1165
1166     mainFrameView->setScrollPosition(pos);
1167 }
1168
1169 void InputHandler::ensureFocusElementVisible(bool centerInView)
1170 {
1171     if (isActivePlugin())
1172         ensureFocusPluginElementVisible();
1173     else
1174         ensureFocusTextElementVisible(centerInView ? CenterAlways : CenterIfNeeded);
1175 }
1176
1177 void InputHandler::frameUnloaded(const Frame* frame)
1178 {
1179     if (!isActiveTextEdit())
1180         return;
1181
1182     if (m_currentFocusElement->document()->frame() != frame)
1183         return;
1184
1185     FocusLog(LogLevelInfo, "InputHandler::frameUnloaded");
1186
1187     setElementUnfocused(false /*refocusOccuring*/);
1188 }
1189
1190 void InputHandler::setDelayKeyboardVisibilityChange(bool value)
1191 {
1192     m_delayKeyboardVisibilityChange = value;
1193     m_pendingKeyboardVisibilityChange = NoChange;
1194 }
1195
1196 void InputHandler::processPendingKeyboardVisibilityChange()
1197 {
1198     if (!m_delayKeyboardVisibilityChange) {
1199         ASSERT(m_pendingKeyboardVisibilityChange == NoChange);
1200         return;
1201     }
1202
1203     m_delayKeyboardVisibilityChange = false;
1204
1205     if (m_pendingKeyboardVisibilityChange == NoChange)
1206         return;
1207
1208     notifyClientOfKeyboardVisibilityChange(m_pendingKeyboardVisibilityChange == Visible);
1209     m_pendingKeyboardVisibilityChange = NoChange;
1210 }
1211
1212 void InputHandler::notifyClientOfKeyboardVisibilityChange(bool visible)
1213 {
1214     // If we aren't ready for input, keyboard changes should be ignored.
1215     if (!isInputModeEnabled() && visible)
1216         return;
1217
1218     if (processingChange()) {
1219         ASSERT(visible);
1220         return;
1221     }
1222
1223     if (!m_delayKeyboardVisibilityChange) {
1224         m_webPage->showVirtualKeyboard(visible);
1225         return;
1226     }
1227
1228     m_pendingKeyboardVisibilityChange = visible ? Visible : NotVisible;
1229 }
1230
1231 bool InputHandler::selectionAtStartOfElement()
1232 {
1233     if (!isActiveTextEdit())
1234         return false;
1235
1236     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1237
1238     if (!selectionStart())
1239         return true;
1240
1241     return false;
1242 }
1243
1244 bool InputHandler::selectionAtEndOfElement()
1245 {
1246     if (!isActiveTextEdit())
1247         return false;
1248
1249     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1250
1251     return selectionStart() == static_cast<int>(elementText().length());
1252 }
1253
1254 int InputHandler::selectionStart() const
1255 {
1256     return selectionPosition(true);
1257 }
1258
1259 int InputHandler::selectionEnd() const
1260 {
1261     return selectionPosition(false);
1262 }
1263
1264 int InputHandler::selectionPosition(bool start) const
1265 {
1266     if (!m_currentFocusElement->document() || !m_currentFocusElement->document()->frame())
1267         return 0;
1268
1269     if (HTMLTextFormControlElement* controlElement = DOMSupport::toTextControlElement(m_currentFocusElement.get()))
1270         return start ? controlElement->selectionStart() : controlElement->selectionEnd();
1271
1272     FrameSelection caretSelection;
1273     caretSelection.setSelection(m_currentFocusElement->document()->frame()->selection()->selection());
1274     RefPtr<Range> rangeSelection = caretSelection.selection().toNormalizedRange();
1275     if (!rangeSelection)
1276         return 0;
1277
1278     int selectionPointInNode = start ? rangeSelection->startOffset() : rangeSelection->endOffset();
1279     Node* containerNode = start ? rangeSelection->startContainer() : rangeSelection->endContainer();
1280
1281     ExceptionCode ec;
1282     RefPtr<Range> rangeForNode = rangeOfContents(m_currentFocusElement.get());
1283     rangeForNode->setEnd(containerNode, selectionPointInNode, ec);
1284     ASSERT(!ec);
1285
1286     return TextIterator::rangeLength(rangeForNode.get());
1287 }
1288
1289 void InputHandler::selectionChanged()
1290 {
1291     // This method can get called during WebPage shutdown process.
1292     // If that is the case, just bail out since the client is not
1293     // in a safe state of trust to request anything else from it.
1294     if (!m_webPage->m_mainFrame)
1295         return;
1296
1297     if (!isActiveTextEdit())
1298         return;
1299
1300     if (processingChange())
1301         return;
1302
1303     // Scroll the field if necessary. This must be done even if we are processing
1304     // a change as the text change may have moved the caret. IMF doesn't require
1305     // the update, but the user needs to see the caret.
1306     ensureFocusTextElementVisible(EdgeIfNeeded);
1307
1308     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1309
1310     int newSelectionStart = selectionStart();
1311     int newSelectionEnd = selectionEnd();
1312
1313     InputLog(LogLevelInfo, "InputHandler::selectionChanged selectionStart=%u, selectionEnd=%u", newSelectionStart, newSelectionEnd);
1314
1315     m_webPage->m_client->inputSelectionChanged(newSelectionStart, newSelectionEnd);
1316
1317     // Remove the attributed text markers as the previous call triggered an end to
1318     // the composition.
1319     removeAttributedTextMarker();
1320 }
1321
1322 bool InputHandler::setCursorPosition(int location)
1323 {
1324     return setSelection(location, location);
1325 }
1326
1327 bool InputHandler::setSelection(int start, int end, bool changeIsPartOfComposition)
1328 {
1329     if (!isActiveTextEdit())
1330         return false;
1331
1332     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1333
1334     ProcessingChangeGuard guard(this);
1335
1336     VisibleSelection newSelection = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end);
1337     m_currentFocusElement->document()->frame()->selection()->setSelection(newSelection, changeIsPartOfComposition ? 0 : FrameSelection::CloseTyping | FrameSelection::ClearTypingStyle);
1338
1339     InputLog(LogLevelInfo, "InputHandler::setSelection selectionStart=%u, selectionEnd=%u", start, end);
1340
1341     return start == selectionStart() && end == selectionEnd();
1342 }
1343
1344 WebCore::IntRect InputHandler::rectForCaret(int index)
1345 {
1346     if (!isActiveTextEdit())
1347         return WebCore::IntRect();
1348
1349     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1350
1351     if (index < 0 || index > static_cast<int>(elementText().length())) {
1352         // Invalid request.
1353         return WebCore::IntRect();
1354     }
1355
1356     FrameSelection caretSelection;
1357     caretSelection.setSelection(DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), index, index).visibleStart());
1358     caretSelection.modify(FrameSelection::AlterationExtend, DirectionForward, CharacterGranularity);
1359     return caretSelection.selection().visibleStart().absoluteCaretBounds();
1360 }
1361
1362 void InputHandler::cancelSelection()
1363 {
1364     if (!isActiveTextEdit())
1365         return;
1366
1367     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1368
1369     int selectionStartPosition = selectionStart();
1370     ProcessingChangeGuard guard(this);
1371     setCursorPosition(selectionStartPosition);
1372 }
1373
1374 bool InputHandler::handleKeyboardInput(const Platform::KeyboardEvent& keyboardEvent, bool changeIsPartOfComposition)
1375 {
1376     InputLog(LogLevelInfo, "InputHandler::handleKeyboardInput received character=%lc, type=%d", keyboardEvent.character(), keyboardEvent.type());
1377
1378     // Enable input mode if we are processing a key event.
1379     setInputModeEnabled();
1380
1381     // If we aren't specifically part of a composition, fail, IMF should never send key input
1382     // while composing text. If IMF has failed, we should have already finished the
1383     // composition manually.
1384     if (!changeIsPartOfComposition && compositionActive())
1385         return false;
1386
1387     ProcessingChangeGuard guard(this);
1388
1389     unsigned adjustedModifiers = keyboardEvent.modifiers();
1390     if (WTF::isASCIIUpper(keyboardEvent.character()))
1391         adjustedModifiers |= KEYMOD_SHIFT;
1392
1393     ASSERT(m_webPage->m_page->focusController());
1394     bool keyboardEventHandled = false;
1395     if (Frame* focusedFrame = m_webPage->m_page->focusController()->focusedFrame()) {
1396         bool isKeyChar = keyboardEvent.type() == Platform::KeyboardEvent::KeyChar;
1397         Platform::KeyboardEvent::Type type = keyboardEvent.type();
1398
1399         // If this is a KeyChar type then we handle it as a keydown followed by a key up.
1400         if (isKeyChar)
1401             type = Platform::KeyboardEvent::KeyDown;
1402
1403         Platform::KeyboardEvent adjustedKeyboardEvent(keyboardEvent.character(), type, adjustedModifiers);
1404         keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent));
1405
1406         if (isKeyChar) {
1407             type = Platform::KeyboardEvent::KeyUp;
1408             adjustedKeyboardEvent = Platform::KeyboardEvent(keyboardEvent.character(), type, adjustedModifiers);
1409             keyboardEventHandled = focusedFrame->eventHandler()->keyEvent(PlatformKeyboardEvent(adjustedKeyboardEvent)) || keyboardEventHandled;
1410         }
1411
1412         if (!changeIsPartOfComposition && type == Platform::KeyboardEvent::KeyUp)
1413             ensureFocusTextElementVisible(EdgeIfNeeded);
1414     }
1415     return keyboardEventHandled;
1416 }
1417
1418 bool InputHandler::deleteSelection()
1419 {
1420     if (!isActiveTextEdit())
1421         return false;
1422
1423     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1424     Frame* frame = m_currentFocusElement->document()->frame();
1425
1426     if (frame->selection()->selectionType() != VisibleSelection::RangeSelection)
1427         return false;
1428
1429     ASSERT(frame->editor());
1430     return frame->editor()->command("DeleteBackward").execute();
1431 }
1432
1433 void InputHandler::insertText(const WTF::String& string)
1434 {
1435     if (!isActiveTextEdit())
1436         return;
1437
1438     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1439     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1440
1441     editor->command("InsertText").execute(string);
1442 }
1443
1444 void InputHandler::clearField()
1445 {
1446     if (!isActiveTextEdit())
1447         return;
1448
1449     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->editor());
1450     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1451
1452     editor->command("SelectAll").execute();
1453     editor->command("DeleteBackward").execute();
1454 }
1455
1456 bool InputHandler::executeTextEditCommand(const WTF::String& commandName)
1457 {
1458     ASSERT(m_webPage->focusedOrMainFrame() && m_webPage->focusedOrMainFrame()->editor());
1459     Editor* editor = m_webPage->focusedOrMainFrame()->editor();
1460
1461     return editor->command(commandName).execute();
1462 }
1463
1464 void InputHandler::cut()
1465 {
1466     executeTextEditCommand("Cut");
1467 }
1468
1469 void InputHandler::copy()
1470 {
1471     executeTextEditCommand("Copy");
1472 }
1473
1474 void InputHandler::paste()
1475 {
1476     executeTextEditCommand("Paste");
1477 }
1478
1479 void InputHandler::selectAll()
1480 {
1481     executeTextEditCommand("SelectAll");
1482 }
1483
1484 void InputHandler::addAttributedTextMarker(int start, int end, const AttributeTextStyle& style)
1485 {
1486     if ((end - start) < 1 || end > static_cast<int>(elementText().length()))
1487         return;
1488
1489     RefPtr<Range> markerRange = DOMSupport::visibleSelectionForRangeInputElement(m_currentFocusElement.get(), start, end).toNormalizedRange();
1490     m_currentFocusElement->document()->markers()->addMarker(markerRange.get(), DocumentMarker::AttributeText, WTF::String("Input Marker"), style);
1491 }
1492
1493 void InputHandler::removeAttributedTextMarker()
1494 {
1495     // Remove all attribute text markers.
1496     if (m_currentFocusElement && m_currentFocusElement->document())
1497         m_currentFocusElement->document()->markers()->removeMarkers(DocumentMarker::AttributeText);
1498
1499     m_composingTextStart = 0;
1500     m_composingTextEnd = 0;
1501 }
1502
1503 void InputHandler::handleInputLocaleChanged(bool isRTL)
1504 {
1505     if (!isActiveTextEdit())
1506         return;
1507
1508     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1509     RenderObject* renderer = m_currentFocusElement->renderer();
1510     if (!renderer)
1511         return;
1512
1513     Editor* editor = m_currentFocusElement->document()->frame()->editor();
1514     ASSERT(editor);
1515     if ((renderer->style()->direction() == RTL) != isRTL)
1516         editor->setBaseWritingDirection(isRTL ? RightToLeftWritingDirection : LeftToRightWritingDirection);
1517 }
1518
1519 void InputHandler::clearCurrentFocusElement()
1520 {
1521     if (m_currentFocusElement)
1522         m_currentFocusElement->blur();
1523 }
1524
1525 bool InputHandler::willOpenPopupForNode(Node* node)
1526 {
1527     // This method must be kept synchronized with InputHandler::didNodeOpenPopup.
1528     if (!node)
1529         return false;
1530
1531     ASSERT(!node->isInShadowTree());
1532
1533     if (node->hasTagName(HTMLNames::selectTag) || node->hasTagName(HTMLNames::optionTag)) {
1534         // We open list popups for options and selects.
1535         return true;
1536     }
1537
1538     if (node->isElementNode()) {
1539         Element* element = static_cast<Element*>(node);
1540         if (DOMSupport::isPopupInputField(element))
1541             return true;
1542     }
1543
1544     return false;
1545 }
1546
1547 bool InputHandler::didNodeOpenPopup(Node* node)
1548 {
1549     // This method must be kept synchronized with InputHandler::willOpenPopupForNode.
1550     if (!node)
1551         return false;
1552
1553     ASSERT(!node->isInShadowTree());
1554
1555     if (node->hasTagName(HTMLNames::selectTag))
1556         return openSelectPopup(static_cast<HTMLSelectElement*>(node));
1557
1558     if (node->hasTagName(HTMLNames::optionTag)) {
1559         HTMLOptionElement* optionElement = static_cast<HTMLOptionElement*>(node);
1560         return openSelectPopup(optionElement->ownerSelectElement());
1561     }
1562
1563     if (HTMLInputElement* element = node->toInputElement()) {
1564         if (DOMSupport::isDateTimeInputField(element))
1565             return openDatePopup(element, elementType(element));
1566
1567         if (DOMSupport::isColorInputField(element))
1568             return openColorPopup(element);
1569     }
1570     return false;
1571 }
1572
1573 bool InputHandler::openSelectPopup(HTMLSelectElement* select)
1574 {
1575     if (!select || select->disabled())
1576         return false;
1577
1578     // If there's no view, do nothing and return.
1579     if (!select->document()->view())
1580         return false;
1581
1582     if (isActiveTextEdit())
1583         clearCurrentFocusElement();
1584
1585     m_currentFocusElement = select;
1586     m_currentFocusElementType = SelectPopup;
1587
1588     const WTF::Vector<HTMLElement*>& listItems = select->listItems();
1589     int size = listItems.size();
1590
1591     bool multiple = select->multiple();
1592     ScopeArray<WebString> labels;
1593     labels.reset(new WebString[size]);
1594
1595     // Check if popup already exists, close it if does.
1596     m_webPage->m_page->chrome()->client()->closePagePopup(0);
1597
1598     bool* enableds = 0;
1599     int* itemTypes = 0;
1600     bool* selecteds = 0;
1601
1602     if (size) {
1603         enableds = new bool[size];
1604         itemTypes = new int[size];
1605         selecteds = new bool[size];
1606         for (int i = 0; i < size; i++) {
1607             if (listItems[i]->hasTagName(HTMLNames::optionTag)) {
1608                 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(listItems[i]);
1609                 labels[i] = option->textIndentedToRespectGroupLabel();
1610                 enableds[i] = option->disabled() ? 0 : 1;
1611                 selecteds[i] = option->selected();
1612                 itemTypes[i] = option->parentNode() && option->parentNode()->hasTagName(HTMLNames::optgroupTag) ? TypeOptionInGroup : TypeOption;
1613             } else if (listItems[i]->hasTagName(HTMLNames::optgroupTag)) {
1614                 HTMLOptGroupElement* optGroup = static_cast<HTMLOptGroupElement*>(listItems[i]);
1615                 labels[i] = optGroup->groupLabelText();
1616                 enableds[i] = optGroup->disabled() ? 0 : 1;
1617                 selecteds[i] = false;
1618                 itemTypes[i] = TypeGroup;
1619             } else if (listItems[i]->hasTagName(HTMLNames::hrTag)) {
1620                 enableds[i] = false;
1621                 selecteds[i] = false;
1622                 itemTypes[i] = TypeSeparator;
1623             }
1624         }
1625     }
1626
1627     SelectPopupClient* selectClient = new SelectPopupClient(multiple, size, labels, enableds, itemTypes, selecteds, m_webPage, select);
1628     WebCore::IntRect elementRectInRootView = select->document()->view()->contentsToRootView(enclosingIntRect(select->getRect()));
1629     // Fail to create HTML popup, use the old path
1630     if (!m_webPage->m_page->chrome()->client()->openPagePopup(selectClient, elementRectInRootView))
1631         m_webPage->m_client->openPopupList(multiple, size, labels, enableds, itemTypes, selecteds);
1632     return true;
1633 }
1634
1635 void InputHandler::setPopupListIndex(int index)
1636 {
1637     if (index == -2) // Abandon
1638         return clearCurrentFocusElement();
1639
1640     if (!isActiveSelectPopup())
1641         return clearCurrentFocusElement();
1642
1643     RenderObject* renderer = m_currentFocusElement->renderer();
1644     if (renderer && renderer->isMenuList()) {
1645         RenderMenuList* renderMenu = toRenderMenuList(renderer);
1646         renderMenu->hidePopup();
1647     }
1648
1649     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
1650     int optionIndex = selectElement->listToOptionIndex(index);
1651     selectElement->optionSelectedByUser(optionIndex, true /* deselect = true */, true /* fireOnChangeNow = false */);
1652     clearCurrentFocusElement();
1653 }
1654
1655 void InputHandler::setPopupListIndexes(int size, const bool* selecteds)
1656 {
1657     if (!isActiveSelectPopup())
1658         return clearCurrentFocusElement();
1659
1660     if (size < 0)
1661         return;
1662
1663     HTMLSelectElement* selectElement = static_cast<HTMLSelectElement*>(m_currentFocusElement.get());
1664     const WTF::Vector<HTMLElement*>& items = selectElement->listItems();
1665     if (items.size() != static_cast<unsigned int>(size))
1666         return;
1667
1668     HTMLOptionElement* option;
1669     for (int i = 0; i < size; i++) {
1670         if (items[i]->hasTagName(HTMLNames::optionTag)) {
1671             option = static_cast<HTMLOptionElement*>(items[i]);
1672             option->setSelectedState(selecteds[i]);
1673         }
1674     }
1675
1676     // Force repaint because we do not send mouse events to the select element
1677     // and the element doesn't automatically repaint itself.
1678     selectElement->dispatchFormControlChangeEvent();
1679     selectElement->renderer()->repaint();
1680     clearCurrentFocusElement();
1681 }
1682
1683 bool InputHandler::setBatchEditingActive(bool active)
1684 {
1685     if (!isActiveTextEdit())
1686         return false;
1687
1688     ASSERT(m_currentFocusElement->document());
1689     ASSERT(m_currentFocusElement->document()->frame());
1690
1691     // FIXME switch this to m_currentFocusElement->document()->frame() when we have separate
1692     // backingstore for each frame.
1693     BackingStoreClient* backingStoreClientForFrame = m_webPage->backingStoreClientForFrame(m_webPage->mainFrame());
1694     ASSERT(backingStoreClientForFrame);
1695
1696     // Enable / Disable the backingstore to prevent visual updates.
1697     if (!active)
1698         backingStoreClientForFrame->backingStore()->resumeScreenAndBackingStoreUpdates(BackingStore::RenderAndBlit);
1699     else
1700         backingStoreClientForFrame->backingStore()->suspendScreenAndBackingStoreUpdates();
1701
1702     return true;
1703 }
1704
1705 int InputHandler::caretPosition() const
1706 {
1707     if (!isActiveTextEdit())
1708         return -1;
1709
1710     // NOTE: This function is expected to return the start of the selection if
1711     // a selection is applied.
1712     return selectionStart();
1713 }
1714
1715 int relativeLeftOffset(int caretPosition, int leftOffset)
1716 {
1717     ASSERT(caretPosition >= 0);
1718
1719     return std::max(0, caretPosition - leftOffset);
1720 }
1721
1722 int relativeRightOffset(int caretPosition, unsigned totalLengthOfText, int rightOffset)
1723 {
1724     ASSERT(caretPosition >= 0);
1725     ASSERT(totalLengthOfText < INT_MAX);
1726
1727     return std::min(caretPosition + rightOffset, static_cast<int>(totalLengthOfText));
1728 }
1729
1730 bool InputHandler::deleteTextRelativeToCursor(int leftOffset, int rightOffset)
1731 {
1732     if (!isActiveTextEdit() || compositionActive())
1733         return false;
1734
1735     ProcessingChangeGuard guard(this);
1736
1737     InputLog(LogLevelInfo, "InputHandler::deleteTextRelativeToCursor left %d right %d", leftOffset, rightOffset);
1738
1739     int caretOffset = caretPosition();
1740     int start = relativeLeftOffset(caretOffset, leftOffset);
1741     int end = relativeRightOffset(caretOffset, elementText().length(), rightOffset);
1742     if (!deleteText(start, end))
1743         return false;
1744
1745     // Scroll the field if necessary. The automatic update is suppressed
1746     // by the processing change guard.
1747     ensureFocusTextElementVisible(EdgeIfNeeded);
1748
1749     return true;
1750 }
1751
1752 bool InputHandler::deleteText(int start, int end)
1753 {
1754     if (!isActiveTextEdit())
1755         return false;
1756
1757     ProcessingChangeGuard guard(this);
1758
1759     if (!setSelection(start, end, true /*changeIsPartOfComposition*/))
1760         return false;
1761
1762     InputLog(LogLevelInfo, "InputHandler::deleteText start %d end %d", start, end);
1763
1764     return deleteSelection();
1765 }
1766
1767 spannable_string_t* InputHandler::spannableTextInRange(int start, int end, int32_t flags)
1768 {
1769     if (!isActiveTextEdit())
1770         return 0;
1771
1772     if (start == end)
1773         return 0;
1774
1775     ASSERT(end > start);
1776     int length = end - start;
1777
1778     WTF::String textString = elementText().substring(start, length);
1779
1780     spannable_string_t* pst = (spannable_string_t*)fastMalloc(sizeof(spannable_string_t));
1781
1782     // Don't use fastMalloc in case the string is unreasonably long. fastMalloc will
1783     // crash immediately on failure.
1784     pst->str = (wchar_t*)malloc(sizeof(wchar_t) * (length + 1));
1785     if (!pst->str) {
1786         logAlways(LogLevelCritical, "InputHandler::spannableTextInRange Cannot allocate memory for string.\n");
1787         free(pst);
1788         return 0;
1789     }
1790
1791     int stringLength = 0;
1792     if (!convertStringToWchar(textString, pst->str, length + 1, &stringLength)) {
1793         logAlways(LogLevelCritical, "InputHandler::spannableTextInRange failed to convert string.\n");
1794         free(pst->str);
1795         free(pst);
1796         return 0;
1797     }
1798
1799     pst->length = stringLength;
1800     pst->spans_count = 0;
1801     pst->spans = 0;
1802
1803     return pst;
1804 }
1805
1806 spannable_string_t* InputHandler::selectedText(int32_t flags)
1807 {
1808     if (!isActiveTextEdit())
1809         return 0;
1810
1811     return spannableTextInRange(selectionStart(), selectionEnd(), flags);
1812 }
1813
1814 spannable_string_t* InputHandler::textBeforeCursor(int32_t length, int32_t flags)
1815 {
1816     if (!isActiveTextEdit())
1817         return 0;
1818
1819     int caretOffset = caretPosition();
1820     int start = relativeLeftOffset(caretOffset, length);
1821     int end = caretOffset;
1822
1823     return spannableTextInRange(start, end, flags);
1824 }
1825
1826 spannable_string_t* InputHandler::textAfterCursor(int32_t length, int32_t flags)
1827 {
1828     if (!isActiveTextEdit())
1829         return 0;
1830
1831     int caretOffset = caretPosition();
1832     int start = caretOffset;
1833     int end = relativeRightOffset(caretOffset, elementText().length(), length);
1834
1835     return spannableTextInRange(start, end, flags);
1836 }
1837
1838 extracted_text_t* InputHandler::extractedTextRequest(extracted_text_request_t* request, int32_t flags)
1839 {
1840     if (!isActiveTextEdit())
1841         return 0;
1842
1843     extracted_text_t* extractedText = (extracted_text_t *)fastMalloc(sizeof(extracted_text_t));
1844
1845     // 'flags' indicates whether the text is being monitored. This is not currently used.
1846
1847     // FIXME add smart limiting based on the hint sizes. Currently return all text.
1848
1849     extractedText->text = spannableTextInRange(0, elementText().length(), flags);
1850
1851     // FIXME confirm these should be 0 on this requested. Text changes will likely require
1852     // the end be the length.
1853     extractedText->partial_start_offset = 0;
1854     extractedText->partial_end_offset = 0;
1855     extractedText->start_offset = 0;
1856
1857     // Adjust selection values relative to the start offset, which may be a subset
1858     // of the text in the field.
1859     extractedText->selection_start = selectionStart() - extractedText->start_offset;
1860     extractedText->selection_end = selectionEnd() - extractedText->start_offset;
1861
1862     // selectionActive is not limited to inside the extracted text.
1863     bool selectionActive = extractedText->selection_start != extractedText->selection_end;
1864     bool singleLine = m_currentFocusElement->hasTagName(HTMLNames::inputTag);
1865
1866     // FIXME flags has two values in doc, enum not in header yet.
1867     extractedText->flags = selectionActive & singleLine;
1868
1869     return extractedText;
1870 }
1871
1872 static void addCompositionTextStyleToAttributeTextStyle(AttributeTextStyle& style)
1873 {
1874     style.setUnderline(AttributeTextStyle::StandardUnderline);
1875 }
1876
1877 static void addActiveTextStyleToAttributeTextStyle(AttributeTextStyle& style)
1878 {
1879     style.setBackgroundColor(Color("blue"));
1880     style.setTextColor(Color("white"));
1881 }
1882
1883 static AttributeTextStyle compositionTextStyle()
1884 {
1885     AttributeTextStyle style;
1886     addCompositionTextStyleToAttributeTextStyle(style);
1887     return style;
1888 }
1889
1890 static AttributeTextStyle textStyleFromMask(int64_t mask)
1891 {
1892     AttributeTextStyle style;
1893     if (mask & COMPOSED_TEXT_ATTRIB)
1894         addCompositionTextStyleToAttributeTextStyle(style);
1895
1896     if (mask & ACTIVE_REGION_ATTRIB)
1897         addActiveTextStyleToAttributeTextStyle(style);
1898
1899     return style;
1900 }
1901
1902 bool InputHandler::removeComposedText()
1903 {
1904     if (compositionActive()) {
1905         if (!deleteText(m_composingTextStart, m_composingTextEnd)) {
1906             // Could not remove the existing composition region.
1907             return false;
1908         }
1909         removeAttributedTextMarker();
1910     }
1911
1912     return true;
1913 }
1914
1915 int32_t InputHandler::setComposingRegion(int32_t start, int32_t end)
1916 {
1917     if (!isActiveTextEdit())
1918         return -1;
1919
1920     if (!removeComposedText()) {
1921         // Could not remove the existing composition region.
1922         return -1;
1923     }
1924
1925     m_composingTextStart = start;
1926     m_composingTextEnd = end;
1927
1928     if (compositionActive())
1929         addAttributedTextMarker(start, end, compositionTextStyle());
1930
1931     InputLog(LogLevelInfo, "InputHandler::setComposingRegion start %d end %d", start, end);
1932
1933     return 0;
1934 }
1935
1936 int32_t InputHandler::finishComposition()
1937 {
1938     if (!isActiveTextEdit())
1939         return -1;
1940
1941     // FIXME if no composition is active, should we return failure -1?
1942     if (!compositionActive())
1943         return 0;
1944
1945     // Remove all markers.
1946     removeAttributedTextMarker();
1947
1948     InputLog(LogLevelInfo, "InputHandler::finishComposition completed");
1949
1950     return 0;
1951 }
1952
1953 span_t* InputHandler::firstSpanInString(spannable_string_t* spannableString, SpannableStringAttribute attrib)
1954 {
1955     span_t* span = spannableString->spans;
1956     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
1957         if (span->attributes_mask & attrib)
1958             return span;
1959         span++;
1960     }
1961
1962     return 0;
1963 }
1964
1965 bool InputHandler::isTrailingSingleCharacter(span_t* span, unsigned stringLength, unsigned composingTextLength)
1966 {
1967     // Make sure the new string is one character larger than the old.
1968     if (composingTextLength != stringLength - 1)
1969         return false;
1970
1971     if (!span)
1972         return false;
1973
1974     // Has only 1 character changed?
1975     if (span->start == span->end) {
1976         // Is this character the last character in the string?
1977         if (span->start == stringLength - 1)
1978             return true;
1979     }
1980     // Return after the first changed_attrib is found.
1981     return false;
1982 }
1983
1984 bool InputHandler::setText(spannable_string_t* spannableString)
1985 {
1986     if (!isActiveTextEdit() || !spannableString)
1987         return false;
1988
1989     ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());
1990     Frame* frame = m_currentFocusElement->document()->frame();
1991
1992     Editor* editor = frame->editor();
1993     ASSERT(editor);
1994
1995     // Disable selectionHandler's active selection as we will be resetting and these
1996     // changes should not be handled as notification event.
1997     m_webPage->m_selectionHandler->setSelectionActive(false);
1998
1999     String textToInsert = convertSpannableStringToString(spannableString);
2000     int textLength = textToInsert.length();
2001
2002     InputLog(LogLevelInfo, "InputHandler::setText spannableString is '%s', of length %d \n", textToInsert.latin1().data(), textLength);
2003
2004     span_t* changedSpan = firstSpanInString(spannableString, CHANGED_ATTRIB);
2005     int composingTextStart = m_composingTextStart;
2006     int composingTextEnd = m_composingTextEnd;
2007     int composingTextLength = compositionLength();
2008     removeAttributedTextMarker();
2009
2010     if (isTrailingSingleCharacter(changedSpan, textLength, composingTextLength)) {
2011         // Handle the case where text is being composed.
2012         if (firstSpanInString(spannableString, COMPOSED_TEXT_ATTRIB)) {
2013             InputLog(LogLevelInfo, "InputHandler::setText Single trailing character detected.  Text is being composed. \n");
2014             return editor->command("InsertText").execute(textToInsert.right(1));
2015         }
2016         InputLog(LogLevelInfo, "InputHandler::setText Single trailing character detected. Text is not being composed. \n");
2017         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[textLength - 1], Platform::KeyboardEvent::KeyChar, 0), false /* changeIsPartOfComposition */);
2018     }
2019
2020     // If no spans have changed, treat it as a delete operation.
2021     if (!changedSpan) {
2022         // If the composition length is the same as our string length, then we don't need to do anything.
2023         if (composingTextLength == textLength) {
2024             InputLog(LogLevelInfo, "InputHandler::setText No spans have changed. New text is the same length as the old. Nothing to do. \n");
2025             return true;
2026         }
2027
2028         if (composingTextLength - textLength == 1) {
2029             InputLog(LogLevelInfo, "InputHandler::setText No spans have changed. New text is one character shorter than the old. Treating as 'delete'. \n");
2030             return editor->command("DeleteBackward").execute();
2031         }
2032     }
2033
2034     if (composingTextLength && !setSelection(composingTextStart, composingTextEnd, true /* changeIsPartOfComposition */))
2035         return false;
2036
2037     // If there is no text to add just delete.
2038     if (!textLength) {
2039         if (selectionActive())
2040             return editor->command("DeleteBackward").execute();
2041
2042         // Nothing to do.
2043         return true;
2044     }
2045
2046     // Triggering an insert of the text with a space character trailing
2047     // causes new text to adopt the previous text style.
2048     // Remove it and apply it as a keypress later.
2049     // Upstream Webkit bug created https://bugs.webkit.org/show_bug.cgi?id=70823
2050     bool requiresSpaceKeyPress = false;
2051     if (textLength > 0 && textToInsert[textLength - 1] == 32 /* space */) {
2052         requiresSpaceKeyPress = true;
2053         textLength--;
2054         textToInsert.remove(textLength, 1);
2055     }
2056
2057     InputLog(LogLevelInfo, "InputHandler::setText Request being processed. Text before processing: '%s'", elementText().latin1().data());
2058
2059     if (textLength == 1 && !spannableString->spans_count) {
2060         // Handle single key non-attributed entry as key press rather than insert to allow
2061         // triggering of javascript events.
2062         InputLog(LogLevelInfo, "InputHandler::setText Single character entry treated as key-press in the absense of spans. \n");
2063         return handleKeyboardInput(Platform::KeyboardEvent(textToInsert[0], Platform::KeyboardEvent::KeyChar, 0), true /* changeIsPartOfComposition */);
2064     }
2065
2066     // Perform the text change as a single command if there is one.
2067     if (!textToInsert.isEmpty() && !editor->command("InsertText").execute(textToInsert)) {
2068         InputLog(LogLevelWarn, "InputHandler::setText Failed to insert text '%s'", textToInsert.latin1().data());
2069         return false;
2070     }
2071
2072     if (requiresSpaceKeyPress)
2073         handleKeyboardInput(Platform::KeyboardEvent(32 /* space */, Platform::KeyboardEvent::KeyChar, 0), true /* changeIsPartOfComposition */);
2074
2075     InputLog(LogLevelInfo, "InputHandler::setText Request being processed. Text after processing '%s'", elementText().latin1().data());
2076
2077     return true;
2078 }
2079
2080 bool InputHandler::setTextAttributes(int insertionPoint, spannable_string_t* spannableString)
2081 {
2082     // Apply the attributes to the field.
2083     span_t* span = spannableString->spans;
2084     for (unsigned int i = 0; i < spannableString->spans_count; i++) {
2085         unsigned int startPosition = insertionPoint + span->start;
2086         // The end point includes the character that it is before. Ie, 0, 0
2087         // applies to the first character as the end point includes the character
2088         // at the position. This means the endPosition is always +1.
2089         unsigned int endPosition = insertionPoint + span->end + 1;
2090         if (endPosition < startPosition || endPosition > elementText().length())
2091             return false;
2092
2093         if (!span->attributes_mask)
2094             continue; // Nothing to do.
2095
2096         // MISSPELLED_WORD_ATTRIB is present as an option, but it is not currently
2097         // used by IMF. When they add support for on the fly spell checking we can
2098         // use it to apply spelling markers and disable continuous spell checking.
2099
2100         InputLog(LogLevelInfo, "InputHandler::setTextAttributes adding marker %d to %d - %llu", startPosition, endPosition, span->attributes_mask);
2101         addAttributedTextMarker(startPosition, endPosition, textStyleFromMask(span->attributes_mask));
2102
2103         span++;
2104     }
2105
2106     InputLog(LogLevelInfo, "InputHandler::setTextAttributes attribute count %d", spannableString->spans_count);
2107
2108     return true;
2109 }
2110
2111 bool InputHandler::setRelativeCursorPosition(int insertionPoint, int relativeCursorPosition)
2112 {
2113     if (!isActiveTextEdit())
2114         return false;
2115
2116     // 1 place cursor at end of insertion text.
2117     if (relativeCursorPosition == 1) {
2118         m_currentFocusElement->document()->frame()->selection()->revealSelection(ScrollAlignment::alignToEdgeIfNeeded);
2119         return true;
2120     }
2121
2122     int cursorPosition = 0;
2123     if (relativeCursorPosition <= 0) {
2124         // Zero = insertionPoint
2125         // Negative value, move the cursor the requested number of characters before
2126         // the start of the inserted text.
2127         cursorPosition = insertionPoint + relativeCursorPosition;
2128     } else {
2129         // Positive value, move the cursor the requested number of characters after
2130         // the end of the inserted text minus 1.
2131         cursorPosition = caretPosition() + relativeCursorPosition - 1;
2132     }
2133
2134     if (cursorPosition < 0 || cursorPosition > (int)elementText().length())
2135         return false;
2136
2137     InputLog(LogLevelInfo, "InputHandler::setRelativeCursorPosition cursor position %d", cursorPosition);
2138
2139     return setCursorPosition(cursorPosition);
2140 }
2141
2142 bool InputHandler::setSpannableTextAndRelativeCursor(spannable_string_t* spannableString, int relativeCursorPosition, bool markTextAsComposing)
2143 {
2144     InputLog(LogLevelInfo, "InputHandler::setSpannableTextAndRelativeCursor(%d, %d, %d)\n", spannableString->length, relativeCursorPosition, markTextAsComposing);
2145     int insertionPoint = compositionActive() ? m_composingTextStart : selectionStart();
2146
2147     ProcessingChangeGuard guard(this);
2148
2149     if (!setText(spannableString))
2150         return false;
2151
2152     if (!setTextAttributes(insertionPoint, spannableString))
2153         return false;
2154
2155     if (!setRelativeCursorPosition(insertionPoint, relativeCursorPosition))
2156         return false;
2157
2158     if (markTextAsComposing) {
2159         m_composingTextStart = insertionPoint;
2160         m_composingTextEnd = insertionPoint + spannableString->length;
2161     }
2162
2163     // Scroll the field if necessary. The automatic update is suppressed
2164     // by the processing change guard.
2165     ensureFocusTextElementVisible(EdgeIfNeeded);
2166
2167     return true;
2168 }
2169
2170 int32_t InputHandler::setComposingText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2171 {
2172     if (!isActiveTextEdit())
2173         return -1;
2174
2175     if (!spannableString)
2176         return -1;
2177
2178     InputLog(LogLevelInfo, "InputHandler::setComposingText at relativeCursorPosition: %d", relativeCursorPosition);
2179
2180     // Enable input mode if we are processing a key event.
2181     setInputModeEnabled();
2182
2183     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, true /* markTextAsComposing */) ? 0 : -1;
2184 }
2185
2186 int32_t InputHandler::commitText(spannable_string_t* spannableString, int32_t relativeCursorPosition)
2187 {
2188     if (!isActiveTextEdit())
2189         return -1;
2190
2191     if (!spannableString)
2192         return -1;
2193
2194     InputLog(LogLevelInfo, "InputHandler::commitText");
2195
2196     return setSpannableTextAndRelativeCursor(spannableString, relativeCursorPosition, false /* markTextAsComposing */) ? 0 : -1;
2197 }
2198
2199 }
2200 }