/** * 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(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(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(evt)->data() == "\n") clickDefaultFormButton = true; if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame() && document()->frame()->doTextFieldCommandFromEvent(this, static_cast(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(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(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