1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 4 * (C) 2001 Dirk Mueller (mueller (at) kde.org) 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap (at) nypop.com) 7 * Copyright (C) 2007 Samuel Weinig (sam (at) webkit.org) 8 * Copyright (C) 2010 Google Inc. All rights reserved. 9 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 10 * Copyright (C) 2012 Samsung Electronics. All rights reserved. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU Library General Public 14 * License as published by the Free Software Foundation; either 15 * version 2 of the License, or (at your option) any later version. 16 * 17 * This library is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 * Library General Public License for more details. 21 * 22 * You should have received a copy of the GNU Library General Public License 23 * along with this library; see the file COPYING.LIB. If not, write to 24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 * Boston, MA 02110-1301, USA. 26 * 27 */ 28 29 #include "config.h" 30 #include "core/html/HTMLInputElement.h" 31 32 #include "CSSPropertyNames.h" 33 #include "HTMLNames.h" 34 #include "RuntimeEnabledFeatures.h" 35 #include "bindings/v8/ExceptionMessages.h" 36 #include "bindings/v8/ExceptionState.h" 37 #include "bindings/v8/ScriptEventListener.h" 38 #include "core/accessibility/AXObjectCache.h" 39 #include "core/dom/Document.h" 40 #include "core/dom/ExceptionCode.h" 41 #include "core/dom/IdTargetObserver.h" 42 #include "core/dom/shadow/ElementShadow.h" 43 #include "core/dom/shadow/InsertionPoint.h" 44 #include "core/dom/shadow/ShadowRoot.h" 45 #include "core/editing/FrameSelection.h" 46 #include "core/editing/SpellChecker.h" 47 #include "core/events/BeforeTextInsertedEvent.h" 48 #include "core/events/KeyboardEvent.h" 49 #include "core/events/MouseEvent.h" 50 #include "core/events/ScopedEventQueue.h" 51 #include "core/events/ThreadLocalEventNames.h" 52 #include "core/events/TouchEvent.h" 53 #include "core/fileapi/FileList.h" 54 #include "core/frame/Frame.h" 55 #include "core/frame/FrameView.h" 56 #include "core/html/HTMLCollection.h" 57 #include "core/html/HTMLDataListElement.h" 58 #include "core/html/HTMLFormElement.h" 59 #include "core/html/HTMLImageLoader.h" 60 #include "core/html/HTMLOptionElement.h" 61 #include "core/html/forms/ColorInputType.h" 62 #include "core/html/forms/FileInputType.h" 63 #include "core/html/forms/FormController.h" 64 #include "core/html/forms/InputType.h" 65 #include "core/html/forms/SearchInputType.h" 66 #include "core/html/parser/HTMLParserIdioms.h" 67 #include "core/html/shadow/ShadowElementNames.h" 68 #include "core/frame/UseCounter.h" 69 #include "core/page/Chrome.h" 70 #include "core/page/ChromeClient.h" 71 #include "core/page/Page.h" 72 #include "core/rendering/RenderTextControlSingleLine.h" 73 #include "core/rendering/RenderTheme.h" 74 #include "platform/DateTimeChooser.h" 75 #include "platform/Language.h" 76 #include "platform/PlatformMouseEvent.h" 77 #include "platform/text/PlatformLocale.h" 78 #include "wtf/MathExtras.h" 79 80 using namespace std; 81 82 namespace WebCore { 83 84 using namespace HTMLNames; 85 86 class ListAttributeTargetObserver : IdTargetObserver { 87 WTF_MAKE_FAST_ALLOCATED; 88 public: 89 static PassOwnPtr<ListAttributeTargetObserver> create(const AtomicString& id, HTMLInputElement*); 90 virtual void idTargetChanged() OVERRIDE; 91 92 private: 93 ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement*); 94 95 HTMLInputElement* m_element; 96 }; 97 98 // FIXME: According to HTML4, the length attribute's value can be arbitrarily 99 // large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things 100 // get rather sluggish when a text field has a larger number of characters than 101 // this, even when just clicking in the text field. 102 const int HTMLInputElement::maximumLength = 524288; 103 const int defaultSize = 20; 104 const int maxSavedResults = 256; 105 106 HTMLInputElement::HTMLInputElement(Document& document, HTMLFormElement* form, bool createdByParser) 107 : HTMLTextFormControlElement(inputTag, document, form) 108 , m_size(defaultSize) 109 , m_maxLength(maximumLength) 110 , m_maxResults(-1) 111 , m_isChecked(false) 112 , m_reflectsCheckedAttribute(true) 113 , m_isIndeterminate(false) 114 , m_hasType(false) 115 , m_isActivatedSubmit(false) 116 , m_autocomplete(Uninitialized) 117 , m_hasNonEmptyList(false) 118 , m_stateRestored(false) 119 , m_parsingInProgress(createdByParser) 120 , m_valueAttributeWasUpdatedAfterParsing(false) 121 , m_canReceiveDroppedFiles(false) 122 , m_hasTouchEventHandler(false) 123 , m_inputType(InputType::createText(*this)) 124 , m_inputTypeView(m_inputType) 125 { 126 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 127 setHasCustomStyleCallbacks(); 128 #endif 129 ScriptWrappable::init(this); 130 } 131 132 PassRefPtr<HTMLInputElement> HTMLInputElement::create(Document& document, HTMLFormElement* form, bool createdByParser) 133 { 134 RefPtr<HTMLInputElement> inputElement = adoptRef(new HTMLInputElement(document, form, createdByParser)); 135 inputElement->ensureUserAgentShadowRoot(); 136 return inputElement.release(); 137 } 138 139 HTMLImageLoader* HTMLInputElement::imageLoader() 140 { 141 if (!m_imageLoader) 142 m_imageLoader = adoptPtr(new HTMLImageLoader(this)); 143 return m_imageLoader.get(); 144 } 145 146 void HTMLInputElement::didAddUserAgentShadowRoot(ShadowRoot&) 147 { 148 m_inputTypeView->createShadowSubtree(); 149 } 150 151 void HTMLInputElement::didAddShadowRoot(ShadowRoot& root) 152 { 153 if (!root.isOldestAuthorShadowRoot()) 154 return; 155 m_inputTypeView->destroyShadowSubtree(); 156 m_inputTypeView = InputTypeView::create(*this); 157 lazyReattachIfAttached(); 158 } 159 160 HTMLInputElement::~HTMLInputElement() 161 { 162 // Need to remove form association while this is still an HTMLInputElement 163 // so that virtual functions are called correctly. 164 setForm(0); 165 // setForm(0) may register this to a document-level radio button group. 166 // We should unregister it to avoid accessing a deleted object. 167 if (isRadioButton()) 168 document().formController()->checkedRadioButtons().removeButton(this); 169 if (m_hasTouchEventHandler) 170 document().didRemoveEventTargetNode(this); 171 } 172 173 const AtomicString& HTMLInputElement::name() const 174 { 175 return m_name.isNull() ? emptyAtom : m_name; 176 } 177 178 Vector<FileChooserFileInfo> HTMLInputElement::filesFromFileInputFormControlState(const FormControlState& state) 179 { 180 return FileInputType::filesFromFormControlState(state); 181 } 182 183 HTMLElement* HTMLInputElement::passwordGeneratorButtonElement() const 184 { 185 return toHTMLElement(userAgentShadowRoot()->getElementById(ShadowElementNames::passwordGenerator())); 186 } 187 188 bool HTMLInputElement::shouldAutocomplete() const 189 { 190 if (m_autocomplete != Uninitialized) 191 return m_autocomplete == On; 192 return HTMLTextFormControlElement::shouldAutocomplete(); 193 } 194 195 bool HTMLInputElement::isValidValue(const String& value) const 196 { 197 if (!m_inputType->canSetStringValue()) { 198 ASSERT_NOT_REACHED(); 199 return false; 200 } 201 return !m_inputType->typeMismatchFor(value) 202 && !m_inputType->stepMismatch(value) 203 && !m_inputType->rangeUnderflow(value) 204 && !m_inputType->rangeOverflow(value) 205 && !tooLong(value, IgnoreDirtyFlag) 206 && !m_inputType->patternMismatch(value) 207 && !m_inputType->valueMissing(value); 208 } 209 210 bool HTMLInputElement::tooLong() const 211 { 212 return willValidate() && tooLong(value(), CheckDirtyFlag); 213 } 214 215 bool HTMLInputElement::typeMismatch() const 216 { 217 return willValidate() && m_inputType->typeMismatch(); 218 } 219 220 bool HTMLInputElement::valueMissing() const 221 { 222 return willValidate() && m_inputType->valueMissing(value()); 223 } 224 225 bool HTMLInputElement::hasBadInput() const 226 { 227 return willValidate() && m_inputType->hasBadInput(); 228 } 229 230 bool HTMLInputElement::patternMismatch() const 231 { 232 return willValidate() && m_inputType->patternMismatch(value()); 233 } 234 235 bool HTMLInputElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const 236 { 237 // We use isTextType() instead of supportsMaxLength() because of the 238 // 'virtual' overhead. 239 if (!isTextType()) 240 return false; 241 int max = maxLength(); 242 if (max < 0) 243 return false; 244 if (check == CheckDirtyFlag) { 245 // Return false for the default value or a value set by a script even if 246 // it is longer than maxLength. 247 if (!hasDirtyValue() || !lastChangeWasUserEdit()) 248 return false; 249 } 250 return value.length() > static_cast<unsigned>(max); 251 } 252 253 bool HTMLInputElement::rangeUnderflow() const 254 { 255 return willValidate() && m_inputType->rangeUnderflow(value()); 256 } 257 258 bool HTMLInputElement::rangeOverflow() const 259 { 260 return willValidate() && m_inputType->rangeOverflow(value()); 261 } 262 263 String HTMLInputElement::validationMessage() const 264 { 265 if (!willValidate()) 266 return String(); 267 268 if (customError()) 269 return customValidationMessage(); 270 271 return m_inputType->validationMessage(); 272 } 273 274 double HTMLInputElement::minimum() const 275 { 276 return m_inputType->minimum(); 277 } 278 279 double HTMLInputElement::maximum() const 280 { 281 return m_inputType->maximum(); 282 } 283 284 bool HTMLInputElement::stepMismatch() const 285 { 286 return willValidate() && m_inputType->stepMismatch(value()); 287 } 288 289 bool HTMLInputElement::getAllowedValueStep(Decimal* step) const 290 { 291 return m_inputType->getAllowedValueStep(step); 292 } 293 294 StepRange HTMLInputElement::createStepRange(AnyStepHandling anyStepHandling) const 295 { 296 return m_inputType->createStepRange(anyStepHandling); 297 } 298 299 Decimal HTMLInputElement::findClosestTickMarkValue(const Decimal& value) 300 { 301 return m_inputType->findClosestTickMarkValue(value); 302 } 303 304 void HTMLInputElement::stepUp(int n, ExceptionState& exceptionState) 305 { 306 m_inputType->stepUp(n, exceptionState); 307 } 308 309 void HTMLInputElement::stepDown(int n, ExceptionState& exceptionState) 310 { 311 m_inputType->stepUp(-n, exceptionState); 312 } 313 314 void HTMLInputElement::blur() 315 { 316 m_inputTypeView->blur(); 317 } 318 319 void HTMLInputElement::defaultBlur() 320 { 321 HTMLTextFormControlElement::blur(); 322 } 323 324 bool HTMLInputElement::hasCustomFocusLogic() const 325 { 326 return m_inputTypeView->hasCustomFocusLogic(); 327 } 328 329 bool HTMLInputElement::isKeyboardFocusable() const 330 { 331 return m_inputType->isKeyboardFocusable(); 332 } 333 334 bool HTMLInputElement::shouldShowFocusRingOnMouseFocus() const 335 { 336 return m_inputType->shouldShowFocusRingOnMouseFocus(); 337 } 338 339 void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection) 340 { 341 if (isTextField()) { 342 if (!restorePreviousSelection || !hasCachedSelection()) 343 select(); 344 else 345 restoreCachedSelection(); 346 if (document().frame()) 347 document().frame()->selection().revealSelection(); 348 } else 349 HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection); 350 } 351 352 void HTMLInputElement::beginEditing() 353 { 354 if (!isTextField()) 355 return; 356 357 if (Frame* frame = document().frame()) 358 frame->spellChecker().didBeginEditing(this); 359 } 360 361 void HTMLInputElement::endEditing() 362 { 363 if (!isTextField()) 364 return; 365 366 if (Frame* frame = document().frame()) { 367 frame->spellChecker().didEndEditingOnTextField(this); 368 if (Page* page = frame->page()) 369 page->chrome().client().didEndEditingOnTextField(*this); 370 } 371 } 372 373 bool HTMLInputElement::shouldUseInputMethod() 374 { 375 return m_inputType->shouldUseInputMethod(); 376 } 377 378 void HTMLInputElement::handleFocusEvent(Element* oldFocusedElement, FocusDirection direction) 379 { 380 m_inputTypeView->handleFocusEvent(oldFocusedElement, direction); 381 m_inputType->enableSecureTextInput(); 382 } 383 384 void HTMLInputElement::handleBlurEvent() 385 { 386 m_inputType->disableSecureTextInput(); 387 m_inputTypeView->handleBlurEvent(); 388 } 389 390 void HTMLInputElement::setType(const AtomicString& type) 391 { 392 // FIXME: This should just call setAttribute. No reason to handle the empty string specially. 393 // We should write a test case to show that setting to the empty string does not remove the 394 // attribute in other browsers and then fix this. Note that setting to null *does* remove 395 // the attribute and setAttribute implements that. 396 if (type.isEmpty()) 397 removeAttribute(typeAttr); 398 else 399 setAttribute(typeAttr, type); 400 } 401 402 void HTMLInputElement::updateType() 403 { 404 const AtomicString& newTypeName = InputType::normalizeTypeName(fastGetAttribute(typeAttr)); 405 bool hadType = m_hasType; 406 m_hasType = true; 407 if (m_inputType->formControlType() == newTypeName) 408 return; 409 410 if (hadType && !InputType::canChangeFromAnotherType(newTypeName)) { 411 // Set the attribute back to the old value. 412 // Useful in case we were called from inside parseAttribute. 413 setAttribute(typeAttr, type()); 414 return; 415 } 416 417 RefPtr<InputType> newType = InputType::create(*this, newTypeName); 418 removeFromRadioButtonGroup(); 419 420 bool didStoreValue = m_inputType->storesValueSeparateFromAttribute(); 421 bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes(); 422 423 m_inputTypeView->destroyShadowSubtree(); 424 lazyReattachIfAttached(); 425 426 m_inputType = newType.release(); 427 if (hasAuthorShadowRoot()) 428 m_inputTypeView = InputTypeView::create(*this); 429 else 430 m_inputTypeView = m_inputType; 431 m_inputTypeView->createShadowSubtree(); 432 433 bool hasTouchEventHandler = m_inputTypeView->hasTouchEventHandler(); 434 if (hasTouchEventHandler != m_hasTouchEventHandler) { 435 if (hasTouchEventHandler) 436 document().didAddTouchEventHandler(this); 437 else 438 document().didRemoveTouchEventHandler(this); 439 m_hasTouchEventHandler = hasTouchEventHandler; 440 } 441 442 setNeedsWillValidateCheck(); 443 444 bool willStoreValue = m_inputType->storesValueSeparateFromAttribute(); 445 446 if (didStoreValue && !willStoreValue && hasDirtyValue()) { 447 setAttribute(valueAttr, AtomicString(m_valueIfDirty)); 448 m_valueIfDirty = String(); 449 } 450 if (!didStoreValue && willStoreValue) { 451 AtomicString valueString = fastGetAttribute(valueAttr); 452 m_valueIfDirty = sanitizeValue(valueString); 453 } else 454 updateValueIfNeeded(); 455 456 setFormControlValueMatchesRenderer(false); 457 m_inputTypeView->updateView(); 458 459 if (didRespectHeightAndWidth != m_inputType->shouldRespectHeightAndWidthAttributes()) { 460 ASSERT(elementData()); 461 if (const Attribute* height = getAttributeItem(heightAttr)) 462 attributeChanged(heightAttr, height->value()); 463 if (const Attribute* width = getAttributeItem(widthAttr)) 464 attributeChanged(widthAttr, width->value()); 465 if (const Attribute* align = getAttributeItem(alignAttr)) 466 attributeChanged(alignAttr, align->value()); 467 } 468 469 if (document().focusedElement() == this) 470 document().updateFocusAppearanceSoon(true /* restore selection */); 471 472 setChangedSinceLastFormControlChangeEvent(false); 473 474 addToRadioButtonGroup(); 475 476 setNeedsValidityCheck(); 477 notifyFormStateChanged(); 478 } 479 480 void HTMLInputElement::subtreeHasChanged() 481 { 482 m_inputTypeView->subtreeHasChanged(); 483 // When typing in an input field, childrenChanged is not called, so we need to force the directionality check. 484 calculateAndAdjustDirectionality(); 485 } 486 487 const AtomicString& HTMLInputElement::formControlType() const 488 { 489 return m_inputType->formControlType(); 490 } 491 492 bool HTMLInputElement::shouldSaveAndRestoreFormControlState() const 493 { 494 if (!m_inputType->shouldSaveAndRestoreFormControlState()) 495 return false; 496 return HTMLTextFormControlElement::shouldSaveAndRestoreFormControlState(); 497 } 498 499 FormControlState HTMLInputElement::saveFormControlState() const 500 { 501 return m_inputType->saveFormControlState(); 502 } 503 504 void HTMLInputElement::restoreFormControlState(const FormControlState& state) 505 { 506 m_inputType->restoreFormControlState(state); 507 m_stateRestored = true; 508 } 509 510 bool HTMLInputElement::canStartSelection() const 511 { 512 if (!isTextField()) 513 return false; 514 return HTMLTextFormControlElement::canStartSelection(); 515 } 516 517 bool HTMLInputElement::canHaveSelection() const 518 { 519 return isTextField(); 520 } 521 522 int HTMLInputElement::selectionStartForBinding(ExceptionState& exceptionState) const 523 { 524 if (!m_inputType->supportsSelectionAPI()) { 525 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 526 return 0; 527 } 528 return HTMLTextFormControlElement::selectionStart(); 529 } 530 531 int HTMLInputElement::selectionEndForBinding(ExceptionState& exceptionState) const 532 { 533 if (!m_inputType->supportsSelectionAPI()) { 534 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 535 return 0; 536 } 537 return HTMLTextFormControlElement::selectionEnd(); 538 } 539 540 String HTMLInputElement::selectionDirectionForBinding(ExceptionState& exceptionState) const 541 { 542 if (!m_inputType->supportsSelectionAPI()) { 543 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 544 return String(); 545 } 546 return HTMLTextFormControlElement::selectionDirection(); 547 } 548 549 void HTMLInputElement::setSelectionStartForBinding(int start, ExceptionState& exceptionState) 550 { 551 if (!m_inputType->supportsSelectionAPI()) { 552 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 553 return; 554 } 555 HTMLTextFormControlElement::setSelectionStart(start); 556 } 557 558 void HTMLInputElement::setSelectionEndForBinding(int end, ExceptionState& exceptionState) 559 { 560 if (!m_inputType->supportsSelectionAPI()) { 561 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 562 return; 563 } 564 HTMLTextFormControlElement::setSelectionEnd(end); 565 } 566 567 void HTMLInputElement::setSelectionDirectionForBinding(const String& direction, ExceptionState& exceptionState) 568 { 569 if (!m_inputType->supportsSelectionAPI()) { 570 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 571 return; 572 } 573 HTMLTextFormControlElement::setSelectionDirection(direction); 574 } 575 576 void HTMLInputElement::setSelectionRangeForBinding(int start, int end, ExceptionState& exceptionState) 577 { 578 if (!m_inputType->supportsSelectionAPI()) { 579 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 580 return; 581 } 582 HTMLTextFormControlElement::setSelectionRange(start, end); 583 } 584 585 void HTMLInputElement::setSelectionRangeForBinding(int start, int end, const String& direction, ExceptionState& exceptionState) 586 { 587 if (!m_inputType->supportsSelectionAPI()) { 588 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 589 return; 590 } 591 HTMLTextFormControlElement::setSelectionRange(start, end, direction); 592 } 593 594 void HTMLInputElement::accessKeyAction(bool sendMouseEvents) 595 { 596 m_inputType->accessKeyAction(sendMouseEvents); 597 } 598 599 bool HTMLInputElement::isPresentationAttribute(const QualifiedName& name) const 600 { 601 if (name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == widthAttr || name == heightAttr || (name == borderAttr && isImageButton())) 602 return true; 603 return HTMLTextFormControlElement::isPresentationAttribute(name); 604 } 605 606 void HTMLInputElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 607 { 608 if (name == vspaceAttr) { 609 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); 610 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); 611 } else if (name == hspaceAttr) { 612 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); 613 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); 614 } else if (name == alignAttr) { 615 if (m_inputType->shouldRespectAlignAttribute()) 616 applyAlignmentAttributeToStyle(value, style); 617 } else if (name == widthAttr) { 618 if (m_inputType->shouldRespectHeightAndWidthAttributes()) 619 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 620 } else if (name == heightAttr) { 621 if (m_inputType->shouldRespectHeightAndWidthAttributes()) 622 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 623 } else if (name == borderAttr && isImageButton()) 624 applyBorderAttributeToStyle(value, style); 625 else 626 HTMLTextFormControlElement::collectStyleForPresentationAttribute(name, value, style); 627 } 628 629 void HTMLInputElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 630 { 631 if (name == nameAttr) { 632 removeFromRadioButtonGroup(); 633 m_name = value; 634 addToRadioButtonGroup(); 635 HTMLTextFormControlElement::parseAttribute(name, value); 636 } else if (name == autocompleteAttr) { 637 if (equalIgnoringCase(value, "off")) 638 m_autocomplete = Off; 639 else { 640 if (value.isEmpty()) 641 m_autocomplete = Uninitialized; 642 else 643 m_autocomplete = On; 644 } 645 } else if (name == typeAttr) 646 updateType(); 647 else if (name == valueAttr) { 648 // We only need to setChanged if the form is looking at the default value right now. 649 if (!hasDirtyValue()) { 650 updatePlaceholderVisibility(false); 651 setNeedsStyleRecalc(); 652 } 653 setFormControlValueMatchesRenderer(false); 654 setNeedsValidityCheck(); 655 m_valueAttributeWasUpdatedAfterParsing = !m_parsingInProgress; 656 m_inputTypeView->valueAttributeChanged(); 657 } else if (name == checkedAttr) { 658 // Another radio button in the same group might be checked by state 659 // restore. We shouldn't call setChecked() even if this has the checked 660 // attribute. So, delay the setChecked() call until 661 // finishParsingChildren() is called if parsing is in progress. 662 if (!m_parsingInProgress && m_reflectsCheckedAttribute) { 663 setChecked(!value.isNull()); 664 m_reflectsCheckedAttribute = true; 665 } 666 } else if (name == maxlengthAttr) 667 parseMaxLengthAttribute(value); 668 else if (name == sizeAttr) { 669 int oldSize = m_size; 670 int valueAsInteger = value.toInt(); 671 m_size = valueAsInteger > 0 ? valueAsInteger : defaultSize; 672 if (m_size != oldSize && renderer()) 673 renderer()->setNeedsLayoutAndPrefWidthsRecalc(); 674 } else if (name == altAttr) 675 m_inputTypeView->altAttributeChanged(); 676 else if (name == srcAttr) 677 m_inputTypeView->srcAttributeChanged(); 678 else if (name == usemapAttr || name == accesskeyAttr) { 679 // FIXME: ignore for the moment 680 } else if (name == onsearchAttr) { 681 // Search field and slider attributes all just cause updateFromElement to be called through style recalcing. 682 setAttributeEventListener(EventTypeNames::search, createAttributeEventListener(this, name, value)); 683 } else if (name == resultsAttr) { 684 int oldResults = m_maxResults; 685 m_maxResults = !value.isNull() ? std::min(value.toInt(), maxSavedResults) : -1; 686 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right 687 // time to relayout for this change. 688 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0)) 689 lazyReattachIfAttached(); 690 setNeedsStyleRecalc(); 691 UseCounter::count(document(), UseCounter::ResultsAttribute); 692 } else if (name == incrementalAttr) { 693 setNeedsStyleRecalc(); 694 UseCounter::count(document(), UseCounter::IncrementalAttribute); 695 } else if (name == minAttr) { 696 m_inputTypeView->minOrMaxAttributeChanged(); 697 m_inputType->sanitizeValueInResponseToMinOrMaxAttributeChange(); 698 setNeedsValidityCheck(); 699 UseCounter::count(document(), UseCounter::MinAttribute); 700 } else if (name == maxAttr) { 701 m_inputTypeView->minOrMaxAttributeChanged(); 702 setNeedsValidityCheck(); 703 UseCounter::count(document(), UseCounter::MaxAttribute); 704 } else if (name == multipleAttr) { 705 m_inputTypeView->multipleAttributeChanged(); 706 setNeedsValidityCheck(); 707 } else if (name == stepAttr) { 708 m_inputTypeView->stepAttributeChanged(); 709 setNeedsValidityCheck(); 710 UseCounter::count(document(), UseCounter::StepAttribute); 711 } else if (name == patternAttr) { 712 setNeedsValidityCheck(); 713 UseCounter::count(document(), UseCounter::PatternAttribute); 714 } else if (name == precisionAttr) { 715 setNeedsValidityCheck(); 716 UseCounter::count(document(), UseCounter::PrecisionAttribute); 717 } else if (name == disabledAttr) { 718 HTMLTextFormControlElement::parseAttribute(name, value); 719 m_inputTypeView->disabledAttributeChanged(); 720 } else if (name == readonlyAttr) { 721 HTMLTextFormControlElement::parseAttribute(name, value); 722 m_inputTypeView->readonlyAttributeChanged(); 723 } else if (name == listAttr) { 724 m_hasNonEmptyList = !value.isEmpty(); 725 if (m_hasNonEmptyList) { 726 resetListAttributeTargetObserver(); 727 listAttributeTargetChanged(); 728 } 729 UseCounter::count(document(), UseCounter::ListAttribute); 730 } 731 #if ENABLE(INPUT_SPEECH) 732 else if (name == webkitspeechAttr) { 733 if (RuntimeEnabledFeatures::speechInputEnabled() && m_inputType->shouldRespectSpeechAttribute()) { 734 // This renderer and its children have quite different layouts and 735 // styles depending on whether the speech button is visible or 736 // not. So we reset the whole thing and recreate to get the right 737 // styles and layout. 738 m_inputTypeView->destroyShadowSubtree(); 739 lazyReattachIfAttached(); 740 m_inputTypeView->createShadowSubtree(); 741 setFormControlValueMatchesRenderer(false); 742 } 743 UseCounter::count(document(), UseCounter::PrefixedSpeechAttribute); 744 } else if (name == onwebkitspeechchangeAttr) 745 setAttributeEventListener(EventTypeNames::webkitspeechchange, createAttributeEventListener(this, name, value)); 746 #endif 747 else if (name == webkitdirectoryAttr) { 748 HTMLTextFormControlElement::parseAttribute(name, value); 749 UseCounter::count(document(), UseCounter::PrefixedDirectoryAttribute); 750 } 751 else 752 HTMLTextFormControlElement::parseAttribute(name, value); 753 m_inputTypeView->attributeChanged(); 754 } 755 756 void HTMLInputElement::finishParsingChildren() 757 { 758 m_parsingInProgress = false; 759 HTMLTextFormControlElement::finishParsingChildren(); 760 if (!m_stateRestored) { 761 bool checked = hasAttribute(checkedAttr); 762 if (checked) 763 setChecked(checked); 764 m_reflectsCheckedAttribute = true; 765 } 766 } 767 768 bool HTMLInputElement::rendererIsNeeded(const RenderStyle& style) 769 { 770 return m_inputType->rendererIsNeeded() && HTMLTextFormControlElement::rendererIsNeeded(style); 771 } 772 773 RenderObject* HTMLInputElement::createRenderer(RenderStyle* style) 774 { 775 return m_inputTypeView->createRenderer(style); 776 } 777 778 void HTMLInputElement::attach(const AttachContext& context) 779 { 780 if (!m_hasType) 781 updateType(); 782 783 HTMLTextFormControlElement::attach(context); 784 785 m_inputTypeView->startResourceLoading(); 786 m_inputType->countUsage(); 787 788 if (document().focusedElement() == this) 789 document().updateFocusAppearanceSoon(true /* restore selection */); 790 } 791 792 void HTMLInputElement::detach(const AttachContext& context) 793 { 794 HTMLTextFormControlElement::detach(context); 795 setFormControlValueMatchesRenderer(false); 796 m_inputTypeView->closePopupView(); 797 } 798 799 String HTMLInputElement::altText() const 800 { 801 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen 802 // also heavily discussed by Hixie on bugzilla 803 // note this is intentionally different to HTMLImageElement::altText() 804 String alt = fastGetAttribute(altAttr); 805 // fall back to title attribute 806 if (alt.isNull()) 807 alt = getAttribute(titleAttr); 808 if (alt.isNull()) 809 alt = getAttribute(valueAttr); 810 if (alt.isEmpty()) 811 alt = locale().queryString(blink::WebLocalizedString::InputElementAltText); 812 return alt; 813 } 814 815 bool HTMLInputElement::canBeSuccessfulSubmitButton() const 816 { 817 return m_inputType->canBeSuccessfulSubmitButton(); 818 } 819 820 bool HTMLInputElement::isActivatedSubmit() const 821 { 822 return m_isActivatedSubmit; 823 } 824 825 void HTMLInputElement::setActivatedSubmit(bool flag) 826 { 827 m_isActivatedSubmit = flag; 828 } 829 830 bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart) 831 { 832 return m_inputType->isFormDataAppendable() && m_inputType->appendFormData(encoding, multipart); 833 } 834 835 String HTMLInputElement::resultForDialogSubmit() 836 { 837 return m_inputType->resultForDialogSubmit(); 838 } 839 840 void HTMLInputElement::resetImpl() 841 { 842 if (m_inputType->storesValueSeparateFromAttribute()) 843 setValue(String()); 844 845 setChecked(hasAttribute(checkedAttr)); 846 m_reflectsCheckedAttribute = true; 847 } 848 849 bool HTMLInputElement::isTextField() const 850 { 851 return m_inputType->isTextField(); 852 } 853 854 bool HTMLInputElement::isTextType() const 855 { 856 return m_inputType->isTextType(); 857 } 858 859 void HTMLInputElement::setChecked(bool nowChecked, TextFieldEventBehavior eventBehavior) 860 { 861 if (checked() == nowChecked) 862 return; 863 864 m_reflectsCheckedAttribute = false; 865 m_isChecked = nowChecked; 866 setNeedsStyleRecalc(); 867 868 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 869 buttons->updateCheckedState(this); 870 if (renderer() && renderer()->style()->hasAppearance()) 871 RenderTheme::theme().stateChanged(renderer(), CheckedState); 872 873 setNeedsValidityCheck(); 874 875 // Ideally we'd do this from the render tree (matching 876 // RenderTextView), but it's not possible to do it at the moment 877 // because of the way the code is structured. 878 if (renderer()) { 879 if (AXObjectCache* cache = renderer()->document().existingAXObjectCache()) 880 cache->checkedStateChanged(this); 881 } 882 883 // Only send a change event for items in the document (avoid firing during 884 // parsing) and don't send a change event for a radio button that's getting 885 // unchecked to match other browsers. DOM is not a useful standard for this 886 // because it says only to fire change events at "lose focus" time, which is 887 // definitely wrong in practice for these types of elements. 888 if (eventBehavior != DispatchNoEvent && inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) { 889 setTextAsOfLastFormControlChangeEvent(String()); 890 dispatchFormControlChangeEvent(); 891 } 892 893 didAffectSelector(AffectedSelectorChecked); 894 } 895 896 void HTMLInputElement::setIndeterminate(bool newValue) 897 { 898 if (indeterminate() == newValue) 899 return; 900 901 m_isIndeterminate = newValue; 902 903 didAffectSelector(AffectedSelectorIndeterminate); 904 905 if (renderer() && renderer()->style()->hasAppearance()) 906 RenderTheme::theme().stateChanged(renderer(), CheckedState); 907 } 908 909 int HTMLInputElement::size() const 910 { 911 return m_size; 912 } 913 914 bool HTMLInputElement::sizeShouldIncludeDecoration(int& preferredSize) const 915 { 916 return m_inputTypeView->sizeShouldIncludeDecoration(defaultSize, preferredSize); 917 } 918 919 void HTMLInputElement::copyNonAttributePropertiesFromElement(const Element& source) 920 { 921 const HTMLInputElement& sourceElement = static_cast<const HTMLInputElement&>(source); 922 923 m_valueIfDirty = sourceElement.m_valueIfDirty; 924 setChecked(sourceElement.m_isChecked); 925 m_reflectsCheckedAttribute = sourceElement.m_reflectsCheckedAttribute; 926 m_isIndeterminate = sourceElement.m_isIndeterminate; 927 928 HTMLTextFormControlElement::copyNonAttributePropertiesFromElement(source); 929 930 setFormControlValueMatchesRenderer(false); 931 m_inputTypeView->updateView(); 932 } 933 934 String HTMLInputElement::value() const 935 { 936 String value; 937 if (m_inputType->getTypeSpecificValue(value)) 938 return value; 939 940 value = m_valueIfDirty; 941 if (!value.isNull()) 942 return value; 943 944 AtomicString valueString = fastGetAttribute(valueAttr); 945 value = sanitizeValue(valueString); 946 if (!value.isNull()) 947 return value; 948 949 return m_inputType->fallbackValue(); 950 } 951 952 String HTMLInputElement::valueWithDefault() const 953 { 954 String value = this->value(); 955 if (!value.isNull()) 956 return value; 957 958 return m_inputType->defaultValue(); 959 } 960 961 void HTMLInputElement::setValueForUser(const String& value) 962 { 963 // Call setValue and make it send a change event. 964 setValue(value, DispatchChangeEvent); 965 } 966 967 const String& HTMLInputElement::suggestedValue() const 968 { 969 return m_suggestedValue; 970 } 971 972 void HTMLInputElement::setSuggestedValue(const String& value) 973 { 974 if (!m_inputType->canSetSuggestedValue()) 975 return; 976 setFormControlValueMatchesRenderer(false); 977 m_suggestedValue = sanitizeValue(value); 978 setNeedsStyleRecalc(); 979 m_inputTypeView->updateView(); 980 } 981 982 void HTMLInputElement::setEditingValue(const String& value) 983 { 984 if (!renderer() || !isTextField()) 985 return; 986 setInnerTextValue(value); 987 subtreeHasChanged(); 988 989 unsigned max = value.length(); 990 if (focused()) 991 setSelectionRange(max, max); 992 else 993 cacheSelectionInResponseToSetValue(max); 994 995 dispatchInputEvent(); 996 } 997 998 void HTMLInputElement::setValue(const String& value, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior) 999 { 1000 if (isFileUpload() && !value.isEmpty()) { 1001 exceptionState.throwDOMException(InvalidStateError, "This input element accepts a filename, which may only be programatically set to the empty string."); 1002 return; 1003 } 1004 setValue(value, eventBehavior); 1005 } 1006 1007 void HTMLInputElement::setValue(const String& value, TextFieldEventBehavior eventBehavior) 1008 { 1009 if (!m_inputType->canSetValue(value)) 1010 return; 1011 1012 RefPtr<HTMLInputElement> protector(this); 1013 EventQueueScope scope; 1014 String sanitizedValue = sanitizeValue(value); 1015 bool valueChanged = sanitizedValue != this->value(); 1016 1017 setLastChangeWasNotUserEdit(); 1018 setFormControlValueMatchesRenderer(false); 1019 m_suggestedValue = String(); // Prevent TextFieldInputType::setValue from using the suggested value. 1020 1021 m_inputType->setValue(sanitizedValue, valueChanged, eventBehavior); 1022 1023 if (valueChanged && eventBehavior == DispatchNoEvent) 1024 setTextAsOfLastFormControlChangeEvent(sanitizedValue); 1025 1026 if (!valueChanged) 1027 return; 1028 1029 notifyFormStateChanged(); 1030 } 1031 1032 void HTMLInputElement::setValueInternal(const String& sanitizedValue, TextFieldEventBehavior eventBehavior) 1033 { 1034 m_valueIfDirty = sanitizedValue; 1035 setNeedsValidityCheck(); 1036 } 1037 1038 double HTMLInputElement::valueAsDate() const 1039 { 1040 return m_inputType->valueAsDate(); 1041 } 1042 1043 void HTMLInputElement::setValueAsDate(double value, ExceptionState& exceptionState) 1044 { 1045 m_inputType->setValueAsDate(value, exceptionState); 1046 } 1047 1048 double HTMLInputElement::valueAsNumber() const 1049 { 1050 return m_inputType->valueAsDouble(); 1051 } 1052 1053 void HTMLInputElement::setValueAsNumber(double newValue, ExceptionState& exceptionState, TextFieldEventBehavior eventBehavior) 1054 { 1055 if (!std::isfinite(newValue)) { 1056 exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(newValue)); 1057 return; 1058 } 1059 m_inputType->setValueAsDouble(newValue, eventBehavior, exceptionState); 1060 } 1061 1062 void HTMLInputElement::setValueFromRenderer(const String& value) 1063 { 1064 // File upload controls will never use this. 1065 ASSERT(!isFileUpload()); 1066 1067 m_suggestedValue = String(); 1068 1069 // Renderer and our event handler are responsible for sanitizing values. 1070 ASSERT(value == sanitizeValue(value) || sanitizeValue(value).isEmpty()); 1071 1072 m_valueIfDirty = value; 1073 1074 setFormControlValueMatchesRenderer(true); 1075 1076 // Input event is fired by the Node::defaultEventHandler for editable controls. 1077 if (!isTextField()) 1078 dispatchInputEvent(); 1079 notifyFormStateChanged(); 1080 1081 setNeedsValidityCheck(); 1082 1083 // Clear autofill flag (and yellow background) on user edit. 1084 setAutofilled(false); 1085 } 1086 1087 void* HTMLInputElement::preDispatchEventHandler(Event* event) 1088 { 1089 if (event->type() == EventTypeNames::textInput && m_inputTypeView->shouldSubmitImplicitly(event)) { 1090 event->stopPropagation(); 1091 return 0; 1092 } 1093 if (event->type() != EventTypeNames::click) 1094 return 0; 1095 if (!event->isMouseEvent() || toMouseEvent(event)->button() != LeftButton) 1096 return 0; 1097 // FIXME: Check whether there are any cases where this actually ends up leaking. 1098 return m_inputTypeView->willDispatchClick().leakPtr(); 1099 } 1100 1101 void HTMLInputElement::postDispatchEventHandler(Event* event, void* dataFromPreDispatch) 1102 { 1103 OwnPtr<ClickHandlingState> state = adoptPtr(static_cast<ClickHandlingState*>(dataFromPreDispatch)); 1104 if (!state) 1105 return; 1106 m_inputTypeView->didDispatchClick(event, *state); 1107 } 1108 1109 void HTMLInputElement::defaultEventHandler(Event* evt) 1110 { 1111 if (evt->isMouseEvent() && evt->type() == EventTypeNames::click && toMouseEvent(evt)->button() == LeftButton) { 1112 m_inputTypeView->handleClickEvent(toMouseEvent(evt)); 1113 if (evt->defaultHandled()) 1114 return; 1115 } 1116 1117 if (evt->isTouchEvent()) { 1118 m_inputTypeView->handleTouchEvent(toTouchEvent(evt)); 1119 if (evt->defaultHandled()) 1120 return; 1121 } 1122 1123 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keydown) { 1124 m_inputTypeView->handleKeydownEvent(toKeyboardEvent(evt)); 1125 if (evt->defaultHandled()) 1126 return; 1127 } 1128 1129 // Call the base event handler before any of our own event handling for almost all events in text fields. 1130 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. 1131 bool callBaseClassEarly = isTextField() && (evt->type() == EventTypeNames::keydown || evt->type() == EventTypeNames::keypress); 1132 if (callBaseClassEarly) { 1133 HTMLTextFormControlElement::defaultEventHandler(evt); 1134 if (evt->defaultHandled()) 1135 return; 1136 } 1137 1138 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means 1139 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks 1140 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element 1141 // must dispatch a DOMActivate event - a click event will not do the job. 1142 if (evt->type() == EventTypeNames::DOMActivate) { 1143 m_inputType->handleDOMActivateEvent(evt); 1144 if (evt->defaultHandled()) 1145 return; 1146 } 1147 1148 // Use key press event here since sending simulated mouse events 1149 // on key down blocks the proper sending of the key press event. 1150 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keypress) { 1151 m_inputTypeView->handleKeypressEvent(toKeyboardEvent(evt)); 1152 if (evt->defaultHandled()) 1153 return; 1154 } 1155 1156 if (evt->isKeyboardEvent() && evt->type() == EventTypeNames::keyup) { 1157 m_inputTypeView->handleKeyupEvent(toKeyboardEvent(evt)); 1158 if (evt->defaultHandled()) 1159 return; 1160 } 1161 1162 if (m_inputTypeView->shouldSubmitImplicitly(evt)) { 1163 if (isSearchField()) 1164 onSearch(); 1165 // Form submission finishes editing, just as loss of focus does. 1166 // If there was a change, send the event now. 1167 if (wasChangedSinceLastFormControlChangeEvent()) 1168 dispatchFormControlChangeEvent(); 1169 1170 RefPtr<HTMLFormElement> formForSubmission = m_inputTypeView->formForSubmission(); 1171 // Form may never have been present, or may have been destroyed by code responding to the change event. 1172 if (formForSubmission) 1173 formForSubmission->submitImplicitly(evt, canTriggerImplicitSubmission()); 1174 1175 evt->setDefaultHandled(); 1176 return; 1177 } 1178 1179 if (evt->isBeforeTextInsertedEvent()) 1180 m_inputTypeView->handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(evt)); 1181 1182 if (evt->isMouseEvent() && evt->type() == EventTypeNames::mousedown) { 1183 m_inputTypeView->handleMouseDownEvent(toMouseEvent(evt)); 1184 if (evt->defaultHandled()) 1185 return; 1186 } 1187 1188 m_inputTypeView->forwardEvent(evt); 1189 1190 if (!callBaseClassEarly && !evt->defaultHandled()) 1191 HTMLTextFormControlElement::defaultEventHandler(evt); 1192 } 1193 1194 bool HTMLInputElement::willRespondToMouseClickEvents() 1195 { 1196 // FIXME: Consider implementing willRespondToMouseClickEvents() in InputType if more accurate results are necessary. 1197 if (!isDisabledFormControl()) 1198 return true; 1199 1200 return HTMLTextFormControlElement::willRespondToMouseClickEvents(); 1201 } 1202 1203 bool HTMLInputElement::isURLAttribute(const Attribute& attribute) const 1204 { 1205 return attribute.name() == srcAttr || attribute.name() == formactionAttr || HTMLTextFormControlElement::isURLAttribute(attribute); 1206 } 1207 1208 const AtomicString& HTMLInputElement::defaultValue() const 1209 { 1210 return fastGetAttribute(valueAttr); 1211 } 1212 1213 void HTMLInputElement::setDefaultValue(const AtomicString& value) 1214 { 1215 setAttribute(valueAttr, value); 1216 } 1217 1218 static inline bool isRFC2616TokenCharacter(UChar ch) 1219 { 1220 return isASCII(ch) && ch > ' ' && ch != '"' && ch != '(' && ch != ')' && ch != ',' && ch != '/' && (ch < ':' || ch > '@') && (ch < '[' || ch > ']') && ch != '{' && ch != '}' && ch != 0x7f; 1221 } 1222 1223 static bool isValidMIMEType(const String& type) 1224 { 1225 size_t slashPosition = type.find('/'); 1226 if (slashPosition == kNotFound || !slashPosition || slashPosition == type.length() - 1) 1227 return false; 1228 for (size_t i = 0; i < type.length(); ++i) { 1229 if (!isRFC2616TokenCharacter(type[i]) && i != slashPosition) 1230 return false; 1231 } 1232 return true; 1233 } 1234 1235 static bool isValidFileExtension(const String& type) 1236 { 1237 if (type.length() < 2) 1238 return false; 1239 return type[0] == '.'; 1240 } 1241 1242 static Vector<String> parseAcceptAttribute(const String& acceptString, bool (*predicate)(const String&)) 1243 { 1244 Vector<String> types; 1245 if (acceptString.isEmpty()) 1246 return types; 1247 1248 Vector<String> splitTypes; 1249 acceptString.split(',', false, splitTypes); 1250 for (size_t i = 0; i < splitTypes.size(); ++i) { 1251 String trimmedType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]); 1252 if (trimmedType.isEmpty()) 1253 continue; 1254 if (!predicate(trimmedType)) 1255 continue; 1256 types.append(trimmedType.lower()); 1257 } 1258 1259 return types; 1260 } 1261 1262 Vector<String> HTMLInputElement::acceptMIMETypes() 1263 { 1264 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidMIMEType); 1265 } 1266 1267 Vector<String> HTMLInputElement::acceptFileExtensions() 1268 { 1269 return parseAcceptAttribute(fastGetAttribute(acceptAttr), isValidFileExtension); 1270 } 1271 1272 const AtomicString& HTMLInputElement::accept() const 1273 { 1274 return fastGetAttribute(acceptAttr); 1275 } 1276 1277 const AtomicString& HTMLInputElement::alt() const 1278 { 1279 return fastGetAttribute(altAttr); 1280 } 1281 1282 int HTMLInputElement::maxLength() const 1283 { 1284 return m_maxLength; 1285 } 1286 1287 void HTMLInputElement::setMaxLength(int maxLength, ExceptionState& exceptionState) 1288 { 1289 if (maxLength < 0) 1290 exceptionState.throwDOMException(IndexSizeError, "The value provided (" + String::number(maxLength) + ") is negative."); 1291 else 1292 setIntegralAttribute(maxlengthAttr, maxLength); 1293 } 1294 1295 bool HTMLInputElement::multiple() const 1296 { 1297 return fastHasAttribute(multipleAttr); 1298 } 1299 1300 void HTMLInputElement::setSize(unsigned size) 1301 { 1302 setUnsignedIntegralAttribute(sizeAttr, size); 1303 } 1304 1305 void HTMLInputElement::setSize(unsigned size, ExceptionState& exceptionState) 1306 { 1307 if (!size) 1308 exceptionState.throwDOMException(IndexSizeError, "The value provided is 0, which is an invalid size."); 1309 else 1310 setSize(size); 1311 } 1312 1313 KURL HTMLInputElement::src() const 1314 { 1315 return document().completeURL(fastGetAttribute(srcAttr)); 1316 } 1317 1318 FileList* HTMLInputElement::files() 1319 { 1320 return m_inputType->files(); 1321 } 1322 1323 void HTMLInputElement::setFiles(PassRefPtr<FileList> files) 1324 { 1325 m_inputType->setFiles(files); 1326 } 1327 1328 bool HTMLInputElement::receiveDroppedFiles(const DragData* dragData) 1329 { 1330 return m_inputType->receiveDroppedFiles(dragData); 1331 } 1332 1333 String HTMLInputElement::droppedFileSystemId() 1334 { 1335 return m_inputType->droppedFileSystemId(); 1336 } 1337 1338 bool HTMLInputElement::canReceiveDroppedFiles() const 1339 { 1340 return m_canReceiveDroppedFiles; 1341 } 1342 1343 void HTMLInputElement::setCanReceiveDroppedFiles(bool canReceiveDroppedFiles) 1344 { 1345 if (m_canReceiveDroppedFiles == canReceiveDroppedFiles) 1346 return; 1347 m_canReceiveDroppedFiles = canReceiveDroppedFiles; 1348 if (renderer()) 1349 renderer()->updateFromElement(); 1350 } 1351 1352 String HTMLInputElement::visibleValue() const 1353 { 1354 return m_inputType->visibleValue(); 1355 } 1356 1357 String HTMLInputElement::sanitizeValue(const String& proposedValue) const 1358 { 1359 if (proposedValue.isNull()) 1360 return proposedValue; 1361 return m_inputType->sanitizeValue(proposedValue); 1362 } 1363 1364 String HTMLInputElement::localizeValue(const String& proposedValue) const 1365 { 1366 if (proposedValue.isNull()) 1367 return proposedValue; 1368 return m_inputType->localizeValue(proposedValue); 1369 } 1370 1371 bool HTMLInputElement::isInRange() const 1372 { 1373 return m_inputType->isInRange(value()); 1374 } 1375 1376 bool HTMLInputElement::isOutOfRange() const 1377 { 1378 return m_inputType->isOutOfRange(value()); 1379 } 1380 1381 bool HTMLInputElement::isRequiredFormControl() const 1382 { 1383 return m_inputType->supportsRequired() && isRequired(); 1384 } 1385 1386 bool HTMLInputElement::matchesReadOnlyPseudoClass() const 1387 { 1388 return m_inputType->supportsReadOnly() && isReadOnly(); 1389 } 1390 1391 bool HTMLInputElement::matchesReadWritePseudoClass() const 1392 { 1393 return m_inputType->supportsReadOnly() && !isReadOnly(); 1394 } 1395 1396 void HTMLInputElement::onSearch() 1397 { 1398 ASSERT(isSearchField()); 1399 if (m_inputType) 1400 static_cast<SearchInputType*>(m_inputType.get())->stopSearchEventTimer(); 1401 dispatchEvent(Event::createBubble(EventTypeNames::search)); 1402 } 1403 1404 void HTMLInputElement::updateClearButtonVisibility() 1405 { 1406 m_inputTypeView->updateClearButtonVisibility(); 1407 } 1408 1409 void HTMLInputElement::willChangeForm() 1410 { 1411 removeFromRadioButtonGroup(); 1412 HTMLTextFormControlElement::willChangeForm(); 1413 } 1414 1415 void HTMLInputElement::didChangeForm() 1416 { 1417 HTMLTextFormControlElement::didChangeForm(); 1418 addToRadioButtonGroup(); 1419 } 1420 1421 Node::InsertionNotificationRequest HTMLInputElement::insertedInto(ContainerNode* insertionPoint) 1422 { 1423 HTMLTextFormControlElement::insertedInto(insertionPoint); 1424 if (insertionPoint->inDocument() && !form()) 1425 addToRadioButtonGroup(); 1426 resetListAttributeTargetObserver(); 1427 return InsertionDone; 1428 } 1429 1430 void HTMLInputElement::removedFrom(ContainerNode* insertionPoint) 1431 { 1432 if (insertionPoint->inDocument() && !form()) 1433 removeFromRadioButtonGroup(); 1434 HTMLTextFormControlElement::removedFrom(insertionPoint); 1435 ASSERT(!inDocument()); 1436 resetListAttributeTargetObserver(); 1437 } 1438 1439 void HTMLInputElement::didMoveToNewDocument(Document& oldDocument) 1440 { 1441 if (hasImageLoader()) 1442 imageLoader()->elementDidMoveToNewDocument(); 1443 1444 if (isRadioButton()) 1445 oldDocument.formController()->checkedRadioButtons().removeButton(this); 1446 if (m_hasTouchEventHandler) 1447 oldDocument.didRemoveEventTargetNode(this); 1448 1449 if (m_hasTouchEventHandler) 1450 document().didAddTouchEventHandler(this); 1451 1452 HTMLTextFormControlElement::didMoveToNewDocument(oldDocument); 1453 } 1454 1455 void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 1456 { 1457 HTMLTextFormControlElement::addSubresourceAttributeURLs(urls); 1458 1459 addSubresourceURL(urls, src()); 1460 } 1461 1462 bool HTMLInputElement::recalcWillValidate() const 1463 { 1464 return m_inputType->supportsValidation() && HTMLTextFormControlElement::recalcWillValidate(); 1465 } 1466 1467 void HTMLInputElement::requiredAttributeChanged() 1468 { 1469 HTMLTextFormControlElement::requiredAttributeChanged(); 1470 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1471 buttons->requiredAttributeChanged(this); 1472 m_inputTypeView->requiredAttributeChanged(); 1473 } 1474 1475 void HTMLInputElement::selectColorInColorChooser(const Color& color) 1476 { 1477 if (!m_inputType->isColorControl()) 1478 return; 1479 static_cast<ColorInputType*>(m_inputType.get())->didChooseColor(color); 1480 } 1481 1482 HTMLElement* HTMLInputElement::list() const 1483 { 1484 return dataList(); 1485 } 1486 1487 HTMLDataListElement* HTMLInputElement::dataList() const 1488 { 1489 if (!m_hasNonEmptyList) 1490 return 0; 1491 1492 if (!m_inputType->shouldRespectListAttribute()) 1493 return 0; 1494 1495 Element* element = treeScope().getElementById(fastGetAttribute(listAttr)); 1496 if (!element) 1497 return 0; 1498 if (!element->hasTagName(datalistTag)) 1499 return 0; 1500 1501 return toHTMLDataListElement(element); 1502 } 1503 1504 bool HTMLInputElement::hasValidDataListOptions() const 1505 { 1506 HTMLDataListElement* dataList = this->dataList(); 1507 if (!dataList) 1508 return false; 1509 RefPtr<HTMLCollection> options = dataList->options(); 1510 for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) { 1511 if (isValidValue(option->value())) 1512 return true; 1513 } 1514 return false; 1515 } 1516 1517 void HTMLInputElement::resetListAttributeTargetObserver() 1518 { 1519 if (inDocument()) 1520 m_listAttributeTargetObserver = ListAttributeTargetObserver::create(fastGetAttribute(listAttr), this); 1521 else 1522 m_listAttributeTargetObserver = nullptr; 1523 } 1524 1525 void HTMLInputElement::listAttributeTargetChanged() 1526 { 1527 m_inputTypeView->listAttributeTargetChanged(); 1528 } 1529 1530 bool HTMLInputElement::isSteppable() const 1531 { 1532 return m_inputType->isSteppable(); 1533 } 1534 1535 #if ENABLE(INPUT_SPEECH) 1536 1537 bool HTMLInputElement::isSpeechEnabled() const 1538 { 1539 // FIXME: Add support for RANGE, EMAIL, URL, COLOR and DATE/TIME input types. 1540 return m_inputType->shouldRespectSpeechAttribute() && RuntimeEnabledFeatures::speechInputEnabled() && hasAttribute(webkitspeechAttr); 1541 } 1542 1543 #endif 1544 1545 bool HTMLInputElement::isTextButton() const 1546 { 1547 return m_inputType->isTextButton(); 1548 } 1549 1550 bool HTMLInputElement::isRadioButton() const 1551 { 1552 return m_inputType->isRadioButton(); 1553 } 1554 1555 bool HTMLInputElement::isSearchField() const 1556 { 1557 return m_inputType->isSearchField(); 1558 } 1559 1560 bool HTMLInputElement::isInputTypeHidden() const 1561 { 1562 return m_inputType->isHiddenType(); 1563 } 1564 1565 bool HTMLInputElement::isPasswordField() const 1566 { 1567 return m_inputType->isPasswordField(); 1568 } 1569 1570 bool HTMLInputElement::isCheckbox() const 1571 { 1572 return m_inputType->isCheckbox(); 1573 } 1574 1575 bool HTMLInputElement::isRangeControl() const 1576 { 1577 return m_inputType->isRangeControl(); 1578 } 1579 1580 bool HTMLInputElement::isColorControl() const 1581 { 1582 return m_inputType->isColorControl(); 1583 } 1584 1585 bool HTMLInputElement::isText() const 1586 { 1587 return m_inputType->isTextType(); 1588 } 1589 1590 bool HTMLInputElement::isEmailField() const 1591 { 1592 return m_inputType->isEmailField(); 1593 } 1594 1595 bool HTMLInputElement::isFileUpload() const 1596 { 1597 return m_inputType->isFileUpload(); 1598 } 1599 1600 bool HTMLInputElement::isImageButton() const 1601 { 1602 return m_inputType->isImageButton(); 1603 } 1604 1605 bool HTMLInputElement::isNumberField() const 1606 { 1607 return m_inputType->isNumberField(); 1608 } 1609 1610 bool HTMLInputElement::isSubmitButton() const 1611 { 1612 return m_inputType->isSubmitButton(); 1613 } 1614 1615 bool HTMLInputElement::isTelephoneField() const 1616 { 1617 return m_inputType->isTelephoneField(); 1618 } 1619 1620 bool HTMLInputElement::isURLField() const 1621 { 1622 return m_inputType->isURLField(); 1623 } 1624 1625 bool HTMLInputElement::isDateField() const 1626 { 1627 return m_inputType->isDateField(); 1628 } 1629 1630 bool HTMLInputElement::isDateTimeLocalField() const 1631 { 1632 return m_inputType->isDateTimeLocalField(); 1633 } 1634 1635 bool HTMLInputElement::isMonthField() const 1636 { 1637 return m_inputType->isMonthField(); 1638 } 1639 1640 bool HTMLInputElement::isTimeField() const 1641 { 1642 return m_inputType->isTimeField(); 1643 } 1644 1645 bool HTMLInputElement::isWeekField() const 1646 { 1647 return m_inputType->isWeekField(); 1648 } 1649 1650 bool HTMLInputElement::isEnumeratable() const 1651 { 1652 return m_inputType->isEnumeratable(); 1653 } 1654 1655 bool HTMLInputElement::supportLabels() const 1656 { 1657 return m_inputType->isInteractiveContent(); 1658 } 1659 1660 bool HTMLInputElement::shouldAppearChecked() const 1661 { 1662 return checked() && m_inputType->isCheckable(); 1663 } 1664 1665 bool HTMLInputElement::supportsPlaceholder() const 1666 { 1667 return m_inputType->supportsPlaceholder(); 1668 } 1669 1670 void HTMLInputElement::updatePlaceholderText() 1671 { 1672 return m_inputType->updatePlaceholderText(); 1673 } 1674 1675 void HTMLInputElement::parseMaxLengthAttribute(const AtomicString& value) 1676 { 1677 int maxLength; 1678 if (!parseHTMLInteger(value, maxLength)) 1679 maxLength = maximumLength; 1680 if (maxLength < 0 || maxLength > maximumLength) 1681 maxLength = maximumLength; 1682 int oldMaxLength = m_maxLength; 1683 m_maxLength = maxLength; 1684 if (oldMaxLength != maxLength) 1685 updateValueIfNeeded(); 1686 setNeedsStyleRecalc(); 1687 setNeedsValidityCheck(); 1688 } 1689 1690 void HTMLInputElement::updateValueIfNeeded() 1691 { 1692 String newValue = sanitizeValue(m_valueIfDirty); 1693 ASSERT(!m_valueIfDirty.isNull() || newValue.isNull()); 1694 if (newValue != m_valueIfDirty) 1695 setValue(newValue); 1696 } 1697 1698 String HTMLInputElement::defaultToolTip() const 1699 { 1700 return m_inputType->defaultToolTip(); 1701 } 1702 1703 bool HTMLInputElement::shouldAppearIndeterminate() const 1704 { 1705 return m_inputType->supportsIndeterminateAppearance() && indeterminate(); 1706 } 1707 1708 #if ENABLE(MEDIA_CAPTURE) 1709 bool HTMLInputElement::capture() const 1710 { 1711 if (!isFileUpload() || !fastHasAttribute(captureAttr)) 1712 return false; 1713 1714 // As per crbug.com/240252, emit a deprecation warning when the "capture" 1715 // attribute is used as an enum. The spec has been updated and "capture" is 1716 // supposed to be used as a boolean. 1717 bool hasDeprecatedUsage = !fastGetAttribute(captureAttr).isNull(); 1718 if (hasDeprecatedUsage) 1719 UseCounter::countDeprecation(document(), UseCounter::CaptureAttributeAsEnum); 1720 else 1721 UseCounter::count(document(), UseCounter::CaptureAttributeAsEnum); 1722 1723 return true; 1724 } 1725 #endif 1726 1727 bool HTMLInputElement::isInRequiredRadioButtonGroup() 1728 { 1729 ASSERT(isRadioButton()); 1730 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1731 return buttons->isInRequiredGroup(this); 1732 return false; 1733 } 1734 1735 HTMLInputElement* HTMLInputElement::checkedRadioButtonForGroup() const 1736 { 1737 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1738 return buttons->checkedButtonForGroup(name()); 1739 return 0; 1740 } 1741 1742 CheckedRadioButtons* HTMLInputElement::checkedRadioButtons() const 1743 { 1744 if (!isRadioButton()) 1745 return 0; 1746 if (HTMLFormElement* formElement = form()) 1747 return &formElement->checkedRadioButtons(); 1748 if (inDocument()) 1749 return &document().formController()->checkedRadioButtons(); 1750 return 0; 1751 } 1752 1753 inline void HTMLInputElement::addToRadioButtonGroup() 1754 { 1755 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1756 buttons->addButton(this); 1757 } 1758 1759 inline void HTMLInputElement::removeFromRadioButtonGroup() 1760 { 1761 if (CheckedRadioButtons* buttons = checkedRadioButtons()) 1762 buttons->removeButton(this); 1763 } 1764 1765 unsigned HTMLInputElement::height() const 1766 { 1767 return m_inputType->height(); 1768 } 1769 1770 unsigned HTMLInputElement::width() const 1771 { 1772 return m_inputType->width(); 1773 } 1774 1775 void HTMLInputElement::setHeight(unsigned height) 1776 { 1777 setUnsignedIntegralAttribute(heightAttr, height); 1778 } 1779 1780 void HTMLInputElement::setWidth(unsigned width) 1781 { 1782 setUnsignedIntegralAttribute(widthAttr, width); 1783 } 1784 1785 PassOwnPtr<ListAttributeTargetObserver> ListAttributeTargetObserver::create(const AtomicString& id, HTMLInputElement* element) 1786 { 1787 return adoptPtr(new ListAttributeTargetObserver(id, element)); 1788 } 1789 1790 ListAttributeTargetObserver::ListAttributeTargetObserver(const AtomicString& id, HTMLInputElement* element) 1791 : IdTargetObserver(element->treeScope().idTargetObserverRegistry(), id) 1792 , m_element(element) 1793 { 1794 } 1795 1796 void ListAttributeTargetObserver::idTargetChanged() 1797 { 1798 m_element->listAttributeTargetChanged(); 1799 } 1800 1801 void HTMLInputElement::setRangeText(const String& replacement, ExceptionState& exceptionState) 1802 { 1803 if (!m_inputType->supportsSelectionAPI()) { 1804 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 1805 return; 1806 } 1807 1808 HTMLTextFormControlElement::setRangeText(replacement, exceptionState); 1809 } 1810 1811 void HTMLInputElement::setRangeText(const String& replacement, unsigned start, unsigned end, const String& selectionMode, ExceptionState& exceptionState) 1812 { 1813 if (!m_inputType->supportsSelectionAPI()) { 1814 exceptionState.throwDOMException(InvalidStateError, "The input element's type ('" + m_inputType->formControlType() + "') does not support selection."); 1815 return; 1816 } 1817 1818 HTMLTextFormControlElement::setRangeText(replacement, start, end, selectionMode, exceptionState); 1819 } 1820 1821 bool HTMLInputElement::setupDateTimeChooserParameters(DateTimeChooserParameters& parameters) 1822 { 1823 if (!document().view()) 1824 return false; 1825 1826 parameters.type = type(); 1827 parameters.minimum = minimum(); 1828 parameters.maximum = maximum(); 1829 parameters.required = isRequired(); 1830 if (!RuntimeEnabledFeatures::langAttributeAwareFormControlUIEnabled()) 1831 parameters.locale = defaultLanguage(); 1832 else { 1833 AtomicString computedLocale = computeInheritedLanguage(); 1834 parameters.locale = computedLocale.isEmpty() ? AtomicString(defaultLanguage()) : computedLocale; 1835 } 1836 1837 StepRange stepRange = createStepRange(RejectAny); 1838 if (stepRange.hasStep()) { 1839 parameters.step = stepRange.step().toDouble(); 1840 parameters.stepBase = stepRange.stepBase().toDouble(); 1841 } else { 1842 parameters.step = 1.0; 1843 parameters.stepBase = 0; 1844 } 1845 1846 parameters.anchorRectInRootView = document().view()->contentsToRootView(pixelSnappedBoundingBox()); 1847 parameters.currentValue = value(); 1848 parameters.doubleValue = m_inputType->valueAsDouble(); 1849 parameters.isAnchorElementRTL = computedStyle()->direction() == RTL; 1850 if (RuntimeEnabledFeatures::dataListElementEnabled()) { 1851 if (HTMLDataListElement* dataList = this->dataList()) { 1852 RefPtr<HTMLCollection> options = dataList->options(); 1853 for (unsigned i = 0; HTMLOptionElement* option = toHTMLOptionElement(options->item(i)); ++i) { 1854 if (!isValidValue(option->value())) 1855 continue; 1856 DateTimeSuggestion suggestion; 1857 suggestion.value = m_inputType->parseToNumber(option->value(), Decimal::nan()).toDouble(); 1858 if (std::isnan(suggestion.value)) 1859 continue; 1860 suggestion.localizedValue = localizeValue(option->value()); 1861 suggestion.label = option->value() == option->label() ? String() : option->label(); 1862 parameters.suggestions.append(suggestion); 1863 } 1864 } 1865 } 1866 return true; 1867 } 1868 1869 bool HTMLInputElement::supportsInputModeAttribute() const 1870 { 1871 return m_inputType->supportsInputModeAttribute(); 1872 } 1873 1874 bool HTMLInputElement::isInteractiveContent() const 1875 { 1876 return m_inputType->isInteractiveContent(); 1877 } 1878 1879 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 1880 PassRefPtr<RenderStyle> HTMLInputElement::customStyleForRenderer() 1881 { 1882 return m_inputTypeView->customStyleForRenderer(originalStyleForRenderer()); 1883 } 1884 #endif 1885 1886 } // namespace 1887