HTMLInputElement.cpp revision 17789fff81dac758d724db8595a5ce80c949af5e
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 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) 7 * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 * 24 */ 25 26#include "config.h" 27#include "HTMLInputElement.h" 28 29#include "AXObjectCache.h" 30#include "CSSPropertyNames.h" 31#include "ChromeClient.h" 32#include "Document.h" 33#include "Editor.h" 34#include "Event.h" 35#include "EventHandler.h" 36#include "EventNames.h" 37#include "ExceptionCode.h" 38#include "File.h" 39#include "FileList.h" 40#include "FocusController.h" 41#include "FormDataList.h" 42#include "Frame.h" 43#include "HTMLDataListElement.h" 44#include "HTMLFormElement.h" 45#include "HTMLImageLoader.h" 46#include "HTMLNames.h" 47#include "HTMLOptionElement.h" 48#include "ISODateTime.h" 49#include "ScriptEventListener.h" 50#include "KeyboardEvent.h" 51#include "LocalizedStrings.h" 52#include "MappedAttribute.h" 53#include "MouseEvent.h" 54#include "Page.h" 55#include "RegularExpression.h" 56#include "RenderButton.h" 57#include "RenderFileUploadControl.h" 58#include "RenderImage.h" 59#include "RenderSlider.h" 60#include "RenderText.h" 61#include "RenderTextControlSingleLine.h" 62#include "RenderTheme.h" 63#include "StringHash.h" 64#include "TextEvent.h" 65#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 66#include "WebViewCore.h" 67#endif 68#include <wtf/HashMap.h> 69#include <wtf/MathExtras.h> 70#include <wtf/StdLibExtras.h> 71#include <wtf/dtoa.h> 72 73using namespace std; 74 75namespace WebCore { 76 77using namespace HTMLNames; 78 79const int maxSavedResults = 256; 80 81// Constant values for getAllowedValueStep(). 82static const double numberDefaultStep = 1.0; 83static const double numberStepScaleFactor = 1.0; 84// Constant values for minimum(). 85static const double numberDefaultMinimum = -DBL_MAX; 86static const double rangeDefaultMinimum = 0.0; 87// Constant values for maximum(). 88static const double numberDefaultMaximum = DBL_MAX; 89static const double rangeDefaultMaximum = 100.0; 90 91HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) 92 : HTMLTextFormControlElement(tagName, doc, f) 93 , m_xPos(0) 94 , m_yPos(0) 95 , m_maxResults(-1) 96 , m_type(TEXT) 97 , m_checked(false) 98 , m_defaultChecked(false) 99 , m_useDefaultChecked(true) 100 , m_indeterminate(false) 101 , m_haveType(false) 102 , m_activeSubmit(false) 103 , m_autocomplete(Uninitialized) 104 , m_autofilled(false) 105 , m_inited(false) 106{ 107 ASSERT(hasTagName(inputTag) || hasTagName(isindexTag)); 108} 109 110HTMLInputElement::~HTMLInputElement() 111{ 112 if (needsActivationCallback()) 113 document()->unregisterForDocumentActivationCallbacks(this); 114 115 document()->checkedRadioButtons().removeButton(this); 116 117 // Need to remove this from the form while it is still an HTMLInputElement, 118 // so can't wait for the base class's destructor to do it. 119 removeFromForm(); 120} 121 122const AtomicString& HTMLInputElement::formControlName() const 123{ 124 return m_data.name(); 125} 126 127bool HTMLInputElement::autoComplete() const 128{ 129 if (m_autocomplete != Uninitialized) 130 return m_autocomplete == On; 131 132 // Assuming we're still in a Form, respect the Form's setting 133 if (HTMLFormElement* form = this->form()) 134 return form->autoComplete(); 135 136 // The default is true 137 return true; 138} 139 140bool HTMLInputElement::valueMissing() const 141{ 142 if (!isRequiredFormControl() || readOnly() || disabled()) 143 return false; 144 145 switch (inputType()) { 146 case DATE: 147 case DATETIME: 148 case DATETIMELOCAL: 149 case EMAIL: 150 case FILE: 151 case MONTH: 152 case NUMBER: 153 case PASSWORD: 154 case SEARCH: 155 case TELEPHONE: 156 case TEXT: 157 case TIME: 158 case URL: 159 case WEEK: 160 return value().isEmpty(); 161 case CHECKBOX: 162 return !checked(); 163 case RADIO: 164 return !document()->checkedRadioButtons().checkedButtonForGroup(name()); 165 case COLOR: 166 return false; 167 case BUTTON: 168 case HIDDEN: 169 case IMAGE: 170 case ISINDEX: 171 case RANGE: 172 case RESET: 173 case SUBMIT: 174 break; 175 } 176 177 ASSERT_NOT_REACHED(); 178 return false; 179} 180 181bool HTMLInputElement::patternMismatch() const 182{ 183 switch (inputType()) { 184 case BUTTON: 185 case CHECKBOX: 186 case COLOR: 187 case DATE: 188 case DATETIME: 189 case DATETIMELOCAL: 190 case FILE: 191 case HIDDEN: 192 case IMAGE: 193 case ISINDEX: 194 case MONTH: 195 case NUMBER: 196 case RADIO: 197 case RANGE: 198 case RESET: 199 case SUBMIT: 200 case TIME: 201 case WEEK: 202 return false; 203 case EMAIL: 204 case PASSWORD: 205 case SEARCH: 206 case TELEPHONE: 207 case TEXT: 208 case URL: 209 const AtomicString& pattern = getAttribute(patternAttr); 210 String value = this->value(); 211 212 // Empty values can't be mismatched 213 if (pattern.isEmpty() || value.isEmpty()) 214 return false; 215 216 RegularExpression patternRegExp(pattern, TextCaseSensitive); 217 int matchLength = 0; 218 int valueLength = value.length(); 219 int matchOffset = patternRegExp.match(value, 0, &matchLength); 220 221 return matchOffset != 0 || matchLength != valueLength; 222 } 223 224 ASSERT_NOT_REACHED(); 225 return false; 226} 227 228bool HTMLInputElement::tooLong() const 229{ 230 switch (inputType()) { 231 case EMAIL: 232 case PASSWORD: 233 case SEARCH: 234 case TELEPHONE: 235 case TEXT: 236 case URL: { 237 int max = maxLength(); 238 if (max < 0) 239 return false; 240 // Return false for the default value even if it is longer than maxLength. 241 bool userEdited = !m_data.value().isNull(); 242 if (!userEdited) 243 return false; 244 return value().length() > static_cast<unsigned>(max); 245 } 246 case BUTTON: 247 case CHECKBOX: 248 case COLOR: 249 case DATE: 250 case DATETIME: 251 case DATETIMELOCAL: 252 case FILE: 253 case HIDDEN: 254 case IMAGE: 255 case ISINDEX: 256 case MONTH: 257 case NUMBER: 258 case RADIO: 259 case RANGE: 260 case RESET: 261 case SUBMIT: 262 case TIME: 263 case WEEK: 264 return false; 265 } 266 ASSERT_NOT_REACHED(); 267 return false; 268} 269 270bool HTMLInputElement::rangeUnderflow() const 271{ 272 if (inputType() == NUMBER || inputType() == RANGE) { 273 double doubleValue; 274 if (formStringToDouble(value(), &doubleValue)) 275 return doubleValue < minimum(); 276 } 277 return false; 278} 279 280bool HTMLInputElement::rangeOverflow() const 281{ 282 if (inputType() == NUMBER || inputType() == RANGE) { 283 double doubleValue; 284 if (formStringToDouble(value(), &doubleValue)) 285 return doubleValue > maximum(); 286 } 287 return false; 288} 289 290double HTMLInputElement::minimum() const 291{ 292 ASSERT(inputType() == NUMBER || inputType() == RANGE); 293 double min = inputType() == RANGE ? rangeDefaultMinimum : numberDefaultMinimum; 294 formStringToDouble(getAttribute(minAttr), &min); 295 return min; 296} 297 298double HTMLInputElement::maximum() const 299{ 300 ASSERT(inputType() == NUMBER || inputType() == RANGE); 301 double defaultMaximum = inputType() == RANGE ? rangeDefaultMaximum : numberDefaultMaximum; 302 double max = defaultMaximum; 303 formStringToDouble(getAttribute(maxAttr), &max); 304 if (inputType() == RANGE) { 305 // A remedy for the inconsistent min/max values for RANGE. 306 // Sets the maxmimum to the default or the minimum value. 307 double min = minimum(); 308 if (max < min) 309 max = std::max(min, defaultMaximum); 310 } 311 return max; 312} 313 314double HTMLInputElement::stepBase() const 315{ 316 if (inputType() == RANGE) 317 return minimum(); 318 if (inputType() == NUMBER) { 319 static const double defaultStepBase = 0.0; 320 double min = defaultStepBase; 321 formStringToDouble(getAttribute(minAttr), &min); 322 return min; 323 } 324 ASSERT_NOT_REACHED(); 325 return 0.0; 326} 327 328bool HTMLInputElement::stepMismatch() const 329{ 330 double step; 331 if (!getAllowedValueStep(&step)) 332 return false; 333 if (inputType() == NUMBER) { 334 double doubleValue; 335 if (!formStringToDouble(value(), &doubleValue)) 336 return false; 337 doubleValue = fabs(doubleValue - stepBase()); 338 if (isinf(doubleValue)) 339 return false; 340 // double's fractional part size is DBL_MAN_DIG-bit. If the current 341 // value is greater than step*2^DBL_MANT_DIG, the following fmod() makes 342 // no sense. 343 if (doubleValue / pow(2.0, DBL_MANT_DIG) > step) 344 return false; 345 double remainder = fmod(doubleValue, step); 346 // Accepts errors in lower 7-bit. 347 double acceptableError = step / pow(2.0, DBL_MANT_DIG - 7); 348 return acceptableError < remainder && remainder < (step - acceptableError); 349 } 350 // Non-RANGE types should be rejected by getAllowedValueStep(). 351 ASSERT(inputType() == RANGE); 352 // stepMismatch doesn't occur for RANGE. RenderSlider guarantees the 353 // value matches to step. 354 return false; 355} 356 357bool HTMLInputElement::getStepParameters(double* defaultStep, double* stepScaleFactor) const 358{ 359 ASSERT(defaultStep); 360 ASSERT(stepScaleFactor); 361 switch (inputType()) { 362 case NUMBER: 363 case RANGE: 364 *defaultStep = numberDefaultStep; 365 *stepScaleFactor = numberStepScaleFactor; 366 return true; 367 case DATE: 368 case DATETIME: 369 case DATETIMELOCAL: 370 case MONTH: 371 case TIME: 372 case WEEK: 373 // FIXME: Implement for these types. 374 return false; 375 case BUTTON: 376 case CHECKBOX: 377 case COLOR: 378 case EMAIL: 379 case FILE: 380 case HIDDEN: 381 case IMAGE: 382 case ISINDEX: 383 case PASSWORD: 384 case RADIO: 385 case RESET: 386 case SEARCH: 387 case SUBMIT: 388 case TELEPHONE: 389 case TEXT: 390 case URL: 391 return false; 392 } 393 ASSERT_NOT_REACHED(); 394 return false; 395} 396 397bool HTMLInputElement::getAllowedValueStep(double* step) const 398{ 399 ASSERT(step); 400 double defaultStep; 401 double stepScaleFactor; 402 if (!getStepParameters(&defaultStep, &stepScaleFactor)) 403 return false; 404 const AtomicString& stepString = getAttribute(stepAttr); 405 if (stepString.isEmpty()) { 406 *step = defaultStep * stepScaleFactor; 407 return true; 408 } 409 if (equalIgnoringCase(stepString, "any")) 410 return false; 411 double parsed; 412 if (!formStringToDouble(stepString, &parsed) || parsed <= 0.0) { 413 *step = defaultStep * stepScaleFactor; 414 return true; 415 } 416 *step = parsed * stepScaleFactor; 417 ASSERT(*step > 0); 418 return true; 419} 420 421void HTMLInputElement::applyStepForNumberOrRange(double count, ExceptionCode& ec) 422{ 423 ASSERT(inputType() == NUMBER || inputType() == RANGE); 424 double step; 425 if (!getAllowedValueStep(&step)) { 426 ec = INVALID_STATE_ERR; 427 return; 428 } 429 double current; 430 if (!formStringToDouble(value(), ¤t)) { 431 ec = INVALID_STATE_ERR; 432 return; 433 } 434 double newValue = current + step * count; 435 if (isinf(newValue)) { 436 ec = INVALID_STATE_ERR; 437 return; 438 } 439 if (newValue < minimum()) { 440 ec = INVALID_STATE_ERR; 441 return; 442 } 443 double base = stepBase(); 444 newValue = base + round((newValue - base) / step) * step; 445 if (newValue > maximum()) { 446 ec = INVALID_STATE_ERR; 447 return; 448 } 449 setValue(formStringFromDouble(newValue)); 450} 451 452void HTMLInputElement::stepUp(int n, ExceptionCode& ec) 453{ 454 if (inputType() != NUMBER && inputType() != RANGE) { 455 ec = INVALID_STATE_ERR; 456 return; 457 } 458 applyStepForNumberOrRange(n, ec); 459} 460 461void HTMLInputElement::stepDown(int n, ExceptionCode& ec) 462{ 463 if (inputType() != NUMBER && inputType() != RANGE) { 464 ec = INVALID_STATE_ERR; 465 return; 466 } 467 applyStepForNumberOrRange(-n, ec); 468} 469 470static inline CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element) 471{ 472 if (HTMLFormElement* form = element->form()) 473 return form->checkedRadioButtons(); 474 475 return element->document()->checkedRadioButtons(); 476} 477 478bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const 479{ 480 // If text fields can be focused, then they should always be keyboard focusable 481 if (isTextField()) 482 return HTMLFormControlElementWithState::isFocusable(); 483 484 // If the base class says we can't be focused, then we can stop now. 485 if (!HTMLFormControlElementWithState::isKeyboardFocusable(event)) 486 return false; 487 488 if (inputType() == RADIO) { 489 490 // Never allow keyboard tabbing to leave you in the same radio group. Always 491 // skip any other elements in the group. 492 Node* currentFocusedNode = document()->focusedNode(); 493 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) { 494 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode); 495 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() && 496 focusedInput->name() == name()) 497 return false; 498 } 499 500 // Allow keyboard focus if we're checked or if nothing in the group is checked. 501 return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name()); 502 } 503 504 return true; 505} 506 507bool HTMLInputElement::isMouseFocusable() const 508{ 509 if (isTextField()) 510 return HTMLFormControlElementWithState::isFocusable(); 511 return HTMLFormControlElementWithState::isMouseFocusable(); 512} 513 514void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection) 515{ 516 if (isTextField()) 517 InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection); 518 else 519 HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection); 520} 521 522void HTMLInputElement::aboutToUnload() 523{ 524 InputElement::aboutToUnload(this, this); 525} 526 527bool HTMLInputElement::shouldUseInputMethod() const 528{ 529 return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX; 530} 531 532void HTMLInputElement::handleFocusEvent() 533{ 534 InputElement::dispatchFocusEvent(this, this); 535 536 if (isTextField()) 537 m_autofilled = false; 538} 539 540void HTMLInputElement::handleBlurEvent() 541{ 542 InputElement::dispatchBlurEvent(this, this); 543} 544 545void HTMLInputElement::setType(const String& t) 546{ 547 if (t.isEmpty()) { 548 int exccode; 549 removeAttribute(typeAttr, exccode); 550 } else 551 setAttribute(typeAttr, t); 552} 553 554typedef HashMap<String, HTMLInputElement::InputType, CaseFoldingHash> InputTypeMap; 555static const InputTypeMap* createTypeMap() 556{ 557 InputTypeMap* map = new InputTypeMap; 558 map->add("button", HTMLInputElement::BUTTON); 559 map->add("checkbox", HTMLInputElement::CHECKBOX); 560 map->add("color", HTMLInputElement::COLOR); 561 map->add("date", HTMLInputElement::DATE); 562 map->add("datetime", HTMLInputElement::DATETIME); 563 map->add("datetime-local", HTMLInputElement::DATETIMELOCAL); 564 map->add("email", HTMLInputElement::EMAIL); 565 map->add("file", HTMLInputElement::FILE); 566 map->add("hidden", HTMLInputElement::HIDDEN); 567 map->add("image", HTMLInputElement::IMAGE); 568 map->add("khtml_isindex", HTMLInputElement::ISINDEX); 569 map->add("month", HTMLInputElement::MONTH); 570 map->add("number", HTMLInputElement::NUMBER); 571 map->add("password", HTMLInputElement::PASSWORD); 572 map->add("radio", HTMLInputElement::RADIO); 573 map->add("range", HTMLInputElement::RANGE); 574 map->add("reset", HTMLInputElement::RESET); 575 map->add("search", HTMLInputElement::SEARCH); 576 map->add("submit", HTMLInputElement::SUBMIT); 577 map->add("tel", HTMLInputElement::TELEPHONE); 578 map->add("time", HTMLInputElement::TIME); 579 map->add("url", HTMLInputElement::URL); 580 map->add("week", HTMLInputElement::WEEK); 581 // No need to register "text" because it is the default type. 582 return map; 583} 584 585void HTMLInputElement::setInputType(const String& t) 586{ 587 static const InputTypeMap* typeMap = createTypeMap(); 588 InputType newType = t.isNull() ? TEXT : typeMap->get(t); 589#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 590 if (newType == PASSWORD && document()->focusedNode() == this) 591 android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, true, String()); 592#endif 593 594 // IMPORTANT: Don't allow the type to be changed to FILE after the first 595 // type change, otherwise a JavaScript programmer would be able to set a text 596 // field's value to something like /etc/passwd and then change it to a file field. 597 if (inputType() != newType) { 598 if (newType == FILE && m_haveType) 599 // Set the attribute back to the old value. 600 // Useful in case we were called from inside parseMappedAttribute. 601 setAttribute(typeAttr, type()); 602 else { 603 checkedRadioButtons(this).removeButton(this); 604 605 if (newType == FILE && !m_fileList) 606 m_fileList = FileList::create(); 607 608 bool wasAttached = attached(); 609 if (wasAttached) 610 detach(); 611 612 bool didStoreValue = storesValueSeparateFromAttribute(); 613 bool wasPasswordField = inputType() == PASSWORD; 614 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs(); 615 m_type = newType; 616 bool willStoreValue = storesValueSeparateFromAttribute(); 617 bool isPasswordField = inputType() == PASSWORD; 618 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs(); 619 620 if (didStoreValue && !willStoreValue && !m_data.value().isNull()) { 621 setAttribute(valueAttr, m_data.value()); 622 m_data.setValue(String()); 623 } 624 if (!didStoreValue && willStoreValue) 625 m_data.setValue(sanitizeValue(getAttribute(valueAttr))); 626 else 627 InputElement::updateValueIfNeeded(m_data, this); 628 629 if (wasPasswordField && !isPasswordField) 630 unregisterForActivationCallbackIfNeeded(); 631 else if (!wasPasswordField && isPasswordField) 632 registerForActivationCallbackIfNeeded(); 633 634 if (didRespectHeightAndWidth != willRespectHeightAndWidth) { 635 NamedMappedAttrMap* map = mappedAttributes(); 636 ASSERT(map); 637 if (Attribute* height = map->getAttributeItem(heightAttr)) 638 attributeChanged(height, false); 639 if (Attribute* width = map->getAttributeItem(widthAttr)) 640 attributeChanged(width, false); 641 if (Attribute* align = map->getAttributeItem(alignAttr)) 642 attributeChanged(align, false); 643 } 644 645 if (wasAttached) { 646 attach(); 647 if (document()->focusedNode() == this) 648 updateFocusAppearance(true); 649 } 650 651 checkedRadioButtons(this).addButton(this); 652 } 653 654 InputElement::notifyFormStateChanged(this); 655 updateValidity(); 656 } 657 m_haveType = true; 658 659 if (inputType() != IMAGE && m_imageLoader) 660 m_imageLoader.clear(); 661} 662 663static const AtomicString* createFormControlTypes() 664{ 665 AtomicString* types = new AtomicString[HTMLInputElement::numberOfTypes]; 666 // The values must be lowercased because they will be the return values of 667 // input.type and it must be lowercase according to DOM Level 2. 668 types[HTMLInputElement::BUTTON] = "button"; 669 types[HTMLInputElement::CHECKBOX] = "checkbox"; 670 types[HTMLInputElement::COLOR] = "color"; 671 types[HTMLInputElement::DATE] = "date"; 672 types[HTMLInputElement::DATETIME] = "datetime"; 673 types[HTMLInputElement::DATETIMELOCAL] = "datetime-local"; 674 types[HTMLInputElement::EMAIL] = "email"; 675 types[HTMLInputElement::FILE] = "file"; 676 types[HTMLInputElement::HIDDEN] = "hidden"; 677 types[HTMLInputElement::IMAGE] = "image"; 678 types[HTMLInputElement::ISINDEX] = emptyAtom; 679 types[HTMLInputElement::MONTH] = "month"; 680 types[HTMLInputElement::NUMBER] = "number"; 681 types[HTMLInputElement::PASSWORD] = "password"; 682 types[HTMLInputElement::RADIO] = "radio"; 683 types[HTMLInputElement::RANGE] = "range"; 684 types[HTMLInputElement::RESET] = "reset"; 685 types[HTMLInputElement::SEARCH] = "search"; 686 types[HTMLInputElement::SUBMIT] = "submit"; 687 types[HTMLInputElement::TELEPHONE] = "tel"; 688 types[HTMLInputElement::TEXT] = "text"; 689 types[HTMLInputElement::TIME] = "time"; 690 types[HTMLInputElement::URL] = "url"; 691 types[HTMLInputElement::WEEK] = "week"; 692 return types; 693} 694 695const AtomicString& HTMLInputElement::formControlType() const 696{ 697 static const AtomicString* formControlTypes = createFormControlTypes(); 698 return formControlTypes[inputType()]; 699} 700 701bool HTMLInputElement::saveFormControlState(String& result) const 702{ 703 if (!autoComplete()) 704 return false; 705 706 switch (inputType()) { 707 case BUTTON: 708 case COLOR: 709 case DATE: 710 case DATETIME: 711 case DATETIMELOCAL: 712 case EMAIL: 713 case FILE: 714 case HIDDEN: 715 case IMAGE: 716 case ISINDEX: 717 case MONTH: 718 case NUMBER: 719 case RANGE: 720 case RESET: 721 case SEARCH: 722 case SUBMIT: 723 case TELEPHONE: 724 case TEXT: 725 case TIME: 726 case URL: 727 case WEEK: 728 result = value(); 729 return true; 730 case CHECKBOX: 731 case RADIO: 732 result = checked() ? "on" : "off"; 733 return true; 734 case PASSWORD: 735 return false; 736 } 737 ASSERT_NOT_REACHED(); 738 return false; 739} 740 741void HTMLInputElement::restoreFormControlState(const String& state) 742{ 743 ASSERT(inputType() != PASSWORD); // should never save/restore password fields 744 switch (inputType()) { 745 case BUTTON: 746 case COLOR: 747 case DATE: 748 case DATETIME: 749 case DATETIMELOCAL: 750 case EMAIL: 751 case FILE: 752 case HIDDEN: 753 case IMAGE: 754 case ISINDEX: 755 case MONTH: 756 case NUMBER: 757 case RANGE: 758 case RESET: 759 case SEARCH: 760 case SUBMIT: 761 case TELEPHONE: 762 case TEXT: 763 case TIME: 764 case URL: 765 case WEEK: 766 setValue(state); 767 break; 768 case CHECKBOX: 769 case RADIO: 770 setChecked(state == "on"); 771 break; 772 case PASSWORD: 773 break; 774 } 775} 776 777bool HTMLInputElement::canStartSelection() const 778{ 779 if (!isTextField()) 780 return false; 781 return HTMLFormControlElementWithState::canStartSelection(); 782} 783 784bool HTMLInputElement::canHaveSelection() const 785{ 786 return isTextField(); 787} 788 789void HTMLInputElement::accessKeyAction(bool sendToAnyElement) 790{ 791 switch (inputType()) { 792 case BUTTON: 793 case CHECKBOX: 794 case FILE: 795 case IMAGE: 796 case RADIO: 797 case RANGE: 798 case RESET: 799 case SUBMIT: 800 focus(false); 801 // send the mouse button events iff the caller specified sendToAnyElement 802 dispatchSimulatedClick(0, sendToAnyElement); 803 break; 804 case HIDDEN: 805 // a no-op for this type 806 break; 807 case COLOR: 808 case DATE: 809 case DATETIME: 810 case DATETIMELOCAL: 811 case EMAIL: 812 case ISINDEX: 813 case MONTH: 814 case NUMBER: 815 case PASSWORD: 816 case SEARCH: 817 case TELEPHONE: 818 case TEXT: 819 case TIME: 820 case URL: 821 case WEEK: 822 // should never restore previous selection here 823 focus(false); 824 break; 825 } 826} 827 828bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const 829{ 830 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) || 831 attrName == vspaceAttr || 832 attrName == hspaceAttr) { 833 result = eUniversal; 834 return false; 835 } 836 837 if (attrName == alignAttr) { 838 if (inputType() == IMAGE) { 839 // Share with <img> since the alignment behavior is the same. 840 result = eReplaced; 841 return false; 842 } 843 } 844 845 return HTMLElement::mapToEntry(attrName, result); 846} 847 848void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr) 849{ 850 if (attr->name() == nameAttr) { 851 checkedRadioButtons(this).removeButton(this); 852 m_data.setName(attr->value()); 853 checkedRadioButtons(this).addButton(this); 854 HTMLFormControlElementWithState::parseMappedAttribute(attr); 855 } else if (attr->name() == autocompleteAttr) { 856 if (equalIgnoringCase(attr->value(), "off")) { 857 m_autocomplete = Off; 858 registerForActivationCallbackIfNeeded(); 859 } else { 860 bool needsToUnregister = m_autocomplete == Off; 861 862 if (attr->isEmpty()) 863 m_autocomplete = Uninitialized; 864 else 865 m_autocomplete = On; 866 867 if (needsToUnregister) 868 unregisterForActivationCallbackIfNeeded(); 869 } 870 } else if (attr->name() == typeAttr) { 871 setInputType(attr->value()); 872 } else if (attr->name() == valueAttr) { 873 // We only need to setChanged if the form is looking at the default value right now. 874 if (m_data.value().isNull()) 875 setNeedsStyleRecalc(); 876 setFormControlValueMatchesRenderer(false); 877 updateValidity(); 878 } else if (attr->name() == checkedAttr) { 879 m_defaultChecked = !attr->isNull(); 880 if (m_useDefaultChecked) { 881 setChecked(m_defaultChecked); 882 m_useDefaultChecked = true; 883 } 884 updateValidity(); 885 } else if (attr->name() == maxlengthAttr) 886 InputElement::parseMaxLengthAttribute(m_data, this, this, attr); 887 else if (attr->name() == sizeAttr) 888 InputElement::parseSizeAttribute(m_data, this, attr); 889 else if (attr->name() == altAttr) { 890 if (renderer() && inputType() == IMAGE) 891 toRenderImage(renderer())->updateAltText(); 892 } else if (attr->name() == srcAttr) { 893 if (renderer() && inputType() == IMAGE) { 894 if (!m_imageLoader) 895 m_imageLoader.set(new HTMLImageLoader(this)); 896 m_imageLoader->updateFromElementIgnoringPreviousError(); 897 } 898 } else if (attr->name() == usemapAttr || 899 attr->name() == accesskeyAttr) { 900 // FIXME: ignore for the moment 901 } else if (attr->name() == vspaceAttr) { 902 addCSSLength(attr, CSSPropertyMarginTop, attr->value()); 903 addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); 904 } else if (attr->name() == hspaceAttr) { 905 addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); 906 addCSSLength(attr, CSSPropertyMarginRight, attr->value()); 907 } else if (attr->name() == alignAttr) { 908 if (inputType() == IMAGE) 909 addHTMLAlignment(attr); 910 } else if (attr->name() == widthAttr) { 911 if (respectHeightAndWidthAttrs()) 912 addCSSLength(attr, CSSPropertyWidth, attr->value()); 913 } else if (attr->name() == heightAttr) { 914 if (respectHeightAndWidthAttrs()) 915 addCSSLength(attr, CSSPropertyHeight, attr->value()); 916 } 917 // Search field and slider attributes all just cause updateFromElement to be called through style 918 // recalcing. 919 else if (attr->name() == onsearchAttr) { 920 setAttributeEventListener(eventNames().searchEvent, createAttributeEventListener(this, attr)); 921 } else if (attr->name() == resultsAttr) { 922 int oldResults = m_maxResults; 923 m_maxResults = !attr->isNull() ? std::min(attr->value().toInt(), maxSavedResults) : -1; 924 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right 925 // time to relayout for this change. 926 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) { 927 detach(); 928 attach(); 929 } 930 setNeedsStyleRecalc(); 931 } else if (attr->name() == autosaveAttr || 932 attr->name() == incrementalAttr || 933 attr->name() == minAttr || 934 attr->name() == maxAttr || 935 attr->name() == multipleAttr || 936 attr->name() == precisionAttr) 937 setNeedsStyleRecalc(); 938 else if (attr->name() == patternAttr) 939 updateValidity(); 940#if ENABLE(DATALIST) 941 else if (attr->name() == listAttr) 942 m_hasNonEmptyList = !attr->isEmpty(); 943 // FIXME: we need to tell this change to a renderer if the attribute affects the appearance. 944#endif 945 else 946 HTMLTextFormControlElement::parseMappedAttribute(attr); 947} 948 949bool HTMLInputElement::rendererIsNeeded(RenderStyle *style) 950{ 951 if (inputType() == HIDDEN) 952 return false; 953 return HTMLFormControlElementWithState::rendererIsNeeded(style); 954} 955 956RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style) 957{ 958 switch (inputType()) { 959 case BUTTON: 960 case RESET: 961 case SUBMIT: 962 return new (arena) RenderButton(this); 963 case CHECKBOX: 964 case RADIO: 965 return RenderObject::createObject(this, style); 966 case FILE: 967 return new (arena) RenderFileUploadControl(this); 968 case HIDDEN: 969 break; 970 case IMAGE: 971 return new (arena) RenderImage(this); 972 case RANGE: 973 return new (arena) RenderSlider(this); 974 case COLOR: 975 case DATE: 976 case DATETIME: 977 case DATETIMELOCAL: 978 case EMAIL: 979 case ISINDEX: 980 case MONTH: 981 case NUMBER: 982 case PASSWORD: 983 case SEARCH: 984 case TELEPHONE: 985 case TEXT: 986 case TIME: 987 case URL: 988 case WEEK: 989 return new (arena) RenderTextControlSingleLine(this, placeholderShouldBeVisible()); 990 } 991 ASSERT(false); 992 return 0; 993} 994 995void HTMLInputElement::attach() 996{ 997 if (!m_inited) { 998 if (!m_haveType) 999 setInputType(getAttribute(typeAttr)); 1000 m_inited = true; 1001 } 1002 1003 HTMLFormControlElementWithState::attach(); 1004 1005 if (inputType() == IMAGE) { 1006 if (!m_imageLoader) 1007 m_imageLoader.set(new HTMLImageLoader(this)); 1008 m_imageLoader->updateFromElement(); 1009 if (renderer() && m_imageLoader->haveFiredBeforeLoadEvent()) { 1010 RenderImage* imageObj = toRenderImage(renderer()); 1011 imageObj->setCachedImage(m_imageLoader->image()); 1012 1013 // If we have no image at all because we have no src attribute, set 1014 // image height and width for the alt text instead. 1015 if (!m_imageLoader->image() && !imageObj->cachedImage()) 1016 imageObj->setImageSizeForAltText(); 1017 } 1018 } 1019} 1020 1021void HTMLInputElement::detach() 1022{ 1023 HTMLFormControlElementWithState::detach(); 1024 setFormControlValueMatchesRenderer(false); 1025} 1026 1027String HTMLInputElement::altText() const 1028{ 1029 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen 1030 // also heavily discussed by Hixie on bugzilla 1031 // note this is intentionally different to HTMLImageElement::altText() 1032 String alt = getAttribute(altAttr); 1033 // fall back to title attribute 1034 if (alt.isNull()) 1035 alt = getAttribute(titleAttr); 1036 if (alt.isNull()) 1037 alt = getAttribute(valueAttr); 1038 if (alt.isEmpty()) 1039 alt = inputElementAltText(); 1040 return alt; 1041} 1042 1043bool HTMLInputElement::isSuccessfulSubmitButton() const 1044{ 1045 // HTML spec says that buttons must have names to be considered successful. 1046 // However, other browsers do not impose this constraint. So we do likewise. 1047 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT); 1048} 1049 1050bool HTMLInputElement::isActivatedSubmit() const 1051{ 1052 return m_activeSubmit; 1053} 1054 1055void HTMLInputElement::setActivatedSubmit(bool flag) 1056{ 1057 m_activeSubmit = flag; 1058} 1059 1060bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart) 1061{ 1062 // image generates its own names, but for other types there is no form data unless there's a name 1063 if (name().isEmpty() && inputType() != IMAGE) 1064 return false; 1065 1066 switch (inputType()) { 1067 case COLOR: 1068 case DATE: 1069 case DATETIME: 1070 case DATETIMELOCAL: 1071 case EMAIL: 1072 case HIDDEN: 1073 case ISINDEX: 1074 case MONTH: 1075 case NUMBER: 1076 case PASSWORD: 1077 case RANGE: 1078 case SEARCH: 1079 case TELEPHONE: 1080 case TEXT: 1081 case TIME: 1082 case URL: 1083 case WEEK: 1084 // always successful 1085 encoding.appendData(name(), value()); 1086 return true; 1087 1088 case CHECKBOX: 1089 case RADIO: 1090 if (checked()) { 1091 encoding.appendData(name(), value()); 1092 return true; 1093 } 1094 break; 1095 1096 case BUTTON: 1097 case RESET: 1098 // these types of buttons are never successful 1099 return false; 1100 1101 case IMAGE: 1102 if (m_activeSubmit) { 1103 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), m_xPos); 1104 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), m_yPos); 1105 if (!name().isEmpty() && !value().isEmpty()) 1106 encoding.appendData(name(), value()); 1107 return true; 1108 } 1109 break; 1110 1111 case SUBMIT: 1112 if (m_activeSubmit) { 1113 String enc_str = valueWithDefault(); 1114 encoding.appendData(name(), enc_str); 1115 return true; 1116 } 1117 break; 1118 1119 case FILE: { 1120 unsigned numFiles = m_fileList->length(); 1121 if (!multipart) { 1122 // Send only the basenames. 1123 // 4.10.16.4 and 4.10.16.6 sections in HTML5. 1124 1125 // Unlike the multipart case, we have no special 1126 // handling for the empty fileList because Netscape 1127 // doesn't support for non-multipart submission of 1128 // file inputs, and Firefox doesn't add "name=" query 1129 // parameter. 1130 1131 for (unsigned i = 0; i < numFiles; ++i) { 1132 encoding.appendData(name(), m_fileList->item(i)->fileName()); 1133 } 1134 return true; 1135 } 1136 1137 // If no filename at all is entered, return successful but empty. 1138 // Null would be more logical, but Netscape posts an empty file. Argh. 1139 if (!numFiles) { 1140 encoding.appendFile(name(), File::create("")); 1141 return true; 1142 } 1143 1144 for (unsigned i = 0; i < numFiles; ++i) 1145 encoding.appendFile(name(), m_fileList->item(i)); 1146 return true; 1147 } 1148 } 1149 return false; 1150} 1151 1152void HTMLInputElement::reset() 1153{ 1154 if (storesValueSeparateFromAttribute()) 1155 setValue(String()); 1156 1157 setChecked(m_defaultChecked); 1158 m_useDefaultChecked = true; 1159} 1160 1161bool HTMLInputElement::isTextField() const 1162{ 1163 switch (inputType()) { 1164 case COLOR: 1165 case DATE: 1166 case DATETIME: 1167 case DATETIMELOCAL: 1168 case EMAIL: 1169 case ISINDEX: 1170 case MONTH: 1171 case NUMBER: 1172 case PASSWORD: 1173 case SEARCH: 1174 case TELEPHONE: 1175 case TEXT: 1176 case TIME: 1177 case URL: 1178 case WEEK: 1179 return true; 1180 case BUTTON: 1181 case CHECKBOX: 1182 case FILE: 1183 case HIDDEN: 1184 case IMAGE: 1185 case RADIO: 1186 case RANGE: 1187 case RESET: 1188 case SUBMIT: 1189 return false; 1190 } 1191 ASSERT_NOT_REACHED(); 1192 return false; 1193} 1194 1195void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent) 1196{ 1197 if (checked() == nowChecked) 1198 return; 1199 1200 checkedRadioButtons(this).removeButton(this); 1201 1202 m_useDefaultChecked = false; 1203 m_checked = nowChecked; 1204 setNeedsStyleRecalc(); 1205 1206 checkedRadioButtons(this).addButton(this); 1207 1208 if (renderer() && renderer()->style()->hasAppearance()) 1209 renderer()->theme()->stateChanged(renderer(), CheckedState); 1210 1211 // Ideally we'd do this from the render tree (matching 1212 // RenderTextView), but it's not possible to do it at the moment 1213 // because of the way the code is structured. 1214 if (renderer() && AXObjectCache::accessibilityEnabled()) 1215 renderer()->document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXCheckedStateChanged, true); 1216 1217 // Only send a change event for items in the document (avoid firing during 1218 // parsing) and don't send a change event for a radio button that's getting 1219 // unchecked to match other browsers. DOM is not a useful standard for this 1220 // because it says only to fire change events at "lose focus" time, which is 1221 // definitely wrong in practice for these types of elements. 1222 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked)) 1223 dispatchFormControlChangeEvent(); 1224} 1225 1226void HTMLInputElement::setIndeterminate(bool _indeterminate) 1227{ 1228 // Only checkboxes honor indeterminate. 1229 if (inputType() != CHECKBOX || indeterminate() == _indeterminate) 1230 return; 1231 1232 m_indeterminate = _indeterminate; 1233 1234 setNeedsStyleRecalc(); 1235 1236 if (renderer() && renderer()->style()->hasAppearance()) 1237 renderer()->theme()->stateChanged(renderer(), CheckedState); 1238} 1239 1240int HTMLInputElement::size() const 1241{ 1242 return m_data.size(); 1243} 1244 1245void HTMLInputElement::copyNonAttributeProperties(const Element* source) 1246{ 1247 const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source); 1248 1249 m_data.setValue(sourceElement->m_data.value()); 1250 m_checked = sourceElement->m_checked; 1251 m_indeterminate = sourceElement->m_indeterminate; 1252 1253 HTMLFormControlElementWithState::copyNonAttributeProperties(source); 1254} 1255 1256String HTMLInputElement::value() const 1257{ 1258 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control 1259 // but we don't want to break existing websites, who may be relying on being able to get the file name as a value. 1260 if (inputType() == FILE) { 1261 if (!m_fileList->isEmpty()) 1262 return m_fileList->item(0)->fileName(); 1263 return String(); 1264 } 1265 1266 String value = m_data.value(); 1267 if (value.isNull()) { 1268 value = sanitizeValue(getAttribute(valueAttr)); 1269 1270 // If no attribute exists, then just use "on" or "" based off the checked() state of the control. 1271 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO)) 1272 return checked() ? "on" : ""; 1273 } 1274 1275 return value; 1276} 1277 1278String HTMLInputElement::valueWithDefault() const 1279{ 1280 String v = value(); 1281 if (v.isNull()) { 1282 switch (inputType()) { 1283 case BUTTON: 1284 case CHECKBOX: 1285 case COLOR: 1286 case DATE: 1287 case DATETIME: 1288 case DATETIMELOCAL: 1289 case EMAIL: 1290 case FILE: 1291 case HIDDEN: 1292 case IMAGE: 1293 case ISINDEX: 1294 case MONTH: 1295 case NUMBER: 1296 case PASSWORD: 1297 case RADIO: 1298 case RANGE: 1299 case SEARCH: 1300 case TELEPHONE: 1301 case TEXT: 1302 case TIME: 1303 case URL: 1304 case WEEK: 1305 break; 1306 case RESET: 1307 v = resetButtonDefaultLabel(); 1308 break; 1309 case SUBMIT: 1310 v = submitButtonDefaultLabel(); 1311 break; 1312 } 1313 } 1314 return v; 1315} 1316 1317void HTMLInputElement::setValueForUser(const String& value) 1318{ 1319 // Call setValue and make it send a change event. 1320 setValue(value, true); 1321} 1322 1323void HTMLInputElement::setValue(const String& value, bool sendChangeEvent) 1324{ 1325 // For security reasons, we don't allow setting the filename, but we do allow clearing it. 1326 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control 1327 // but we don't want to break existing websites, who may be relying on this method to clear things. 1328 if (inputType() == FILE && !value.isEmpty()) 1329 return; 1330 1331 setFormControlValueMatchesRenderer(false); 1332 if (storesValueSeparateFromAttribute()) { 1333 if (inputType() == FILE) 1334 m_fileList->clear(); 1335 else { 1336 m_data.setValue(sanitizeValue(value)); 1337 if (isTextField()) { 1338 updatePlaceholderVisibility(false); 1339 if (inDocument()) 1340 document()->updateStyleIfNeeded(); 1341 } 1342 } 1343 if (renderer()) 1344 renderer()->updateFromElement(); 1345 setNeedsStyleRecalc(); 1346 } else 1347 setAttribute(valueAttr, sanitizeValue(value)); 1348 1349 if (isTextField()) { 1350 unsigned max = m_data.value().length(); 1351#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 1352 // Make sure our UI side textfield changes to match the RenderTextControl 1353 android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, false, value); 1354#endif 1355 if (document()->focusedNode() == this) 1356 InputElement::updateSelectionRange(this, this, max, max); 1357 else 1358 cacheSelection(max, max); 1359 } 1360 1361 // Don't dispatch the change event when focused, it will be dispatched 1362 // when the control loses focus. 1363 if (sendChangeEvent && document()->focusedNode() != this) 1364 dispatchFormControlChangeEvent(); 1365 1366 InputElement::notifyFormStateChanged(this); 1367 updateValidity(); 1368} 1369 1370String HTMLInputElement::placeholder() const 1371{ 1372 return getAttribute(placeholderAttr).string(); 1373} 1374 1375void HTMLInputElement::setPlaceholder(const String& value) 1376{ 1377 setAttribute(placeholderAttr, value); 1378} 1379 1380bool HTMLInputElement::searchEventsShouldBeDispatched() const 1381{ 1382 return hasAttribute(incrementalAttr); 1383} 1384 1385void HTMLInputElement::setValueFromRenderer(const String& value) 1386{ 1387 // File upload controls will always use setFileListFromRenderer. 1388 ASSERT(inputType() != FILE); 1389 updatePlaceholderVisibility(false); 1390 InputElement::setValueFromRenderer(m_data, this, this, value); 1391 updateValidity(); 1392} 1393 1394void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths) 1395{ 1396 m_fileList->clear(); 1397 int size = paths.size(); 1398 for (int i = 0; i < size; i++) 1399 m_fileList->append(File::create(paths[i])); 1400 1401 setFormControlValueMatchesRenderer(true); 1402 InputElement::notifyFormStateChanged(this); 1403 updateValidity(); 1404} 1405 1406bool HTMLInputElement::storesValueSeparateFromAttribute() const 1407{ 1408 switch (inputType()) { 1409 case BUTTON: 1410 case CHECKBOX: 1411 case HIDDEN: 1412 case IMAGE: 1413 case RADIO: 1414 case RESET: 1415 case SUBMIT: 1416 return false; 1417 case COLOR: 1418 case DATE: 1419 case DATETIME: 1420 case DATETIMELOCAL: 1421 case EMAIL: 1422 case FILE: 1423 case ISINDEX: 1424 case MONTH: 1425 case NUMBER: 1426 case PASSWORD: 1427 case RANGE: 1428 case SEARCH: 1429 case TELEPHONE: 1430 case TEXT: 1431 case TIME: 1432 case URL: 1433 case WEEK: 1434 return true; 1435 } 1436 return false; 1437} 1438 1439void* HTMLInputElement::preDispatchEventHandler(Event *evt) 1440{ 1441 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here. 1442 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here. 1443 void* result = 0; 1444 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent() 1445 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1446 if (inputType() == CHECKBOX) { 1447 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for 1448 // indeterminate. 1449 if (indeterminate()) { 1450 result = (void*)0x2; 1451 setIndeterminate(false); 1452 } else { 1453 if (checked()) 1454 result = (void*)0x1; 1455 setChecked(!checked(), true); 1456 } 1457 } else { 1458 // For radio buttons, store the current selected radio object. 1459 // We really want radio groups to end up in sane states, i.e., to have something checked. 1460 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since 1461 // we want some object in the radio group to actually get selected. 1462 HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name()); 1463 if (currRadio) { 1464 // We have a radio button selected that is not us. Cache it in our result field and ref it so 1465 // that it can't be destroyed. 1466 currRadio->ref(); 1467 result = currRadio; 1468 } 1469 setChecked(true, true); 1470 } 1471 } 1472 return result; 1473} 1474 1475void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data) 1476{ 1477 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent() 1478 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1479 if (inputType() == CHECKBOX) { 1480 // Reverse the checking we did in preDispatch. 1481 if (evt->defaultPrevented() || evt->defaultHandled()) { 1482 if (data == (void*)0x2) 1483 setIndeterminate(true); 1484 else 1485 setChecked(data); 1486 } 1487 } else if (data) { 1488 HTMLInputElement* input = static_cast<HTMLInputElement*>(data); 1489 if (evt->defaultPrevented() || evt->defaultHandled()) { 1490 // Restore the original selected radio button if possible. 1491 // Make sure it is still a radio button and only do the restoration if it still 1492 // belongs to our group. 1493 1494 if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) { 1495 // Ok, the old radio button is still in our form and in our group and is still a 1496 // radio button, so it's safe to restore selection to it. 1497 input->setChecked(true); 1498 } 1499 } 1500 input->deref(); 1501 } 1502 1503 // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler(). 1504 evt->setDefaultHandled(); 1505 } 1506} 1507 1508void HTMLInputElement::defaultEventHandler(Event* evt) 1509{ 1510 // FIXME: It would be better to refactor this for the different types of input element. 1511 // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific. 1512 1513 bool clickDefaultFormButton = false; 1514 1515 if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n") 1516 clickDefaultFormButton = true; 1517 1518 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == eventNames().clickEvent) { 1519 // record the mouse position for when we get the DOMActivate event 1520 MouseEvent* me = static_cast<MouseEvent*>(evt); 1521 // FIXME: We could just call offsetX() and offsetY() on the event, 1522 // but that's currently broken, so for now do the computation here. 1523 if (me->isSimulated() || !renderer()) { 1524 m_xPos = 0; 1525 m_yPos = 0; 1526 } else { 1527 // FIXME: This doesn't work correctly with transforms. 1528 // FIXME: pageX/pageY need adjusting for pageZoomFactor(). Use actualPageLocation()? 1529 IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute()); 1530 m_xPos = me->pageX() - absOffset.x(); 1531 m_yPos = me->pageY() - absOffset.y(); 1532 } 1533 } 1534 1535 if (isTextField() 1536 && evt->type() == eventNames().keydownEvent 1537 && evt->isKeyboardEvent() 1538 && focused() 1539 && document()->frame() 1540 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { 1541 evt->setDefaultHandled(); 1542 return; 1543 } 1544 1545 if (inputType() == RADIO 1546 && evt->isMouseEvent() 1547 && evt->type() == eventNames().clickEvent 1548 && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1549 evt->setDefaultHandled(); 1550 return; 1551 } 1552 1553 // Call the base event handler before any of our own event handling for almost all events in text fields. 1554 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. 1555 bool callBaseClassEarly = isTextField() && !clickDefaultFormButton 1556 && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent); 1557 if (callBaseClassEarly) { 1558 HTMLFormControlElementWithState::defaultEventHandler(evt); 1559 if (evt->defaultHandled()) 1560 return; 1561 } 1562 1563 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means 1564 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks 1565 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element 1566 // must dispatch a DOMActivate event - a click event will not do the job. 1567 if (evt->type() == eventNames().DOMActivateEvent && !disabled()) { 1568 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) { 1569 if (!form()) 1570 return; 1571 if (inputType() == RESET) 1572 form()->reset(); 1573 else { 1574 m_activeSubmit = true; 1575 // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse 1576 // event (if any) here instead of relying on the variables set above when 1577 // processing the click event. Even better, appendFormData could pass the 1578 // event in, and then we could get rid of m_xPos and m_yPos altogether! 1579 if (!form()->prepareSubmit(evt)) { 1580 m_xPos = 0; 1581 m_yPos = 0; 1582 } 1583 m_activeSubmit = false; 1584 } 1585 } else if (inputType() == FILE && renderer()) 1586 toRenderFileUploadControl(renderer())->click(); 1587 } 1588 1589 // Use key press event here since sending simulated mouse events 1590 // on key down blocks the proper sending of the key press event. 1591 if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) { 1592 bool clickElement = false; 1593 1594 int charCode = static_cast<KeyboardEvent*>(evt)->charCode(); 1595 1596 if (charCode == '\r') { 1597 switch (inputType()) { 1598 case CHECKBOX: 1599 case COLOR: 1600 case DATE: 1601 case DATETIME: 1602 case DATETIMELOCAL: 1603 case EMAIL: 1604 case HIDDEN: 1605 case ISINDEX: 1606 case MONTH: 1607 case NUMBER: 1608 case PASSWORD: 1609 case RANGE: 1610 case SEARCH: 1611 case TELEPHONE: 1612 case TEXT: 1613 case TIME: 1614 case URL: 1615 case WEEK: 1616 // Simulate mouse click on the default form button for enter for these types of elements. 1617 clickDefaultFormButton = true; 1618 break; 1619 case BUTTON: 1620 case FILE: 1621 case IMAGE: 1622 case RESET: 1623 case SUBMIT: 1624 // Simulate mouse click for enter for these types of elements. 1625 clickElement = true; 1626 break; 1627 case RADIO: 1628 break; // Don't do anything for enter on a radio button. 1629 } 1630 } else if (charCode == ' ') { 1631 switch (inputType()) { 1632 case BUTTON: 1633 case CHECKBOX: 1634 case FILE: 1635 case IMAGE: 1636 case RESET: 1637 case SUBMIT: 1638 case RADIO: 1639 // Prevent scrolling down the page. 1640 evt->setDefaultHandled(); 1641 return; 1642 default: 1643 break; 1644 } 1645 } 1646 1647 if (clickElement) { 1648 dispatchSimulatedClick(evt); 1649 evt->setDefaultHandled(); 1650 return; 1651 } 1652 } 1653 1654 if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) { 1655 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); 1656 1657 if (key == "U+0020") { 1658 switch (inputType()) { 1659 case BUTTON: 1660 case CHECKBOX: 1661 case FILE: 1662 case IMAGE: 1663 case RESET: 1664 case SUBMIT: 1665 case RADIO: 1666 setActive(true, true); 1667 // No setDefaultHandled(), because IE dispatches a keypress in this case 1668 // and the caller will only dispatch a keypress if we don't call setDefaultHandled. 1669 return; 1670 default: 1671 break; 1672 } 1673 } 1674 1675// allow enter to change state of radio 1676 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) { 1677 // Left and up mean "previous radio button". 1678 // Right and down mean "next radio button". 1679 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves 1680 // to the right). Seems strange, but we'll match it. 1681 bool forward = (key == "Down" || key == "Right"); 1682 1683 // We can only stay within the form's children if the form hasn't been demoted to a leaf because 1684 // of malformed HTML. 1685 Node* n = this; 1686 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) { 1687 // Once we encounter a form element, we know we're through. 1688 if (n->hasTagName(formTag)) 1689 break; 1690 1691 // Look for more radio buttons. 1692 if (n->hasTagName(inputTag)) { 1693 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n); 1694 if (elt->form() != form()) 1695 break; 1696 if (n->hasTagName(inputTag)) { 1697 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n); 1698 if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) { 1699 inputElt->setChecked(true); 1700 document()->setFocusedNode(inputElt); 1701 inputElt->dispatchSimulatedClick(evt, false, false); 1702 evt->setDefaultHandled(); 1703 break; 1704 } 1705 } 1706 } 1707 } 1708 } 1709 } 1710 1711 if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) { 1712 bool clickElement = false; 1713 1714 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); 1715 1716 if (key == "U+0020") { 1717 switch (inputType()) { 1718 case BUTTON: 1719 case CHECKBOX: 1720 case FILE: 1721 case IMAGE: 1722 case RESET: 1723 case SUBMIT: 1724 // Simulate mouse click for spacebar for these types of elements. 1725 // The AppKit already does this for some, but not all, of them. 1726 clickElement = true; 1727 break; 1728 case RADIO: 1729 // If an unselected radio is tabbed into (because the entire group has nothing 1730 // checked, or because of some explicit .focus() call), then allow space to check it. 1731 if (!checked()) 1732 clickElement = true; 1733 break; 1734 case COLOR: 1735 case DATE: 1736 case DATETIME: 1737 case DATETIMELOCAL: 1738 case EMAIL: 1739 case HIDDEN: 1740 case ISINDEX: 1741 case MONTH: 1742 case NUMBER: 1743 case PASSWORD: 1744 case RANGE: 1745 case SEARCH: 1746 case TELEPHONE: 1747 case TEXT: 1748 case TIME: 1749 case URL: 1750 case WEEK: 1751 break; 1752 } 1753 } 1754 1755 if (clickElement) { 1756 if (active()) 1757 dispatchSimulatedClick(evt); 1758 evt->setDefaultHandled(); 1759 return; 1760 } 1761 } 1762 1763 if (clickDefaultFormButton) { 1764 if (isSearchField()) { 1765 addSearchResult(); 1766 onSearch(); 1767 } 1768 // Fire onChange for text fields. 1769 RenderObject* r = renderer(); 1770 if (r && r->isTextField() && toRenderTextControl(r)->wasChangedSinceLastChangeEvent()) { 1771 dispatchFormControlChangeEvent(); 1772 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it. 1773 r = renderer(); 1774 if (r && r->isTextField()) 1775 toRenderTextControl(r)->setChangedSinceLastChangeEvent(false); 1776 } 1777 1778 RefPtr<HTMLFormElement> formForSubmission = form(); 1779 // If there is no form and the element is an <isindex>, then create a temporary form just to be used for submission. 1780 if (!formForSubmission && inputType() == ISINDEX) 1781 formForSubmission = createTemporaryFormForIsIndex(); 1782 1783 // Form may never have been present, or may have been destroyed by code responding to the change event. 1784 if (formForSubmission) 1785 formForSubmission->submitClick(evt); 1786 1787 evt->setDefaultHandled(); 1788 return; 1789 } 1790 1791 if (evt->isBeforeTextInsertedEvent()) 1792 InputElement::handleBeforeTextInsertedEvent(m_data, this, this, evt); 1793 1794 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent)) 1795 toRenderTextControlSingleLine(renderer())->forwardEvent(evt); 1796 1797 if (inputType() == RANGE && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())) 1798 toRenderSlider(renderer())->forwardEvent(evt); 1799 1800 if (!callBaseClassEarly && !evt->defaultHandled()) 1801 HTMLFormControlElementWithState::defaultEventHandler(evt); 1802} 1803 1804PassRefPtr<HTMLFormElement> HTMLInputElement::createTemporaryFormForIsIndex() 1805{ 1806 RefPtr<HTMLFormElement> form = new HTMLFormElement(formTag, document()); 1807 form->registerFormElement(this); 1808 form->setMethod("GET"); 1809 if (!document()->baseURL().isEmpty()) { 1810 // We treat the href property of the <base> element as the form action, as per section 7.5 1811 // "Queries and Indexes" of the HTML 2.0 spec. <http://www.w3.org/MarkUp/html-spec/html-spec_7.html#SEC7.5>. 1812 form->setAction(document()->baseURL().string()); 1813 } 1814 return form.release(); 1815} 1816 1817bool HTMLInputElement::isURLAttribute(Attribute *attr) const 1818{ 1819 return (attr->name() == srcAttr); 1820} 1821 1822String HTMLInputElement::defaultValue() const 1823{ 1824 return getAttribute(valueAttr); 1825} 1826 1827void HTMLInputElement::setDefaultValue(const String &value) 1828{ 1829 setAttribute(valueAttr, value); 1830} 1831 1832bool HTMLInputElement::defaultChecked() const 1833{ 1834 return !getAttribute(checkedAttr).isNull(); 1835} 1836 1837void HTMLInputElement::setDefaultChecked(bool defaultChecked) 1838{ 1839 setAttribute(checkedAttr, defaultChecked ? "" : 0); 1840} 1841 1842void HTMLInputElement::setDefaultName(const AtomicString& name) 1843{ 1844 m_data.setName(name); 1845} 1846 1847String HTMLInputElement::accept() const 1848{ 1849 return getAttribute(acceptAttr); 1850} 1851 1852void HTMLInputElement::setAccept(const String &value) 1853{ 1854 setAttribute(acceptAttr, value); 1855} 1856 1857String HTMLInputElement::accessKey() const 1858{ 1859 return getAttribute(accesskeyAttr); 1860} 1861 1862void HTMLInputElement::setAccessKey(const String &value) 1863{ 1864 setAttribute(accesskeyAttr, value); 1865} 1866 1867String HTMLInputElement::align() const 1868{ 1869 return getAttribute(alignAttr); 1870} 1871 1872void HTMLInputElement::setAlign(const String &value) 1873{ 1874 setAttribute(alignAttr, value); 1875} 1876 1877String HTMLInputElement::alt() const 1878{ 1879 return getAttribute(altAttr); 1880} 1881 1882void HTMLInputElement::setAlt(const String &value) 1883{ 1884 setAttribute(altAttr, value); 1885} 1886 1887int HTMLInputElement::maxLength() const 1888{ 1889 return m_data.maxLength(); 1890} 1891 1892void HTMLInputElement::setMaxLength(int maxLength, ExceptionCode& ec) 1893{ 1894 if (maxLength < 0) 1895 ec = INDEX_SIZE_ERR; 1896 else 1897 setAttribute(maxlengthAttr, String::number(maxLength)); 1898} 1899 1900bool HTMLInputElement::multiple() const 1901{ 1902 return !getAttribute(multipleAttr).isNull(); 1903} 1904 1905void HTMLInputElement::setMultiple(bool multiple) 1906{ 1907 setAttribute(multipleAttr, multiple ? "" : 0); 1908} 1909 1910void HTMLInputElement::setSize(unsigned _size) 1911{ 1912 setAttribute(sizeAttr, String::number(_size)); 1913} 1914 1915KURL HTMLInputElement::src() const 1916{ 1917 return document()->completeURL(getAttribute(srcAttr)); 1918} 1919 1920void HTMLInputElement::setSrc(const String &value) 1921{ 1922 setAttribute(srcAttr, value); 1923} 1924 1925String HTMLInputElement::useMap() const 1926{ 1927 return getAttribute(usemapAttr); 1928} 1929 1930void HTMLInputElement::setUseMap(const String &value) 1931{ 1932 setAttribute(usemapAttr, value); 1933} 1934 1935void HTMLInputElement::setAutofilled(bool b) 1936{ 1937 if (b == m_autofilled) 1938 return; 1939 1940 m_autofilled = b; 1941 setNeedsStyleRecalc(); 1942} 1943 1944FileList* HTMLInputElement::files() 1945{ 1946 if (inputType() != FILE) 1947 return 0; 1948 return m_fileList.get(); 1949} 1950 1951String HTMLInputElement::sanitizeValue(const String& proposedValue) const 1952{ 1953 if (isTextField()) 1954 return InputElement::sanitizeValue(this, proposedValue); 1955 return proposedValue; 1956} 1957 1958bool HTMLInputElement::needsActivationCallback() 1959{ 1960 return inputType() == PASSWORD || m_autocomplete == Off; 1961} 1962 1963void HTMLInputElement::registerForActivationCallbackIfNeeded() 1964{ 1965 if (needsActivationCallback()) 1966 document()->registerForDocumentActivationCallbacks(this); 1967} 1968 1969void HTMLInputElement::unregisterForActivationCallbackIfNeeded() 1970{ 1971 if (!needsActivationCallback()) 1972 document()->unregisterForDocumentActivationCallbacks(this); 1973} 1974 1975bool HTMLInputElement::isRequiredFormControl() const 1976{ 1977 if (!required()) 1978 return false; 1979 1980 switch (inputType()) { 1981 case CHECKBOX: 1982 case DATE: 1983 case DATETIME: 1984 case DATETIMELOCAL: 1985 case EMAIL: 1986 case FILE: 1987 case MONTH: 1988 case NUMBER: 1989 case PASSWORD: 1990 case RADIO: 1991 case SEARCH: 1992 case TELEPHONE: 1993 case TEXT: 1994 case TIME: 1995 case URL: 1996 case WEEK: 1997 return true; 1998 case BUTTON: 1999 case COLOR: 2000 case HIDDEN: 2001 case IMAGE: 2002 case ISINDEX: 2003 case RANGE: 2004 case RESET: 2005 case SUBMIT: 2006 return false; 2007 } 2008 2009 ASSERT_NOT_REACHED(); 2010 return false; 2011} 2012 2013void HTMLInputElement::cacheSelection(int start, int end) 2014{ 2015 m_data.setCachedSelectionStart(start); 2016 m_data.setCachedSelectionEnd(end); 2017} 2018 2019void HTMLInputElement::addSearchResult() 2020{ 2021 ASSERT(isSearchField()); 2022 if (renderer()) 2023 toRenderTextControlSingleLine(renderer())->addSearchResult(); 2024} 2025 2026void HTMLInputElement::onSearch() 2027{ 2028 ASSERT(isSearchField()); 2029 if (renderer()) 2030 toRenderTextControlSingleLine(renderer())->stopSearchEventTimer(); 2031 dispatchEvent(Event::create(eventNames().searchEvent, true, false)); 2032} 2033 2034void HTMLInputElement::documentDidBecomeActive() 2035{ 2036 ASSERT(needsActivationCallback()); 2037 reset(); 2038} 2039 2040void HTMLInputElement::willMoveToNewOwnerDocument() 2041{ 2042 // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered 2043 if (needsActivationCallback()) 2044 document()->unregisterForDocumentActivationCallbacks(this); 2045 2046 document()->checkedRadioButtons().removeButton(this); 2047 2048 HTMLFormControlElementWithState::willMoveToNewOwnerDocument(); 2049} 2050 2051void HTMLInputElement::didMoveToNewOwnerDocument() 2052{ 2053 registerForActivationCallbackIfNeeded(); 2054 2055 HTMLFormControlElementWithState::didMoveToNewOwnerDocument(); 2056} 2057 2058void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 2059{ 2060 HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls); 2061 2062 addSubresourceURL(urls, src()); 2063} 2064 2065bool HTMLInputElement::willValidate() const 2066{ 2067 // FIXME: This shall check for new WF2 input types too 2068 return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN && 2069 inputType() != BUTTON && inputType() != RESET; 2070} 2071 2072String HTMLInputElement::formStringFromDouble(double number) 2073{ 2074 // According to HTML5, "the best representation of the number n as a floating 2075 // point number" is a string produced by applying ToString() to n. 2076 DtoaBuffer buffer; 2077 unsigned length; 2078 doubleToStringInJavaScriptFormat(number, buffer, &length); 2079 return String(buffer, length); 2080} 2081 2082bool HTMLInputElement::formStringToDouble(const String& src, double* out) 2083{ 2084 // See HTML5 2.4.4.3 `Real numbers.' 2085 2086 if (src.isEmpty()) 2087 return false; 2088 // String::toDouble() accepts leading + \t \n \v \f \r and SPACE, which are invalid in HTML5. 2089 // So, check the first character. 2090 if (src[0] != '-' && (src[0] < '0' || src[0] > '9')) 2091 return false; 2092 2093 bool valid = false; 2094 double value = src.toDouble(&valid); 2095 if (!valid) 2096 return false; 2097 // NaN and Infinity are not valid numbers according to the standard. 2098 if (isnan(value) || isinf(value)) 2099 return false; 2100 if (out) 2101 *out = value; 2102 return true; 2103} 2104 2105bool HTMLInputElement::formStringToISODateTime(InputType type, const String& formString, ISODateTime* out) 2106{ 2107 ISODateTime ignoredResult; 2108 if (!out) 2109 out = &ignoredResult; 2110 const UChar* characters = formString.characters(); 2111 unsigned length = formString.length(); 2112 unsigned end; 2113 2114 switch (type) { 2115 case DATE: 2116 return out->parseDate(characters, length, 0, end) && end == length; 2117 case DATETIME: 2118 return out->parseDateTime(characters, length, 0, end) && end == length; 2119 case DATETIMELOCAL: 2120 return out->parseDateTimeLocal(characters, length, 0, end) && end == length; 2121 case MONTH: 2122 return out->parseMonth(characters, length, 0, end) && end == length; 2123 case WEEK: 2124 return out->parseWeek(characters, length, 0, end) && end == length; 2125 case TIME: 2126 return out->parseTime(characters, length, 0, end) && end == length; 2127 default: 2128 ASSERT_NOT_REACHED(); 2129 return false; 2130 } 2131} 2132 2133#if ENABLE(DATALIST) 2134HTMLElement* HTMLInputElement::list() const 2135{ 2136 return dataList(); 2137} 2138 2139HTMLDataListElement* HTMLInputElement::dataList() const 2140{ 2141 if (!m_hasNonEmptyList) 2142 return 0; 2143 2144 switch (inputType()) { 2145 case COLOR: 2146 case DATE: 2147 case DATETIME: 2148 case DATETIMELOCAL: 2149 case EMAIL: 2150 case MONTH: 2151 case NUMBER: 2152 case RANGE: 2153 case SEARCH: 2154 case TELEPHONE: 2155 case TEXT: 2156 case TIME: 2157 case URL: 2158 case WEEK: { 2159 Element* element = document()->getElementById(getAttribute(listAttr)); 2160 if (element && element->hasTagName(datalistTag)) 2161 return static_cast<HTMLDataListElement*>(element); 2162 break; 2163 } 2164 case BUTTON: 2165 case CHECKBOX: 2166 case FILE: 2167 case HIDDEN: 2168 case IMAGE: 2169 case ISINDEX: 2170 case PASSWORD: 2171 case RADIO: 2172 case RESET: 2173 case SUBMIT: 2174 break; 2175 } 2176 return 0; 2177} 2178 2179HTMLOptionElement* HTMLInputElement::selectedOption() const 2180{ 2181 String currentValue = value(); 2182 // The empty value never matches to a datalist option because it 2183 // doesn't represent a suggestion according to the standard. 2184 if (currentValue.isEmpty()) 2185 return 0; 2186 2187 HTMLDataListElement* sourceElement = dataList(); 2188 if (!sourceElement) 2189 return 0; 2190 RefPtr<HTMLCollection> options = sourceElement->options(); 2191 for (unsigned i = 0; options && i < options->length(); ++i) { 2192 HTMLOptionElement* option = static_cast<HTMLOptionElement*>(options->item(i)); 2193 if (!option->disabled() && currentValue == option->value()) 2194 return option; 2195 } 2196 return 0; 2197} 2198#endif // ENABLE(DATALIST) 2199 2200} // namespace 2201