TextFieldInputType.cpp [plain text]
#include "config.h"
#include "TextFieldInputType.h"
#include "BeforeTextInsertedEvent.h"
#include "Chrome.h"
#include "ChromeClient.h"
#include "FormDataList.h"
#include "Frame.h"
#include "HTMLInputElement.h"
#include "HTMLNames.h"
#include "KeyboardEvent.h"
#include "Page.h"
#include "RenderLayer.h"
#include "RenderTextControlSingleLine.h"
#include "RenderTheme.h"
#include "ShadowRoot.h"
#include "ShadowTree.h"
#include "TextControlInnerElements.h"
#include "TextEvent.h"
#include "TextIterator.h"
#include "WheelEvent.h"
#include <wtf/text/WTFString.h>
namespace WebCore {
using namespace HTMLNames;
TextFieldInputType::TextFieldInputType(HTMLInputElement* element)
: InputType(element)
{
}
TextFieldInputType::~TextFieldInputType()
{
}
bool TextFieldInputType::isTextField() const
{
return true;
}
bool TextFieldInputType::valueMissing(const String& value) const
{
return element()->required() && value.isEmpty();
}
bool TextFieldInputType::canSetSuggestedValue()
{
return true;
}
void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior)
{
RefPtr<HTMLInputElement> input(element());
InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent);
if (valueChanged)
input->updateInnerTextValue();
unsigned max = visibleValue().length();
if (input->focused())
input->setSelectionRange(max, max);
else
input->cacheSelectionInResponseToSetValue(max);
if (!valueChanged)
return;
switch (eventBehavior) {
case DispatchChangeEvent:
if (input->focused())
input->dispatchFormControlInputEvent();
else
input->dispatchFormControlChangeEvent();
break;
case DispatchInputAndChangeEvent: {
input->dispatchFormControlInputEvent();
input->dispatchFormControlChangeEvent();
break;
}
case DispatchNoEvent:
break;
}
if (!input->focused() || eventBehavior == DispatchNoEvent)
input->setTextAsOfLastFormControlChangeEvent(sanitizedValue);
}
void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event)
{
if (!element()->focused())
return;
Frame* frame = element()->document()->frame();
if (!frame || !frame->editor()->doTextFieldCommandFromEvent(element(), event))
return;
event->setDefaultHandled();
}
void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event)
{
if (element()->disabled() || element()->readOnly())
return;
const String& key = event->keyIdentifier();
int step = 0;
if (key == "Up")
step = 1;
else if (key == "Down")
step = -1;
else
return;
element()->stepUpFromRenderer(step);
event->setDefaultHandled();
}
void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event)
{
if (element()->disabled() || element()->readOnly() || !element()->focused())
return;
int step = 0;
if (event->wheelDeltaY() > 0)
step = 1;
else if (event->wheelDeltaY() < 0)
step = -1;
else
return;
element()->stepUpFromRenderer(step);
event->setDefaultHandled();
}
void TextFieldInputType::forwardEvent(Event* event)
{
if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(eventNames().interfaceForWheelEvent) || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) {
RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element()->renderer());
if (event->type() == eventNames().blurEvent) {
if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) {
if (RenderLayer* innerLayer = innerTextRenderer->layer())
innerLayer->scrollToOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0, RenderLayer::ScrollOffsetClamped);
}
renderTextControl->capsLockStateMayHaveChanged();
} else if (event->type() == eventNames().focusEvent)
renderTextControl->capsLockStateMayHaveChanged();
element()->forwardEvent(event);
}
}
void TextFieldInputType::handleBlurEvent()
{
InputType::handleBlurEvent();
if (Frame* frame = element()->document()->frame())
frame->editor()->textFieldDidEndEditing(element());
}
bool TextFieldInputType::shouldSubmitImplicitly(Event* event)
{
return (event->type() == eventNames().textInputEvent && event->hasInterface(eventNames().interfaceForTextEvent) && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event);
}
RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const
{
return new (arena) RenderTextControlSingleLine(element());
}
bool TextFieldInputType::needsContainer() const
{
#if ENABLE(INPUT_SPEECH)
return element()->isSpeechEnabled();
#else
return false;
#endif
}
bool TextFieldInputType::shouldHaveSpinButton() const
{
Document* document = element()->document();
RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme();
return theme->shouldHaveSpinButton(element());
}
void TextFieldInputType::createShadowSubtree()
{
ASSERT(element()->hasShadowRoot());
ASSERT(!m_innerText);
ASSERT(!m_innerBlock);
ASSERT(!m_innerSpinButton);
Document* document = element()->document();
ChromeClient* chromeClient = document->page() ? document->page()->chrome()->client() : 0;
bool shouldAddDecorations = chromeClient && chromeClient->willAddTextFieldDecorationsTo(element());
bool shouldHaveSpinButton = this->shouldHaveSpinButton();
bool createsContainer = shouldHaveSpinButton || needsContainer() || shouldAddDecorations;
ExceptionCode ec = 0;
m_innerText = TextControlInnerTextElement::create(document);
if (!createsContainer) {
element()->shadowTree()->oldestShadowRoot()->appendChild(m_innerText, ec);
return;
}
ShadowRoot* shadowRoot = element()->shadowTree()->oldestShadowRoot();
m_container = HTMLDivElement::create(document);
m_container->setShadowPseudoId("-webkit-textfield-decoration-container");
shadowRoot->appendChild(m_container, ec);
m_innerBlock = TextControlInnerElement::create(document);
m_innerBlock->appendChild(m_innerText, ec);
m_container->appendChild(m_innerBlock, ec);
#if ENABLE(INPUT_SPEECH)
ASSERT(!m_speechButton);
if (element()->isSpeechEnabled()) {
m_speechButton = InputFieldSpeechButtonElement::create(document);
m_container->appendChild(m_speechButton, ec);
}
#endif
if (shouldHaveSpinButton) {
m_innerSpinButton = SpinButtonElement::create(document);
m_container->appendChild(m_innerSpinButton, ec);
}
if (shouldAddDecorations)
chromeClient->addTextFieldDecorationsTo(element());
}
HTMLElement* TextFieldInputType::containerElement() const
{
return m_container.get();
}
HTMLElement* TextFieldInputType::innerBlockElement() const
{
return m_innerBlock.get();
}
HTMLElement* TextFieldInputType::innerTextElement() const
{
ASSERT(m_innerText);
return m_innerText.get();
}
HTMLElement* TextFieldInputType::innerSpinButtonElement() const
{
return m_innerSpinButton.get();
}
#if ENABLE(INPUT_SPEECH)
HTMLElement* TextFieldInputType::speechButtonElement() const
{
return m_speechButton.get();
}
#endif
HTMLElement* TextFieldInputType::placeholderElement() const
{
return m_placeholder.get();
}
void TextFieldInputType::destroyShadowSubtree()
{
InputType::destroyShadowSubtree();
m_innerText.clear();
m_placeholder.clear();
m_innerBlock.clear();
#if ENABLE(INPUT_SPEECH)
m_speechButton.clear();
#endif
m_innerSpinButton.clear();
m_container.clear();
}
void TextFieldInputType::disabledAttributeChanged()
{
if (m_innerSpinButton)
m_innerSpinButton->releaseCapture();
}
void TextFieldInputType::readonlyAttributeChanged()
{
if (m_innerSpinButton)
m_innerSpinButton->releaseCapture();
}
bool TextFieldInputType::shouldUseInputMethod() const
{
return true;
}
static bool isASCIILineBreak(UChar c)
{
return c == '\r' || c == '\n';
}
static String limitLength(const String& string, int maxLength)
{
unsigned newLength = numCharactersInGraphemeClusters(string, maxLength);
for (unsigned i = 0; i < newLength; ++i) {
const UChar current = string[i];
if (current < ' ' && current != '\t') {
newLength = i;
break;
}
}
return string.left(newLength);
}
String TextFieldInputType::sanitizeValue(const String& proposedValue) const
{
return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength);
}
void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event)
{
unsigned oldLength = numGraphemeClusters(element()->innerTextValue());
unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0;
ASSERT(oldLength >= selectionLength);
unsigned baseLength = oldLength - selectionLength;
unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0;
String eventText = event->text();
eventText.replace("\r\n", " ");
eventText.replace('\r', ' ');
eventText.replace('\n', ' ');
event->setText(limitLength(eventText, appendableLength));
}
bool TextFieldInputType::shouldRespectListAttribute()
{
return InputType::themeSupportsDataListUI(this);
}
void TextFieldInputType::updatePlaceholderText()
{
if (!supportsPlaceholder())
return;
ExceptionCode ec = 0;
String placeholderText = usesFixedPlaceholder() ? fixedPlaceholder() : element()->strippedPlaceholder();
if (placeholderText.isEmpty()) {
if (m_placeholder) {
m_placeholder->parentNode()->removeChild(m_placeholder.get(), ec);
ASSERT(!ec);
m_placeholder.clear();
}
return;
}
if (!m_placeholder) {
m_placeholder = HTMLDivElement::create(element()->document());
m_placeholder->setShadowPseudoId("-webkit-input-placeholder");
element()->shadowTree()->oldestShadowRoot()->insertBefore(m_placeholder, m_container ? m_container->nextSibling() : innerTextElement()->nextSibling(), ec);
ASSERT(!ec);
}
m_placeholder->setInnerText(placeholderText, ec);
ASSERT(!ec);
}
bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const
{
InputType::appendFormData(list, multipart);
const AtomicString& dirnameAttrValue = element()->fastGetAttribute(dirnameAttr);
if (!dirnameAttrValue.isNull())
list.appendData(dirnameAttrValue, element()->directionForFormData());
return true;
}
}