InputHandler.cpp 84.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * Copyright (C) 2009, 2010, 2011, 2012 Research In Motion Limited. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "config.h"
#include "InputHandler.h"

#include "BackingStore.h"
#include "BackingStoreClient.h"
#include "CSSStyleDeclaration.h"
#include "Chrome.h"
26
#include "ColorPickerClient.h"
27
#include "DOMSupport.h"
28
#include "DatePickerClient.h"
29 30 31 32 33 34
#include "Document.h"
#include "DocumentLoader.h"
#include "DocumentMarkerController.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameView.h"
35
#include "HTMLFormElement.h"
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "HTMLOptGroupElement.h"
#include "HTMLOptionElement.h"
#include "HTMLSelectElement.h"
#include "HTMLTextAreaElement.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PlatformKeyboardEvent.h"
#include "PluginView.h"
#include "Range.h"
#include "RenderLayer.h"
#include "RenderMenuList.h"
#include "RenderPart.h"
#include "RenderText.h"
#include "RenderTextControl.h"
#include "RenderWidget.h"
53
#include "RenderedDocumentMarker.h"
54
#include "ScopePointer.h"
55
#include "SelectPopupClient.h"
56
#include "SelectionHandler.h"
57 58
#include "SpellChecker.h"
#include "TextCheckerClient.h"
59
#include "TextIterator.h"
60
#include "VisiblePosition.h"
61 62 63
#include "WebPageClient.h"
#include "WebPage_p.h"
#include "WebSettings.h"
64
#include "htmlediting.h"
65
#include "visible_units.h"
66 67

#include <BlackBerryPlatformKeyboardEvent.h>
68
#include <BlackBerryPlatformLog.h>
69
#include <BlackBerryPlatformMisc.h>
70
#include <BlackBerryPlatformScreen.h>
71 72
#include <BlackBerryPlatformSettings.h>
#include <sys/keycodes.h>
73
#include <wtf/text/CString.h>
74 75 76

#define ENABLE_INPUT_LOG 0
#define ENABLE_FOCUS_LOG 0
77
#define ENABLE_SPELLING_LOG 0
78

79
static const unsigned MaxLearnTextDataSize = 500;
80
static const unsigned MaxSpellCheckingStringLength = 250;
81 82 83 84

using namespace BlackBerry::Platform;
using namespace WebCore;

85
#if ENABLE_INPUT_LOG
86
#define InputLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
87 88 89 90 91
#else
#define InputLog(severity, format, ...)
#endif // ENABLE_INPUT_LOG

#if ENABLE_FOCUS_LOG
92
#define FocusLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
93 94 95 96
#else
#define FocusLog(severity, format, ...)
#endif // ENABLE_FOCUS_LOG

97 98 99 100 101 102
#if ENABLE_SPELLING_LOG
#define SpellingLog(severity, format, ...) logAlways(severity, format, ## __VA_ARGS__)
#else
#define SpellingLog(severity, format, ...)
#endif // ENABLE_SPELLING_LOG

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
namespace BlackBerry {
namespace WebKit {

class ProcessingChangeGuard {
public:
    ProcessingChangeGuard(InputHandler* inputHandler)
        : m_inputHandler(inputHandler)
        , m_savedProcessingChange(false)
    {
        ASSERT(m_inputHandler);

        m_savedProcessingChange = m_inputHandler->processingChange();
        m_inputHandler->setProcessingChange(true);
    }

