HTMLInputElement.cpp [plain text]
#include "config.h"
#include "HTMLInputElement.h"
#include "ChromeClient.h"
#include "CSSPropertyNames.h"
#include "Document.h"
#include "Editor.h"
#include "Event.h"
#include "EventHandler.h"
#include "EventNames.h"
#include "File.h"
#include "FileList.h"
#include "FocusController.h"
#include "FormDataList.h"
#include "Frame.h"
#include "HTMLFormElement.h"
#include "HTMLImageLoader.h"
#include "HTMLNames.h"
#include "KeyboardEvent.h"
#include "LocalizedStrings.h"
#include "MouseEvent.h"
#include "Page.h"
#include "RenderButton.h"
#include "RenderFileUploadControl.h"
#include "RenderImage.h"
#include "RenderSlider.h"
#include "RenderText.h"
#include "RenderTextControlSingleLine.h"
#include "RenderTheme.h"
#include "TextEvent.h"
#include <wtf/StdLibExtras.h>
using namespace std;
namespace WebCore {
using namespace HTMLNames;
const int maxSavedResults = 256;
HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
: HTMLFormControlElementWithState(tagName, doc, f)
, m_data(this, this)
, m_xPos(0)
, m_yPos(0)
, m_maxResults(-1)
, m_type(TEXT)
, m_checked(false)
, m_defaultChecked(false)
, m_useDefaultChecked(true)
, m_indeterminate(false)
, m_haveType(false)
, m_activeSubmit(false)
, m_autocomplete(Uninitialized)
, m_autofilled(false)
, m_inited(false)
{
ASSERT(hasTagName(inputTag) || hasTagName(isindexTag));
}
HTMLInputElement::~HTMLInputElement()
{
if (needsActivationCallback())
document()->unregisterForDocumentActivationCallbacks(this);
document()->checkedRadioButtons().removeButton(this);
removeFromForm();
}
const AtomicString& HTMLInputElement::name() const
{
return m_data.name();
}
bool HTMLInputElement::autoComplete() const
{
if (m_autocomplete != Uninitialized)
return m_autocomplete == On;
if (HTMLFormElement* form = this->form())
return form->autoComplete();
return true;
}
static inline HTMLFormElement::CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
{
if (HTMLFormElement* form = element->form())
return form->checkedRadioButtons();
return element->document()->checkedRadioButtons();
}
bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
{
if (readOnly())
return false;
if (isTextField())
return HTMLFormControlElementWithState::isFocusable();
if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
return false;
if (inputType() == RADIO) {
Node* currentFocusedNode = document()->focusedNode();
if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) {
HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode);
if (focusedInput->inputType() == RADIO && focusedInput->form() == form() &&
focusedInput->name() == name())
return false;
}
return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name());
}
return true;
}
bool HTMLInputElement::isMouseFocusable() const
{
if (isTextField())
return HTMLFormControlElementWithState::isFocusable();
return HTMLFormControlElementWithState::isMouseFocusable();
}
void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
{
if (isTextField())
InputElement::updateFocusAppearance(m_data, document(), restorePreviousSelection);
else
HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
}
void HTMLInputElement::aboutToUnload()
{
InputElement::aboutToUnload(m_data, document());
}
bool HTMLInputElement::shouldUseInputMethod() const
{
return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX;
}
void HTMLInputElement::dispatchFocusEvent()
{
InputElement::dispatchFocusEvent(m_data, document());
if (isTextField())
m_autofilled = false;
HTMLFormControlElementWithState::dispatchFocusEvent();
}
void HTMLInputElement::dispatchBlurEvent()
{
InputElement::dispatchBlurEvent(m_data, document());
HTMLFormControlElementWithState::dispatchBlurEvent();
}
void HTMLInputElement::setType(const String& t)
{
if (t.isEmpty()) {
int exccode;
removeAttribute(typeAttr, exccode);
} else
setAttribute(typeAttr, t);
}
void HTMLInputElement::setInputType(const String& t)
{
InputType newType;
if (equalIgnoringCase(t, "password"))
newType = PASSWORD;
else if (equalIgnoringCase(t, "checkbox"))
newType = CHECKBOX;
else if (equalIgnoringCase(t, "radio"))
newType = RADIO;
else if (equalIgnoringCase(t, "submit"))
newType = SUBMIT;
else if (equalIgnoringCase(t, "reset"))
newType = RESET;
else if (equalIgnoringCase(t, "file"))
newType = FILE;
else if (equalIgnoringCase(t, "hidden"))
newType = HIDDEN;
else if (equalIgnoringCase(t, "image"))
newType = IMAGE;
else if (equalIgnoringCase(t, "button"))
newType = BUTTON;
else if (equalIgnoringCase(t, "khtml_isindex"))
newType = ISINDEX;
else if (equalIgnoringCase(t, "search"))
newType = SEARCH;
else if (equalIgnoringCase(t, "range"))
newType = RANGE;
else
newType = TEXT;
if (inputType() != newType) {
if (newType == FILE)
setDisabled(true);
if (newType == FILE && m_haveType)
setAttribute(typeAttr, type());
else {
checkedRadioButtons(this).removeButton(this);
if (newType == FILE && !m_fileList)
m_fileList = FileList::create();
bool wasAttached = attached();
if (wasAttached)
detach();
bool didStoreValue = storesValueSeparateFromAttribute();
bool wasPasswordField = inputType() == PASSWORD;
bool didRespectHeightAndWidth = respectHeightAndWidthAttrs();
m_type = newType;
bool willStoreValue = storesValueSeparateFromAttribute();
bool isPasswordField = inputType() == PASSWORD;
bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
if (didStoreValue && !willStoreValue && !m_data.value().isNull()) {
setAttribute(valueAttr, m_data.value());
m_data.setValue(String());
}
if (!didStoreValue && willStoreValue)
m_data.setValue(constrainValue(getAttribute(valueAttr)));
else
InputElement::updateValueIfNeeded(m_data);
if (wasPasswordField && !isPasswordField)
unregisterForActivationCallbackIfNeeded();
else if (!wasPasswordField && isPasswordField)
registerForActivationCallbackIfNeeded();
if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
NamedMappedAttrMap* map = mappedAttributes();
if (Attribute* height = map->getAttributeItem(heightAttr))
attributeChanged(height, false);
if (Attribute* width = map->getAttributeItem(widthAttr))
attributeChanged(width, false);
if (Attribute* align = map->getAttributeItem(alignAttr))
attributeChanged(align, false);
}
if (wasAttached) {
attach();
if (document()->focusedNode() == this)
updateFocusAppearance(true);
}
checkedRadioButtons(this).addButton(this);
}
InputElement::notifyFormStateChanged(m_data, document());
}
m_haveType = true;
if (inputType() != IMAGE && m_imageLoader)
m_imageLoader.clear();
}
const AtomicString& HTMLInputElement::type() const
{
switch (inputType()) {
case BUTTON: {
DEFINE_STATIC_LOCAL(const AtomicString, button, ("button"));
return button;
}
case CHECKBOX: {
DEFINE_STATIC_LOCAL(const AtomicString, checkbox, ("checkbox"));
return checkbox;
}
case FILE: {
DEFINE_STATIC_LOCAL(const AtomicString, file, ("file"));
return file;
}
case HIDDEN: {
DEFINE_STATIC_LOCAL(const AtomicString, hidden, ("hidden"));
return hidden;
}
case IMAGE: {
DEFINE_STATIC_LOCAL(const AtomicString, image, ("image"));
return image;
}
case ISINDEX:
return emptyAtom;
case PASSWORD: {
DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
return password;
}
case RADIO: {
DEFINE_STATIC_LOCAL(const AtomicString, radio, ("radio"));
return radio;
}
case RANGE: {
DEFINE_STATIC_LOCAL(const AtomicString, range, ("range"));
return range;
}
case RESET: {
DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset"));
return reset;
}
case SEARCH: {
DEFINE_STATIC_LOCAL(const AtomicString, search, ("search"));
return search;
}
case SUBMIT: {
DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit"));
return submit;
}
case TEXT: {
DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
return text;
}
}
return emptyAtom;
}
bool HTMLInputElement::saveState(String& result) const
{
if (!autoComplete())
return false;
switch (inputType()) {
case BUTTON:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
case TEXT:
result = value();
return true;
case CHECKBOX:
case RADIO:
result = checked() ? "on" : "off";
return true;
case PASSWORD:
return false;
}
ASSERT_NOT_REACHED();
return false;
}
void HTMLInputElement::restoreState(const String& state)
{
ASSERT(inputType() != PASSWORD); switch (inputType()) {
case BUTTON:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
case TEXT:
setValue(state);
break;
case CHECKBOX:
case RADIO:
setChecked(state == "on");
break;
case PASSWORD:
break;
}
}
bool HTMLInputElement::canStartSelection() const
{
if (!isTextField())
return false;
return HTMLFormControlElementWithState::canStartSelection();
}
bool HTMLInputElement::canHaveSelection() const
{
return isTextField();
}
int HTMLInputElement::selectionStart() const
{
if (!isTextField())
return 0;
if (document()->focusedNode() != this && m_data.cachedSelectionStart() != -1)
return m_data.cachedSelectionStart();
if (!renderer())
return 0;
return static_cast<RenderTextControl*>(renderer())->selectionStart();
}
int HTMLInputElement::selectionEnd() const
{
if (!isTextField())
return 0;
if (document()->focusedNode() != this && m_data.cachedSelectionEnd() != -1)
return m_data.cachedSelectionEnd();
if (!renderer())
return 0;
return static_cast<RenderTextControl*>(renderer())->selectionEnd();
}
void HTMLInputElement::setSelectionStart(int start)
{
if (!isTextField())
return;
if (!renderer())
return;
static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
}
void HTMLInputElement::setSelectionEnd(int end)
{
if (!isTextField())
return;
if (!renderer())
return;
static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
}
void HTMLInputElement::select()
{
if (!isTextField())
return;
if (!renderer())
return;
static_cast<RenderTextControl*>(renderer())->select();
}
void HTMLInputElement::setSelectionRange(int start, int end)
{
InputElement::updateSelectionRange(m_data, start, end);
}
void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
{
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case IMAGE:
case RADIO:
case RANGE:
case RESET:
case SUBMIT:
focus(false);
dispatchSimulatedClick(0, sendToAnyElement);
break;
case HIDDEN:
break;
case ISINDEX:
case PASSWORD:
case SEARCH:
case TEXT:
focus(false);
break;
}
}
bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
{
if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) ||
attrName == vspaceAttr ||
attrName == hspaceAttr) {
result = eUniversal;
return false;
}
if (attrName == alignAttr) {
if (inputType() == IMAGE) {
result = eReplaced;
return false;
}
}
return HTMLElement::mapToEntry(attrName, result);
}
void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
{
if (attr->name() == nameAttr) {
checkedRadioButtons(this).removeButton(this);
m_data.setName(attr->value());
checkedRadioButtons(this).addButton(this);
} else if (attr->name() == autocompleteAttr) {
if (equalIgnoringCase(attr->value(), "off")) {
m_autocomplete = Off;
registerForActivationCallbackIfNeeded();
} else {
if (m_autocomplete == Off)
unregisterForActivationCallbackIfNeeded();
if (attr->isEmpty())
m_autocomplete = Uninitialized;
else
m_autocomplete = On;
}
} else if (attr->name() == typeAttr) {
setInputType(attr->value());
} else if (attr->name() == valueAttr) {
if (m_data.value().isNull())
setChanged();
setValueMatchesRenderer(false);
} else if (attr->name() == checkedAttr) {
m_defaultChecked = !attr->isNull();
if (m_useDefaultChecked) {
setChecked(m_defaultChecked);
m_useDefaultChecked = true;
}
} else if (attr->name() == maxlengthAttr)
InputElement::parseMaxLengthAttribute(m_data, attr);
else if (attr->name() == sizeAttr)
InputElement::parseSizeAttribute(m_data, attr);
else if (attr->name() == altAttr) {
if (renderer() && inputType() == IMAGE)
static_cast<RenderImage*>(renderer())->updateAltText();
} else if (attr->name() == srcAttr) {
if (renderer() && inputType() == IMAGE) {
if (!m_imageLoader)
m_imageLoader.set(new HTMLImageLoader(this));
m_imageLoader->updateFromElementIgnoringPreviousError();
}
} else if (attr->name() == usemapAttr ||
attr->name() == accesskeyAttr) {
} else if (attr->name() == vspaceAttr) {
addCSSLength(attr, CSSPropertyMarginTop, attr->value());
addCSSLength(attr, CSSPropertyMarginBottom, attr->value());
} else if (attr->name() == hspaceAttr) {
addCSSLength(attr, CSSPropertyMarginLeft, attr->value());
addCSSLength(attr, CSSPropertyMarginRight, attr->value());
} else if (attr->name() == alignAttr) {
if (inputType() == IMAGE)
addHTMLAlignment(attr);
} else if (attr->name() == widthAttr) {
if (respectHeightAndWidthAttrs())
addCSSLength(attr, CSSPropertyWidth, attr->value());
} else if (attr->name() == heightAttr) {
if (respectHeightAndWidthAttrs())
addCSSLength(attr, CSSPropertyHeight, attr->value());
} else if (attr->name() == onfocusAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().focusEvent, attr);
} else if (attr->name() == onblurAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().blurEvent, attr);
} else if (attr->name() == onselectAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().selectEvent, attr);
} else if (attr->name() == onchangeAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().changeEvent, attr);
} else if (attr->name() == oninputAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().inputEvent, attr);
}
else if (attr->name() == onsearchAttr) {
setInlineEventListenerForTypeAndAttribute(eventNames().searchEvent, attr);
} else if (attr->name() == resultsAttr) {
int oldResults = m_maxResults;
m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) {
detach();
attach();
}
setChanged();
} else if (attr->name() == placeholderAttr) {
if (isTextField())
InputElement::updatePlaceholderVisibility(m_data, document(), true);
} else if (attr->name() == autosaveAttr ||
attr->name() == incrementalAttr ||
attr->name() == minAttr ||
attr->name() == maxAttr ||
attr->name() == multipleAttr ||
attr->name() == precisionAttr)
setChanged();
else
HTMLFormControlElementWithState::parseMappedAttribute(attr);
}
bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
{
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case IMAGE:
case ISINDEX:
case PASSWORD:
case RADIO:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
case TEXT:
return HTMLFormControlElementWithState::rendererIsNeeded(style);
case HIDDEN:
return false;
}
ASSERT(false);
return false;
}
RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style)
{
switch (inputType()) {
case BUTTON:
case RESET:
case SUBMIT:
return new (arena) RenderButton(this);
case CHECKBOX:
case RADIO:
return RenderObject::createObject(this, style);
case FILE:
return new (arena) RenderFileUploadControl(this);
case HIDDEN:
break;
case IMAGE:
return new (arena) RenderImage(this);
case RANGE:
return new (arena) RenderSlider(this);
case ISINDEX:
case PASSWORD:
case SEARCH:
case TEXT:
return new (arena) RenderTextControlSingleLine(this);
}
ASSERT(false);
return 0;
}
void HTMLInputElement::attach()
{
if (!m_inited) {
if (!m_haveType)
setInputType(getAttribute(typeAttr));
m_inited = true;
}
HTMLFormControlElementWithState::attach();
if (inputType() == IMAGE) {
if (!m_imageLoader)
m_imageLoader.set(new HTMLImageLoader(this));
m_imageLoader->updateFromElement();
if (renderer()) {
RenderImage* imageObj = static_cast<RenderImage*>(renderer());
imageObj->setCachedImage(m_imageLoader->image());
if (!m_imageLoader->image() && !imageObj->cachedImage())
imageObj->setImageSizeForAltText();
}
}
}
void HTMLInputElement::detach()
{
HTMLFormControlElementWithState::detach();
setValueMatchesRenderer(false);
}
String HTMLInputElement::altText() const
{
String alt = getAttribute(altAttr);
if (alt.isNull())
alt = getAttribute(titleAttr);
if (alt.isNull())
alt = getAttribute(valueAttr);
if (alt.isEmpty())
alt = inputElementAltText();
return alt;
}
bool HTMLInputElement::isSuccessfulSubmitButton() const
{
return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT);
}
bool HTMLInputElement::isActivatedSubmit() const
{
return m_activeSubmit;
}
void HTMLInputElement::setActivatedSubmit(bool flag)
{
m_activeSubmit = flag;
}
bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
{
if (name().isEmpty() && inputType() != IMAGE)
return false;
switch (inputType()) {
case HIDDEN:
case ISINDEX:
case PASSWORD:
case RANGE:
case SEARCH:
case TEXT:
encoding.appendData(name(), value());
return true;
case CHECKBOX:
case RADIO:
if (checked()) {
encoding.appendData(name(), value());
return true;
}
break;
case BUTTON:
case RESET:
return false;
case IMAGE:
if (m_activeSubmit) {
encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), m_xPos);
encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), m_yPos);
if (!name().isEmpty() && !value().isEmpty())
encoding.appendData(name(), value());
return true;
}
break;
case SUBMIT:
if (m_activeSubmit) {
String enc_str = valueWithDefault();
encoding.appendData(name(), enc_str);
return true;
}
break;
case FILE: {
if (!multipart)
return false;
unsigned numFiles = m_fileList->length();
if (!numFiles) {
encoding.appendFile(name(), File::create(""));
return true;
}
for (unsigned i = 0; i < numFiles; ++i)
encoding.appendFile(name(), m_fileList->item(i));
return true;
}
}
return false;
}
void HTMLInputElement::reset()
{
if (storesValueSeparateFromAttribute())
setValue(String());
setChecked(m_defaultChecked);
m_useDefaultChecked = true;
}
void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
{
if (checked() == nowChecked)
return;
checkedRadioButtons(this).removeButton(this);
m_useDefaultChecked = false;
m_checked = nowChecked;
setChanged();
checkedRadioButtons(this).addButton(this);
if (renderer() && renderer()->style()->hasAppearance())
theme()->stateChanged(renderer(), CheckedState);
if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
onChange();
}
void HTMLInputElement::setIndeterminate(bool _indeterminate)
{
if (inputType() != CHECKBOX || indeterminate() == _indeterminate)
return;
m_indeterminate = _indeterminate;
setChanged();
if (renderer() && renderer()->style()->hasAppearance())
theme()->stateChanged(renderer(), CheckedState);
}
int HTMLInputElement::size() const
{
return m_data.size();
}
void HTMLInputElement::copyNonAttributeProperties(const Element* source)
{
const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source);
m_data.setValue(sourceElement->m_data.value());
m_checked = sourceElement->m_checked;
m_indeterminate = sourceElement->m_indeterminate;
HTMLFormControlElementWithState::copyNonAttributeProperties(source);
}
String HTMLInputElement::value() const
{
if (inputType() == FILE) {
if (!m_fileList->isEmpty())
return m_fileList->item(0)->fileName();
return String();
}
String value = m_data.value();
if (value.isNull()) {
value = constrainValue(getAttribute(valueAttr));
if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO))
return checked() ? "on" : "";
}
return value;
}
String HTMLInputElement::valueWithDefault() const
{
String v = value();
if (v.isNull()) {
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
case PASSWORD:
case RADIO:
case RANGE:
case SEARCH:
case TEXT:
break;
case RESET:
v = resetButtonDefaultLabel();
break;
case SUBMIT:
v = submitButtonDefaultLabel();
break;
}
}
return v;
}
void HTMLInputElement::setValue(const String& value)
{
if (inputType() == FILE && !value.isEmpty())
return;
setValueMatchesRenderer(false);
if (storesValueSeparateFromAttribute()) {
if (inputType() == FILE)
m_fileList->clear();
else {
m_data.setValue(constrainValue(value));
if (isTextField()) {
InputElement::updatePlaceholderVisibility(m_data, document());
if (inDocument())
document()->updateRendering();
}
}
if (renderer())
renderer()->updateFromElement();
setChanged();
} else
setAttribute(valueAttr, constrainValue(value));
if (isTextField()) {
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());
if (document() && document()->frame())
document()->frame()->formElementDidSetValue(this);
}
String HTMLInputElement::placeholderValue() const
{
return getAttribute(placeholderAttr).string();
}
bool HTMLInputElement::searchEventsShouldBeDispatched() const
{
return hasAttribute(incrementalAttr);
}
void HTMLInputElement::setValueFromRenderer(const String& value)
{
ASSERT(inputType() != FILE);
InputElement::setValueFromRenderer(m_data, document(), value);
}
void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths)
{
m_fileList->clear();
int size = paths.size();
for (int i = 0; i < size; i++)
m_fileList->append(File::create(paths[i]));
setValueMatchesRenderer();
InputElement::notifyFormStateChanged(m_data, document());
}
bool HTMLInputElement::storesValueSeparateFromAttribute() const
{
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case HIDDEN:
case IMAGE:
case RADIO:
case RESET:
case SUBMIT:
return false;
case FILE:
case ISINDEX:
case PASSWORD:
case RANGE:
case SEARCH:
case TEXT:
return true;
}
return false;
}
void* HTMLInputElement::preDispatchEventHandler(Event *evt)
{
void* result = 0;
if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
&& evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
if (inputType() == CHECKBOX) {
if (indeterminate()) {
result = (void*)0x2;
setIndeterminate(false);
} else {
if (checked())
result = (void*)0x1;
setChecked(!checked(), true);
}
} else {
HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
if (currRadio) {
currRadio->ref();
result = currRadio;
}
setChecked(true, true);
}
}
return result;
}
void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
{
if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
&& evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
if (inputType() == CHECKBOX) {
if (evt->defaultPrevented() || evt->defaultHandled()) {
if (data == (void*)0x2)
setIndeterminate(true);
else
setChecked(data);
}
} else if (data) {
HTMLInputElement* input = static_cast<HTMLInputElement*>(data);
if (evt->defaultPrevented() || evt->defaultHandled()) {
if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
input->setChecked(true);
}
}
input->deref();
}
evt->setDefaultHandled();
}
}
void HTMLInputElement::defaultEventHandler(Event* evt)
{
bool clickDefaultFormButton = false;
if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
clickDefaultFormButton = true;
if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == eventNames().clickEvent) {
MouseEvent* me = static_cast<MouseEvent*>(evt);
if (me->isSimulated() || !renderer()) {
m_xPos = 0;
m_yPos = 0;
} else {
IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute());
m_xPos = me->pageX() - absOffset.x();
m_yPos = me->pageY() - absOffset.y();
}
}
if (isTextField() && evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() && document()->frame()
&& document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) {
evt->setDefaultHandled();
return;
}
if (inputType() == RADIO && evt->isMouseEvent()
&& evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
evt->setDefaultHandled();
return;
}
if (!clickDefaultFormButton) {
HTMLFormControlElementWithState::defaultEventHandler(evt);
if (evt->defaultHandled())
return;
}
if (evt->type() == eventNames().DOMActivateEvent && !disabled()) {
if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) {
if (!form())
return;
if (inputType() == RESET)
form()->reset();
else {
m_activeSubmit = true;
if (!form()->prepareSubmit(evt)) {
m_xPos = 0;
m_yPos = 0;
}
m_activeSubmit = false;
}
} else if (inputType() == FILE && renderer())
static_cast<RenderFileUploadControl*>(renderer())->click();
}
if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) {
bool clickElement = false;
int charCode = static_cast<KeyboardEvent*>(evt)->charCode();
if (charCode == '\r') {
switch (inputType()) {
case CHECKBOX:
case HIDDEN:
case ISINDEX:
case PASSWORD:
case RANGE:
case SEARCH:
case TEXT:
clickDefaultFormButton = true;
break;
case BUTTON:
case FILE:
case IMAGE:
case RESET:
case SUBMIT:
clickElement = true;
break;
case RADIO:
break; }
} else if (charCode == ' ') {
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case IMAGE:
case RESET:
case SUBMIT:
case RADIO:
evt->setDefaultHandled();
return;
default:
break;
}
}
if (clickElement) {
dispatchSimulatedClick(evt);
evt->setDefaultHandled();
return;
}
}
if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) {
String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
if (key == "U+0020") {
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case IMAGE:
case RESET:
case SUBMIT:
case RADIO:
setActive(true, true);
return;
default:
break;
}
}
if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
bool forward = (key == "Down" || key == "Right");
Node* n = this;
while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
if (n->hasTagName(formTag))
break;
if (n->hasTagName(inputTag)) {
HTMLInputElement* elt = static_cast<HTMLInputElement*>(n);
if (elt->form() != form())
break;
if (n->hasTagName(inputTag)) {
HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) {
inputElt->setChecked(true);
document()->setFocusedNode(inputElt);
inputElt->dispatchSimulatedClick(evt, false, false);
evt->setDefaultHandled();
break;
}
}
}
}
}
}
if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) {
bool clickElement = false;
String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier();
if (key == "U+0020") {
switch (inputType()) {
case BUTTON:
case CHECKBOX:
case FILE:
case IMAGE:
case RESET:
case SUBMIT:
clickElement = true;
break;
case RADIO:
if (!checked())
clickElement = true;
break;
case HIDDEN:
case ISINDEX:
case PASSWORD:
case RANGE:
case SEARCH:
case TEXT:
break;
}
}
if (clickElement) {
if (active())
dispatchSimulatedClick(evt);
evt->setDefaultHandled();
return;
}
}
if (clickDefaultFormButton) {
if (isSearchField()) {
addSearchResult();
onSearch();
}
RenderObject* r = renderer();
if (r && r->isTextField() && r->isEdited()) {
onChange();
r = renderer();
if (r)
r->setEdited(false);
}
if (form())
form()->submitClick(evt);
evt->setDefaultHandled();
return;
}
if (evt->isBeforeTextInsertedEvent())
InputElement::handleBeforeTextInsertedEvent(m_data, document(), evt);
if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent))
static_cast<RenderTextControlSingleLine*>(renderer())->forwardEvent(evt);
if (inputType() == RANGE && renderer()) {
RenderSlider* slider = static_cast<RenderSlider*>(renderer());
if (evt->isMouseEvent() && evt->type() == eventNames().mousedownEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
MouseEvent* mEvt = static_cast<MouseEvent*>(evt);
if (!slider->mouseEventIsInThumb(mEvt)) {
IntPoint eventOffset(mEvt->offsetX(), mEvt->offsetY());
if (mEvt->target() != this) eventOffset = roundedIntPoint(renderer()->absoluteToLocal(FloatPoint(mEvt->pageX(), mEvt->pageY()), false, true));
slider->setValueForPosition(slider->positionForOffset(eventOffset));
}
}
if (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())
slider->forwardEvent(evt);
}
}
bool HTMLInputElement::isURLAttribute(Attribute *attr) const
{
return (attr->name() == srcAttr);
}
String HTMLInputElement::defaultValue() const
{
return getAttribute(valueAttr);
}
void HTMLInputElement::setDefaultValue(const String &value)
{
setAttribute(valueAttr, value);
}
bool HTMLInputElement::defaultChecked() const
{
return !getAttribute(checkedAttr).isNull();
}
void HTMLInputElement::setDefaultChecked(bool defaultChecked)
{
setAttribute(checkedAttr, defaultChecked ? "" : 0);
}
void HTMLInputElement::setDefaultName(const AtomicString& name)
{
m_data.setName(name);
}
String HTMLInputElement::accept() const
{
return getAttribute(acceptAttr);
}
void HTMLInputElement::setAccept(const String &value)
{
setAttribute(acceptAttr, value);
}
String HTMLInputElement::accessKey() const
{
return getAttribute(accesskeyAttr);
}
void HTMLInputElement::setAccessKey(const String &value)
{
setAttribute(accesskeyAttr, value);
}
String HTMLInputElement::align() const
{
return getAttribute(alignAttr);
}
void HTMLInputElement::setAlign(const String &value)
{
setAttribute(alignAttr, value);
}
String HTMLInputElement::alt() const
{
return getAttribute(altAttr);
}
void HTMLInputElement::setAlt(const String &value)
{
setAttribute(altAttr, value);
}
int HTMLInputElement::maxLength() const
{
return m_data.maxLength();
}
void HTMLInputElement::setMaxLength(int _maxLength)
{
setAttribute(maxlengthAttr, String::number(_maxLength));
}
void HTMLInputElement::setSize(unsigned _size)
{
setAttribute(sizeAttr, String::number(_size));
}
KURL HTMLInputElement::src() const
{
return document()->completeURL(getAttribute(srcAttr));
}
void HTMLInputElement::setSrc(const String &value)
{
setAttribute(srcAttr, value);
}
String HTMLInputElement::useMap() const
{
return getAttribute(usemapAttr);
}
void HTMLInputElement::setUseMap(const String &value)
{
setAttribute(usemapAttr, value);
}
void HTMLInputElement::setAutofilled(bool b)
{
if (b == m_autofilled)
return;
m_autofilled = b;
setChanged();
}
FileList* HTMLInputElement::files()
{
if (inputType() != FILE)
return 0;
return m_fileList.get();
}
String HTMLInputElement::constrainValue(const String& proposedValue) const
{
return InputElement::constrainValue(m_data, proposedValue, m_data.maxLength());
}
bool HTMLInputElement::needsActivationCallback()
{
return inputType() == PASSWORD || m_autocomplete == Off;
}
void HTMLInputElement::registerForActivationCallbackIfNeeded()
{
if (needsActivationCallback())
document()->registerForDocumentActivationCallbacks(this);
}
void HTMLInputElement::unregisterForActivationCallbackIfNeeded()
{
if (!needsActivationCallback())
document()->unregisterForDocumentActivationCallbacks(this);
}
void HTMLInputElement::cacheSelection(int start, int end)
{
m_data.setCachedSelectionStart(start);
m_data.setCachedSelectionEnd(end);
}
void HTMLInputElement::addSearchResult()
{
}
void HTMLInputElement::onSearch()
{
}
Selection HTMLInputElement::selection() const
{
if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1)
return Selection();
return static_cast<RenderTextControl*>(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd());
}
void HTMLInputElement::documentDidBecomeActive()
{
ASSERT(needsActivationCallback());
reset();
}
void HTMLInputElement::willMoveToNewOwnerDocument()
{
if (needsActivationCallback())
document()->unregisterForDocumentActivationCallbacks(this);
document()->checkedRadioButtons().removeButton(this);
HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
}
void HTMLInputElement::didMoveToNewOwnerDocument()
{
registerForActivationCallbackIfNeeded();
HTMLFormControlElementWithState::didMoveToNewOwnerDocument();
}
bool HTMLInputElement::willRespondToMouseClickEvents()
{
return !disabled();
}
void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
{
HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls);
addSubresourceURL(urls, src());
}
bool HTMLInputElement::willValidate() const
{
return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
inputType() != BUTTON && inputType() != RESET;
}
bool HTMLInputElement::placeholderShouldBeVisible() const
{
return m_data.placeholderShouldBeVisible();
}
}