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