summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp')
-rw-r--r--src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp579
1 files changed, 265 insertions, 314 deletions
diff --git a/src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp b/src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp
index dae0be5..dbc818b 100644
--- a/src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp
+++ b/src/3rdparty/webkit/WebCore/html/HTMLInputElement.cpp
@@ -26,9 +26,8 @@
#include "config.h"
#include "HTMLInputElement.h"
-#include "BeforeTextInsertedEvent.h"
-#include "ChromeClient.h"
#include "CSSPropertyNames.h"
+#include "ChromeClient.h"
#include "Document.h"
#include "Editor.h"
#include "Event.h"
@@ -42,8 +41,10 @@
#include "HTMLFormElement.h"
#include "HTMLImageLoader.h"
#include "HTMLNames.h"
+#include "ScriptEventListener.h"
#include "KeyboardEvent.h"
#include "LocalizedStrings.h"
+#include "MappedAttribute.h"
#include "MouseEvent.h"
#include "Page.h"
#include "RenderButton.h"
@@ -53,10 +54,7 @@
#include "RenderText.h"
#include "RenderTextControlSingleLine.h"
#include "RenderTheme.h"
-#include "SelectionController.h"
-#include "TextBreakIterator.h"
#include "TextEvent.h"
-#include "TextIterator.h"
#include <wtf/StdLibExtras.h>
using namespace std;
@@ -67,79 +65,23 @@ using namespace HTMLNames;
const int maxSavedResults = 256;
-// FIXME: According to HTML4, the length attribute's value can be arbitrarily
-// large. However, due to http://bugs.webkit.org/show_bugs.cgi?id=14536 things
-// get rather sluggish when a text field has a larger number of characters than
-// this, even when just clicking in the text field.
-static const int cMaxLen = 524288;
-
-static int numGraphemeClusters(StringImpl* s)
-{
- if (!s)
- return 0;
-
- TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
- if (!it)
- return 0;
- int num = 0;
- while (textBreakNext(it) != TextBreakDone)
- ++num;
- return num;
-}
-
-static int numCharactersInGraphemeClusters(StringImpl* s, int numGraphemeClusters)
-{
- if (!s)
- return 0;
-
- TextBreakIterator* it = characterBreakIterator(s->characters(), s->length());
- if (!it)
- return 0;
- for (int i = 0; i < numGraphemeClusters; ++i)
- if (textBreakNext(it) == TextBreakDone)
- return s->length();
- return textBreakCurrent(it);
-}
-
-static inline void notifyFormStateChanged(const HTMLInputElement* element)
-{
- Frame* frame = element->document()->frame();
- if (!frame)
- return;
- frame->page()->chrome()->client()->formStateDidChange(element);
-}
-
HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f)
: HTMLFormControlElementWithState(tagName, doc, f)
+ , 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));
- init();
-}
-
-void HTMLInputElement::init()
-{
- m_type = TEXT;
- m_maxLen = cMaxLen;
- m_size = 20;
- m_checked = false;
- m_defaultChecked = false;
- m_useDefaultChecked = true;
- m_indeterminate = false;
-
- m_haveType = false;
- m_activeSubmit = false;
- m_autocomplete = Uninitialized;
- m_inited = false;
- m_autofilled = false;
- m_placeholderShouldBeVisible = false;
-
- xPos = 0;
- yPos = 0;
-
- cachedSelStart = -1;
- cachedSelEnd = -1;
-
- m_maxResults = -1;
}
HTMLInputElement::~HTMLInputElement()
@@ -154,9 +96,9 @@ HTMLInputElement::~HTMLInputElement()
removeFromForm();
}
-const AtomicString& HTMLInputElement::name() const
+const AtomicString& HTMLInputElement::formControlName() const
{
- return m_name.isNull() ? emptyAtom : m_name;
+ return m_data.name();
}
bool HTMLInputElement::autoComplete() const
@@ -172,8 +114,7 @@ bool HTMLInputElement::autoComplete() const
return true;
}
-
-static inline HTMLFormElement::CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
+static inline CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element)
{
if (HTMLFormElement* form = element->form())
return form->checkedRadioButtons();
@@ -192,9 +133,6 @@ bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const
return false;
if (inputType() == RADIO) {
- // Unnamed radio buttons are never focusable (matches WinIE).
- if (name().isEmpty())
- return false;
// Never allow keyboard tabbing to leave you in the same radio group. Always
// skip any other elements in the group.
@@ -222,23 +160,15 @@ bool HTMLInputElement::isMouseFocusable() const
void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection)
{
- if (isTextField()) {
- if (!restorePreviousSelection || cachedSelStart == -1)
- select();
- else
- // Restore the cached selection.
- setSelectionRange(cachedSelStart, cachedSelEnd);
-
- if (document() && document()->frame())
- document()->frame()->revealSelection();
- } else
+ if (isTextField())
+ InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection);
+ else
HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection);
}
void HTMLInputElement::aboutToUnload()
{
- if (isTextField() && focused() && document()->frame())
- document()->frame()->textFieldDidEndEditing(this);
+ InputElement::aboutToUnload(this, this);
}
bool HTMLInputElement::shouldUseInputMethod() const
@@ -248,23 +178,17 @@ bool HTMLInputElement::shouldUseInputMethod() const
void HTMLInputElement::dispatchFocusEvent()
{
- if (isTextField()) {
- setAutofilled(false);
- updatePlaceholderVisibility();
- if (inputType() == PASSWORD && document()->frame())
- document()->setUseSecureKeyboardEntryWhenActive(true);
- }
+ InputElement::dispatchFocusEvent(m_data, this, this);
+
+ if (isTextField())
+ m_autofilled = false;
+
HTMLFormControlElementWithState::dispatchFocusEvent();
}
void HTMLInputElement::dispatchBlurEvent()
{
- if (isTextField() && document()->frame()) {
- updatePlaceholderVisibility();
- if (inputType() == PASSWORD)
- document()->setUseSecureKeyboardEntryWhenActive(false);
- document()->frame()->textFieldDidEndEditing(this);
- }
+ InputElement::dispatchBlurEvent(m_data, this, this);
HTMLFormControlElementWithState::dispatchBlurEvent();
}
@@ -305,6 +229,14 @@ void HTMLInputElement::setInputType(const String& t)
newType = SEARCH;
else if (equalIgnoringCase(t, "range"))
newType = RANGE;
+ else if (equalIgnoringCase(t, "email"))
+ newType = EMAIL;
+ else if (equalIgnoringCase(t, "number"))
+ newType = NUMBER;
+ else if (equalIgnoringCase(t, "tel"))
+ newType = TELEPHONE;
+ else if (equalIgnoringCase(t, "url"))
+ newType = URL;
else
newType = TEXT;
@@ -334,14 +266,14 @@ void HTMLInputElement::setInputType(const String& t)
bool isPasswordField = inputType() == PASSWORD;
bool willRespectHeightAndWidth = respectHeightAndWidthAttrs();
- if (didStoreValue && !willStoreValue && !m_value.isNull()) {
- setAttribute(valueAttr, m_value);
- m_value = String();
+ if (didStoreValue && !willStoreValue && !m_data.value().isNull()) {
+ setAttribute(valueAttr, m_data.value());
+ m_data.setValue(String());
}
if (!didStoreValue && willStoreValue)
- m_value = constrainValue(getAttribute(valueAttr));
+ m_data.setValue(constrainValue(getAttribute(valueAttr)));
else
- recheckValue();
+ InputElement::updateValueIfNeeded(m_data, this);
if (wasPasswordField && !isPasswordField)
unregisterForActivationCallbackIfNeeded();
@@ -350,6 +282,7 @@ void HTMLInputElement::setInputType(const String& t)
if (didRespectHeightAndWidth != willRespectHeightAndWidth) {
NamedMappedAttrMap* map = mappedAttributes();
+ ASSERT(map);
if (Attribute* height = map->getAttributeItem(heightAttr))
attributeChanged(height, false);
if (Attribute* width = map->getAttributeItem(widthAttr))
@@ -367,7 +300,7 @@ void HTMLInputElement::setInputType(const String& t)
checkedRadioButtons(this).addButton(this);
}
- notifyFormStateChanged(this);
+ InputElement::notifyFormStateChanged(this);
}
m_haveType = true;
@@ -375,7 +308,7 @@ void HTMLInputElement::setInputType(const String& t)
m_imageLoader.clear();
}
-const AtomicString& HTMLInputElement::type() const
+const AtomicString& HTMLInputElement::formControlType() const
{
// needs to be lowercase according to DOM spec
switch (inputType()) {
@@ -387,6 +320,10 @@ const AtomicString& HTMLInputElement::type() const
DEFINE_STATIC_LOCAL(const AtomicString, checkbox, ("checkbox"));
return checkbox;
}
+ case EMAIL: {
+ DEFINE_STATIC_LOCAL(const AtomicString, email, ("email"));
+ return email;
+ }
case FILE: {
DEFINE_STATIC_LOCAL(const AtomicString, file, ("file"));
return file;
@@ -401,6 +338,10 @@ const AtomicString& HTMLInputElement::type() const
}
case ISINDEX:
return emptyAtom;
+ case NUMBER: {
+ DEFINE_STATIC_LOCAL(const AtomicString, number, ("number"));
+ return number;
+ }
case PASSWORD: {
DEFINE_STATIC_LOCAL(const AtomicString, password, ("password"));
return password;
@@ -425,30 +366,42 @@ const AtomicString& HTMLInputElement::type() const
DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit"));
return submit;
}
+ case TELEPHONE: {
+ DEFINE_STATIC_LOCAL(const AtomicString, telephone, ("tel"));
+ return telephone;
+ }
case TEXT: {
DEFINE_STATIC_LOCAL(const AtomicString, text, ("text"));
return text;
}
+ case URL: {
+ DEFINE_STATIC_LOCAL(const AtomicString, url, ("url"));
+ return url;
+ }
}
return emptyAtom;
}
-bool HTMLInputElement::saveState(String& result) const
+bool HTMLInputElement::saveFormControlState(String& result) const
{
if (!autoComplete())
return false;
switch (inputType()) {
case BUTTON:
+ case EMAIL:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
+ case NUMBER:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
+ case TELEPHONE:
case TEXT:
+ case URL:
result = value();
return true;
case CHECKBOX:
@@ -462,20 +415,24 @@ bool HTMLInputElement::saveState(String& result) const
return false;
}
-void HTMLInputElement::restoreState(const String& state)
+void HTMLInputElement::restoreFormControlState(const String& state)
{
ASSERT(inputType() != PASSWORD); // should never save/restore password fields
switch (inputType()) {
case BUTTON:
+ case EMAIL:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
+ case NUMBER:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
+ case TELEPHONE:
case TEXT:
+ case URL:
setValue(state);
break;
case CHECKBOX:
@@ -503,22 +460,22 @@ int HTMLInputElement::selectionStart() const
{
if (!isTextField())
return 0;
- if (document()->focusedNode() != this && cachedSelStart != -1)
- return cachedSelStart;
+ if (document()->focusedNode() != this && m_data.cachedSelectionStart() != -1)
+ return m_data.cachedSelectionStart();
if (!renderer())
return 0;
- return static_cast<RenderTextControl*>(renderer())->selectionStart();
+ return toRenderTextControl(renderer())->selectionStart();
}
int HTMLInputElement::selectionEnd() const
{
if (!isTextField())
return 0;
- if (document()->focusedNode() != this && cachedSelEnd != -1)
- return cachedSelEnd;
+ if (document()->focusedNode() != this && m_data.cachedSelectionEnd() != -1)
+ return m_data.cachedSelectionEnd();
if (!renderer())
return 0;
- return static_cast<RenderTextControl*>(renderer())->selectionEnd();
+ return toRenderTextControl(renderer())->selectionEnd();
}
void HTMLInputElement::setSelectionStart(int start)
@@ -527,7 +484,7 @@ void HTMLInputElement::setSelectionStart(int start)
return;
if (!renderer())
return;
- static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
+ toRenderTextControl(renderer())->setSelectionStart(start);
}
void HTMLInputElement::setSelectionEnd(int end)
@@ -536,7 +493,7 @@ void HTMLInputElement::setSelectionEnd(int end)
return;
if (!renderer())
return;
- static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
+ toRenderTextControl(renderer())->setSelectionEnd(end);
}
void HTMLInputElement::select()
@@ -545,16 +502,12 @@ void HTMLInputElement::select()
return;
if (!renderer())
return;
- static_cast<RenderTextControl*>(renderer())->select();
+ toRenderTextControl(renderer())->select();
}
void HTMLInputElement::setSelectionRange(int start, int end)
{
- if (!isTextField())
- return;
- if (!renderer())
- return;
- static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
+ InputElement::updateSelectionRange(this, this, start, end);
}
void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
@@ -575,10 +528,14 @@ void HTMLInputElement::accessKeyAction(bool sendToAnyElement)
case HIDDEN:
// a no-op for this type
break;
+ case EMAIL:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
// should never restore previous selection here
focus(false);
break;
@@ -609,7 +566,7 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
{
if (attr->name() == nameAttr) {
checkedRadioButtons(this).removeButton(this);
- m_name = attr->value();
+ m_data.setName(attr->value());
checkedRadioButtons(this).addButton(this);
} else if (attr->name() == autocompleteAttr) {
if (equalIgnoringCase(attr->value(), "off")) {
@@ -627,30 +584,22 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
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_value.isNull())
- setChanged();
- setValueMatchesRenderer(false);
+ if (m_data.value().isNull())
+ setNeedsStyleRecalc();
+ setFormControlValueMatchesRenderer(false);
} else if (attr->name() == checkedAttr) {
m_defaultChecked = !attr->isNull();
if (m_useDefaultChecked) {
setChecked(m_defaultChecked);
m_useDefaultChecked = true;
}
- } else if (attr->name() == maxlengthAttr) {
- int oldMaxLen = m_maxLen;
- m_maxLen = !attr->isNull() ? attr->value().toInt() : cMaxLen;
- if (m_maxLen <= 0 || m_maxLen > cMaxLen)
- m_maxLen = cMaxLen;
- if (oldMaxLen != m_maxLen)
- recheckValue();
- setChanged();
- } else if (attr->name() == sizeAttr) {
- m_size = !attr->isNull() ? attr->value().toInt() : 20;
- if (renderer())
- renderer()->setNeedsLayoutAndPrefWidthsRecalc();
- } else if (attr->name() == altAttr) {
+ } else if (attr->name() == maxlengthAttr)
+ InputElement::parseMaxLengthAttribute(m_data, this, this, attr);
+ else if (attr->name() == sizeAttr)
+ InputElement::parseSizeAttribute(m_data, this, attr);
+ else if (attr->name() == altAttr) {
if (renderer() && inputType() == IMAGE)
- static_cast<RenderImage*>(renderer())->updateAltText();
+ toRenderImage(renderer())->updateAltText();
} else if (attr->name() == srcAttr) {
if (renderer() && inputType() == IMAGE) {
if (!m_imageLoader)
@@ -676,20 +625,20 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
if (respectHeightAndWidthAttrs())
addCSSLength(attr, CSSPropertyHeight, attr->value());
} else if (attr->name() == onfocusAttr) {
- setInlineEventListenerForTypeAndAttribute(eventNames().focusEvent, attr);
+ setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr));
} else if (attr->name() == onblurAttr) {
- setInlineEventListenerForTypeAndAttribute(eventNames().blurEvent, attr);
+ setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr));
} else if (attr->name() == onselectAttr) {
- setInlineEventListenerForTypeAndAttribute(eventNames().selectEvent, attr);
+ setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr));
} else if (attr->name() == onchangeAttr) {
- setInlineEventListenerForTypeAndAttribute(eventNames().changeEvent, attr);
+ setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr));
} else if (attr->name() == oninputAttr) {
- setInlineEventListenerForTypeAndAttribute(eventNames().inputEvent, attr);
+ setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, 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);
+ setAttributeEventListener(eventNames().searchEvent, createAttributeEventListener(this, attr));
} else if (attr->name() == resultsAttr) {
int oldResults = m_maxResults;
m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1;
@@ -699,17 +648,17 @@ void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr)
detach();
attach();
}
- setChanged();
+ setNeedsStyleRecalc();
} else if (attr->name() == placeholderAttr) {
if (isTextField())
- updatePlaceholderVisibility(true);
+ updatePlaceholderVisibility();
} else if (attr->name() == autosaveAttr ||
- attr->name() == incrementalAttr ||
- attr->name() == minAttr ||
- attr->name() == maxAttr ||
- attr->name() == multipleAttr ||
- attr->name() == precisionAttr)
- setChanged();
+ attr->name() == incrementalAttr ||
+ attr->name() == minAttr ||
+ attr->name() == maxAttr ||
+ attr->name() == multipleAttr ||
+ attr->name() == precisionAttr)
+ setNeedsStyleRecalc();
else
HTMLFormControlElementWithState::parseMappedAttribute(attr);
}
@@ -719,16 +668,20 @@ bool HTMLInputElement::rendererIsNeeded(RenderStyle *style)
switch (inputType()) {
case BUTTON:
case CHECKBOX:
+ case EMAIL:
case FILE:
case IMAGE:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RADIO:
case RANGE:
case RESET:
case SEARCH:
case SUBMIT:
+ case TELEPHONE:
case TEXT:
+ case URL:
return HTMLFormControlElementWithState::rendererIsNeeded(style);
case HIDDEN:
return false;
@@ -755,10 +708,14 @@ RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *
return new (arena) RenderImage(this);
case RANGE:
return new (arena) RenderSlider(this);
+ case EMAIL:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
return new (arena) RenderTextControlSingleLine(this);
}
ASSERT(false);
@@ -780,7 +737,7 @@ void HTMLInputElement::attach()
m_imageLoader.set(new HTMLImageLoader(this));
m_imageLoader->updateFromElement();
if (renderer()) {
- RenderImage* imageObj = static_cast<RenderImage*>(renderer());
+ RenderImage* imageObj = toRenderImage(renderer());
imageObj->setCachedImage(m_imageLoader->image());
// If we have no image at all because we have no src attribute, set
@@ -794,7 +751,7 @@ void HTMLInputElement::attach()
void HTMLInputElement::detach()
{
HTMLFormControlElementWithState::detach();
- setValueMatchesRenderer(false);
+ setFormControlValueMatchesRenderer(false);
}
String HTMLInputElement::altText() const
@@ -837,12 +794,16 @@ bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
return false;
switch (inputType()) {
+ case EMAIL:
case HIDDEN:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RANGE:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
// always successful
encoding.appendData(name(), value());
return true;
@@ -862,8 +823,8 @@ bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart)
case IMAGE:
if (m_activeSubmit) {
- encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), xPos);
- encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), yPos);
+ 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;
@@ -917,7 +878,7 @@ void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
m_useDefaultChecked = false;
m_checked = nowChecked;
- setChanged();
+ setNeedsStyleRecalc();
checkedRadioButtons(this).addButton(this);
@@ -930,7 +891,7 @@ void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent)
// 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();
+ dispatchFormControlChangeEvent();
}
void HTMLInputElement::setIndeterminate(bool _indeterminate)
@@ -941,20 +902,25 @@ void HTMLInputElement::setIndeterminate(bool _indeterminate)
m_indeterminate = _indeterminate;
- setChanged();
+ setNeedsStyleRecalc();
if (renderer() && renderer()->style()->hasAppearance())
theme()->stateChanged(renderer(), CheckedState);
}
-void HTMLInputElement::copyNonAttributeProperties(const Element *source)
+int HTMLInputElement::size() const
+{
+ return m_data.size();
+}
+
+void HTMLInputElement::copyNonAttributeProperties(const Element* source)
{
- const HTMLInputElement *sourceElem = static_cast<const HTMLInputElement *>(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;
- m_value = sourceElem->m_value;
- m_checked = sourceElem->m_checked;
- m_indeterminate = sourceElem->m_indeterminate;
-
HTMLFormControlElementWithState::copyNonAttributeProperties(source);
}
@@ -968,7 +934,7 @@ String HTMLInputElement::value() const
return String();
}
- String value = m_value;
+ String value = m_data.value();
if (value.isNull()) {
value = constrainValue(getAttribute(valueAttr));
@@ -987,15 +953,19 @@ String HTMLInputElement::valueWithDefault() const
switch (inputType()) {
case BUTTON:
case CHECKBOX:
+ case EMAIL:
case FILE:
case HIDDEN:
case IMAGE:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RADIO:
case RANGE:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
break;
case RESET:
v = resetButtonDefaultLabel();
@@ -1016,61 +986,54 @@ void HTMLInputElement::setValue(const String& value)
if (inputType() == FILE && !value.isEmpty())
return;
- if (isTextField())
- updatePlaceholderVisibility();
-
- setValueMatchesRenderer(false);
+ setFormControlValueMatchesRenderer(false);
if (storesValueSeparateFromAttribute()) {
if (inputType() == FILE)
m_fileList->clear();
else {
- m_value = constrainValue(value);
- if (isTextField() && inDocument())
- document()->updateRendering();
+ m_data.setValue(constrainValue(value));
+ if (isTextField()) {
+ InputElement::updatePlaceholderVisibility(m_data, this, this);
+ if (inDocument())
+ document()->updateStyleIfNeeded();
+ }
}
if (renderer())
renderer()->updateFromElement();
- setChanged();
+ setNeedsStyleRecalc();
} else
setAttribute(valueAttr, constrainValue(value));
if (isTextField()) {
- unsigned max = m_value.length();
+ unsigned max = m_data.value().length();
if (document()->focusedNode() == this)
- setSelectionRange(max, max);
- else {
- cachedSelStart = max;
- cachedSelEnd = max;
- }
+ InputElement::updateSelectionRange(this, this, max, max);
+ else
+ cacheSelection(max, max);
}
- notifyFormStateChanged(this);
+ InputElement::notifyFormStateChanged(this);
}
-void HTMLInputElement::setValueFromRenderer(const String& value)
+String HTMLInputElement::placeholder() const
{
- // Renderer and our event handler are responsible for constraining values.
- ASSERT(value == constrainValue(value) || constrainValue(value).isEmpty());
-
- // File upload controls will always use setFileListFromRenderer.
- ASSERT (inputType() != FILE);
-
- if (isTextField())
- updatePlaceholderVisibility();
-
- // Workaround for bug where trailing \n is included in the result of textContent.
- // The assert macro above may also be simplified to: value == constrainValue(value)
- // http://bugs.webkit.org/show_bug.cgi?id=9661
- if (value == "\n")
- m_value = "";
- else
- m_value = value;
+ return getAttribute(placeholderAttr).string();
+}
- setValueMatchesRenderer();
+void HTMLInputElement::setPlaceholder(const String& value)
+{
+ setAttribute(placeholderAttr, value);
+}
- // Fire the "input" DOM event.
- dispatchEventForType(eventNames().inputEvent, true, false);
+bool HTMLInputElement::searchEventsShouldBeDispatched() const
+{
+ return hasAttribute(incrementalAttr);
+}
- notifyFormStateChanged(this);
+void HTMLInputElement::setValueFromRenderer(const String& value)
+{
+ // File upload controls will always use setFileListFromRenderer.
+ ASSERT(inputType() != FILE);
+ InputElement::setValueFromRenderer(m_data, this, this, value);
}
void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths)
@@ -1080,8 +1043,8 @@ void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths)
for (int i = 0; i < size; i++)
m_fileList->append(File::create(paths[i]));
- setValueMatchesRenderer();
- notifyFormStateChanged(this);
+ setFormControlValueMatchesRenderer(true);
+ InputElement::notifyFormStateChanged(this);
}
bool HTMLInputElement::storesValueSeparateFromAttribute() const
@@ -1095,12 +1058,16 @@ bool HTMLInputElement::storesValueSeparateFromAttribute() const
case RESET:
case SUBMIT:
return false;
+ case EMAIL:
case FILE:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RANGE:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
return true;
}
return false;
@@ -1126,11 +1093,6 @@ void* HTMLInputElement::preDispatchEventHandler(Event *evt)
}
} else {
// For radio buttons, store the current selected radio object.
- if (name().isEmpty() || checked())
- return 0; // Match WinIE and don't allow unnamed radio buttons to be checked.
- // Checked buttons just stay checked.
- // FIXME: Need to learn to work without a form.
-
// 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.
@@ -1166,8 +1128,7 @@ void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
// Make sure it is still a radio button and only do the restoration if it still
// belongs to our group.
- // Match WinIE and don't allow unnamed radio buttons to be checked.
- if (input->form() == form() && input->inputType() == RADIO && !name().isEmpty() && input->name() == name()) {
+ 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);
@@ -1183,6 +1144,9 @@ void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data)
void HTMLInputElement::defaultEventHandler(Event* evt)
{
+ // FIXME: It would be better to refactor this for the different types of input element.
+ // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific.
+
bool clickDefaultFormButton = false;
if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n")
@@ -1194,30 +1158,40 @@ void HTMLInputElement::defaultEventHandler(Event* 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()) {
- xPos = 0;
- yPos = 0;
+ m_xPos = 0;
+ m_yPos = 0;
} else {
// FIXME: This doesn't work correctly with transforms.
+ // FIXME: pageX/pageY need adjusting for pageZoomFactor(). Use actualPageLocation()?
IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute());
- xPos = me->pageX() - absOffset.x();
- yPos = me->pageY() - absOffset.y();
+ 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))) {
+ 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) {
+ 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) {
+ // Call the base event handler before any of our own event handling for almost all events in text fields.
+ // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function.
+ bool callBaseClassEarly = isTextField() && !clickDefaultFormButton
+ && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent);
+ if (callBaseClassEarly) {
HTMLFormControlElementWithState::defaultEventHandler(evt);
if (evt->defaultHandled())
return;
@@ -1235,13 +1209,13 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
form()->reset();
else {
m_activeSubmit = true;
- // FIXME: Would be cleaner to get xPos and yPos out of the underlying mouse
+ // 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 xPos and yPos altogether!
+ // event in, and then we could get rid of m_xPos and m_yPos altogether!
if (!form()->prepareSubmit(evt)) {
- xPos = 0;
- yPos = 0;
+ m_xPos = 0;
+ m_yPos = 0;
}
m_activeSubmit = false;
}
@@ -1259,12 +1233,16 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
if (charCode == '\r') {
switch (inputType()) {
case CHECKBOX:
+ case EMAIL:
case HIDDEN:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RANGE:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
// Simulate mouse click on the default form button for enter for these types of elements.
clickDefaultFormButton = true;
break;
@@ -1316,7 +1294,8 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
case SUBMIT:
case RADIO:
setActive(true, true);
- // No setDefaultHandled() - IE dispatches a keypress in this case.
+ // No setDefaultHandled(), because IE dispatches a keypress in this case
+ // and the caller will only dispatch a keypress if we don't call setDefaultHandled.
return;
default:
break;
@@ -1344,9 +1323,8 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
if (elt->form() != form())
break;
if (n->hasTagName(inputTag)) {
- // Match WinIE and don't allow unnamed radio buttons to be checked.
HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n);
- if (inputElt->inputType() == RADIO && !name().isEmpty() && inputElt->name() == name() && inputElt->isFocusable()) {
+ if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) {
inputElt->setChecked(true);
document()->setFocusedNode(inputElt);
inputElt->dispatchSimulatedClick(evt, false, false);
@@ -1382,12 +1360,16 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
if (!checked())
clickElement = true;
break;
+ case EMAIL:
case HIDDEN:
case ISINDEX:
+ case NUMBER:
case PASSWORD:
case RANGE:
case SEARCH:
+ case TELEPHONE:
case TEXT:
+ case URL:
break;
}
}
@@ -1407,12 +1389,12 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
}
// Fire onChange for text fields.
RenderObject* r = renderer();
- if (r && r->isTextField() && r->isEdited()) {
- onChange();
+ if (r && r->isTextField() && toRenderTextControl(r)->isEdited()) {
+ dispatchFormControlChangeEvent();
// Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it.
r = renderer();
- if (r)
- r->setEdited(false);
+ if (r && r->isTextField())
+ toRenderTextControl(r)->setEdited(false);
}
// Form may never have been present, or may have been destroyed by the change event.
if (form())
@@ -1421,36 +1403,17 @@ void HTMLInputElement::defaultEventHandler(Event* evt)
return;
}
- if (evt->isBeforeTextInsertedEvent()) {
- // Make sure that the text to be inserted will not violate the maxLength.
- int oldLen = numGraphemeClusters(value().impl());
- ASSERT(oldLen <= maxLength());
- int selectionLen = numGraphemeClusters(plainText(document()->frame()->selection()->selection().toRange().get()).impl());
- ASSERT(oldLen >= selectionLen);
- int maxNewLen = maxLength() - (oldLen - selectionLen);
-
- // Truncate the inserted text to avoid violating the maxLength and other constraints.
- BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(evt);
- textEvent->setText(constrainValue(textEvent->text(), maxNewLen));
- }
+ if (evt->isBeforeTextInsertedEvent())
+ InputElement::handleBeforeTextInsertedEvent(m_data, this, 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);
- }
+ if (inputType() == RANGE && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent()))
+ static_cast<RenderSlider*>(renderer())->forwardEvent(evt);
+
+ if (!callBaseClassEarly && !evt->defaultHandled())
+ HTMLFormControlElementWithState::defaultEventHandler(evt);
}
bool HTMLInputElement::isURLAttribute(Attribute *attr) const
@@ -1478,6 +1441,11 @@ 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);
@@ -1518,11 +1486,26 @@ 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));
}
+bool HTMLInputElement::multiple() const
+{
+ return !getAttribute(multipleAttr).isNull();
+}
+
+void HTMLInputElement::setMultiple(bool multiple)
+{
+ setAttribute(multipleAttr, multiple ? "" : 0);
+}
+
void HTMLInputElement::setSize(unsigned _size)
{
setAttribute(sizeAttr, String::number(_size));
@@ -1554,7 +1537,7 @@ void HTMLInputElement::setAutofilled(bool b)
return;
m_autofilled = b;
- setChanged();
+ setNeedsStyleRecalc();
}
FileList* HTMLInputElement::files()
@@ -1566,15 +1549,7 @@ FileList* HTMLInputElement::files()
String HTMLInputElement::constrainValue(const String& proposedValue) const
{
- return constrainValue(proposedValue, m_maxLen);
-}
-
-void HTMLInputElement::recheckValue()
-{
- String oldValue = value();
- String newValue = constrainValue(oldValue);
- if (newValue != oldValue)
- setValue(newValue);
+ return InputElement::constrainValue(this, proposedValue, m_data.maxLength());
}
bool HTMLInputElement::needsActivationCallback()
@@ -1594,40 +1569,10 @@ void HTMLInputElement::unregisterForActivationCallbackIfNeeded()
document()->unregisterForDocumentActivationCallbacks(this);
}
-void HTMLInputElement::updatePlaceholderVisibility(bool placeholderValueChanged)
-{
- ASSERT(isTextField());
-
- bool oldPlaceholderShouldBeVisible = m_placeholderShouldBeVisible;
-
- m_placeholderShouldBeVisible = value().isEmpty()
- && document()->focusedNode() != this
- && !getAttribute(placeholderAttr).isEmpty();
-
- if ((oldPlaceholderShouldBeVisible != m_placeholderShouldBeVisible || placeholderValueChanged) && renderer())
- static_cast<RenderTextControlSingleLine*>(renderer())->updatePlaceholderVisibility();
-}
-
-String HTMLInputElement::constrainValue(const String& proposedValue, int maxLen) const
+void HTMLInputElement::cacheSelection(int start, int end)
{
- String string = proposedValue;
- if (isTextField()) {
- string.replace("\r\n", " ");
- string.replace('\r', ' ');
- string.replace('\n', ' ');
- StringImpl* s = string.impl();
- int newLen = numCharactersInGraphemeClusters(s, maxLen);
- for (int i = 0; i < newLen; ++i) {
- const UChar current = (*s)[i];
- if (current < ' ' && current != '\t') {
- newLen = i;
- break;
- }
- }
- if (newLen < static_cast<int>(string.length()))
- return string.substring(0, newLen);
- }
- return string;
+ m_data.setCachedSelectionStart(start);
+ m_data.setCachedSelectionEnd(end);
}
void HTMLInputElement::addSearchResult()
@@ -1642,14 +1587,14 @@ void HTMLInputElement::onSearch()
ASSERT(isSearchField());
if (renderer())
static_cast<RenderTextControlSingleLine*>(renderer())->stopSearchEventTimer();
- dispatchEventForType(eventNames().searchEvent, true, false);
+ dispatchEvent(eventNames().searchEvent, true, false);
}
-Selection HTMLInputElement::selection() const
+VisibleSelection HTMLInputElement::selection() const
{
- if (!renderer() || !isTextField() || cachedSelStart == -1 || cachedSelEnd == -1)
- return Selection();
- return static_cast<RenderTextControl*>(renderer())->selection(cachedSelStart, cachedSelEnd);
+ if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1)
+ return VisibleSelection();
+ return toRenderTextControl(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd());
}
void HTMLInputElement::documentDidBecomeActive()
@@ -1689,4 +1634,10 @@ bool HTMLInputElement::willValidate() const
return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN &&
inputType() != BUTTON && inputType() != RESET;
}
+
+bool HTMLInputElement::placeholderShouldBeVisible() const
+{
+ return m_data.placeholderShouldBeVisible();
+}
+
} // namespace