Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1505)

Unified Diff: third_party/WebKit/WebCore/html/HTMLInputElement.cpp

Issue 21165: Revert the merge. Mac build is mysteriously broken. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/WebKit/WebCore/html/HTMLFormElement.cpp ('k') | third_party/WebKit/WebCore/html/HTMLParser.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/WebCore/html/HTMLInputElement.cpp
===================================================================
--- third_party/WebKit/WebCore/html/HTMLInputElement.cpp (revision 9383)
+++ third_party/WebKit/WebCore/html/HTMLInputElement.cpp (working copy)
@@ -1,1560 +1,1560 @@
-/*
- * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
- * (C) 1999 Antti Koivisto (koivisto@kde.org)
- * (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
- * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
- * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
- *
- * 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"
-#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);
-
- // Need to remove this from the form while it is still an HTMLInputElement,
- // so can't wait for the base class's destructor to do it.
- removeFromForm();
-}
-
-const AtomicString& HTMLInputElement::name() const
-{
- return m_data.name();
-}
-
-bool HTMLInputElement::autoComplete() const
-{
- if (m_autocomplete != Uninitialized)
- return m_autocomplete == On;
-
- // Assuming we're still in a Form, respect the Form's setting
- if (HTMLFormElement* form = this->form())
- return form->autoComplete();
-
- // The default is true
- 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 text fields can be focused, then they should always be keyboard focusable
- if (isTextField())
- return HTMLFormControlElementWithState::isFocusable();
-
- // If the base class says we can't be focused, then we can stop now.
- if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
- return false;
-
- if (inputType() == RADIO) {
-
- // Never allow keyboard tabbing to leave you in the same radio group. Always
- // skip any other elements in the group.
- 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;
- }
-
- // Allow keyboard focus if we're checked or if nothing in the group is checked.
- 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;
-
- // IMPORTANT: Don't allow the type to be changed to FILE after the first
- // type change, otherwise a JavaScript programmer would be able to set a text
- // field's value to something like /etc/passwd and then change it to a file field.
- if (inputType() != newType) {
- if (newType == FILE && m_haveType)
- // Set the attribute back to the old value.
- // Useful in case we were called from inside parseMappedAttribute.
- 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
-{
- // needs to be lowercase according to DOM spec
- 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); // should never save/restore password fields
- 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);
- // send the mouse button events iff the caller specified sendToAnyElement
- dispatchSimulatedClick(0, sendToAnyElement);
- break;
- case HIDDEN:
- // a no-op for this type
- break;
- case ISINDEX:
- case PASSWORD:
- case SEARCH:
- case TEXT:
- // should never restore previous selection here
- 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) {
- // Share with <img> since the alignment behavior is the same.
- 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) {
- // 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() == 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)
- toRenderImage(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) {
- // FIXME: ignore for the moment
- } 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);
- }
- // Search field and slider attributes all just cause updateFromElement to be called through style
- // recalcing.
- 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;
- // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
- // time to relayout for this change.
- 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 = toRenderImage(renderer());
- imageObj->setCachedImage(m_imageLoader->image());
-
- // If we have no image at all because we have no src attribute, set
- // image height and width for the alt text instead.
- if (!m_imageLoader->image() && !imageObj->cachedImage())
- imageObj->setImageSizeForAltText();
- }
- }
-}
-
-void HTMLInputElement::detach()
-{
- HTMLFormControlElementWithState::detach();
- setValueMatchesRenderer(false);
-}
-
-String HTMLInputElement::altText() const
-{
- // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
- // also heavily discussed by Hixie on bugzilla
- // note this is intentionally different to HTMLImageElement::altText()
- String alt = getAttribute(altAttr);
- // fall back to title attribute
- if (alt.isNull())
- alt = getAttribute(titleAttr);
- if (alt.isNull())
- alt = getAttribute(valueAttr);
- if (alt.isEmpty())
- alt = inputElementAltText();
- return alt;
-}
-
-bool HTMLInputElement::isSuccessfulSubmitButton() const
-{
- // HTML spec says that buttons must have names to be considered successful.
- // However, other browsers do not impose this constraint. So we do likewise.
- 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)
-{
- // image generates its own names, but for other types there is no form data unless there's a name
- if (name().isEmpty() && inputType() != IMAGE)
- return false;
-
- switch (inputType()) {
- case HIDDEN:
- case ISINDEX:
- case PASSWORD:
- case RANGE:
- case SEARCH:
- case TEXT:
- // always successful
- encoding.appendData(name(), value());
- return true;
-
- case CHECKBOX:
- case RADIO:
- if (checked()) {
- encoding.appendData(name(), value());
- return true;
- }
- break;
-
- case BUTTON:
- case RESET:
- // these types of buttons are never successful
- 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: {
- // Can't submit file on GET.
- if (!multipart)
- return false;
-
- // If no filename at all is entered, return successful but empty.
- // Null would be more logical, but Netscape posts an empty file. Argh.
- 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);
-
- // Only send a change event for items in the document (avoid firing during
- // parsing) and don't send a change event for a radio button that's getting
- // unchecked to match other browsers. DOM is not a useful standard for this
- // because it says only to fire change events at "lose focus" time, which is
- // definitely wrong in practice for these types of elements.
- if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
- onChange();
-}
-
-void HTMLInputElement::setIndeterminate(bool _indeterminate)
-{
- // Only checkboxes honor 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
-{
- // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
- // but we don't want to break existing websites, who may be relying on being able to get the file name as a value.
- 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 no attribute exists, then just use "on" or "" based off the checked() state of the control.
- 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)
-{
- // For security reasons, we don't allow setting the filename, but we do allow clearing it.
- // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
- // but we don't want to break existing websites, who may be relying on this method to clear things.
- if (inputType() == FILE && !value.isEmpty())
- return;
-
- if (isTextField())
- InputElement::updatePlaceholderVisibility(m_data, document());
-
- setValueMatchesRenderer(false);
- if (storesValueSeparateFromAttribute()) {
- if (inputType() == FILE)
- m_fileList->clear();
- else {
- m_data.setValue(constrainValue(value));
- if (isTextField() && 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());
-}
-
-String HTMLInputElement::placeholderValue() const
-{
- return getAttribute(placeholderAttr).string();
-}
-
-bool HTMLInputElement::searchEventsShouldBeDispatched() const
-{
- return hasAttribute(incrementalAttr);
-}
-
-void HTMLInputElement::setValueFromRenderer(const String& value)
-{
- // File upload controls will always use setFileListFromRenderer.
- 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)
-{
- // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
- // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
- void* result = 0;
- if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
- && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
- if (inputType() == CHECKBOX) {
- // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
- // indeterminate.
- if (indeterminate()) {
- result = (void*)0x2;
- setIndeterminate(false);
- } else {
- if (checked())
- result = (void*)0x1;
- setChecked(!checked(), true);
- }
- } else {
- // For radio buttons, store the current selected radio object.
- // We really want radio groups to end up in sane states, i.e., to have something checked.
- // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
- // we want some object in the radio group to actually get selected.
- HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
- if (currRadio) {
- // We have a radio button selected that is not us. Cache it in our result field and ref it so
- // that it can't be destroyed.
- 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) {
- // Reverse the checking we did in preDispatch.
- 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()) {
- // Restore the original selected radio button if possible.
- // Make sure it is still a radio button and only do the restoration if it still
- // belongs to our group.
-
- if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
- // Ok, the old radio button is still in our form and in our group and is still a
- // radio button, so it's safe to restore selection to it.
- input->setChecked(true);
- }
- }
- input->deref();
- }
-
- // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
- 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) {
- // record the mouse position for when we get the DOMActivate event
- MouseEvent* me = static_cast<MouseEvent*>(evt);
- // FIXME: We could just call offsetX() and offsetY() on the event,
- // but that's currently broken, so for now do the computation here.
- if (me->isSimulated() || !renderer()) {
- m_xPos = 0;
- m_yPos = 0;
- } else {
- // FIXME: This doesn't work correctly with transforms.
- 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;
- }
-
- // Let the key handling done in Node take precedence over the event handling here for editable text fields
- if (!clickDefaultFormButton) {
- HTMLFormControlElementWithState::defaultEventHandler(evt);
- if (evt->defaultHandled())
- return;
- }
-
- // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
- // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
- // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
- // must dispatch a DOMActivate event - a click event will not do the job.
- 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;
- // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse
- // event (if any) here instead of relying on the variables set above when
- // processing the click event. Even better, appendFormData could pass the
- // event in, and then we could get rid of m_xPos and m_yPos altogether!
- if (!form()->prepareSubmit(evt)) {
- m_xPos = 0;
- m_yPos = 0;
- }
- m_activeSubmit = false;
- }
- } else if (inputType() == FILE && renderer())
- static_cast<RenderFileUploadControl*>(renderer())->click();
- }
-
- // 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()) {
- 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:
- // Simulate mouse click on the default form button for enter for these types of elements.
- clickDefaultFormButton = true;
- break;
- case BUTTON:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- // Simulate mouse click for enter for these types of elements.
- clickElement = true;
- break;
- case RADIO:
- break; // Don't do anything for enter on a radio button.
- }
- } else if (charCode == ' ') {
- switch (inputType()) {
- case BUTTON:
- case CHECKBOX:
- case FILE:
- case IMAGE:
- case RESET:
- case SUBMIT:
- case RADIO:
- // Prevent scrolling down the page.
- 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);
- // No setDefaultHandled() - IE dispatches a keypress in this case.
- return;
- default:
- break;
- }
- }
-
- if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
- // Left and up mean "previous radio button".
- // Right and down mean "next radio button".
- // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
- // to the right). Seems strange, but we'll match it.
- bool forward = (key == "Down" || key == "Right");
-
- // We can only stay within the form's children if the form hasn't been demoted to a leaf because
- // of malformed HTML.
- Node* n = this;
- while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
- // Once we encounter a form element, we know we're through.
- if (n->hasTagName(formTag))
- break;
-
- // Look for more radio buttons.
- 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:
- // Simulate mouse click for spacebar for these types of elements.
- // The AppKit already does this for some, but not all, of them.
- clickElement = true;
- break;
- case RADIO:
- // If an unselected radio is tabbed into (because the entire group has nothing
- // checked, or because of some explicit .focus() call), then allow space to check it.
- 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();
- }
- // Fire onChange for text fields.
- RenderObject* r = renderer();
- if (r && r->isTextField() && r->isEdited()) {
- onChange();
- // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
- r = renderer();
- if (r)
- r->setEdited(false);
- }
- // Form may never have been present, or may have been destroyed by the change event.
- 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) // Does this ever happen now? Was added for <video> controls
- 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()
-{
- ASSERT(isSearchField());
- if (renderer())
- static_cast<RenderTextControlSingleLine*>(renderer())->addSearchResult();
-}
-
-void HTMLInputElement::onSearch()
-{
- ASSERT(isSearchField());
- if (renderer())
- static_cast<RenderTextControlSingleLine*>(renderer())->stopSearchEventTimer();
- dispatchEventForType(eventNames().searchEvent, true, false);
-}
-
-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()
-{
- // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
- if (needsActivationCallback())
- document()->unregisterForDocumentActivationCallbacks(this);
-
- document()->checkedRadioButtons().removeButton(this);
-
- HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
-}
-
-void HTMLInputElement::didMoveToNewOwnerDocument()
-{
- registerForActivationCallbackIfNeeded();
-
- HTMLFormControlElementWithState::didMoveToNewOwnerDocument();
-}
-
-void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
-{
- HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls);
-
- addSubresourceURL(urls, src());
-}
-
-bool HTMLInputElement::willValidate() const
-{
- // FIXME: This shall check for new WF2 input types too
- return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
- inputType() != BUTTON && inputType() != RESET;
-}
-
-bool HTMLInputElement::placeholderShouldBeVisible() const
-{
- return m_data.placeholderShouldBeVisible();
-}
-
-} // namespace
-
-
+/*
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
+ * (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * (C) 2001 Dirk Mueller (mueller@kde.org)
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
+ * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
+ *
+ * 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"
+#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);
+
+ // Need to remove this from the form while it is still an HTMLInputElement,
+ // so can't wait for the base class's destructor to do it.
+ removeFromForm();
+}
+
+const AtomicString& HTMLInputElement::name() const
+{
+ return m_data.name();
+}
+
+bool HTMLInputElement::autoComplete() const
+{
+ if (m_autocomplete != Uninitialized)
+ return m_autocomplete == On;
+
+ // Assuming we're still in a Form, respect the Form's setting
+ if (HTMLFormElement* form = this->form())
+ return form->autoComplete();
+
+ // The default is true
+ 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 text fields can be focused, then they should always be keyboard focusable
+ if (isTextField())
+ return HTMLFormControlElementWithState::isFocusable();
+
+ // If the base class says we can't be focused, then we can stop now.
+ if (!HTMLFormControlElementWithState::isKeyboardFocusable(event))
+ return false;
+
+ if (inputType() == RADIO) {
+
+ // Never allow keyboard tabbing to leave you in the same radio group. Always
+ // skip any other elements in the group.
+ 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;
+ }
+
+ // Allow keyboard focus if we're checked or if nothing in the group is checked.
+ 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;
+
+ // IMPORTANT: Don't allow the type to be changed to FILE after the first
+ // type change, otherwise a JavaScript programmer would be able to set a text
+ // field's value to something like /etc/passwd and then change it to a file field.
+ if (inputType() != newType) {
+ if (newType == FILE && m_haveType)
+ // Set the attribute back to the old value.
+ // Useful in case we were called from inside parseMappedAttribute.
+ 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
+{
+ // needs to be lowercase according to DOM spec
+ 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); // should never save/restore password fields
+ 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);
+ // send the mouse button events iff the caller specified sendToAnyElement
+ dispatchSimulatedClick(0, sendToAnyElement);
+ break;
+ case HIDDEN:
+ // a no-op for this type
+ break;
+ case ISINDEX:
+ case PASSWORD:
+ case SEARCH:
+ case TEXT:
+ // should never restore previous selection here
+ 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) {
+ // Share with <img> since the alignment behavior is the same.
+ 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) {
+ // 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() == 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)
+ toRenderImage(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) {
+ // FIXME: ignore for the moment
+ } 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);
+ }
+ // Search field and slider attributes all just cause updateFromElement to be called through style
+ // recalcing.
+ 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;
+ // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right
+ // time to relayout for this change.
+ 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 = toRenderImage(renderer());
+ imageObj->setCachedImage(m_imageLoader->image());
+
+ // If we have no image at all because we have no src attribute, set
+ // image height and width for the alt text instead.
+ if (!m_imageLoader->image() && !imageObj->cachedImage())
+ imageObj->setImageSizeForAltText();
+ }
+ }
+}
+
+void HTMLInputElement::detach()
+{
+ HTMLFormControlElementWithState::detach();
+ setValueMatchesRenderer(false);
+}
+
+String HTMLInputElement::altText() const
+{
+ // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
+ // also heavily discussed by Hixie on bugzilla
+ // note this is intentionally different to HTMLImageElement::altText()
+ String alt = getAttribute(altAttr);
+ // fall back to title attribute
+ if (alt.isNull())
+ alt = getAttribute(titleAttr);
+ if (alt.isNull())
+ alt = getAttribute(valueAttr);
+ if (alt.isEmpty())
+ alt = inputElementAltText();
+ return alt;
+}
+
+bool HTMLInputElement::isSuccessfulSubmitButton() const
+{
+ // HTML spec says that buttons must have names to be considered successful.
+ // However, other browsers do not impose this constraint. So we do likewise.
+ 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)
+{
+ // image generates its own names, but for other types there is no form data unless there's a name
+ if (name().isEmpty() && inputType() != IMAGE)
+ return false;
+
+ switch (inputType()) {
+ case HIDDEN:
+ case ISINDEX:
+ case PASSWORD:
+ case RANGE:
+ case SEARCH:
+ case TEXT:
+ // always successful
+ encoding.appendData(name(), value());
+ return true;
+
+ case CHECKBOX:
+ case RADIO:
+ if (checked()) {
+ encoding.appendData(name(), value());
+ return true;
+ }
+ break;
+
+ case BUTTON:
+ case RESET:
+ // these types of buttons are never successful
+ 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: {
+ // Can't submit file on GET.
+ if (!multipart)
+ return false;
+
+ // If no filename at all is entered, return successful but empty.
+ // Null would be more logical, but Netscape posts an empty file. Argh.
+ 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);
+
+ // Only send a change event for items in the document (avoid firing during
+ // parsing) and don't send a change event for a radio button that's getting
+ // unchecked to match other browsers. DOM is not a useful standard for this
+ // because it says only to fire change events at "lose focus" time, which is
+ // definitely wrong in practice for these types of elements.
+ if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked))
+ onChange();
+}
+
+void HTMLInputElement::setIndeterminate(bool _indeterminate)
+{
+ // Only checkboxes honor 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
+{
+ // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
+ // but we don't want to break existing websites, who may be relying on being able to get the file name as a value.
+ 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 no attribute exists, then just use "on" or "" based off the checked() state of the control.
+ 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)
+{
+ // For security reasons, we don't allow setting the filename, but we do allow clearing it.
+ // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control
+ // but we don't want to break existing websites, who may be relying on this method to clear things.
+ if (inputType() == FILE && !value.isEmpty())
+ return;
+
+ if (isTextField())
+ InputElement::updatePlaceholderVisibility(m_data, document());
+
+ setValueMatchesRenderer(false);
+ if (storesValueSeparateFromAttribute()) {
+ if (inputType() == FILE)
+ m_fileList->clear();
+ else {
+ m_data.setValue(constrainValue(value));
+ if (isTextField() && 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());
+}
+
+String HTMLInputElement::placeholderValue() const
+{
+ return getAttribute(placeholderAttr).string();
+}
+
+bool HTMLInputElement::searchEventsShouldBeDispatched() const
+{
+ return hasAttribute(incrementalAttr);
+}
+
+void HTMLInputElement::setValueFromRenderer(const String& value)
+{
+ // File upload controls will always use setFileListFromRenderer.
+ 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)
+{
+ // preventDefault or "return false" are used to reverse the automatic checking/selection we do here.
+ // This result gives us enough info to perform the "undo" in postDispatch of the action we take here.
+ void* result = 0;
+ if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent()
+ && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) {
+ if (inputType() == CHECKBOX) {
+ // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for
+ // indeterminate.
+ if (indeterminate()) {
+ result = (void*)0x2;
+ setIndeterminate(false);
+ } else {
+ if (checked())
+ result = (void*)0x1;
+ setChecked(!checked(), true);
+ }
+ } else {
+ // For radio buttons, store the current selected radio object.
+ // We really want radio groups to end up in sane states, i.e., to have something checked.
+ // Therefore if nothing is currently selected, we won't allow this action to be "undone", since
+ // we want some object in the radio group to actually get selected.
+ HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name());
+ if (currRadio) {
+ // We have a radio button selected that is not us. Cache it in our result field and ref it so
+ // that it can't be destroyed.
+ 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) {
+ // Reverse the checking we did in preDispatch.
+ 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()) {
+ // Restore the original selected radio button if possible.
+ // Make sure it is still a radio button and only do the restoration if it still
+ // belongs to our group.
+
+ if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) {
+ // Ok, the old radio button is still in our form and in our group and is still a
+ // radio button, so it's safe to restore selection to it.
+ input->setChecked(true);
+ }
+ }
+ input->deref();
+ }
+
+ // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler().
+ 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) {
+ // record the mouse position for when we get the DOMActivate event
+ MouseEvent* me = static_cast<MouseEvent*>(evt);
+ // FIXME: We could just call offsetX() and offsetY() on the event,
+ // but that's currently broken, so for now do the computation here.
+ if (me->isSimulated() || !renderer()) {
+ m_xPos = 0;
+ m_yPos = 0;
+ } else {
+ // FIXME: This doesn't work correctly with transforms.
+ 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;
+ }
+
+ // Let the key handling done in EventTargetNode take precedence over the event handling here for editable text fields
+ if (!clickDefaultFormButton) {
+ HTMLFormControlElementWithState::defaultEventHandler(evt);
+ if (evt->defaultHandled())
+ return;
+ }
+
+ // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
+ // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
+ // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element
+ // must dispatch a DOMActivate event - a click event will not do the job.
+ 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;
+ // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse
+ // event (if any) here instead of relying on the variables set above when
+ // processing the click event. Even better, appendFormData could pass the
+ // event in, and then we could get rid of m_xPos and m_yPos altogether!
+ if (!form()->prepareSubmit(evt)) {
+ m_xPos = 0;
+ m_yPos = 0;
+ }
+ m_activeSubmit = false;
+ }
+ } else if (inputType() == FILE && renderer())
+ static_cast<RenderFileUploadControl*>(renderer())->click();
+ }
+
+ // 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()) {
+ 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:
+ // Simulate mouse click on the default form button for enter for these types of elements.
+ clickDefaultFormButton = true;
+ break;
+ case BUTTON:
+ case FILE:
+ case IMAGE:
+ case RESET:
+ case SUBMIT:
+ // Simulate mouse click for enter for these types of elements.
+ clickElement = true;
+ break;
+ case RADIO:
+ break; // Don't do anything for enter on a radio button.
+ }
+ } else if (charCode == ' ') {
+ switch (inputType()) {
+ case BUTTON:
+ case CHECKBOX:
+ case FILE:
+ case IMAGE:
+ case RESET:
+ case SUBMIT:
+ case RADIO:
+ // Prevent scrolling down the page.
+ 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);
+ // No setDefaultHandled() - IE dispatches a keypress in this case.
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) {
+ // Left and up mean "previous radio button".
+ // Right and down mean "next radio button".
+ // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves
+ // to the right). Seems strange, but we'll match it.
+ bool forward = (key == "Down" || key == "Right");
+
+ // We can only stay within the form's children if the form hasn't been demoted to a leaf because
+ // of malformed HTML.
+ Node* n = this;
+ while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) {
+ // Once we encounter a form element, we know we're through.
+ if (n->hasTagName(formTag))
+ break;
+
+ // Look for more radio buttons.
+ 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:
+ // Simulate mouse click for spacebar for these types of elements.
+ // The AppKit already does this for some, but not all, of them.
+ clickElement = true;
+ break;
+ case RADIO:
+ // If an unselected radio is tabbed into (because the entire group has nothing
+ // checked, or because of some explicit .focus() call), then allow space to check it.
+ 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();
+ }
+ // Fire onChange for text fields.
+ RenderObject* r = renderer();
+ if (r && r->isTextField() && r->isEdited()) {
+ onChange();
+ // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
+ r = renderer();
+ if (r)
+ r->setEdited(false);
+ }
+ // Form may never have been present, or may have been destroyed by the change event.
+ 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) // Does this ever happen now? Was added for <video> controls
+ 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()
+{
+ ASSERT(isSearchField());
+ if (renderer())
+ static_cast<RenderTextControlSingleLine*>(renderer())->addSearchResult();
+}
+
+void HTMLInputElement::onSearch()
+{
+ ASSERT(isSearchField());
+ if (renderer())
+ static_cast<RenderTextControlSingleLine*>(renderer())->stopSearchEventTimer();
+ dispatchEventForType(eventNames().searchEvent, true, false);
+}
+
+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()
+{
+ // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered
+ if (needsActivationCallback())
+ document()->unregisterForDocumentActivationCallbacks(this);
+
+ document()->checkedRadioButtons().removeButton(this);
+
+ HTMLFormControlElementWithState::willMoveToNewOwnerDocument();
+}
+
+void HTMLInputElement::didMoveToNewOwnerDocument()
+{
+ registerForActivationCallbackIfNeeded();
+
+ HTMLFormControlElementWithState::didMoveToNewOwnerDocument();
+}
+
+void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
+{
+ HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls);
+
+ addSubresourceURL(urls, src());
+}
+
+bool HTMLInputElement::willValidate() const
+{
+ // FIXME: This shall check for new WF2 input types too
+ return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
+ inputType() != BUTTON && inputType() != RESET;
+}
+
+bool HTMLInputElement::placeholderShouldBeVisible() const
+{
+ return m_data.placeholderShouldBeVisible();
+}
+
+} // namespace
+
+
« no previous file with comments | « third_party/WebKit/WebCore/html/HTMLFormElement.cpp ('k') | third_party/WebKit/WebCore/html/HTMLParser.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698