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