#include "config.h"
#include "InputElement.h"
#include "BeforeTextInsertedEvent.h"
#include "ChromeClient.h"
#include "Document.h"
#include "Event.h"
#include "EventNames.h"
#include "FormControlElement.h"
#include "Frame.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "Page.h"
#include "RenderTextControlSingleLine.h"
#include "SelectionController.h"
#include "TextIterator.h"
#include "TextBreakIterator.h"
#if ENABLE(WML)
#include "WMLInputElement.h"
#include "WMLNames.h"
#endif
namespace WebCore {
using namespace HTMLNames;
const int InputElement::s_maximumLength = 524288;
const int InputElement::s_defaultSize = 20;
void InputElement::dispatchFocusEvent(InputElementData& data, Document* document)
{
if (!data.inputElement()->isTextField())
return;
updatePlaceholderVisibility(data, document);
if (data.inputElement()->isPasswordField() && document->frame())
document->setUseSecureKeyboardEntryWhenActive(true);
}
void InputElement::dispatchBlurEvent(InputElementData& data, Document* document)
{
if (!data.inputElement()->isTextField())
return;
Frame* frame = document->frame();
if (!frame)
return;
updatePlaceholderVisibility(data, document);
if (data.inputElement()->isPasswordField())
document->setUseSecureKeyboardEntryWhenActive(false);
frame->textFieldDidEndEditing(data.element());
}
void InputElement::updatePlaceholderVisibility(InputElementData& data, Document* document, bool placeholderValueChanged)
{
ASSERT(data.inputElement()->isTextField());
bool oldPlaceholderShouldBeVisible = data.placeholderShouldBeVisible();
Element* element = data.element();
data.setPlaceholderShouldBeVisible(data.inputElement()->value().isEmpty()
&& document->focusedNode() != element
&& !data.inputElement()->placeholderValue().isEmpty());
if ((oldPlaceholderShouldBeVisible != data.placeholderShouldBeVisible() || placeholderValueChanged) && element->renderer())
static_cast<RenderTextControlSingleLine*>(element->renderer())->updatePlaceholderVisibility();
}
void InputElement::updateFocusAppearance(InputElementData& data, Document* document, bool restorePreviousSelection)
{
ASSERT(data.inputElement()->isTextField());
if (!restorePreviousSelection || data.cachedSelectionStart() == -1)
data.inputElement()->select();
else
updateSelectionRange(data, data.cachedSelectionStart(), data.cachedSelectionEnd());
if (document && document->frame())
document->frame()->revealSelection();
}
void InputElement::updateSelectionRange(InputElementData& data, int start, int end)
{
if (!data.inputElement()->isTextField())
return;
if (RenderTextControl* renderer = static_cast<RenderTextControl*>(data.element()->renderer()))
renderer->setSelectionRange(start, end);
}
void InputElement::aboutToUnload(InputElementData& data, Document* document)
{
if (!data.inputElement()->isTextField() || !data.element()->focused() || !document->frame())
return;
document->frame()->textFieldDidEndEditing(data.element());
}
void InputElement::setValueFromRenderer(InputElementData& data, Document* document, const String& value)
{
ASSERT(value == data.inputElement()->constrainValue(value) || data.inputElement()->constrainValue(value).isEmpty());
if (data.inputElement()->isTextField())
updatePlaceholderVisibility(data, document);
if (value == "\n")
data.setValue("");
else
data.setValue(value);
Element* element = data.element();
FormControlElement* formControlElement = toFormControlElement(element);
ASSERT(formControlElement);
formControlElement->setValueMatchesRenderer();
element->dispatchEventForType(eventNames().inputEvent, true, false);
notifyFormStateChanged(data, document);
}
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);
}
String InputElement::constrainValue(const InputElementData& data, const String& proposedValue, int maxLength)
{
String string = proposedValue;
if (!data.inputElement()->isTextField())
return string;
string.replace("\r\n", " ");
string.replace('\r', ' ');
string.replace('\n', ' ');
StringImpl* s = string.impl();
int newLength = numCharactersInGraphemeClusters(s, maxLength);
for (int i = 0; i < newLength; ++i) {
const UChar& current = (*s)[i];
if (current < ' ' && current != '\t') {
newLength = i;
break;
}
}
if (newLength < static_cast<int>(string.length()))
return string.left(newLength);
return string;
}
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;
}
void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, Document* document, Event* event)
{
ASSERT(event->isBeforeTextInsertedEvent());
int oldLength = numGraphemeClusters(data.inputElement()->value().impl());
ASSERT(oldLength <= data.maxLength());
int selectionLength = numGraphemeClusters(plainText(document->frame()->selection()->selection().toRange().get()).impl());
ASSERT(oldLength >= selectionLength);
int maxNewLength = data.maxLength() - (oldLength - selectionLength);
BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event);
textEvent->setText(constrainValue(data, textEvent->text(), maxNewLength));
}
void InputElement::parseSizeAttribute(InputElementData& data, MappedAttribute* attribute)
{
data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt());
if (RenderObject* renderer = data.element()->renderer())
renderer->setNeedsLayoutAndPrefWidthsRecalc();
}
void InputElement::parseMaxLengthAttribute(InputElementData& data, MappedAttribute* attribute)
{
int maxLength = attribute->isNull() ? InputElement::s_maximumLength : attribute->value().toInt();
if (maxLength <= 0 || maxLength > InputElement::s_maximumLength)
maxLength = InputElement::s_maximumLength;
int oldMaxLength = data.maxLength();
data.setMaxLength(maxLength);
if (oldMaxLength != maxLength)
updateValueIfNeeded(data);
data.element()->setChanged();
}
void InputElement::updateValueIfNeeded(InputElementData& data)
{
String oldValue = data.value();
String newValue = data.inputElement()->constrainValue(oldValue);
if (newValue != oldValue)
data.inputElement()->setValue(newValue);
}
void InputElement::notifyFormStateChanged(InputElementData& data, Document* document)
{
Frame* frame = document->frame();
if (!frame)
return;
if (Page* page = frame->page())
page->chrome()->client()->formStateDidChange(data.element());
}
InputElementData::InputElementData(InputElement* inputElement, Element* element)
: m_inputElement(inputElement)
, m_element(element)
, m_placeholderShouldBeVisible(false)
, m_size(InputElement::s_defaultSize)
, m_maxLength(InputElement::s_maximumLength)
, m_cachedSelectionStart(-1)
, m_cachedSelectionEnd(-1)
{
ASSERT(m_inputElement);
ASSERT(m_element);
}
InputElementData::~InputElementData()
{
}
const AtomicString& InputElementData::name() const
{
return m_name.isNull() ? emptyAtom : m_name;
}
InputElement* toInputElement(Element* element)
{
if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag)))
return static_cast<HTMLInputElement*>(element);
#if ENABLE(WML)
if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag))
return static_cast<WMLInputElement*>(element);
#endif
return 0;
}
}