diff -r c8a366e56285 -r 60c5402cb945 webengine/osswebengine/WebKit/s60/webview/WebView.cpp --- a/webengine/osswebengine/WebKit/s60/webview/WebView.cpp Thu Sep 24 12:53:48 2009 +0300 +++ b/webengine/osswebengine/WebKit/s60/webview/WebView.cpp Mon Oct 26 08:28:45 2009 +0200 @@ -143,7 +143,10 @@ : coreFrame->tree()->traversePreviousWithWrap(wrapFlag)); } - +#define IS_UP_KEY(keyevent) ((keyevent.iCode == EKeyUpArrow) || (keyevent.iCode == EStdKeyUpArrow)) +#define IS_DOWN_KEY(keyevent) ((keyevent.iCode == EStdKeyDevice12) || (keyevent.iCode == EKeyDownArrow) || (keyevent.iCode == EStdKeyDownArrow)) +#define IS_RIGHT_KEY(keyevent) ((keyevent.iCode == EStdKeyDevice11) || (keyevent.iCode == EKeyRightArrow) || (keyevent.iCode == EStdKeyRightArrow)) +#define IS_LEFT_KEY(keyevent) ((keyevent.iCode == EStdKeyDevice13) || (keyevent.iCode == EKeyLeftArrow) || (keyevent.iCode == EStdKeyLeftArrow)) // ----------------------------------------------------------------------------- // WebView::NewL @@ -196,11 +199,14 @@ , m_showCursor(false) , m_allowRepaints(true) , m_prevEditMode(false) +, m_firedEvent(0) { } WebView::~WebView() { + StaticObjectsContainer::instance()->webCursor()->stopTransparencyTimer(); + // the zoom handler is a client of WebView (also owned by // WebView--a circular dependency) so it must be deleted before // the WebView object is destroyed because in its destructor it @@ -782,6 +788,7 @@ m_focusedElementType = TBrCtlDefs::EElementNone; } m_brctl->updateDefaultSoftkeys(); + page()->chrome()->client()->setElementVisibilityChanged(false); } void WebView::updatePageScaler() @@ -898,7 +905,8 @@ event.iPosition = pos; event.iModifiers = 0; event.iType = eventType; - + + setMouseEventFired(); switch (eventType) { case TPointerEvent::EButton1Down: { @@ -918,6 +926,7 @@ break; } }; + clearMouseEventFired(); } @@ -931,22 +940,21 @@ } } -bool WebView::needDeactivateEditable(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame) +bool WebView::needDeactivateEditable(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame, bool consumed) { - bool upOrDown = ((keyevent.iCode == EKeyUpArrow) || - (keyevent.iCode == EStdKeyUpArrow) || - (keyevent.iCode == EKeyRightUpArrow) || - (keyevent.iCode == EKeyDownArrow) || - (keyevent.iCode == EStdKeyDownArrow) || - (keyevent.iCode == EKeyLeftDownArrow)); - bool shouldEndEditing = frame->editor()->client()->shouldEndEditing(NULL); - bool inEditState = (m_isEditable && (m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox)); - bool isSelectBoxActive = (m_focusedElementType == TBrCtlDefs::EElementSelectBox); - bool deactivateInputBox = (inEditState && upOrDown && m_webfeptexteditor->validateTextFormat()); - bool deactivateSelectBox = (isSelectBoxActive && isNaviKey(keyevent)); - + /* + * Here we are making an assumption that if element visibility has been changed + * than JavaScript used the key event. If this the the case or consumed is tru and + * keys are Up or Down we don't want to deactivate an editable element. + * Google suggest is examples if such case. + */ + bool upOrDownConsumed = (consumed || //JavaScript consumed event or + (m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox && // style of input box + page()->chrome()->client()->elementVisibilityChanged())) && // changed + (IS_UP_KEY(keyevent) || IS_DOWN_KEY(keyevent)); + bool shouldEndEditing = frame->editor()->client()->shouldEndEditing(NULL) && !upOrDownConsumed; bool deactivateEditable = (m_isEditable && shouldEndEditing && m_webfeptexteditor->validateTextFormat()); - return deactivateEditable || deactivateSelectBox; + return deactivateEditable; } @@ -981,19 +989,12 @@ } } else { - if (keyevent.iCode == EKeyDevice3) { //selection key (MSK) - // mimic ccb's behavior of onFocus + if (keyevent.iCode == EKeyDevice3) { // selection key (MSK) consumed = handleMSK(keyevent, oldKeyCode, frame); - - // Toolbar is activated on long key press only if the element - // type is EElementNone during EEventKeyDown and EEventKey. - // This prevents toolbar from popping up in DHTML pages. Also, - // toolbar is activated when the user is not in fast scroll - // mode, or in page overview mode, or on wml page. } else if (isNaviKey(keyevent)) { consumed = handleNaviKeyEvent(keyevent, oldKeyCode, frame); - } // if (m_brctl->settings()->getNavigationType() + } else { // Not an arrow key.. // activate hovered input element by just start typing consumed = !m_isEditable && handleInputElement(keyevent, eventcode, frame); @@ -1005,90 +1006,190 @@ return consumed; } + bool WebView::handleMSK(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame) { WebCursor* cursor = StaticObjectsContainer::instance()->webCursor(); bool prevEditableState = m_isEditable; - if (m_focusedElementType != TBrCtlDefs::EElementActivatedInputBox ) { - sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Down, - cursor->position(), frame); + bool tabbedNavigation = (m_brctl->settings()->getNavigationType() == SettingsContainer::NavigationTypeTabbed); + Node* focusedNode = frame->document()->focusedNode(); + bool multiSelectInTabbed = (m_focusedElementType == TBrCtlDefs::EElementSelectMultiBox) && // multiselect element + tabbedNavigation; // in tabbed mode. + /* + * Avoiding resetting cursor position to top left corner of Select Box + * See comment in handleInputElement + */ + if (!multiSelectInTabbed) { + setFocusNone(); + } + + sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Down, + cursor->position(), frame); + if (!tabbedNavigation) { + setFocusedNode(frame); //TODO: Do we need it here? } - setFocusedNode(frame); + else { + /* + * Restore focused by tabbed navigation node since mouse event + * handler might set it to NULL. + */ + page()->focusController()->setFocusedNode(focusedNode, frame); + } + + /* + * MSK key event on editable will be interpreted as Enter. + * We don't want to send Enter key in case of activated + * input box. Also if the text area has been just activated + * we also don't want to send Enter since it will cause extra + * line feed. + */ bool textAreaJustActivated = (!prevEditableState && m_isEditable && m_focusedElementType == TBrCtlDefs::EElementTextAreaBox); + if (eventcode == EEventKeyDown && m_focusedElementType != TBrCtlDefs::EElementActivatedInputBox && !textAreaJustActivated) { + sendKeyEventToEngine(keyevent, EEventKeyDown, frame); } + + /* + * Toolbar is activated on long key press only if the element + * type is EElementNone during EEventKeyDown and EEventKey. + * This prevents toolbar from popping up in DHTML pages. Also, + * toolbar is activated when the user is not in fast scroll + * mode, or in page overview mode, or on wml page. + */ if ((m_focusedElementType == TBrCtlDefs::EElementNone || m_focusedElementType == TBrCtlDefs::EElementBrokenImage ) && keyevent.iRepeats && !m_brctl->wmlMode() ) { launchToolBarL(); } + return true; } -bool WebView::handleNaviKeyEvent(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame) + +bool WebView::handleNaviKeyEvent(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame) { bool downEventConsumed = false; bool consumed = false; bool tabbedNavigation = (m_brctl->settings()->getNavigationType() == SettingsContainer::NavigationTypeTabbed); /* - * For each platform keyDown event EventHandler::keEvent() generates + * For each platform keyDown event EventHandler::keEvent() generates * keydown and keypress. - * For keypress event we need a char code and since we don't - * have it at the time of EEventKeyDown we pospond it until EEventKey + * For keypress event we need a char code and since we don't + * have it at the time of EEventKeyDown we pospond it until EEventKey * and send it here. */ if (eventcode == EEventKeyDown){ downEventConsumed = sendKeyEventToEngine(keyevent, EEventKeyDown, frame); } - - if (!downEventConsumed && needDeactivateEditable(keyevent, eventcode, frame)) { + /* + * downEventConsumed will be true if JavaScript consumes key event + * If we are not in the widget mode we want to deactivate input box + * regardless of whether event was consumed by JavaScript or not. + * Othrerwise we have a risk to be trapped inside input box. + */ + bool widgetDownEventConsumed = downEventConsumed && (m_widgetextension != NULL); + + if (!widgetDownEventConsumed && needDeactivateEditable(keyevent, eventcode, frame, downEventConsumed)) { deactivateEditable(); } if (tabbedNavigation) { consumed = downEventConsumed || handleTabbedNavigation(m_currentEventKey, m_currentEventCode); } - else { - consumed = (!m_isEditable && //avoid showing the cursor when we are in the input box + else { + consumed = (!m_isEditable && //avoid showing the cursor when we are in the input box handleKeyNavigation(keyevent, eventcode, frame)) || downEventConsumed; } return consumed; } - bool WebView::handleInputElement(const TKeyEvent& keyevent, TEventCode eventcode, Frame* frame) { WebCursor* cursor = StaticObjectsContainer::instance()->webCursor(); + TBrCtlDefs::TBrCtlElementType elTypeBeforeMouseEvent = m_focusedElementType; + bool tabbedNavigation = (m_brctl->settings()->getNavigationType() == SettingsContainer::NavigationTypeTabbed); + bool navigationNone = (m_brctl->settings()->getNavigationType() == SettingsContainer::NavigationTypeNone); + bool prevEditableState = m_isEditable; + bool enterOnMultiSelectInTabbed = tabbedNavigation && (keyevent.iCode == EKeyEnter) && // EnterKey in case of + (m_focusedElementType == TBrCtlDefs::EElementSelectMultiBox); // multiselect eelement in tabbed mode. + + bool sendMousedEvent = false; if (m_focusedElementType == TBrCtlDefs::EElementInputBox || - m_focusedElementType == TBrCtlDefs::EElementTextAreaBox) { + m_focusedElementType == TBrCtlDefs::EElementTextAreaBox || + (tabbedNavigation && + (m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox))) { sendMousedEvent = true; } else if (m_focusedElementType == TBrCtlDefs::EElementSelectBox || m_focusedElementType == TBrCtlDefs::EElementSelectMultiBox) { - if (m_brctl->settings()->getNavigationType() != SettingsContainer::NavigationTypeNone || - keyevent.iCode == EKeyDevice3) { + if (!navigationNone || keyevent.iCode == EKeyDevice3) { sendMousedEvent = true; } } + + if (m_focusedElementType == TBrCtlDefs::EElementTextAreaBox || + m_focusedElementType == TBrCtlDefs::EElementInputBox || + m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox) { + page()->chrome()->client()->setElementVisibilityChanged(false); + } + if (sendMousedEvent) { + /* + * Sending mouse event to WebCore will trigger FocusController->setFocusedNode() to be called. + * It will do anything only if focused node has been changed. Among other thing it will + * trigger the setEditable() call, which we need. The exception is the case when + * we have tabbed navigation and EnterKey was hit in MultiSelect Box. + * In this case we just want to select item in the select element. + * HTMLSelectElement::listBoxDefaultEventHandler() will call focus() which will + * set the focused node, which is already set anyway, but this will cause the + * reseting cursor position to the top left corner of the select box. + * To avoid thos we put the check here before we reset the foocusedNode. + */ + if (!enterOnMultiSelectInTabbed) { + frame->document()->setFocusedNode(NULL); + } + + /* + * Tweek the m_isEditable flag, so setEditable will do something + * usefull when it's called from FocusController->setFocusedNode() + */ + if (m_isEditable && tabbedNavigation) { + m_isEditable = false; + } + sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Down, cursor->position(), frame); sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Up, cursor->position(), frame); - - if (m_focusedElementType == TBrCtlDefs::EElementInputBox || - m_focusedElementType == TBrCtlDefs::EElementTextAreaBox || - m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox) { + + /* + * If enter key was pressed to activate an input box we don't want to + * send it to WebCore since it may cause immediate form submission. + * Same for MultiSelect Box in tabbed mode. + */ + bool inputBoxJustActivated = (!prevEditableState && m_isEditable && + (m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox|| + m_focusedElementType == TBrCtlDefs::EElementTextAreaBox)); + bool inputBoxJustActivatedByEnter = inputBoxJustActivated && (keyevent.iCode == EKeyEnter); + if (!inputBoxJustActivatedByEnter && + !enterOnMultiSelectInTabbed && + (m_focusedElementType == TBrCtlDefs::EElementInputBox || + m_focusedElementType == TBrCtlDefs::EElementTextAreaBox || + m_focusedElementType == TBrCtlDefs::EElementActivatedInputBox)) { + if (!m_fepTimer) { m_fepTimer = new WebCore::Timer(this, &WebView::fepTimerFired); } m_fepTimer->startOneShot(0.2f); - setEditable(true); + if (!m_isEditable) { + setEditable(true); + } } + m_keyevent = keyevent; m_eventcode = eventcode; return true; @@ -1122,6 +1223,16 @@ cursor->scrollAndMoveCursor(keyevent.iCode, m_scrollingSpeed, fastscroll); } updateScrollbars(); + /* + * In order to Enter key activate a link the link node has to be focused. + * When WebCursor is finding next node to snap it stores it, so here we can + * set it to focused. + */ + if (m_brctl->settings()->brctlSetting(TBrCtlDefs::ESettingsEnterKeyMode) == + TBrCtlDefs::EEnterKeyCanActivateLink) { + setFocusedNodeUnderCursor(frame); + } + if (!fastscroll) { m_fastScrollTimer->Start(KCursorInitialDelay,KCursorUpdateFrquency,TCallBack(&scrollTimerCb,this)); m_scrollingStartTime.HomeTime(); @@ -1138,6 +1249,18 @@ return consumed; } +void WebView::setFocusedNodeUnderCursor(Frame* frame) +{ + WebCursor* cursor = StaticObjectsContainer::instance()->webCursor(); + // focus the node we snapped to + if (m_focusedElementType != TBrCtlDefs::EElementNone) { + frame->document()->setFocusedNode(cursor->getElementUnderCursor()); + } + // or reset if there is none + else if (frame->document()->focusedNode() != NULL) { + frame->document()->setFocusedNode(NULL); + } +} bool WebView::handleMinimapNavigation() { @@ -1255,8 +1378,10 @@ } } if (m_brctl->settings()->getNavigationType() != SettingsContainer::NavigationTypeNone) { - if (!sendKeyEventToEngine(correctedKeyEvent, eventcode, frame)) { - sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Up, cursor->position(), frame); + if (!sendKeyEventToEngine(correctedKeyEvent, eventcode, frame)) { + if (keyevent.iScanCode == EStdKeyDevice3) { //MSK + sendMouseEventToEngineIfNeeded(TPointerEvent::EButton1Up, cursor->position(), frame); + } } consumed = true; } @@ -1301,10 +1426,12 @@ { bool tabbedNavigation = (m_brctl->settings()->getNavigationType() == SettingsContainer::NavigationTypeTabbed); Node* targetNode = frame->document()->focusedNode(); - if (!m_isEditable) { - - - + /* + * we dont want to send key event for navi key to the elements + * like select element since it has default key event handling, + * which will prevent user to leave the element. + */ + if (!m_isEditable && isNaviKey(keyevent)) { frame->document()->setFocusedNode(NULL); } bool consumed = frame->eventHandler()->keyEvent(PlatformKeyboardEvent(keyevent,eventcode));