    ~ProcessingChangeGuard()
    {
        m_inputHandler->setProcessingChange(m_savedProcessingChange);
    }

private:
    InputHandler* m_inputHandler;
    bool m_savedProcessingChange;
};

InputHandler::InputHandler(WebPagePrivate* page)
    : m_webPage(page)
    , m_currentFocusElement(0)
131
    , m_inputModeEnabled(false)
132
    , m_processingChange(false)
133
    , m_changingFocus(false)
134 135 136 137
    , m_currentFocusElementType(TextEdit)
    , m_currentFocusElementTextEditMask(DEFAULT_STYLE)
    , m_composingTextStart(0)
    , m_composingTextEnd(0)
138 139
    , m_pendingKeyboardVisibilityChange(NoChange)
    , m_delayKeyboardVisibilityChange(false)
140 141
    , m_request(0)
    , m_processingTransactionId(-1)
142
    , m_focusZoomScale(0.0)
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
{
}

InputHandler::~InputHandler()
{
}

static BlackBerryInputType convertInputType(const HTMLInputElement* inputElement)
{
    if (inputElement->isPasswordField())
        return InputTypePassword;
    if (inputElement->isSearchField())
        return InputTypeSearch;
    if (inputElement->isEmailField())
        return InputTypeEmail;
    if (inputElement->isMonthControl())
        return InputTypeMonth;
    if (inputElement->isNumberField())
        return InputTypeNumber;
    if (inputElement->isTelephoneField())
        return InputTypeTelephone;
    if (inputElement->isURLField())
        return InputTypeURL;
166
#if ENABLE(INPUT_TYPE_COLOR)
167 168 169 170 171 172 173 174 175 176 177 178
    if (inputElement->isColorControl())
        return InputTypeColor;
#endif
    if (inputElement->isDateControl())
        return InputTypeDate;
    if (inputElement->isDateTimeControl())
        return InputTypeDateTime;
    if (inputElement->isDateTimeLocalControl())
        return InputTypeDateTimeLocal;
    if (inputElement->isTimeControl())
        return InputTypeTime;
    // FIXME: missing WEEK popup selector
179 180 181 182
    if (DOMSupport::elementIdOrNameIndicatesEmail(inputElement))
        return InputTypeEmail;
    if (DOMSupport::elementIdOrNameIndicatesUrl(inputElement))
        return InputTypeURL;
183 184
    if (DOMSupport::elementPatternIndicatesNumber(inputElement))
        return InputTypeNumber;
185 186
    if (DOMSupport::elementPatternIndicatesHexadecimal(inputElement))
        return InputTypeHexadecimal;
187 188 189 190 191 192 193

    return InputTypeText;
}

static int inputStyle(BlackBerryInputType type, const Element* element)
{
    switch (type) {
194 195
    case InputTypeEmail:
    case InputTypeURL:
196
    case InputTypeSearch:
197
    case InputTypeText:
198
    case InputTypeTextArea:
199
        {
200
            DOMSupport::AttributeState autoCompleteState = DOMSupport::elementSupportsAutocomplete(element);
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
            DOMSupport::AttributeState autoCorrectState = DOMSupport::elementSupportsAutocorrect(element);

            // Autocomplete disabled explicitly.
            if (autoCompleteState == DOMSupport::Off) {
                if (autoCorrectState == DOMSupport::On)
                    return NO_PREDICTION;
                return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
            }

            // Autocomplete enabled explicitly.
            if (autoCompleteState == DOMSupport::On) {
                if (autoCorrectState == DOMSupport::Off)
                    return NO_AUTO_TEXT | NO_AUTO_CORRECTION;
                return DEFAULT_STYLE;
            }

            // Check for reserved strings and known types.
            if (type == InputTypeEmail || type == InputTypeURL || DOMSupport::elementIdOrNameIndicatesNoAutocomplete(element)) {
                if (autoCorrectState == DOMSupport::On)
                    return NO_PREDICTION;
221
                return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
222 223 224 225 226 227 228 229 230
            }

            // Autocomplete state wasn't provided if it is a text area or autocorrect is on, apply support.
            if (autoCorrectState == DOMSupport::On || (type == InputTypeTextArea && autoCorrectState != DOMSupport::Off))
                return DEFAULT_STYLE;

            // Single line text input, without special features explicitly turned on or a textarea
            // with autocorrect disabled explicitly. Only enable predictions.
            return NO_AUTO_TEXT | NO_AUTO_CORRECTION;
231 232 233 234 235
        }
    case InputTypeIsIndex:
    case InputTypePassword:
    case InputTypeNumber:
    case InputTypeTelephone:
236
    case InputTypeHexadecimal:
237 238 239 240 241 242 243 244
        // Disable special handling.
        return NO_AUTO_TEXT | NO_PREDICTION | NO_AUTO_CORRECTION;
    default:
        break;
    }
    return DEFAULT_STYLE;
}

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
static VirtualKeyboardType convertStringToKeyboardType(const AtomicString& string)
{
    DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
    DEFINE_STATIC_LOCAL(AtomicString, Url, ("url"));
    DEFINE_STATIC_LOCAL(AtomicString, Email, ("email"));
    DEFINE_STATIC_LOCAL(AtomicString, Password, ("password"));
    DEFINE_STATIC_LOCAL(AtomicString, Web, ("web"));
    DEFINE_STATIC_LOCAL(AtomicString, Number, ("number"));
    DEFINE_STATIC_LOCAL(AtomicString, Symbol, ("symbol"));
    DEFINE_STATIC_LOCAL(AtomicString, Phone, ("phone"));
    DEFINE_STATIC_LOCAL(AtomicString, Pin, ("pin"));
    DEFINE_STATIC_LOCAL(AtomicString, Hex, ("hexadecimal"));

    if (string.isEmpty())
        return VKBTypeNotSet;
    if (equalIgnoringCase(string, Default))
        return VKBTypeDefault;
    if (equalIgnoringCase(string, Url))
        return VKBTypeUrl;
    if (equalIgnoringCase(string, Email))
        return VKBTypeEmail;
    if (equalIgnoringCase(string, Password))
        return VKBTypePassword;
    if (equalIgnoringCase(string, Web))
        return VKBTypeWeb;
    if (equalIgnoringCase(string, Number))
        return VKBTypeNumPunc;
    if (equalIgnoringCase(string, Symbol))
        return VKBTypeSymbol;
    if (equalIgnoringCase(string, Phone))
        return VKBTypePhone;
    if (equalIgnoringCase(string, Pin) || equalIgnoringCase(string, Hex))
        return VKBTypePin;
    return VKBTypeNotSet;
}

static VirtualKeyboardType keyboardTypeAttribute(const WebCore::Element* element)
{
    DEFINE_STATIC_LOCAL(QualifiedName, keyboardTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-type", nullAtom));

    if (element->fastHasAttribute(keyboardTypeAttr)) {
        AtomicString attributeString = element->fastGetAttribute(keyboardTypeAttr);
        return convertStringToKeyboardType(attributeString);
    }

    if (element->isFormControlElement()) {
        const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
        if (formElement->form() && formElement->form()->fastHasAttribute(keyboardTypeAttr)) {
            AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardTypeAttr);
            return convertStringToKeyboardType(attributeString);
        }
    }

    return VKBTypeNotSet;
}

static VirtualKeyboardEnterKeyType convertStringToKeyboardEnterKeyType(const AtomicString& string)
{
    DEFINE_STATIC_LOCAL(AtomicString, Default, ("default"));
    DEFINE_STATIC_LOCAL(AtomicString, Connect, ("connect"));
    DEFINE_STATIC_LOCAL(AtomicString, Done, ("done"));
    DEFINE_STATIC_LOCAL(AtomicString, Go, ("go"));
    DEFINE_STATIC_LOCAL(AtomicString, Join, ("join"));
    DEFINE_STATIC_LOCAL(AtomicString, Next, ("next"));
    DEFINE_STATIC_LOCAL(AtomicString, Search, ("search"));
    DEFINE_STATIC_LOCAL(AtomicString, Send, ("send"));
    DEFINE_STATIC_LOCAL(AtomicString, Submit, ("submit"));

    if (string.isEmpty())
        return VKBEnterKeyNotSet;
    if (equalIgnoringCase(string, Default))
        return VKBEnterKeyDefault;
    if (equalIgnoringCase(string, Connect))
        return VKBEnterKeyConnect;
    if (equalIgnoringCase(string, Done))
        return VKBEnterKeyDone;
    if (equalIgnoringCase(string, Go))
        return VKBEnterKeyGo;
    if (equalIgnoringCase(string, Join))
        return VKBEnterKeyJoin;
    if (equalIgnoringCase(string, Next))
        return VKBEnterKeyNext;
    if (equalIgnoringCase(string, Search))
        return VKBEnterKeySearch;
    if (equalIgnoringCase(string, Send))
        return VKBEnterKeySend;
    if (equalIgnoringCase(string, Submit))
        return VKBEnterKeySubmit;
    return VKBEnterKeyNotSet;
}

static VirtualKeyboardEnterKeyType keyboardEnterKeyTypeAttribute(const WebCore::Element* element)
{
    DEFINE_STATIC_LOCAL(QualifiedName, keyboardEnterKeyTypeAttr, (nullAtom, "data-blackberry-virtual-keyboard-enter-key", nullAtom));

    if (element->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
        AtomicString attributeString = element->fastGetAttribute(keyboardEnterKeyTypeAttr);
        return convertStringToKeyboardEnterKeyType(attributeString);
    }

    if (element->isFormControlElement()) {
        const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
        if (formElement->form() && formElement->form()->fastHasAttribute(keyboardEnterKeyTypeAttr)) {
            AtomicString attributeString = formElement->form()->fastGetAttribute(keyboardEnterKeyTypeAttr);
            return convertStringToKeyboardEnterKeyType(attributeString);
        }
    }

    return VKBEnterKeyNotSet;
}

356 357 358 359 360 361 362 363 364 365 366
void InputHandler::setProcessingChange(bool processingChange)
{
    if (processingChange == m_processingChange)
        return;

    m_processingChange = processingChange;

    if (!m_processingChange)
        m_webPage->m_selectionHandler->inputHandlerDidFinishProcessingChange();
}

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
WTF::String InputHandler::elementText()
{
    if (!isActiveTextEdit())
        return WTF::String();

    return DOMSupport::inputElementText(m_currentFocusElement.get());
}

BlackBerryInputType InputHandler::elementType(Element* element) const
{
    // <isIndex> is bundled with input so we need to check the formControlName
    // first to differentiate it from input which is essentially the same as
    // isIndex has been deprecated.
    if (element->formControlName() == HTMLNames::isindexTag)
        return InputTypeIsIndex;

    if (const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element->toInputElement()))
        return convertInputType(inputElement);

