This source file includes following definitions.
- createInputTypeFactoryMap
- factoryMap
- create
- createText
- normalizeTypeName
- canChangeFromAnotherType
- isTextField
- isTextType
- isRangeControl
- shouldSaveAndRestoreFormControlState
- saveFormControlState
- restoreFormControlState
- isFormDataAppendable
- appendFormData
- resultForDialogSubmit
- valueAsDate
- setValueAsDate
- valueAsDouble
- setValueAsDouble
- setValueAsDecimal
- supportsValidation
- typeMismatchFor
- typeMismatch
- supportsRequired
- valueMissing
- hasBadInput
- patternMismatch
- rangeUnderflow
- rangeOverflow
- defaultValueForStepUp
- minimum
- maximum
- isInRange
- isOutOfRange
- stepMismatch
- badInputText
- rangeOverflowText
- rangeUnderflowText
- typeMismatchText
- valueMissingText
- validationMessage
- shouldSubmitImplicitly
- parseToNumber
- parseToNumberOrNaN
- serialize
- dispatchSimulatedClickIfActive
- chrome
- locale
- canSetStringValue
- hasCustomFocusLogic
- isKeyboardFocusable
- shouldShowFocusRingOnMouseFocus
- shouldUseInputMethod
- enableSecureTextInput
- disableSecureTextInput
- accessKeyAction
- countUsage
- shouldRespectAlignAttribute
- sanitizeValueInResponseToMinOrMaxAttributeChange
- canBeSuccessfulSubmitButton
- rendererIsNeeded
- files
- setFiles
- getTypeSpecificValue
- fallbackValue
- defaultValue
- canSetSuggestedValue
- shouldSendChangeEventAfterCheckedChanged
- storesValueSeparateFromAttribute
- setValue
- canSetValue
- localizeValue
- visibleValue
- sanitizeValue
- receiveDroppedFiles
- droppedFileSystemId
- shouldRespectListAttribute
- shouldRespectSpeechAttribute
- isTextButton
- isRadioButton
- isSearchField
- isHiddenType
- isPasswordField
- isCheckbox
- isEmailField
- isFileUpload
- isImageButton
- isInteractiveContent
- isNumberField
- isTelephoneField
- isURLField
- isDateField
- isDateTimeLocalField
- isMonthField
- isTimeField
- isWeekField
- isEnumeratable
- isCheckable
- isSteppable
- isColorControl
- shouldRespectHeightAndWidthAttributes
- supportsPlaceholder
- supportsReadOnly
- defaultToolTip
- findClosestTickMarkValue
- handleDOMActivateEvent
- hasLegalLinkAttribute
- subResourceAttributeName
- supportsIndeterminateAppearance
- supportsInputModeAttribute
- supportsSelectionAPI
- height
- width
- applyStep
- getAllowedValueStep
- createStepRange
- stepUp
- stepUpFromRenderer
- countUsageIfVisible
- findStepBase
- createStepRange
#include "config.h"
#include "core/html/forms/InputType.h"
#include "InputTypeNames.h"
#include "RuntimeEnabledFeatures.h"
#include "bindings/v8/ExceptionMessages.h"
#include "bindings/v8/ExceptionState.h"
#include "core/accessibility/AXObjectCache.h"
#include "core/dom/NodeRenderStyle.h"
#include "core/events/KeyboardEvent.h"
#include "core/events/ScopedEventQueue.h"
#include "core/fileapi/FileList.h"
#include "core/frame/FrameHost.h"
#include "core/html/FormDataList.h"
#include "core/html/HTMLInputElement.h"
#include "core/html/HTMLShadowElement.h"
#include "core/html/forms/ButtonInputType.h"
#include "core/html/forms/CheckboxInputType.h"
#include "core/html/forms/ColorInputType.h"
#include "core/html/forms/DateInputType.h"
#include "core/html/forms/DateTimeLocalInputType.h"
#include "core/html/forms/EmailInputType.h"
#include "core/html/forms/FileInputType.h"
#include "core/html/forms/FormController.h"
#include "core/html/forms/HiddenInputType.h"
#include "core/html/forms/ImageInputType.h"
#include "core/html/forms/MonthInputType.h"
#include "core/html/forms/NumberInputType.h"
#include "core/html/forms/PasswordInputType.h"
#include "core/html/forms/RadioInputType.h"
#include "core/html/forms/RangeInputType.h"
#include "core/html/forms/ResetInputType.h"
#include "core/html/forms/SearchInputType.h"
#include "core/html/forms/SubmitInputType.h"
#include "core/html/forms/TelephoneInputType.h"
#include "core/html/forms/TextInputType.h"
#include "core/html/forms/TimeInputType.h"
#include "core/html/forms/URLInputType.h"
#include "core/html/forms/WeekInputType.h"
#include "core/html/parser/HTMLParserIdioms.h"
#include "core/rendering/RenderTheme.h"
#include "platform/text/PlatformLocale.h"
#include "platform/text/TextBreakIterator.h"
namespace WebCore {
using blink::WebLocalizedString;
using namespace HTMLNames;
using namespace std;
typedef PassRefPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement&);
typedef HashMap<AtomicString, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap;
static PassOwnPtr<InputTypeFactoryMap> createInputTypeFactoryMap()
{
OwnPtr<InputTypeFactoryMap> map = adoptPtr(new InputTypeFactoryMap);
map->add(InputTypeNames::button, ButtonInputType::create);
map->add(InputTypeNames::checkbox, CheckboxInputType::create);
map->add(InputTypeNames::color, ColorInputType::create);
map->add(InputTypeNames::date, DateInputType::create);
map->add(InputTypeNames::datetime_local, DateTimeLocalInputType::create);
map->add(InputTypeNames::email, EmailInputType::create);
map->add(InputTypeNames::file, FileInputType::create);
map->add(InputTypeNames::hidden, HiddenInputType::create);
map->add(InputTypeNames::image, ImageInputType::create);
map->add(InputTypeNames::month, MonthInputType::create);
map->add(InputTypeNames::number, NumberInputType::create);
map->add(InputTypeNames::password, PasswordInputType::create);
map->add(InputTypeNames::radio, RadioInputType::create);
map->add(InputTypeNames::range, RangeInputType::create);
map->add(InputTypeNames::reset, ResetInputType::create);
map->add(InputTypeNames::search, SearchInputType::create);
map->add(InputTypeNames::submit, SubmitInputType::create);
map->add(InputTypeNames::tel, TelephoneInputType::create);
map->add(InputTypeNames::time, TimeInputType::create);
map->add(InputTypeNames::url, URLInputType::create);
map->add(InputTypeNames::week, WeekInputType::create);
return map.release();
}
static const InputTypeFactoryMap* factoryMap()
{
static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr();
return factoryMap;
}
PassRefPtr<InputType> InputType::create(HTMLInputElement& element, const AtomicString& typeName)
{
InputTypeFactoryFunction factory = typeName.isEmpty() ? 0 : factoryMap()->get(typeName);
if (!factory)
factory = TextInputType::create;
return factory(element);
}
PassRefPtr<InputType> InputType::createText(HTMLInputElement& element)
{
return TextInputType::create(element);
}
const AtomicString& InputType::normalizeTypeName(const AtomicString& typeName)
{
if (typeName.isEmpty())
return InputTypeNames::text;
InputTypeFactoryMap::const_iterator it = factoryMap()->find(typeName);
return it == factoryMap()->end() ? InputTypeNames::text : it->key;
}
bool InputType::canChangeFromAnotherType(const AtomicString& normalizedTypeName)
{
return normalizedTypeName != InputTypeNames::file;
}
InputType::~InputType()
{
}
bool InputType::isTextField() const
{
return false;
}
bool InputType::isTextType() const
{
return false;
}
bool InputType::isRangeControl() const
{
return false;
}
bool InputType::shouldSaveAndRestoreFormControlState() const
{
return true;
}
FormControlState InputType::saveFormControlState() const
{
String currentValue = element().value();
if (currentValue == element().defaultValue())
return FormControlState();
return FormControlState(currentValue);
}
void InputType::restoreFormControlState(const FormControlState& state)
{
element().setValue(state[0]);
}
bool InputType::isFormDataAppendable() const
{
return !element().name().isEmpty();
}
bool InputType::appendFormData(FormDataList& encoding, bool) const
{
encoding.appendData(element().name(), element().value());
return true;
}
String InputType::resultForDialogSubmit() const
{
return element().fastGetAttribute(valueAttr);
}
double InputType::valueAsDate() const
{
return DateComponents::invalidMilliseconds();
}
void InputType::setValueAsDate(double, ExceptionState& exceptionState) const
{
exceptionState.throwDOMException(InvalidStateError, "This input element does not support Date values.");
}
double InputType::valueAsDouble() const
{
return numeric_limits<double>::quiet_NaN();
}
void InputType::setValueAsDouble(double doubleValue, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState) const
{
exceptionState.throwDOMException(InvalidStateError, "This input element does not support Number values.");
}
void InputType::setValueAsDecimal(const Decimal& newValue, TextFieldEventBehavior eventBehavior, ExceptionState&) const
{
element().setValue(serialize(newValue), eventBehavior);
}
bool InputType::supportsValidation() const
{
return true;
}
bool InputType::typeMismatchFor(const String&) const
{
return false;
}
bool InputType::typeMismatch() const
{
return false;
}
bool InputType::supportsRequired() const
{
return supportsValidation();
}
bool InputType::valueMissing(const String&) const
{
return false;
}
bool InputType::hasBadInput() const
{
return false;
}
bool InputType::patternMismatch(const String&) const
{
return false;
}
bool InputType::rangeUnderflow(const String& value) const
{
if (!isSteppable())
return false;
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return false;
return numericValue < createStepRange(RejectAny).minimum();
}
bool InputType::rangeOverflow(const String& value) const
{
if (!isSteppable())
return false;
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return false;
return numericValue > createStepRange(RejectAny).maximum();
}
Decimal InputType::defaultValueForStepUp() const
{
return 0;
}
double InputType::minimum() const
{
return createStepRange(RejectAny).minimum().toDouble();
}
double InputType::maximum() const
{
return createStepRange(RejectAny).maximum().toDouble();
}
bool InputType::isInRange(const String& value) const
{
if (!isSteppable())
return false;
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return true;
StepRange stepRange(createStepRange(RejectAny));
return numericValue >= stepRange.minimum() && numericValue <= stepRange.maximum();
}
bool InputType::isOutOfRange(const String& value) const
{
if (!isSteppable())
return false;
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return true;
StepRange stepRange(createStepRange(RejectAny));
return numericValue < stepRange.minimum() || numericValue > stepRange.maximum();
}
bool InputType::stepMismatch(const String& value) const
{
if (!isSteppable())
return false;
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return false;
return createStepRange(RejectAny).stepMismatch(numericValue);
}
String InputType::badInputText() const
{
ASSERT_NOT_REACHED();
return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
}
String InputType::rangeOverflowText(const Decimal&) const
{
ASSERT_NOT_REACHED();
return String();
}
String InputType::rangeUnderflowText(const Decimal&) const
{
ASSERT_NOT_REACHED();
return String();
}
String InputType::typeMismatchText() const
{
return locale().queryString(WebLocalizedString::ValidationTypeMismatch);
}
String InputType::valueMissingText() const
{
return locale().queryString(WebLocalizedString::ValidationValueMissing);
}
String InputType::validationMessage() const
{
const String value = element().value();
if (hasBadInput())
return badInputText();
if (valueMissing(value))
return valueMissingText();
if (typeMismatch())
return typeMismatchText();
if (patternMismatch(value))
return locale().queryString(WebLocalizedString::ValidationPatternMismatch);
if (element().tooLong())
return locale().validationMessageTooLongText(value.length(), element().maxLength());
if (!isSteppable())
return emptyString();
const Decimal numericValue = parseToNumberOrNaN(value);
if (!numericValue.isFinite())
return emptyString();
StepRange stepRange(createStepRange(RejectAny));
if (numericValue < stepRange.minimum())
return rangeUnderflowText(stepRange.minimum());
if (numericValue > stepRange.maximum())
return rangeOverflowText(stepRange.maximum());
if (stepRange.stepMismatch(numericValue)) {
ASSERT(stepRange.hasStep());
Decimal candidate1 = stepRange.clampValue(numericValue);
String localizedCandidate1 = localizeValue(serialize(candidate1));
Decimal candidate2 = candidate1 < numericValue ? candidate1 + stepRange.step() : candidate1 - stepRange.step();
if (!candidate2.isFinite() || candidate2 < stepRange.minimum() || candidate2 > stepRange.maximum())
return locale().queryString(WebLocalizedString::ValidationStepMismatchCloseToLimit, localizedCandidate1);
String localizedCandidate2 = localizeValue(serialize(candidate2));
if (candidate1 < candidate2)
return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate1, localizedCandidate2);
return locale().queryString(WebLocalizedString::ValidationStepMismatch, localizedCandidate2, localizedCandidate1);
}
return emptyString();
}
bool InputType::shouldSubmitImplicitly(Event* event)
{
return event->isKeyboardEvent() && event->type() == EventTypeNames::keypress && toKeyboardEvent(event)->charCode() == '\r';
}
Decimal InputType::parseToNumber(const String&, const Decimal& defaultValue) const
{
ASSERT_NOT_REACHED();
return defaultValue;
}
Decimal InputType::parseToNumberOrNaN(const String& string) const
{
return parseToNumber(string, Decimal::nan());
}
String InputType::serialize(const Decimal&) const
{
ASSERT_NOT_REACHED();
return String();
}
void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const
{
if (element().active())
element().dispatchSimulatedClick(event);
event->setDefaultHandled();
}
Chrome* InputType::chrome() const
{
if (FrameHost* host = element().document().frameHost())
return &host->chrome();
return 0;
}
Locale& InputType::locale() const
{
return element().locale();
}
bool InputType::canSetStringValue() const
{
return true;
}
bool InputType::hasCustomFocusLogic() const
{
return true;
}
bool InputType::isKeyboardFocusable() const
{
return element().isFocusable();
}
bool InputType::shouldShowFocusRingOnMouseFocus() const
{
return false;
}
bool InputType::shouldUseInputMethod() const
{
return false;
}
void InputType::enableSecureTextInput()
{
}
void InputType::disableSecureTextInput()
{
}
void InputType::accessKeyAction(bool)
{
element().focus(false);
}
void InputType::countUsage()
{
}
bool InputType::shouldRespectAlignAttribute()
{
return false;
}
void InputType::sanitizeValueInResponseToMinOrMaxAttributeChange()
{
}
bool InputType::canBeSuccessfulSubmitButton()
{
return false;
}
bool InputType::rendererIsNeeded()
{
return true;
}
FileList* InputType::files()
{
return 0;
}
void InputType::setFiles(PassRefPtrWillBeRawPtr<FileList>)
{
}
bool InputType::getTypeSpecificValue(String&)
{
return false;
}
String InputType::fallbackValue() const
{
return String();
}
String InputType::defaultValue() const
{
return String();
}
bool InputType::canSetSuggestedValue()
{
return false;
}
bool InputType::shouldSendChangeEventAfterCheckedChanged()
{
return true;
}
bool InputType::storesValueSeparateFromAttribute()
{
return true;
}
void InputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
{
element().setValueInternal(sanitizedValue, eventBehavior);
element().setNeedsStyleRecalc(SubtreeStyleChange);
if (!valueChanged)
return;
switch (eventBehavior) {
case DispatchChangeEvent:
element().dispatchFormControlChangeEvent();
break;
case DispatchInputAndChangeEvent:
element().dispatchFormControlInputEvent();
element().dispatchFormControlChangeEvent();
break;
case DispatchNoEvent:
break;
}
}
bool InputType::canSetValue(const String&)
{
return true;
}
String InputType::localizeValue(const String& proposedValue) const
{
return proposedValue;
}
String InputType::visibleValue() const
{
return element().value();
}
String InputType::sanitizeValue(const String& proposedValue) const
{
return proposedValue;
}
bool InputType::receiveDroppedFiles(const DragData*)
{
ASSERT_NOT_REACHED();
return false;
}
String InputType::droppedFileSystemId()
{
ASSERT_NOT_REACHED();
return String();
}
bool InputType::shouldRespectListAttribute()
{
return false;
}
bool InputType::shouldRespectSpeechAttribute()
{
return false;
}
bool InputType::isTextButton() const
{
return false;
}
bool InputType::isRadioButton() const
{
return false;
}
bool InputType::isSearchField() const
{
return false;
}
bool InputType::isHiddenType() const
{
return false;
}
bool InputType::isPasswordField() const
{
return false;
}
bool InputType::isCheckbox() const
{
return false;
}
bool InputType::isEmailField() const
{
return false;
}
bool InputType::isFileUpload() const
{
return false;
}
bool InputType::isImageButton() const
{
return false;
}
bool InputType::isInteractiveContent() const
{
return true;
}
bool InputType::isNumberField() const
{
return false;
}
bool InputType::isTelephoneField() const
{
return false;
}
bool InputType::isURLField() const
{
return false;
}
bool InputType::isDateField() const
{
return false;
}
bool InputType::isDateTimeLocalField() const
{
return false;
}
bool InputType::isMonthField() const
{
return false;
}
bool InputType::isTimeField() const
{
return false;
}
bool InputType::isWeekField() const
{
return false;
}
bool InputType::isEnumeratable()
{
return true;
}
bool InputType::isCheckable()
{
return false;
}
bool InputType::isSteppable() const
{
return false;
}
bool InputType::isColorControl() const
{
return false;
}
bool InputType::shouldRespectHeightAndWidthAttributes()
{
return false;
}
bool InputType::supportsPlaceholder() const
{
return false;
}
bool InputType::supportsReadOnly() const
{
return false;
}
String InputType::defaultToolTip() const
{
return String();
}
Decimal InputType::findClosestTickMarkValue(const Decimal&)
{
ASSERT_NOT_REACHED();
return Decimal::nan();
}
void InputType::handleDOMActivateEvent(Event*)
{
}
bool InputType::hasLegalLinkAttribute(const QualifiedName&) const
{
return false;
}
const QualifiedName& InputType::subResourceAttributeName() const
{
return nullQName();
}
bool InputType::supportsIndeterminateAppearance() const
{
return false;
}
bool InputType::supportsInputModeAttribute() const
{
return false;
}
bool InputType::supportsSelectionAPI() const
{
return false;
}
unsigned InputType::height() const
{
return 0;
}
unsigned InputType::width() const
{
return 0;
}
void InputType::applyStep(const Decimal& current, int count, AnyStepHandling anyStepHandling, TextFieldEventBehavior eventBehavior, ExceptionState& exceptionState)
{
StepRange stepRange(createStepRange(anyStepHandling));
if (!stepRange.hasStep()) {
exceptionState.throwDOMException(InvalidStateError, "This form element does not have an allowed value step.");
return;
}
EventQueueScope scope;
const Decimal step = stepRange.step();
const AtomicString& stepString = element().fastGetAttribute(stepAttr);
if (!equalIgnoringCase(stepString, "any") && stepRange.stepMismatch(current)) {
ASSERT(!step.isZero());
Decimal newValue;
const Decimal base = stepRange.stepBase();
if (count < 0)
newValue = base + ((current - base) / step).floor() * step;
else if (count > 0)
newValue = base + ((current - base) / step).ceiling() * step;
else
newValue = current;
if (newValue < stepRange.minimum())
newValue = stepRange.minimum();
if (newValue > stepRange.maximum())
newValue = stepRange.maximum();
setValueAsDecimal(newValue, count == 1 || count == -1 ? DispatchChangeEvent : DispatchNoEvent, IGNORE_EXCEPTION);
if (count > 1) {
applyStep(newValue, count - 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
return;
}
if (count < -1) {
applyStep(newValue, count + 1, AnyIsDefaultStep, DispatchChangeEvent, IGNORE_EXCEPTION);
return;
}
} else {
Decimal newValue = current + stepRange.step() * count;
if (!equalIgnoringCase(stepString, "any"))
newValue = stepRange.alignValueForStep(current, newValue);
if (newValue > stepRange.maximum())
newValue = newValue - stepRange.step();
else if (newValue < stepRange.minimum())
newValue = newValue + stepRange.step();
setValueAsDecimal(newValue, eventBehavior, exceptionState);
}
if (AXObjectCache* cache = element().document().existingAXObjectCache())
cache->postNotification(&element(), AXObjectCache::AXValueChanged, true);
}
bool InputType::getAllowedValueStep(Decimal* step) const
{
StepRange stepRange(createStepRange(RejectAny));
*step = stepRange.step();
return stepRange.hasStep();
}
StepRange InputType::createStepRange(AnyStepHandling) const
{
ASSERT_NOT_REACHED();
return StepRange();
}
void InputType::stepUp(int n, ExceptionState& exceptionState)
{
if (!isSteppable()) {
exceptionState.throwDOMException(InvalidStateError, "This form element is not steppable.");
return;
}
const Decimal current = parseToNumber(element().value(), 0);
applyStep(current, n, RejectAny, DispatchNoEvent, exceptionState);
}
void InputType::stepUpFromRenderer(int n)
{
ASSERT(isSteppable());
if (!isSteppable())
return;
ASSERT(n);
if (!n)
return;
StepRange stepRange(createStepRange(AnyIsDefaultStep));
if (!stepRange.hasStep())
return;
EventQueueScope scope;
const Decimal step = stepRange.step();
int sign;
if (step > 0)
sign = n;
else if (step < 0)
sign = -n;
else
sign = 0;
Decimal current = parseToNumberOrNaN(element().value());
if (!current.isFinite()) {
current = defaultValueForStepUp();
const Decimal nextDiff = step * n;
if (current < stepRange.minimum() - nextDiff)
current = stepRange.minimum() - nextDiff;
if (current > stepRange.maximum() - nextDiff)
current = stepRange.maximum() - nextDiff;
setValueAsDecimal(current, DispatchNoEvent, IGNORE_EXCEPTION);
}
if ((sign > 0 && current < stepRange.minimum()) || (sign < 0 && current > stepRange.maximum())) {
setValueAsDecimal(sign > 0 ? stepRange.minimum() : stepRange.maximum(), DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
return;
}
applyStep(current, n, AnyIsDefaultStep, DispatchInputAndChangeEvent, IGNORE_EXCEPTION);
}
void InputType::countUsageIfVisible(UseCounter::Feature feature) const
{
if (RenderStyle* style = element().renderStyle()) {
if (style->visibility() != HIDDEN)
UseCounter::count(element().document(), feature);
}
}
Decimal InputType::findStepBase(const Decimal& defaultValue) const
{
Decimal stepBase = parseToNumber(element().fastGetAttribute(minAttr), Decimal::nan());
if (!stepBase.isFinite())
stepBase = parseToNumber(element().fastGetAttribute(valueAttr), defaultValue);
return stepBase;
}
StepRange InputType::createStepRange(AnyStepHandling anyStepHandling, const Decimal& stepBaseDefault, const Decimal& minimumDefault, const Decimal& maximumDefault, const StepRange::StepDescription& stepDescription) const
{
const Decimal stepBase = findStepBase(stepBaseDefault);
const Decimal minimum = parseToNumber(element().fastGetAttribute(minAttr), minimumDefault);
const Decimal maximum = parseToNumber(element().fastGetAttribute(maxAttr), maximumDefault);
const Decimal step = StepRange::parseStep(anyStepHandling, stepDescription, element().fastGetAttribute(stepAttr));
return StepRange(stepBase, minimum, maximum, step, stepDescription);
}
}