WMLInputElement.cpp   [plain text]


/**
 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#include "config.h"

#if ENABLE(WML)
#include "WMLInputElement.h"

#include "Document.h"
#include "EventNames.h"
#include "FormDataList.h"
#include "Frame.h"
#include "HTMLNames.h"
#include "KeyboardEvent.h"
#include "RenderTextControlSingleLine.h"
#include "TextEvent.h"

namespace WebCore {

WMLInputElement::WMLInputElement(const QualifiedName& tagName, Document* doc)
    : WMLFormControlElementWithState(tagName, doc)
    , m_data(this, this)
    , m_isPasswordField(false)
{
}

WMLInputElement::~WMLInputElement()
{
    if (m_isPasswordField)
        document()->unregisterForDocumentActivationCallbacks(this);
}

static inline bool isInputFocusable(RenderObject* renderer)
{
    if (!renderer || !renderer->isBox())
        return false;

    if (toRenderBox(renderer)->size().isEmpty())
        return false;

    if (RenderStyle* style = renderer->style()) {
        if (style->visibility() != VISIBLE)
            return false;
    }

    return true;
}

bool WMLInputElement::isKeyboardFocusable(KeyboardEvent*) const
{
    return isInputFocusable(renderer());
}

bool WMLInputElement::isMouseFocusable() const
{
    return isInputFocusable(renderer());
}

void WMLInputElement::dispatchFocusEvent()
{
    InputElement::dispatchFocusEvent(m_data, document());
    WMLElement::dispatchFocusEvent();
}

void WMLInputElement::dispatchBlurEvent()
{
    InputElement::dispatchBlurEvent(m_data, document());
    WMLElement::dispatchBlurEvent();
}

void WMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
{
    InputElement::updateFocusAppearance(m_data, document(), restorePreviousSelection);
}

void WMLInputElement::aboutToUnload()
{
    InputElement::aboutToUnload(m_data, document());
}

int WMLInputElement::size() const
{
    return m_data.size();
}

const AtomicString& WMLInputElement::type() const
{
    // needs to be lowercase according to DOM spec
    if (m_isPasswordField) {
        DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
        return password;
    }

    DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
    return text;
}

const AtomicString& WMLInputElement::name() const
{
    return m_data.name();
}

String WMLInputElement::value() const
{
    String value = m_data.value();
    if (value.isNull())
        value = constrainValue(getAttribute(HTMLNames::valueAttr));

    return value;
}

void WMLInputElement::setValue(const String& value)
{
    InputElement::updatePlaceholderVisibility(m_data, document());
    setValueMatchesRenderer(false);
    m_data.setValue(constrainValue(value));
    if (inDocument())
        document()->updateRendering();
    if (renderer())
        renderer()->updateFromElement();
    setChanged();

    unsigned max = m_data.value().length();
    if (document()->focusedNode() == this)
        InputElement::updateSelectionRange(m_data, max, max);
    else
        cacheSelection(max, max);

    InputElement::notifyFormStateChanged(m_data, document());
}

void WMLInputElement::setValueFromRenderer(const String& value)
{
    InputElement::setValueFromRenderer(m_data, document(), value);
}

bool WMLInputElement::saveState(String& result) const
{
    if (m_isPasswordField)
        return false;

    result = value();
    return true;
}

void WMLInputElement::restoreState(const String& state)
{
    ASSERT(!m_isPasswordField); // should never save/restore password fields
    setValue(state);
}

void WMLInputElement::select()
{
    if (RenderTextControl* r = static_cast<RenderTextControl*>(renderer()))
        r->select();
}

void WMLInputElement::accessKeyAction(bool)
{
    // should never restore previous selection here
    focus(false);
}

void WMLInputElement::parseMappedAttribute(MappedAttribute* attr)
{
    if (attr->name() == HTMLNames::nameAttr)
        m_data.setName(parseValueForbiddingVariableReferences(attr->value()));
    else if (attr->name() == HTMLNames::typeAttr) {
        String type = parseValueForbiddingVariableReferences(attr->value());
        m_isPasswordField = (type == "password");
    } else if (attr->name() == HTMLNames::valueAttr) {
        // We only need to setChanged if the form is looking at the default value right now.
        if (m_data.value().isNull())
            setChanged();
        setValueMatchesRenderer(false);
    } else if (attr->name() == HTMLNames::maxlengthAttr)
        InputElement::parseMaxLengthAttribute(m_data, attr);
    else if (attr->name() == HTMLNames::sizeAttr)
        InputElement::parseSizeAttribute(m_data, attr);
    else
        WMLElement::parseMappedAttribute(attr);

    // FIXME: Handle 'accesskey' attribute
    // FIXME: Handle 'format' attribute
    // FIXME: Handle 'emptyok' attribute
    // FIXME: Handle 'tabindex' attribute
    // FIXME: Handle 'title' attribute
}

void WMLInputElement::copyNonAttributeProperties(const Element* source)
{
    const WMLInputElement* sourceElement = static_cast<const WMLInputElement*>(source);
    m_data.setValue(sourceElement->m_data.value());
    WMLElement::copyNonAttributeProperties(source);
}

RenderObject* WMLInputElement::createRenderer(RenderArena* arena, RenderStyle*)
{
    return new (arena) RenderTextControlSingleLine(this);
}

void WMLInputElement::attach()
{
    ASSERT(!attached());
    WMLElement::attach();

    // The call to updateFromElement() needs to go after the call through
    // to the base class's attach() because that can sometimes do a close
    // on the renderer.
    if (renderer())
        renderer()->updateFromElement();
}  

void WMLInputElement::detach()
{
    WMLElement::detach();
    setValueMatchesRenderer(false);
}
    
bool WMLInputElement::appendFormData(FormDataList& encoding, bool)
{
    if (name().isEmpty())
        return false;

    encoding.appendData(name(), value());
    return true;
}

void WMLInputElement::reset()
{
    setValue(String());
}

void WMLInputElement::defaultEventHandler(Event* evt)
{
    bool clickDefaultFormButton = false;

    if (evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
        clickDefaultFormButton = true;

    if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame()
        && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
        evt->setDefaultHandled();
        return;
    }
    
    // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
    if (!clickDefaultFormButton) {
        WMLElement::defaultEventHandler(evt);
        if (evt->defaultHandled())
            return;
    }

    // Use key press event here since sending simulated mouse events
    // on key down blocks the proper sending of the key press event.
    if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
        // Simulate mouse click on the default form button for enter for these types of elements.
        if (static_cast<KeyboardEvent*>(evt)->charCode() == '\r')
            clickDefaultFormButton = true;
    }

    if (clickDefaultFormButton) {
        // Fire onChange for text fields.
        RenderObject* r = renderer();
        if (r && r->isEdited()) {
            dispatchEventForType(eventNames().changeEvent, true, false);
            
            // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
            r = renderer();
            if (r)
                r->setEdited(false);
        }

        evt->setDefaultHandled();
        return;
    }

    if (evt->isBeforeTextInsertedEvent())
        InputElement::handleBeforeTextInsertedEvent(m_data, document(), evt);

    if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
        static_cast<RenderTextControlSingleLine*>(renderer())->forwardEvent(evt);
}

void WMLInputElement::cacheSelection(int start, int end)
{
    m_data.setCachedSelectionStart(start);
    m_data.setCachedSelectionEnd(end);
}

String WMLInputElement::constrainValue(const String& proposedValue) const
{
    return InputElement::constrainValue(m_data, proposedValue, m_data.maxLength());
}

void WMLInputElement::documentDidBecomeActive()
{
    ASSERT(m_isPasswordField);
    reset();
}

bool WMLInputElement::placeholderShouldBeVisible() const
{
    return m_data.placeholderShouldBeVisible();
}

void WMLInputElement::willMoveToNewOwnerDocument()
{
    // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
    if (m_isPasswordField)
        document()->unregisterForDocumentActivationCallbacks(this);

    WMLElement::willMoveToNewOwnerDocument();
}

void WMLInputElement::didMoveToNewOwnerDocument()
{
    if (m_isPasswordField)
        document()->registerForDocumentActivationCallbacks(this);

    WMLElement::didMoveToNewOwnerDocument();
}

}

#endif