    if (element->hasTagName(HTMLNames::textareaTag))
        return InputTypeTextArea;

    // Default to InputTypeTextArea for content editable fields.
    return InputTypeTextArea;
}

393
void InputHandler::focusedNodeChanged()
394
{
395 396 397 398 399 400 401
    ASSERT(m_webPage->m_page->focusController());
    Frame* frame = m_webPage->m_page->focusController()->focusedOrMainFrame();
    if (!frame || !frame->document())
        return;

    Node* node = frame->document()->focusedNode();

402
    if (isActiveTextEdit() && m_currentFocusElement == node) {
403
        notifyClientOfKeyboardVisibilityChange(true);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
        return;
    }

    if (node && node->isElementNode()) {
        Element* element = static_cast<Element*>(node);
        if (DOMSupport::isElementTypePlugin(element)) {
            setPluginFocused(element);
            return;
        }

        if (DOMSupport::isTextBasedContentEditableElement(element)) {
            // Focused node is a text based input field, textarea or content editable field.
            setElementFocused(element);
            return;
        }
    }

    if (isActiveTextEdit() && m_currentFocusElement->isContentEditable()) {
        // This is a special handler for content editable fields. The focus node is the top most
        // field that is content editable, however, by enabling / disabling designmode and
        // content editable, it is possible through javascript or selection to trigger the focus node to
        // change even while maintaining editing on the same selection point. If the focus element
        // isn't contentEditable, but the current selection is, don't send a change notification.

        // When processing changes selection changes occur that may reset the focus element
        // and could potentially cause a crash as m_currentFocusElement should not be
        // changed during processing of an EditorCommand.
        if (processingChange())
            return;

        Frame* frame = m_currentFocusElement->document()->frame();
        ASSERT(frame);

        // Test the current selection to make sure that the content in focus is still content
        // editable. This may mean Javascript triggered a focus change by modifying the
        // top level parent of this object's content editable state without actually modifying
        // this particular object.
        // Example site: html5demos.com/contentEditable - blur event triggers focus change.
        if (frame == m_webPage->focusedOrMainFrame() && frame->selection()->start().anchorNode()
            && frame->selection()->start().anchorNode()->isContentEditable())
                return;
    }

    // No valid focus element found for handling.
    setElementUnfocused();
}

void InputHandler::setPluginFocused(Element* element)
{
    ASSERT(DOMSupport::isElementTypePlugin(element));

    if (isActiveTextEdit())
        setElementUnfocused();

    m_currentFocusElementType = Plugin;
    m_currentFocusElement = element;
}

static bool convertStringToWchar(const String& string, wchar_t* dest, int destCapacity, int* destLength)
{
    ASSERT(dest);

    if (!string.length()) {
        destLength = 0;
        return true;
    }

    UErrorCode ec = U_ZERO_ERROR;
    // wchar_t strings sent to IMF are 32 bit so casting to UChar32 is safe.
    u_strToUTF32(reinterpret_cast<UChar32*>(dest), destCapacity, destLength, string.characters(), string.length(), &ec);
    if (ec) {
475
        logAlways(LogLevelCritical, "InputHandler::convertStringToWchar Error converting string ec (%d).", ec);
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
        destLength = 0;
        return false;
    }
    return true;
}

static bool convertStringToWcharVector(const String& string, WTF::Vector<wchar_t>& wcharString)
{
    ASSERT(wcharString.isEmpty());

    int length = string.length();
    if (!length)
        return true;

    if (!wcharString.tryReserveCapacity(length + 1)) {
491
        logAlways(LogLevelCritical, "InputHandler::convertStringToWcharVector Cannot allocate memory for string.");
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
        return false;
    }

    int destLength = 0;
    if (!convertStringToWchar(string, wcharString.data(), length + 1, &destLength))
        return false;

    wcharString.resize(destLength);
    return true;
}

static String convertSpannableStringToString(spannable_string_t* src)
{
    if (!src || !src->str || !src->length)
        return String();

    WTF::Vector<UChar> dest;
    int destCapacity = (src->length * 2) + 1;
    if (!dest.tryReserveCapacity(destCapacity)) {
511
        logAlways(LogLevelCritical, "InputHandler::convertSpannableStringToString Cannot allocate memory for string.");
512 513 514 515 516 517 518 519
        return String();
    }

    int destLength = 0;
    UErrorCode ec = U_ZERO_ERROR;
    // wchar_t strings sent from IMF are 32 bit so casting to UChar32 is safe.
    u_strFromUTF32(dest.data(), destCapacity, &destLength, reinterpret_cast<UChar32*>(src->str), src->length, &ec);
    if (ec) {
520
        logAlways(LogLevelCritical, "InputHandler::convertSpannableStringToString Error converting string ec (%d).", ec);
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
        return String();
    }
    dest.resize(destLength);
    return String(dest.data(), destLength);
}

void InputHandler::sendLearnTextDetails(const WTF::String& string)
{
    Vector<wchar_t> wcharString;
    if (!convertStringToWcharVector(string, wcharString) || wcharString.isEmpty())
        return;

    m_webPage->m_client->inputLearnText(wcharString.data(), wcharString.size());
}

void InputHandler::learnText()
{
    if (!isActiveTextEdit())
        return;

    // Do not send (or calculate) the text when the field is NO_PREDICTION or NO_AUTO_TEXT.
    if (m_currentFocusElementTextEditMask & NO_PREDICTION || m_currentFocusElementTextEditMask & NO_AUTO_TEXT)
        return;

    String textInField(elementText());
546
    textInField = textInField.substring(std::max(0, static_cast<int>(textInField.length() - MaxLearnTextDataSize)), textInField.length());
547 548 549 550 551 552 553 554 555
    textInField.remove(0, textInField.find(" "));

    // Build up the 500 character strings in word chunks.
    // Spec says 1000, but memory corruption has been observed.
    ASSERT(textInField.length() <= MaxLearnTextDataSize);

    if (textInField.isEmpty())
        return;

556
    InputLog(LogLevelInfo, "InputHandler::learnText '%s'", textInField.latin1().data());
557 558 559
    sendLearnTextDetails(textInField);
}

560 561
void InputHandler::requestCheckingOfString(PassRefPtr<WebCore::TextCheckingRequest> textCheckingRequest)
{
562
    m_request = textCheckingRequest;
563

564 565
    if (!m_request) {
        SpellingLog(LogLevelWarn, "InputHandler::requestCheckingOfString did not receive a valid request.");
566 567 568
        return;
    }

569
    unsigned requestLength = m_request->text().length();
570

571 572 573
    // Check if the field should be spellchecked.
    if (!isActiveTextEdit() || !shouldSpellCheckElement(m_currentFocusElement.get()) || requestLength < 2) {
        m_request->didCancel();
574 575 576 577 578
        return;
    }

    if (requestLength > MaxSpellCheckingStringLength) {
        // Cancel this request and send it off in newly created chunks.
579 580
        m_request->didCancel();
        if (m_currentFocusElement->document() && m_currentFocusElement->document()->frame() && m_currentFocusElement->document()->frame()->selection()) {
581 582 583 584 585 586
            // 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
            // enter to finish composing a word and create a new line.
            VisiblePosition caretPosition = m_currentFocusElement->document()->frame()->selection()->start();
            VisibleSelection visibleSelection = VisibleSelection(previousLinePosition(caretPosition, caretPosition.lineDirectionPointForBlockDirectionNavigation()), caretPosition);
            spellCheckBlock(visibleSelection, TextCheckingProcessIncremental);
        }
587 588 589 590 591
        return;
    }

    wchar_t* checkingString = (wchar_t*)malloc(sizeof(wchar_t) * (requestLength + 1));
    if (!checkingString) {
592
        logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Cannot allocate memory for string.");
593
        m_request->didCancel();
594 595 596
        return;
    }

597
    int paragraphLength = 0;
598
    if (!convertStringToWchar(m_request->text(), checkingString, requestLength + 1, &paragraphLength)) {
599
        logAlways(LogLevelCritical, "InputHandler::requestCheckingOfString Failed to convert String to wchar type.");
600
        free(checkingString);
601
        m_request->didCancel();
602 603 604
        return;
    }

605
    m_processingTransactionId = m_webPage->m_client->checkSpellingOfStringAsync(checkingString, paragraphLength);
606
    free(checkingString);
607

608 609 610
    // If the call to the input service did not go through, then cancel the request so we don't block endlessly.
    // This should still take transactionId as a parameter to maintain the same behavior as if InputMethodSupport
    // were to cancel a request during processing.
611 612
    if (m_processingTransactionId == -1) { // Error before sending request to input service.
        m_request->didCancel();
613 614 615 616
        return;
    }
}

617
void InputHandler::spellCheckingRequestCancelled(int32_t transactionId)
618
{
619 620 621 622 623
    SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestCancelled Expected transaction id %d, received %d. %s"
                , transactionId
                , m_processingTransactionId
                , transactionId == m_processingTransactionId ? "" : "We are out of sync with input service.");

624 625
    m_request->didCancel();
    m_processingTransactionId = -1;
626 627 628 629
}

