1/* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * Copyright (C) 2011 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "config.h" 33#include "TextFieldInputType.h" 34 35#include "BeforeTextInsertedEvent.h" 36#include "Chrome.h" 37#include "Editor.h" 38#include "ElementShadow.h" 39#include "FormDataList.h" 40#include "Frame.h" 41#include "FrameSelection.h" 42#include "HTMLInputElement.h" 43#include "HTMLNames.h" 44#include "KeyboardEvent.h" 45#include "NodeRenderStyle.h" 46#include "Page.h" 47#include "RenderLayer.h" 48#include "RenderTextControlSingleLine.h" 49#include "RenderTheme.h" 50#include "ShadowRoot.h" 51#include "TextControlInnerElements.h" 52#include "TextEvent.h" 53#include "TextIterator.h" 54#include "WheelEvent.h" 55#include <wtf/text/WTFString.h> 56 57namespace WebCore { 58 59using namespace HTMLNames; 60 61TextFieldInputType::TextFieldInputType(HTMLInputElement* element) 62 : InputType(element) 63{ 64} 65 66TextFieldInputType::~TextFieldInputType() 67{ 68 if (m_innerSpinButton) 69 m_innerSpinButton->removeSpinButtonOwner(); 70} 71 72bool TextFieldInputType::isKeyboardFocusable(KeyboardEvent*) const 73{ 74 return element()->isTextFormControlFocusable(); 75} 76 77bool TextFieldInputType::isMouseFocusable() const 78{ 79 return element()->isTextFormControlFocusable(); 80} 81 82bool TextFieldInputType::isTextField() const 83{ 84 return true; 85} 86 87bool TextFieldInputType::valueMissing(const String& value) const 88{ 89 return element()->isRequired() && value.isEmpty(); 90} 91 92bool TextFieldInputType::canSetSuggestedValue() 93{ 94 return true; 95} 96 97void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, TextFieldEventBehavior eventBehavior) 98{ 99 // Grab this input element to keep reference even if JS event handler 100 // changes input type. 101 RefPtr<HTMLInputElement> input(element()); 102 103 // We don't ask InputType::setValue to dispatch events because 104 // TextFieldInputType dispatches events different way from InputType. 105 InputType::setValue(sanitizedValue, valueChanged, DispatchNoEvent); 106 107 if (valueChanged) 108 updateInnerTextValue(); 109 110 unsigned max = visibleValue().length(); 111 if (input->focused()) 112 input->setSelectionRange(max, max); 113 else 114 input->cacheSelectionInResponseToSetValue(max); 115 116 if (!valueChanged) 117 return; 118 119 switch (eventBehavior) { 120 case DispatchChangeEvent: 121 // If the user is still editing this field, dispatch an input event rather than a change event. 122 // The change event will be dispatched when editing finishes. 123 if (input->focused()) 124 input->dispatchFormControlInputEvent(); 125 else 126 input->dispatchFormControlChangeEvent(); 127 break; 128 129 case DispatchInputAndChangeEvent: { 130 input->dispatchFormControlInputEvent(); 131 input->dispatchFormControlChangeEvent(); 132 break; 133 } 134 135 case DispatchNoEvent: 136 break; 137 } 138 139 // FIXME: Why do we do this when eventBehavior == DispatchNoEvent 140 if (!input->focused() || eventBehavior == DispatchNoEvent) 141 input->setTextAsOfLastFormControlChangeEvent(sanitizedValue); 142} 143 144void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event) 145{ 146 if (!element()->focused()) 147 return; 148 Frame* frame = element()->document()->frame(); 149 if (!frame || !frame->editor().doTextFieldCommandFromEvent(element(), event)) 150 return; 151 event->setDefaultHandled(); 152} 153 154void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event) 155{ 156 if (element()->isDisabledOrReadOnly()) 157 return; 158 const String& key = event->keyIdentifier(); 159 if (key == "Up") 160 spinButtonStepUp(); 161 else if (key == "Down") 162 spinButtonStepDown(); 163 else 164 return; 165 event->setDefaultHandled(); 166} 167 168void TextFieldInputType::forwardEvent(Event* event) 169{ 170 if (m_innerSpinButton) { 171 m_innerSpinButton->forwardEvent(event); 172 if (event->defaultHandled()) 173 return; 174 } 175 176 if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(eventNames().interfaceForWheelEvent) || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) { 177 RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element()->renderer()); 178 if (event->type() == eventNames().blurEvent) { 179 if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) { 180 if (RenderLayer* innerLayer = innerTextRenderer->layer()) { 181 IntSize scrollOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0); 182 innerLayer->scrollToOffset(scrollOffset, RenderLayer::ScrollOffsetClamped); 183 } 184 } 185 186 renderTextControl->capsLockStateMayHaveChanged(); 187 } else if (event->type() == eventNames().focusEvent) 188 renderTextControl->capsLockStateMayHaveChanged(); 189 190 element()->forwardEvent(event); 191 } 192} 193 194void TextFieldInputType::handleBlurEvent() 195{ 196 InputType::handleBlurEvent(); 197 element()->endEditing(); 198} 199 200bool TextFieldInputType::shouldSubmitImplicitly(Event* event) 201{ 202 return (event->type() == eventNames().textInputEvent && event->hasInterface(eventNames().interfaceForTextEvent) && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event); 203} 204 205RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const 206{ 207 return new (arena) RenderTextControlSingleLine(element()); 208} 209 210bool TextFieldInputType::needsContainer() const 211{ 212#if ENABLE(INPUT_SPEECH) 213 return element()->isSpeechEnabled(); 214#else 215 return false; 216#endif 217} 218 219bool TextFieldInputType::shouldHaveSpinButton() const 220{ 221 Document* document = element()->document(); 222 RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme(); 223 return theme->shouldHaveSpinButton(element()); 224} 225 226void TextFieldInputType::createShadowSubtree() 227{ 228 ASSERT(element()->shadow()); 229 230 ASSERT(!m_innerText); 231 ASSERT(!m_innerBlock); 232 ASSERT(!m_innerSpinButton); 233 234 Document* document = element()->document(); 235 bool shouldHaveSpinButton = this->shouldHaveSpinButton(); 236 bool createsContainer = shouldHaveSpinButton || needsContainer(); 237 238 m_innerText = TextControlInnerTextElement::create(document); 239 if (!createsContainer) { 240 element()->userAgentShadowRoot()->appendChild(m_innerText, IGNORE_EXCEPTION); 241 return; 242 } 243 244 ShadowRoot* shadowRoot = element()->userAgentShadowRoot(); 245 m_container = TextControlInnerContainer::create(document); 246 m_container->setPseudo(AtomicString("-webkit-textfield-decoration-container", AtomicString::ConstructFromLiteral)); 247 shadowRoot->appendChild(m_container, IGNORE_EXCEPTION); 248 249 m_innerBlock = TextControlInnerElement::create(document); 250 m_innerBlock->appendChild(m_innerText, IGNORE_EXCEPTION); 251 m_container->appendChild(m_innerBlock, IGNORE_EXCEPTION); 252 253#if ENABLE(INPUT_SPEECH) 254 ASSERT(!m_speechButton); 255 if (element()->isSpeechEnabled()) { 256 m_speechButton = InputFieldSpeechButtonElement::create(document); 257 m_container->appendChild(m_speechButton, IGNORE_EXCEPTION); 258 } 259#endif 260 261 if (shouldHaveSpinButton) { 262 m_innerSpinButton = SpinButtonElement::create(document, *this); 263 m_container->appendChild(m_innerSpinButton, IGNORE_EXCEPTION); 264 } 265} 266 267HTMLElement* TextFieldInputType::containerElement() const 268{ 269 return m_container.get(); 270} 271 272HTMLElement* TextFieldInputType::innerBlockElement() const 273{ 274 return m_innerBlock.get(); 275} 276 277HTMLElement* TextFieldInputType::innerTextElement() const 278{ 279 ASSERT(m_innerText); 280 return m_innerText.get(); 281} 282 283HTMLElement* TextFieldInputType::innerSpinButtonElement() const 284{ 285 return m_innerSpinButton.get(); 286} 287 288#if ENABLE(INPUT_SPEECH) 289HTMLElement* TextFieldInputType::speechButtonElement() const 290{ 291 return m_speechButton.get(); 292} 293#endif 294 295HTMLElement* TextFieldInputType::placeholderElement() const 296{ 297 return m_placeholder.get(); 298} 299 300void TextFieldInputType::destroyShadowSubtree() 301{ 302 InputType::destroyShadowSubtree(); 303 m_innerText.clear(); 304 m_placeholder.clear(); 305 m_innerBlock.clear(); 306#if ENABLE(INPUT_SPEECH) 307 m_speechButton.clear(); 308#endif 309 if (m_innerSpinButton) 310 m_innerSpinButton->removeSpinButtonOwner(); 311 m_innerSpinButton.clear(); 312 m_container.clear(); 313} 314 315void TextFieldInputType::attributeChanged() 316{ 317 // FIXME: Updating the inner text on any attribute update should 318 // be unnecessary. We should figure out what attributes affect. 319 updateInnerTextValue(); 320} 321 322void TextFieldInputType::disabledAttributeChanged() 323{ 324 if (m_innerSpinButton) 325 m_innerSpinButton->releaseCapture(); 326} 327 328void TextFieldInputType::readonlyAttributeChanged() 329{ 330 if (m_innerSpinButton) 331 m_innerSpinButton->releaseCapture(); 332} 333 334bool TextFieldInputType::supportsReadOnly() const 335{ 336 return true; 337} 338 339bool TextFieldInputType::shouldUseInputMethod() const 340{ 341 return true; 342} 343 344static bool isASCIILineBreak(UChar c) 345{ 346 return c == '\r' || c == '\n'; 347} 348 349static String limitLength(const String& string, int maxLength) 350{ 351 unsigned newLength = numCharactersInGraphemeClusters(string, maxLength); 352 for (unsigned i = 0; i < newLength; ++i) { 353 const UChar current = string[i]; 354 if (current < ' ' && current != '\t') { 355 newLength = i; 356 break; 357 } 358 } 359 return string.left(newLength); 360} 361 362String TextFieldInputType::sanitizeValue(const String& proposedValue) const 363{ 364 return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength); 365} 366 367void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event) 368{ 369 // Make sure that the text to be inserted will not violate the maxLength. 370 371 // We use RenderTextControlSingleLine::text() instead of InputElement::value() 372 // because they can be mismatched by sanitizeValue() in 373 // HTMLInputElement::subtreeHasChanged() in some cases. 374 unsigned oldLength = numGraphemeClusters(element()->innerTextValue()); 375 376 // selectionLength represents the selection length of this text field to be 377 // removed by this insertion. 378 // If the text field has no focus, we don't need to take account of the 379 // selection length. The selection is the source of text drag-and-drop in 380 // that case, and nothing in the text field will be removed. 381 unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0; 382 ASSERT(oldLength >= selectionLength); 383 384 // Selected characters will be removed by the next text event. 385 unsigned baseLength = oldLength - selectionLength; 386 unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative. 387 unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0; 388 389 // Truncate the inserted text to avoid violating the maxLength and other constraints. 390 String eventText = event->text(); 391 unsigned textLength = eventText.length(); 392 while (textLength > 0 && isASCIILineBreak(eventText[textLength - 1])) 393 textLength--; 394 eventText.truncate(textLength); 395 eventText.replace("\r\n", " "); 396 eventText.replace('\r', ' '); 397 eventText.replace('\n', ' '); 398 399 event->setText(limitLength(eventText, appendableLength)); 400} 401 402bool TextFieldInputType::shouldRespectListAttribute() 403{ 404 return InputType::themeSupportsDataListUI(this); 405} 406 407void TextFieldInputType::updatePlaceholderText() 408{ 409 if (!supportsPlaceholder()) 410 return; 411 String placeholderText = element()->strippedPlaceholder(); 412 if (placeholderText.isEmpty()) { 413 if (m_placeholder) { 414 m_placeholder->parentNode()->removeChild(m_placeholder.get(), ASSERT_NO_EXCEPTION); 415 m_placeholder.clear(); 416 } 417 return; 418 } 419 if (!m_placeholder) { 420 m_placeholder = HTMLDivElement::create(element()->document()); 421 m_placeholder->setPseudo(AtomicString("-webkit-input-placeholder", AtomicString::ConstructFromLiteral)); 422 element()->userAgentShadowRoot()->insertBefore(m_placeholder, m_container ? m_container->nextSibling() : innerTextElement()->nextSibling(), ASSERT_NO_EXCEPTION); 423 } 424 m_placeholder->setInnerText(placeholderText, ASSERT_NO_EXCEPTION); 425 element()->fixPlaceholderRenderer(m_placeholder.get(), m_container ? m_container.get() : m_innerText.get()); 426} 427 428void TextFieldInputType::attach() 429{ 430 InputType::attach(); 431 // If container exists, the container should not have any content data. 432 ASSERT(!m_container || !m_container->renderStyle() || !m_container->renderStyle()->hasContent()); 433 434 element()->fixPlaceholderRenderer(m_placeholder.get(), m_container ? m_container.get() : m_innerText.get()); 435} 436 437bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const 438{ 439 InputType::appendFormData(list, multipart); 440 const AtomicString& dirnameAttrValue = element()->fastGetAttribute(dirnameAttr); 441 if (!dirnameAttrValue.isNull()) 442 list.appendData(dirnameAttrValue, element()->directionForFormData()); 443 return true; 444} 445 446String TextFieldInputType::convertFromVisibleValue(const String& visibleValue) const 447{ 448 return visibleValue; 449} 450 451void TextFieldInputType::subtreeHasChanged() 452{ 453 ASSERT(element()->renderer()); 454 455 bool wasChanged = element()->wasChangedSinceLastFormControlChangeEvent(); 456 element()->setChangedSinceLastFormControlChangeEvent(true); 457 458 // We don't need to call sanitizeUserInputValue() function here because 459 // HTMLInputElement::handleBeforeTextInsertedEvent() has already called 460 // sanitizeUserInputValue(). 461 // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent. 462 element()->setValueFromRenderer(sanitizeValue(convertFromVisibleValue(element()->innerTextValue()))); 463 element()->updatePlaceholderVisibility(false); 464 // Recalc for :invalid change. 465 element()->setNeedsStyleRecalc(); 466 467 didSetValueByUserEdit(wasChanged ? ValueChangeStateChanged : ValueChangeStateNone); 468} 469 470void TextFieldInputType::didSetValueByUserEdit(ValueChangeState state) 471{ 472 if (!element()->focused()) 473 return; 474 if (Frame* frame = element()->document()->frame()) { 475 if (state == ValueChangeStateNone) 476 frame->editor().textFieldDidBeginEditing(element()); 477 frame->editor().textDidChangeInTextField(element()); 478 } 479} 480 481void TextFieldInputType::spinButtonStepDown() 482{ 483 stepUpFromRenderer(-1); 484} 485 486void TextFieldInputType::spinButtonStepUp() 487{ 488 stepUpFromRenderer(1); 489} 490 491void TextFieldInputType::updateInnerTextValue() 492{ 493 if (!element()->suggestedValue().isNull()) { 494 element()->setInnerTextValue(element()->suggestedValue()); 495 element()->updatePlaceholderVisibility(false); 496 } else if (!element()->formControlValueMatchesRenderer()) { 497 // Update the renderer value if the formControlValueMatchesRenderer() flag is false. 498 // It protects an unacceptable renderer value from being overwritten with the DOM value. 499 element()->setInnerTextValue(visibleValue()); 500 element()->updatePlaceholderVisibility(false); 501 } 502} 503 504void TextFieldInputType::focusAndSelectSpinButtonOwner() 505{ 506 RefPtr<HTMLInputElement> input(element()); 507 input->focus(); 508 input->select(); 509} 510 511bool TextFieldInputType::shouldSpinButtonRespondToMouseEvents() 512{ 513 return !element()->isDisabledOrReadOnly(); 514} 515 516bool TextFieldInputType::shouldSpinButtonRespondToWheelEvents() 517{ 518 return shouldSpinButtonRespondToMouseEvents() && element()->focused(); 519} 520 521} // namespace WebCore 522