void InputHandler::spellCheckingRequestProcessed(int32_t transactionId, spannable_string_t* spannableString)
{
630 631 632 633
    SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestProcessed Expected transaction id %d, received %d. %s"
                , transactionId
                , m_processingTransactionId
                , transactionId == m_processingTransactionId ? "" : "We are out of sync with input service.");
634

635
    if (!spannableString || !isActiveTextEdit()) {
636 637 638
        SpellingLog(LogLevelWarn, "InputHandler::spellCheckingRequestProcessed Cancelling request with transactionId %d.", transactionId);
        m_request->didCancel();
        m_processingTransactionId = -1;
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
        return;
    }

    Vector<TextCheckingResult> results;

    // Convert the spannableString to TextCheckingResult then append to results vector.
    String replacement;
    TextCheckingResult textCheckingResult;
    textCheckingResult.type = TextCheckingTypeSpelling;
    textCheckingResult.replacement = replacement;
    textCheckingResult.location = 0;
    textCheckingResult.length = 0;

    span_t* span = spannableString->spans;
    for (unsigned int i = 0; i < spannableString->spans_count; i++) {
        if (!span)
            break;
        if (span->end < span->start) {
657
            m_request->didCancel();
658 659 660 661 662 663 664 665 666 667 668 669 670
            return;
        }
        if (span->attributes_mask & MISSPELLED_WORD_ATTRIB) {
            textCheckingResult.location = span->start;
            // The end point includes the character that it is before. Ie, 0, 0
            // applies to the first character as the end point includes the character
            // at the position. This means the endPosition is always +1.
            textCheckingResult.length = span->end - span->start + 1;
            results.append(textCheckingResult);
        }
        span++;
    }

671
    m_request->didSucceed(results);
672 673 674 675
}

SpellChecker* InputHandler::getSpellChecker()
{
676 677
    if (!m_currentFocusElement || !m_currentFocusElement->document())
        return 0;
678

679 680 681 682 683
    if (Frame* frame = m_currentFocusElement->document()->frame())
        if (Editor* editor = frame->editor())
            return editor->spellChecker();

    return 0;
684 685
}

686 687 688 689 690 691 692 693 694 695 696 697 698 699 700
bool InputHandler::shouldRequestSpellCheckingOptionsForPoint(Platform::IntPoint& point, const Element* touchedElement, imf_sp_text_t& spellCheckingOptionRequest)
{
    if (!isActiveTextEdit() || touchedElement != m_currentFocusElement)
        return false;

    LayoutPoint contentPos(m_webPage->mapFromViewportToContents(point));
    contentPos = DOMSupport::convertPointToFrame(m_webPage->mainFrame(), m_webPage->focusedOrMainFrame(), roundedIntPoint(contentPos));

    Document* document = m_currentFocusElement->document();
    ASSERT(document);

    RenderedDocumentMarker* marker = document->markers()->renderedMarkerContainingPoint(contentPos, DocumentMarker::Spelling);
    if (!marker)
        return false;

701 702
    // Populate the marker details in preparation for the request as the marker is
    // not guaranteed to be valid after the cursor is placed.
703 704 705
    spellCheckingOptionRequest.startTextPosition = marker->startOffset();
    spellCheckingOptionRequest.endTextPosition = marker->endOffset();

706 707
    SpellingLog(LogLevelInfo, "InputHandler::shouldRequestSpellCheckingOptionsForPoint Found spelling marker at point %d, %d\nMarker start %d end %d",
        point.x(), point.y(), spellCheckingOptionRequest.startTextPosition, spellCheckingOptionRequest.endTextPosition);
708

709 710 711 712 713
    return true;
}

void InputHandler::requestSpellingCheckingOptions(imf_sp_text_t& spellCheckingOptionRequest)
{
714 715 716 717 718 719 720 721 722 723 724 725 726 727
    // If the caret is no longer active, no message should be sent.
    if (m_webPage->focusedOrMainFrame()->selection()->selectionType() != VisibleSelection::CaretSelection)
        return;

    // imf_sp_text_t should be generated in pixel viewport coordinates.
    WebCore::IntRect caretLocation = m_webPage->focusedOrMainFrame()->selection()->selection().visibleStart().absoluteCaretBounds();
    caretLocation = m_webPage->mapToTransformed(m_webPage->focusedOrMainFrame()->view()->contentsToWindow(enclosingIntRect(caretLocation)));
    m_webPage->clipToTransformedContentsRect(caretLocation);

    spellCheckingOptionRequest.caret_rect.caret_top_x = caretLocation.x();
    spellCheckingOptionRequest.caret_rect.caret_top_y = caretLocation.y();
    spellCheckingOptionRequest.caret_rect.caret_bottom_x = caretLocation.x();
    spellCheckingOptionRequest.caret_rect.caret_bottom_y = caretLocation.y() + caretLocation.height();

728 729 730 731 732 733
    SpellingLog(LogLevelInfo, "InputHandler::requestSpellingCheckingOptions Sending request:\ncaret_rect.caret_top_x = %d\ncaret_rect.caret_top_y = %d" \
                              "\ncaret_rect.caret_bottom_x = %d\ncaret_rect.caret_bottom_y = %d\nstartTextPosition = %d\nendTextPosition = %d",
                              spellCheckingOptionRequest.caret_rect.caret_top_x, spellCheckingOptionRequest.caret_rect.caret_top_y,
                              spellCheckingOptionRequest.caret_rect.caret_bottom_x, spellCheckingOptionRequest.caret_rect.caret_bottom_y,
                              spellCheckingOptionRequest.startTextPosition, spellCheckingOptionRequest.endTextPosition);

734
    m_webPage->m_client->requestSpellingCheckingOptions(spellCheckingOptionRequest);
735 736
}

737 738 739
void InputHandler::setElementUnfocused(bool refocusOccuring)
{
    if (isActiveTextEdit()) {
740
        FocusLog(LogLevelInfo, "InputHandler::setElementUnfocused");
741 742 743 744 745 746 747

        // Pass any text into the field to IMF to learn.
        learnText();

        // End any composition that is in progress.
        finishComposition();

748 749
        // Only hide the keyboard if we aren't refocusing on a new input field.
        if (!refocusOccuring)
750
            notifyClientOfKeyboardVisibilityChange(false, true /* triggeredByFocusChange */);
751

752
        m_webPage->m_client->inputFocusLost();
753 754 755 756

        // If the frame selection isn't focused, focus it.
        if (!m_currentFocusElement->document()->frame()->selection()->isFocused())
            m_currentFocusElement->document()->frame()->selection()->setFocused(true);
757 758 759 760 761 762 763
    }

    // Clear the node details.
    m_currentFocusElement = 0;
    m_currentFocusElementType = TextEdit;
}

764
bool InputHandler::isInputModeEnabled() const
765
{
766
    // Input mode is enabled when set, or when dump render tree or always show keyboard setting is enabled.
767
    return m_inputModeEnabled || m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus();
768
}
769

770 771 772 773
void InputHandler::setInputModeEnabled(bool active)
{
    FocusLog(LogLevelInfo, "InputHandler::setInputModeEnabled '%s', override is '%s'"
             , active ? "true" : "false"
774
             , m_webPage->m_dumpRenderTree || Platform::Settings::instance()->alwaysShowKeyboardOnFocus() ? "true" : "false");
775

776
    m_inputModeEnabled = active;
777

778
    // If the frame selection isn't focused, focus it.
779
    if (isInputModeEnabled() && isActiveTextEdit() && !m_currentFocusElement->document()->frame()->selection()->isFocused())
780
        m_currentFocusElement->document()->frame()->selection()->setFocused(true);
781 782 783 784 785
}

void InputHandler::setElementFocused(Element* element)
{
    ASSERT(DOMSupport::isTextBasedContentEditableElement(element));
786 787 788 789 790 791 792 793 794 795 796 797 798
    ASSERT(element && element->document() && element->document()->frame());

#ifdef ENABLE_SPELLING_LOG
    BlackBerry::Platform::StopWatch timer;
    timer.start();
#endif

    if (!element || !(element->document()))
        return;

    Frame* frame = element->document()->frame();
    if (!frame)
        return;
799

800 801
    if (frame->selection()->isFocused() != isInputModeEnabled())
        frame->selection()->setFocused(isInputModeEnabled());
802 803 804 805 806 807 808 809 810 811 812 813

    // Clear the existing focus node details.
    setElementUnfocused(true /*refocusOccuring*/);

    // Mark this element as active and add to frame set.
    m_currentFocusElement = element;
    m_currentFocusElementType = TextEdit;

    // Send details to the client about this element.
    BlackBerryInputType type = elementType(element);
    m_currentFocusElementTextEditMask = inputStyle(type, element);

814 815 816
    VirtualKeyboardType keyboardType = keyboardTypeAttribute(element);
    VirtualKeyboardEnterKeyType enterKeyType = keyboardEnterKeyTypeAttribute(element);

817 818 819 820 821 822 823 824
    if (enterKeyType == VKBEnterKeyNotSet && type != InputTypeTextArea) {
        if (element->isFormControlElement()) {
            const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
            if (formElement->form() && formElement->form()->defaultButton())
                enterKeyType = VKBEnterKeySubmit;
        }
    }

825 826
    FocusLog(LogLevelInfo, "InputHandler::setElementFocused, Type=%d, Style=%d, Keyboard Type=%d, Enter Key=%d", type, m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
    m_webPage->m_client->inputFocusGained(type, m_currentFocusElementTextEditMask, keyboardType, enterKeyType);
827 828

    handleInputLocaleChanged(m_webPage->m_webSettings->isWritingDirectionRTL());
829 830

    if (!m_delayKeyboardVisibilityChange)
831
        notifyClientOfKeyboardVisibilityChange(true, true /* triggeredByFocusChange */);
832 833 834 835 836

#ifdef ENABLE_SPELLING_LOG
    SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Focusing the field took %f seconds.", timer.elapsed());
#endif

837 838
    // Check if the field should be spellchecked.
    if (!shouldSpellCheckElement(element))
839 840 841 842 843 844 845 846 847 848 849
        return;

    // Spellcheck the field in its entirety.
    VisibleSelection focusedBlock = DOMSupport::visibleSelectionForInputElement(element);
    spellCheckBlock(focusedBlock, TextCheckingProcessBatch);

#ifdef ENABLE_SPELLING_LOG
    SpellingLog(LogLevelInfo, "InputHandler::setElementFocused Spellchecking the field increased the total time to focus to %f seconds.", timer.elapsed());
#endif
}

850 851 852 853 854 855 856 857 858 859 860 861 862 863 864
bool InputHandler::shouldSpellCheckElement(const Element* element) const
{
    DOMSupport::AttributeState spellCheckAttr = DOMSupport::elementSupportsSpellCheck(element);

    // Explicitly set to off.
    if (spellCheckAttr == DOMSupport::Off)
        return false;

    // Undefined and part of a set of cases which we do not wish to check. This includes user names and email addresses, so we are piggybacking on NoAutocomplete cases.
    if (spellCheckAttr == DOMSupport::Default && (m_currentFocusElementTextEditMask & NO_AUTO_TEXT))
        return false;

    return true;
}

865 866 867 868 869
void InputHandler::spellCheckBlock(VisibleSelection& visibleSelection, TextCheckingProcessType textCheckingProcessType)
{
    if (!isActiveTextEdit())
        return;

870 871 872 873
    RefPtr<Range> rangeForSpellChecking = visibleSelection.toNormalizedRange();
    if (!rangeForSpellChecking || !rangeForSpellChecking->text() || !rangeForSpellChecking->text().length())
        return;

874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891
    SpellChecker* spellChecker = getSpellChecker();
    if (!spellChecker) {
        SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Failed to spellcheck the current focused element.");
        return;
    }

    // If we have a batch request, try to send off the entire block.
    if (textCheckingProcessType == TextCheckingProcessBatch) {
        // If total block text is under the limited amount, send the entire chunk.
        if (rangeForSpellChecking->text().length() < MaxSpellCheckingStringLength) {
            spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
            return;
        }
    }

    // Since we couldn't check the entire block at once, set up starting and ending markers to fire incrementally.
    VisiblePosition startPos = visibleSelection.visibleStart();
    VisiblePosition startOfCurrentLine = startOfLine(startPos);
892
    VisiblePosition endOfCurrentLine = endOfLine(startOfCurrentLine);
893

894
    while (!isEndOfBlock(startOfCurrentLine)) {
895 896 897
        // Create a selection with the start and end points of the line, and convert to Range to create a SpellCheckRequest.
        rangeForSpellChecking = VisibleSelection(startOfCurrentLine, endOfCurrentLine).toNormalizedRange();

898 899 900 901
        if (rangeForSpellChecking->text().length() < MaxSpellCheckingStringLength) {
            startOfCurrentLine = nextLinePosition(startOfCurrentLine, startOfCurrentLine.lineDirectionPointForBlockDirectionNavigation());
            endOfCurrentLine = endOfLine(startOfCurrentLine);
        } else {
902 903 904 905 906 907 908
            // Iterate through words from the start of the line to the end.
            rangeForSpellChecking = getRangeForSpellCheckWithFineGranularity(startOfCurrentLine, endOfCurrentLine);
            if (!rangeForSpellChecking) {
                SpellingLog(LogLevelWarn, "InputHandler::spellCheckBlock Failed to set text range for spellchecking");
                return;
            }
            startOfCurrentLine = VisiblePosition(rangeForSpellChecking->endPosition());
909 910
            endOfCurrentLine = endOfLine(startOfCurrentLine);
            rangeForSpellChecking = DOMSupport::trimWhitespaceFromRange(VisiblePosition(rangeForSpellChecking->startPosition()), VisiblePosition(rangeForSpellChecking->endPosition()));
911 912 913 914 915 916 917 918 919 920 921 922
        }

        SpellingLog(LogLevelInfo, "InputHandler::spellCheckBlock Substring text is '%s', of size %d", rangeForSpellChecking->text().latin1().data(), rangeForSpellChecking->text().length());

        // Call spellcheck with substring.
        spellChecker->requestCheckingFor(SpellCheckRequest::create(TextCheckingTypeSpelling, TextCheckingProcessBatch, rangeForSpellChecking, rangeForSpellChecking));
    }
}

PassRefPtr<Range> InputHandler::getRangeForSpellCheckWithFineGranularity(VisiblePosition startPosition, VisiblePosition endPosition)
{
    VisiblePosition endOfCurrentWord = endOfWord(startPosition);
923 924 925 926 927 928

    // Keep iterating until one of our cases is hit, or we've incremented the starting position right to the end.
    while (startPosition != endPosition) {
        // Check the text length within this range.
        if (VisibleSelection(startPosition, endOfCurrentWord).toNormalizedRange()->text().length() >= MaxSpellCheckingStringLength) {
            // If this is not the first word, return a Range with end boundary set to the previous word.
929
            if (startOfWord(endOfCurrentWord, LeftWordIfOnBoundary) != startPosition && !DOMSupport::isEmptyRangeOrAllSpaces(startPosition, endOfCurrentWord))
930 931 932 933
                return VisibleSelection(startPosition, endOfWord(previousWordPosition(endOfCurrentWord), LeftWordIfOnBoundary)).toNormalizedRange();

            // Our first word has gone over the character limit. Increment the starting position past an uncheckable word.
            startPosition = endOfCurrentWord;
934
            endOfCurrentWord = endOfWord(nextWordPosition(endOfCurrentWord));
935 936 937 938 939 940 941
        } else if (endOfCurrentWord == endPosition) {
            // Return the last segment if the end of our word lies at the end of the range.
            return VisibleSelection(startPosition, endPosition).toNormalizedRange();
        } else {
            // Increment the current word.
            endOfCurrentWord = endOfWord(nextWordPosition(endOfCurrentWord));
        }
942 943
    }
    return 0;
944 945 946 947 948 949 950 951 952 953
}

bool InputHandler::openDatePopup(HTMLInputElement* element, BlackBerryInputType type)
{
    if (!element || element->disabled() || !DOMSupport::isDateTimeInputField(element))
        return false;

    if (isActiveTextEdit())
        clearCurrentFocusElement();

954 955 956 957
    switch (type) {
    case BlackBerry::Platform::InputTypeDate:
    case BlackBerry::Platform::InputTypeTime:
    case BlackBerry::Platform::InputTypeDateTime:
958 959
    case BlackBerry::Platform::InputTypeDateTimeLocal:
    case BlackBerry::Platform::InputTypeMonth: {
960 961 962 963 964 965 966 967
        // Check if popup already exists, close it if does.
        m_webPage->m_page->chrome()->client()->closePagePopup(0);
        String value = element->value();
        String min = element->getAttribute(HTMLNames::minAttr).string();
        String max = element->getAttribute(HTMLNames::maxAttr).string();
        double step = element->getAttribute(HTMLNames::stepAttr).toDouble();

        DatePickerClient* client = new DatePickerClient(type, value, min, max, step,  m_webPage, element);
968
        return m_webPage->m_page->chrome()->client()->openPagePopup(client,  WebCore::IntRect());
969 970 971 972
        }
    default: // Other types not supported
        return false;
    }
973 974 975 976 977 978 979 980 981 982 983 984 985
}

bool InputHandler::openColorPopup(HTMLInputElement* element)
{
    if (!element || element->disabled() || !DOMSupport::isColorInputField(element))
        return false;

    if (isActiveTextEdit())
        clearCurrentFocusElement();

    m_currentFocusElement = element;
    m_currentFocusElementType = TextPopup;

986 987
    // Check if popup already exists, close it if does.
    m_webPage->m_page->chrome()->client()->closePagePopup(0);
988

989
    ColorPickerClient* client = new ColorPickerClient(element->value(), m_webPage, element);
990
    return m_webPage->m_page->chrome()->client()->openPagePopup(client,  WebCore::IntRect());
991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010
}

void InputHandler::setInputValue(const WTF::String& value)
{
    if (!isActiveTextPopup())
        return;

    HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(m_currentFocusElement.get());
    inputElement->setValue(value);
    clearCurrentFocusElement();
}

void InputHandler::nodeTextChanged(const Node* node)
{
    if (processingChange() || !node)
        return;

    if (node != m_currentFocusElement)
        return;

1011
    InputLog(LogLevelInfo, "InputHandler::nodeTextChanged");
1012 1013 1014 1015 1016 1017 1018 1019

    m_webPage->m_client->inputTextChanged();

    // Remove the attributed text markers as the previous call triggered an end to
    // the composition.
    removeAttributedTextMarker();
}

1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
WebCore::IntRect InputHandler::boundingBoxForInputField()
{
    if (!isActiveTextEdit())
        return WebCore::IntRect();

    if (!m_currentFocusElement->renderer())
        return WebCore::IntRect();

    return m_currentFocusElement->renderer()->absoluteBoundingBoxRect();
}

1031 1032
void InputHandler::ensureFocusTextElementVisible(CaretScrollType scrollType)
{
1033
    if (!isActiveTextEdit() || !isInputModeEnabled() || !m_currentFocusElement->document())
1034 1035
        return;

1036
    if (!(Platform::Settings::instance()->allowedScrollAdjustmentForInputFields() & scrollType))
1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
        return;

    Frame* elementFrame = m_currentFocusElement->document()->frame();
    if (!elementFrame)
        return;

    Frame* mainFrame = m_webPage->mainFrame();
    if (!mainFrame)
        return;

    FrameView* mainFrameView = mainFrame->view();
    if (!mainFrameView)
        return;

    WebCore::IntRect selectionFocusRect;
    switch (elementFrame->selection()->selectionType()) {
    case VisibleSelection::CaretSelection:
        selectionFocusRect = elementFrame->selection()->absoluteCaretBounds();
        break;
    case VisibleSelection::RangeSelection: {
        Position selectionPosition;
        if (m_webPage->m_selectionHandler->lastUpdatedEndPointIsValid())
            selectionPosition = elementFrame->selection()->end();
        else
            selectionPosition = elementFrame->selection()->start();
        selectionFocusRect = VisiblePosition(selectionPosition).absoluteCaretBounds();
        break;
    }
    case VisibleSelection::NoSelection:
1066 1067 1068 1069 1070
        if (m_focusZoomScale) {
            m_webPage->zoomAboutPoint(m_focusZoomScale, m_focusZoomLocation);
            m_focusZoomScale = 0.0;
            m_focusZoomLocation = WebCore::IntPoint();
        }
1071 1072 1073 1074 1075
        return;
    }

    int fontHeight = selectionFocusRect.height();

1076 1077
    m_webPage->suspendBackingStore();

1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092
    // If the text is too small, zoom in to make it a minimum size.
    // The minimum size being defined as 3 mm is a good value based on my observations.
    static const int s_minimumTextHeightInPixels = Graphics::Screen::primaryScreen()->heightInMMToPixels(3);

    if (fontHeight && fontHeight * m_webPage->currentScale() < s_minimumTextHeightInPixels) {
        if (!m_focusZoomScale) {
            m_focusZoomScale = m_webPage->currentScale();
            m_focusZoomLocation = selectionFocusRect.location();
        }
        m_webPage->zoomAboutPoint(s_minimumTextHeightInPixels / fontHeight, m_focusZoomLocation);
    } else {
        m_focusZoomScale = 0.0;
        m_focusZoomLocation = WebCore::IntPoint();
    }

1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140
    if (elementFrame != mainFrame) { // Element is in a subframe.
        // Remove any scroll offset within the subframe to get the point relative to the main frame.
        selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());

        // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
        if (elementFrame->ownerRenderer()) {
            WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
            selectionFocusRect.move(frameOffset.x(), frameOffset.y());
        }
    }

    Position start = elementFrame->selection()->start();
    if (start.anchorNode() && start.anchorNode()->renderer()) {
        if (RenderLayer* layer = start.anchorNode()->renderer()->enclosingLayer()) {
            WebCore::IntRect actualScreenRect = WebCore::IntRect(mainFrameView->scrollPosition(), m_webPage->actualVisibleSize());
            ScrollAlignment horizontalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;
            ScrollAlignment verticalScrollAlignment = ScrollAlignment::alignToEdgeIfNeeded;

            if (scrollType != EdgeIfNeeded) {
                // Align the selection rect if possible so that we show the field's
                // outline if the caret is at the edge of the field.
                if (RenderObject* focusedRenderer = m_currentFocusElement->renderer()) {
                    WebCore::IntRect nodeOutlineBounds = focusedRenderer->absoluteOutlineBounds();
                    WebCore::IntRect caretAtEdgeRect = rectForCaret(0);
                    int paddingX = abs(caretAtEdgeRect.x() - nodeOutlineBounds.x());
                    int paddingY = abs(caretAtEdgeRect.y() - nodeOutlineBounds.y());

                    if (selectionFocusRect.x() - paddingX == nodeOutlineBounds.x())
                        selectionFocusRect.setX(nodeOutlineBounds.x());
                    else if (selectionFocusRect.maxX() + paddingX == nodeOutlineBounds.maxX())
                        selectionFocusRect.setX(nodeOutlineBounds.maxX() - selectionFocusRect.width());
                    if (selectionFocusRect.y() - paddingY == nodeOutlineBounds.y())
                        selectionFocusRect.setY(nodeOutlineBounds.y() - selectionFocusRect.height());
                    else if (selectionFocusRect.maxY() + paddingY == nodeOutlineBounds.maxY())
                        selectionFocusRect.setY(nodeOutlineBounds.maxY() - selectionFocusRect.height());

                    // If the editing point is on the left hand side of the screen when the node's
                    // rect is edge aligned, edge align the node rect.
                    if (selectionFocusRect.x() - caretAtEdgeRect.x() < actualScreenRect.width() / 2)
                        selectionFocusRect.setX(nodeOutlineBounds.x());
                    else
                        horizontalScrollAlignment = ScrollAlignment::alignCenterIfNeeded;

                }
                verticalScrollAlignment = (scrollType == CenterAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
            }

            // Pad the rect to improve the visual appearance.
1141 1142 1143 1144 1145 1146
            // Convert the padding back from transformed to ensure a consistent padding regardless of
            // zoom level as controls do not zoom.
            static const int s_focusRectPaddingSize = Graphics::Screen::primaryScreen()->heightInMMToPixels(3);
            selectionFocusRect.inflate(m_webPage->mapFromTransformed(WebCore::IntSize(0, s_focusRectPaddingSize)).height());

            WebCore::IntRect revealRect(layer->getRectToExpose(actualScreenRect, selectionFocusRect,
1147
                                                                 horizontalScrollAlignment,
1148
                                                                 verticalScrollAlignment));
1149

1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
            mainFrameView->setConstrainsScrollingToContentEdge(false);
            // In order to adjust the scroll position to ensure the focused input field is visible,
            // we allow overscrolling. However this overscroll has to be strictly allowed towards the
            // bottom of the page on the y axis only, where the virtual keyboard pops up from.
            WebCore::IntPoint scrollLocation = revealRect.location();
            scrollLocation.clampNegativeToZero();
            WebCore::IntPoint maximumScrollPosition = WebCore::IntPoint(mainFrameView->contentsWidth() - actualScreenRect.width(), mainFrameView->contentsHeight() - actualScreenRect.height());
            scrollLocation = scrollLocation.shrunkTo(maximumScrollPosition);
            mainFrameView->setScrollPosition(scrollLocation);
            mainFrameView->setConstrainsScrollingToContentEdge(true);
1160 1161
        }
    }
1162
    m_webPage->resumeBackingStore();
1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
}

void InputHandler::ensureFocusPluginElementVisible()
{
    if (!isActivePlugin() || !m_currentFocusElement->document())
        return;

    Frame* elementFrame = m_currentFocusElement->document()->frame();
    if (!elementFrame)
        return;

    Frame* mainFrame = m_webPage->mainFrame();
    if (!mainFrame)
        return;

    FrameView* mainFrameView = mainFrame->view();
    if (!mainFrameView)
        return;

    WebCore::IntRect selectionFocusRect;

    RenderWidget* renderWidget = static_cast<RenderWidget*>(m_currentFocusElement->renderer());
    if (renderWidget) {
        PluginView* pluginView = static_cast<PluginView*>(renderWidget->widget());

        if (pluginView)
            selectionFocusRect = pluginView->ensureVisibleRect();
    }

    if (selectionFocusRect.isEmpty())
        return;

    // FIXME: We may need to scroll the subframe (recursively) in the future. Revisit this...
    if (elementFrame != mainFrame) { // Element is in a subframe.
        // Remove any scroll offset within the subframe to get the point relative to the main frame.
        selectionFocusRect.move(-elementFrame->view()->scrollPosition().x(), -elementFrame->view()->scrollPosition().y());

        // Adjust the selection rect based on the frame offset in relation to the main frame if it's a subframe.
        if (elementFrame->ownerRenderer()) {
            WebCore::IntPoint frameOffset = elementFrame->ownerRenderer()->absoluteContentBox().location();
            selectionFocusRect.move(frameOffset.x(), frameOffset.y());
        }
    }

    WebCore::IntRect actualScreenRect = WebCore::IntRect(mainFrameView->scrollPosition(), m_webPage->actualVisibleSize());
    if (actualScreenRect.contains(selectionFocusRect))
        return;

    // Calculate a point such that the center of the requested rectangle
    // is at the center of the screen. FIXME: If the element was partially on screen
    // we might want to just bring the offscreen portion into view, someone needs
    // to decide if that's the behavior we want or not.
    WebCore::IntPoint pos(selectionFocusRect.center().x() - actualScreenRect.width() / 2,
                 selectionFocusRect.center().y() - actualScreenRect.height() / 2);

    mainFrameView->setScrollPosition(pos);
}

void InputHandler::ensureFocusElementVisible(bool centerInView)
{
    if (isActivePlugin())
        ensureFocusPluginElementVisible();
    else
        ensureFocusTextElementVisible(centerInView ? CenterAlways : CenterIfNeeded);
}

1229
void InputHandler::frameUnloaded(const Frame* frame)
1230 1231 1232 1233 1234 1235 1236
{
    if (!isActiveTextEdit())
        return;

    if (m_currentFocusElement->document()->frame() != frame)
        return;

1237
    FocusLog(LogLevelInfo, "InputHandler::frameUnloaded");
1238 1239 1240 1241

    setElementUnfocused(false /*refocusOccuring*/);
}

1242
void InputHandler::setDelayKeyboardVisibilityChange(bool value)
1243
{
1244 1245
    m_delayKeyboardVisibilityChange = value;
    m_pendingKeyboardVisibilityChange = NoChange;
1246 1247
}

1248
void InputHandler::processPendingKeyboardVisibilityChange()
1249
{
1250 1251
    if (!m_delayKeyboardVisibilityChange) {
        ASSERT(m_pendingKeyboardVisibilityChange == NoChange);
1252 1253 1254
        return;
    }

1255
    m_delayKeyboardVisibilityChange = false;
1256

1257
    if (m_pendingKeyboardVisibilityChange == NoChange)
1258 1259
        return;

1260 1261
    notifyClientOfKeyboardVisibilityChange(m_pendingKeyboardVisibilityChange == Visible);
    m_pendingKeyboardVisibilityChange = NoChange;
1262 1263
}

1264
void InputHandler::notifyClientOfKeyboardVisibilityChange(bool visible, bool triggeredByFocusChange)
1265
{
1266
    // If we aren't ready for input, keyboard changes should be ignored.
1267
    if (!isInputModeEnabled() && visible)
1268 1269
        return;

1270 1271 1272 1273 1274
    // If we are processing a change assume the keyboard is visbile to avoid
    // flooding the VKB with show requests.
    if (!triggeredByFocusChange && processingChange() && visible)
        return;

1275 1276
    if (!m_delayKeyboardVisibilityChange) {
        m_webPage->showVirtualKeyboard(visible);
1277 1278 1279
        return;
    }

1280
    m_pendingKeyboardVisibilityChange = visible ? Visible : NotVisible;
1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364
}

bool InputHandler::selectionAtStartOfElement()
{
    if (!isActiveTextEdit())
        return false;

    ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());

    if (!selectionStart())
        return true;

    return false;
}

bool InputHandler::selectionAtEndOfElement()
{
    if (!isActiveTextEdit())
        return false;

    ASSERT(m_currentFocusElement->document() && m_currentFocusElement->document()->frame());

    return selectionStart() == static_cast<int>(elementText().length());
}

int InputHandler::selectionStart() const
{
    return selectionPosition(true);
}

<