HTMLInputElement.cpp revision 5f1ab04193ad0130ca8204aadaceae083aca9881
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 "CSSPropertyNames.h" 30#include "ChromeClient.h" 31#include "Document.h" 32#include "Editor.h" 33#include "Event.h" 34#include "EventHandler.h" 35#include "EventNames.h" 36#include "File.h" 37#include "FileList.h" 38#include "FocusController.h" 39#include "FormDataList.h" 40#include "Frame.h" 41#include "HTMLFormElement.h" 42#include "HTMLImageLoader.h" 43#include "HTMLNames.h" 44#include "ScriptEventListener.h" 45#include "KeyboardEvent.h" 46#include "LocalizedStrings.h" 47#include "MappedAttribute.h" 48#include "MouseEvent.h" 49#include "Page.h" 50#include "RenderButton.h" 51#include "RenderFileUploadControl.h" 52#include "RenderImage.h" 53#include "RenderSlider.h" 54#include "RenderText.h" 55#include "RenderTextControlSingleLine.h" 56#include "RenderTheme.h" 57#include "TextEvent.h" 58#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 59#include "WebViewCore.h" 60#endif 61#include <wtf/StdLibExtras.h> 62 63using namespace std; 64 65namespace WebCore { 66 67using namespace HTMLNames; 68 69const int maxSavedResults = 256; 70 71HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) 72 : HTMLFormControlElementWithState(tagName, doc, f) 73 , m_xPos(0) 74 , m_yPos(0) 75 , m_maxResults(-1) 76 , m_type(TEXT) 77 , m_checked(false) 78 , m_defaultChecked(false) 79 , m_useDefaultChecked(true) 80 , m_indeterminate(false) 81 , m_haveType(false) 82 , m_activeSubmit(false) 83 , m_autocomplete(Uninitialized) 84 , m_autofilled(false) 85 , m_inited(false) 86{ 87 ASSERT(hasTagName(inputTag) || hasTagName(isindexTag)); 88} 89 90HTMLInputElement::~HTMLInputElement() 91{ 92 if (needsActivationCallback()) 93 document()->unregisterForDocumentActivationCallbacks(this); 94 95 document()->checkedRadioButtons().removeButton(this); 96 97 // Need to remove this from the form while it is still an HTMLInputElement, 98 // so can't wait for the base class's destructor to do it. 99 removeFromForm(); 100} 101 102const AtomicString& HTMLInputElement::formControlName() const 103{ 104 return m_data.name(); 105} 106 107bool HTMLInputElement::autoComplete() const 108{ 109 if (m_autocomplete != Uninitialized) 110 return m_autocomplete == On; 111 112 // Assuming we're still in a Form, respect the Form's setting 113 if (HTMLFormElement* form = this->form()) 114 return form->autoComplete(); 115 116 // The default is true 117 return true; 118} 119 120static inline CheckedRadioButtons& checkedRadioButtons(const HTMLInputElement *element) 121{ 122 if (HTMLFormElement* form = element->form()) 123 return form->checkedRadioButtons(); 124 125 return element->document()->checkedRadioButtons(); 126} 127 128bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const 129{ 130 // If text fields can be focused, then they should always be keyboard focusable 131 if (isTextField()) 132 return HTMLFormControlElementWithState::isFocusable(); 133 134 // If the base class says we can't be focused, then we can stop now. 135 if (!HTMLFormControlElementWithState::isKeyboardFocusable(event)) 136 return false; 137 138 if (inputType() == RADIO) { 139 140 // Never allow keyboard tabbing to leave you in the same radio group. Always 141 // skip any other elements in the group. 142 Node* currentFocusedNode = document()->focusedNode(); 143 if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) { 144 HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode); 145 if (focusedInput->inputType() == RADIO && focusedInput->form() == form() && 146 focusedInput->name() == name()) 147 return false; 148 } 149 150 // Allow keyboard focus if we're checked or if nothing in the group is checked. 151 return checked() || !checkedRadioButtons(this).checkedButtonForGroup(name()); 152 } 153 154 return true; 155} 156 157bool HTMLInputElement::isMouseFocusable() const 158{ 159 if (isTextField()) 160 return HTMLFormControlElementWithState::isFocusable(); 161 return HTMLFormControlElementWithState::isMouseFocusable(); 162} 163 164void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection) 165{ 166 if (isTextField()) 167 InputElement::updateFocusAppearance(m_data, this, this, restorePreviousSelection); 168 else 169 HTMLFormControlElementWithState::updateFocusAppearance(restorePreviousSelection); 170} 171 172void HTMLInputElement::aboutToUnload() 173{ 174 InputElement::aboutToUnload(this, this); 175} 176 177bool HTMLInputElement::shouldUseInputMethod() const 178{ 179 return m_type == TEXT || m_type == SEARCH || m_type == ISINDEX; 180} 181 182void HTMLInputElement::dispatchFocusEvent() 183{ 184 InputElement::dispatchFocusEvent(m_data, this, this); 185 186 if (isTextField()) 187 m_autofilled = false; 188 189 HTMLFormControlElementWithState::dispatchFocusEvent(); 190} 191 192void HTMLInputElement::dispatchBlurEvent() 193{ 194 InputElement::dispatchBlurEvent(m_data, this, this); 195 HTMLFormControlElementWithState::dispatchBlurEvent(); 196} 197 198void HTMLInputElement::setType(const String& t) 199{ 200 if (t.isEmpty()) { 201 int exccode; 202 removeAttribute(typeAttr, exccode); 203 } else 204 setAttribute(typeAttr, t); 205} 206 207void HTMLInputElement::setInputType(const String& t) 208{ 209 InputType newType; 210 211 if (equalIgnoringCase(t, "password")) 212#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 213 { 214 if (document()->focusedNode() == this) 215 { 216 android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, true, String()); 217 } 218#endif 219 newType = PASSWORD; 220#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 221 } 222#endif 223 else if (equalIgnoringCase(t, "checkbox")) 224 newType = CHECKBOX; 225 else if (equalIgnoringCase(t, "radio")) 226 newType = RADIO; 227 else if (equalIgnoringCase(t, "submit")) 228 newType = SUBMIT; 229 else if (equalIgnoringCase(t, "reset")) 230 newType = RESET; 231 else if (equalIgnoringCase(t, "file")) 232 newType = FILE; 233 else if (equalIgnoringCase(t, "hidden")) 234 newType = HIDDEN; 235 else if (equalIgnoringCase(t, "image")) 236 newType = IMAGE; 237 else if (equalIgnoringCase(t, "button")) 238 newType = BUTTON; 239 else if (equalIgnoringCase(t, "khtml_isindex")) 240 newType = ISINDEX; 241 else if (equalIgnoringCase(t, "search")) 242 newType = SEARCH; 243 else if (equalIgnoringCase(t, "range")) 244 newType = RANGE; 245 else if (equalIgnoringCase(t, "email")) 246 newType = EMAIL; 247 else if (equalIgnoringCase(t, "number")) 248 newType = NUMBER; 249 else if (equalIgnoringCase(t, "tel")) 250 newType = TELEPHONE; 251 else if (equalIgnoringCase(t, "url")) 252 newType = URL; 253 else 254 newType = TEXT; 255 256 // IMPORTANT: Don't allow the type to be changed to FILE after the first 257 // type change, otherwise a JavaScript programmer would be able to set a text 258 // field's value to something like /etc/passwd and then change it to a file field. 259 if (inputType() != newType) { 260 if (newType == FILE && m_haveType) 261 // Set the attribute back to the old value. 262 // Useful in case we were called from inside parseMappedAttribute. 263 setAttribute(typeAttr, type()); 264 else { 265 checkedRadioButtons(this).removeButton(this); 266 267 if (newType == FILE && !m_fileList) 268 m_fileList = FileList::create(); 269 270 bool wasAttached = attached(); 271 if (wasAttached) 272 detach(); 273 274 bool didStoreValue = storesValueSeparateFromAttribute(); 275 bool wasPasswordField = inputType() == PASSWORD; 276 bool didRespectHeightAndWidth = respectHeightAndWidthAttrs(); 277 m_type = newType; 278 bool willStoreValue = storesValueSeparateFromAttribute(); 279 bool isPasswordField = inputType() == PASSWORD; 280 bool willRespectHeightAndWidth = respectHeightAndWidthAttrs(); 281 282 if (didStoreValue && !willStoreValue && !m_data.value().isNull()) { 283 setAttribute(valueAttr, m_data.value()); 284 m_data.setValue(String()); 285 } 286 if (!didStoreValue && willStoreValue) 287 m_data.setValue(constrainValue(getAttribute(valueAttr))); 288 else 289 InputElement::updateValueIfNeeded(m_data, this); 290 291 if (wasPasswordField && !isPasswordField) 292 unregisterForActivationCallbackIfNeeded(); 293 else if (!wasPasswordField && isPasswordField) 294 registerForActivationCallbackIfNeeded(); 295 296 if (didRespectHeightAndWidth != willRespectHeightAndWidth) { 297 NamedMappedAttrMap* map = mappedAttributes(); 298 ASSERT(map); 299 if (Attribute* height = map->getAttributeItem(heightAttr)) 300 attributeChanged(height, false); 301 if (Attribute* width = map->getAttributeItem(widthAttr)) 302 attributeChanged(width, false); 303 if (Attribute* align = map->getAttributeItem(alignAttr)) 304 attributeChanged(align, false); 305 } 306 307 if (wasAttached) { 308 attach(); 309 if (document()->focusedNode() == this) 310 updateFocusAppearance(true); 311 } 312 313 checkedRadioButtons(this).addButton(this); 314 } 315 316 InputElement::notifyFormStateChanged(this); 317 } 318 m_haveType = true; 319 320 if (inputType() != IMAGE && m_imageLoader) 321 m_imageLoader.clear(); 322} 323 324const AtomicString& HTMLInputElement::formControlType() const 325{ 326 // needs to be lowercase according to DOM spec 327 switch (inputType()) { 328 case BUTTON: { 329 DEFINE_STATIC_LOCAL(const AtomicString, button, ("button")); 330 return button; 331 } 332 case CHECKBOX: { 333 DEFINE_STATIC_LOCAL(const AtomicString, checkbox, ("checkbox")); 334 return checkbox; 335 } 336 case EMAIL: { 337 DEFINE_STATIC_LOCAL(const AtomicString, email, ("email")); 338 return email; 339 } 340 case FILE: { 341 DEFINE_STATIC_LOCAL(const AtomicString, file, ("file")); 342 return file; 343 } 344 case HIDDEN: { 345 DEFINE_STATIC_LOCAL(const AtomicString, hidden, ("hidden")); 346 return hidden; 347 } 348 case IMAGE: { 349 DEFINE_STATIC_LOCAL(const AtomicString, image, ("image")); 350 return image; 351 } 352 case ISINDEX: 353 return emptyAtom; 354 case NUMBER: { 355 DEFINE_STATIC_LOCAL(const AtomicString, number, ("number")); 356 return number; 357 } 358 case PASSWORD: { 359 DEFINE_STATIC_LOCAL(const AtomicString, password, ("password")); 360 return password; 361 } 362 case RADIO: { 363 DEFINE_STATIC_LOCAL(const AtomicString, radio, ("radio")); 364 return radio; 365 } 366 case RANGE: { 367 DEFINE_STATIC_LOCAL(const AtomicString, range, ("range")); 368 return range; 369 } 370 case RESET: { 371 DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset")); 372 return reset; 373 } 374 case SEARCH: { 375 DEFINE_STATIC_LOCAL(const AtomicString, search, ("search")); 376 return search; 377 } 378 case SUBMIT: { 379 DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit")); 380 return submit; 381 } 382 case TELEPHONE: { 383 DEFINE_STATIC_LOCAL(const AtomicString, telephone, ("tel")); 384 return telephone; 385 } 386 case TEXT: { 387 DEFINE_STATIC_LOCAL(const AtomicString, text, ("text")); 388 return text; 389 } 390 case URL: { 391 DEFINE_STATIC_LOCAL(const AtomicString, url, ("url")); 392 return url; 393 } 394 } 395 return emptyAtom; 396} 397 398bool HTMLInputElement::saveFormControlState(String& result) const 399{ 400 if (!autoComplete()) 401 return false; 402 403 switch (inputType()) { 404 case BUTTON: 405 case EMAIL: 406 case FILE: 407 case HIDDEN: 408 case IMAGE: 409 case ISINDEX: 410 case NUMBER: 411 case RANGE: 412 case RESET: 413 case SEARCH: 414 case SUBMIT: 415 case TELEPHONE: 416 case TEXT: 417 case URL: 418 result = value(); 419 return true; 420 case CHECKBOX: 421 case RADIO: 422 result = checked() ? "on" : "off"; 423 return true; 424 case PASSWORD: 425 return false; 426 } 427 ASSERT_NOT_REACHED(); 428 return false; 429} 430 431void HTMLInputElement::restoreFormControlState(const String& state) 432{ 433 ASSERT(inputType() != PASSWORD); // should never save/restore password fields 434 switch (inputType()) { 435 case BUTTON: 436 case EMAIL: 437 case FILE: 438 case HIDDEN: 439 case IMAGE: 440 case ISINDEX: 441 case NUMBER: 442 case RANGE: 443 case RESET: 444 case SEARCH: 445 case SUBMIT: 446 case TELEPHONE: 447 case TEXT: 448 case URL: 449 setValue(state); 450 break; 451 case CHECKBOX: 452 case RADIO: 453 setChecked(state == "on"); 454 break; 455 case PASSWORD: 456 break; 457 } 458} 459 460bool HTMLInputElement::canStartSelection() const 461{ 462 if (!isTextField()) 463 return false; 464 return HTMLFormControlElementWithState::canStartSelection(); 465} 466 467bool HTMLInputElement::canHaveSelection() const 468{ 469 return isTextField(); 470} 471 472int HTMLInputElement::selectionStart() const 473{ 474 if (!isTextField()) 475 return 0; 476 if (document()->focusedNode() != this && m_data.cachedSelectionStart() != -1) 477 return m_data.cachedSelectionStart(); 478 if (!renderer()) 479 return 0; 480 return toRenderTextControl(renderer())->selectionStart(); 481} 482 483int HTMLInputElement::selectionEnd() const 484{ 485 if (!isTextField()) 486 return 0; 487 if (document()->focusedNode() != this && m_data.cachedSelectionEnd() != -1) 488 return m_data.cachedSelectionEnd(); 489 if (!renderer()) 490 return 0; 491 return toRenderTextControl(renderer())->selectionEnd(); 492} 493 494void HTMLInputElement::setSelectionStart(int start) 495{ 496 if (!isTextField()) 497 return; 498 if (!renderer()) 499 return; 500 toRenderTextControl(renderer())->setSelectionStart(start); 501} 502 503void HTMLInputElement::setSelectionEnd(int end) 504{ 505 if (!isTextField()) 506 return; 507 if (!renderer()) 508 return; 509 toRenderTextControl(renderer())->setSelectionEnd(end); 510} 511 512void HTMLInputElement::select() 513{ 514 if (!isTextField()) 515 return; 516 if (!renderer()) 517 return; 518 toRenderTextControl(renderer())->select(); 519} 520 521void HTMLInputElement::setSelectionRange(int start, int end) 522{ 523 InputElement::updateSelectionRange(this, this, start, end); 524} 525 526void HTMLInputElement::accessKeyAction(bool sendToAnyElement) 527{ 528 switch (inputType()) { 529 case BUTTON: 530 case CHECKBOX: 531 case FILE: 532 case IMAGE: 533 case RADIO: 534 case RANGE: 535 case RESET: 536 case SUBMIT: 537 focus(false); 538 // send the mouse button events iff the caller specified sendToAnyElement 539 dispatchSimulatedClick(0, sendToAnyElement); 540 break; 541 case HIDDEN: 542 // a no-op for this type 543 break; 544 case EMAIL: 545 case ISINDEX: 546 case NUMBER: 547 case PASSWORD: 548 case SEARCH: 549 case TELEPHONE: 550 case TEXT: 551 case URL: 552 // should never restore previous selection here 553 focus(false); 554 break; 555 } 556} 557 558bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const 559{ 560 if (((attrName == heightAttr || attrName == widthAttr) && respectHeightAndWidthAttrs()) || 561 attrName == vspaceAttr || 562 attrName == hspaceAttr) { 563 result = eUniversal; 564 return false; 565 } 566 567 if (attrName == alignAttr) { 568 if (inputType() == IMAGE) { 569 // Share with <img> since the alignment behavior is the same. 570 result = eReplaced; 571 return false; 572 } 573 } 574 575 return HTMLElement::mapToEntry(attrName, result); 576} 577 578void HTMLInputElement::parseMappedAttribute(MappedAttribute *attr) 579{ 580 if (attr->name() == nameAttr) { 581 checkedRadioButtons(this).removeButton(this); 582 m_data.setName(attr->value()); 583 checkedRadioButtons(this).addButton(this); 584 } else if (attr->name() == autocompleteAttr) { 585 if (equalIgnoringCase(attr->value(), "off")) { 586 m_autocomplete = Off; 587 registerForActivationCallbackIfNeeded(); 588 } else { 589 if (m_autocomplete == Off) 590 unregisterForActivationCallbackIfNeeded(); 591 if (attr->isEmpty()) 592 m_autocomplete = Uninitialized; 593 else 594 m_autocomplete = On; 595 } 596 } else if (attr->name() == typeAttr) { 597 setInputType(attr->value()); 598 } else if (attr->name() == valueAttr) { 599 // We only need to setChanged if the form is looking at the default value right now. 600 if (m_data.value().isNull()) 601 setNeedsStyleRecalc(); 602 setFormControlValueMatchesRenderer(false); 603 } else if (attr->name() == checkedAttr) { 604 m_defaultChecked = !attr->isNull(); 605 if (m_useDefaultChecked) { 606 setChecked(m_defaultChecked); 607 m_useDefaultChecked = true; 608 } 609 } else if (attr->name() == maxlengthAttr) 610 InputElement::parseMaxLengthAttribute(m_data, this, this, attr); 611 else if (attr->name() == sizeAttr) 612 InputElement::parseSizeAttribute(m_data, this, attr); 613 else if (attr->name() == altAttr) { 614 if (renderer() && inputType() == IMAGE) 615 toRenderImage(renderer())->updateAltText(); 616 } else if (attr->name() == srcAttr) { 617 if (renderer() && inputType() == IMAGE) { 618 if (!m_imageLoader) 619 m_imageLoader.set(new HTMLImageLoader(this)); 620 m_imageLoader->updateFromElementIgnoringPreviousError(); 621 } 622 } else if (attr->name() == usemapAttr || 623 attr->name() == accesskeyAttr) { 624 // FIXME: ignore for the moment 625 } else if (attr->name() == vspaceAttr) { 626 addCSSLength(attr, CSSPropertyMarginTop, attr->value()); 627 addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); 628 } else if (attr->name() == hspaceAttr) { 629 addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); 630 addCSSLength(attr, CSSPropertyMarginRight, attr->value()); 631 } else if (attr->name() == alignAttr) { 632 if (inputType() == IMAGE) 633 addHTMLAlignment(attr); 634 } else if (attr->name() == widthAttr) { 635 if (respectHeightAndWidthAttrs()) 636 addCSSLength(attr, CSSPropertyWidth, attr->value()); 637 } else if (attr->name() == heightAttr) { 638 if (respectHeightAndWidthAttrs()) 639 addCSSLength(attr, CSSPropertyHeight, attr->value()); 640 } else if (attr->name() == onfocusAttr) { 641 setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr)); 642 } else if (attr->name() == onblurAttr) { 643 setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr)); 644 } else if (attr->name() == onselectAttr) { 645 setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr)); 646 } else if (attr->name() == onchangeAttr) { 647 setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr)); 648 } else if (attr->name() == oninputAttr) { 649 setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, attr)); 650 } 651 // Search field and slider attributes all just cause updateFromElement to be called through style 652 // recalcing. 653 else if (attr->name() == onsearchAttr) { 654 setAttributeEventListener(eventNames().searchEvent, createAttributeEventListener(this, attr)); 655 } else if (attr->name() == resultsAttr) { 656 int oldResults = m_maxResults; 657 m_maxResults = !attr->isNull() ? min(attr->value().toInt(), maxSavedResults) : -1; 658 // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right 659 // time to relayout for this change. 660 if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0) && attached()) { 661 detach(); 662 attach(); 663 } 664 setNeedsStyleRecalc(); 665 } else if (attr->name() == placeholderAttr) { 666 if (isTextField()) 667 updatePlaceholderVisibility(); 668 } else if (attr->name() == autosaveAttr || 669 attr->name() == incrementalAttr || 670 attr->name() == minAttr || 671 attr->name() == maxAttr || 672 attr->name() == multipleAttr || 673 attr->name() == precisionAttr) 674 setNeedsStyleRecalc(); 675 else 676 HTMLFormControlElementWithState::parseMappedAttribute(attr); 677} 678 679bool HTMLInputElement::rendererIsNeeded(RenderStyle *style) 680{ 681 switch (inputType()) { 682 case BUTTON: 683 case CHECKBOX: 684 case EMAIL: 685 case FILE: 686 case IMAGE: 687 case ISINDEX: 688 case NUMBER: 689 case PASSWORD: 690 case RADIO: 691 case RANGE: 692 case RESET: 693 case SEARCH: 694 case SUBMIT: 695 case TELEPHONE: 696 case TEXT: 697 case URL: 698 return HTMLFormControlElementWithState::rendererIsNeeded(style); 699 case HIDDEN: 700 return false; 701 } 702 ASSERT(false); 703 return false; 704} 705 706RenderObject *HTMLInputElement::createRenderer(RenderArena *arena, RenderStyle *style) 707{ 708 switch (inputType()) { 709 case BUTTON: 710 case RESET: 711 case SUBMIT: 712 return new (arena) RenderButton(this); 713 case CHECKBOX: 714 case RADIO: 715 return RenderObject::createObject(this, style); 716 case FILE: 717 return new (arena) RenderFileUploadControl(this); 718 case HIDDEN: 719 break; 720 case IMAGE: 721 return new (arena) RenderImage(this); 722 case RANGE: 723 return new (arena) RenderSlider(this); 724 case EMAIL: 725 case ISINDEX: 726 case NUMBER: 727 case PASSWORD: 728 case SEARCH: 729 case TELEPHONE: 730 case TEXT: 731 case URL: 732 return new (arena) RenderTextControlSingleLine(this); 733 } 734 ASSERT(false); 735 return 0; 736} 737 738void HTMLInputElement::attach() 739{ 740 if (!m_inited) { 741 if (!m_haveType) 742 setInputType(getAttribute(typeAttr)); 743 m_inited = true; 744 } 745 746 HTMLFormControlElementWithState::attach(); 747 748 if (inputType() == IMAGE) { 749 if (!m_imageLoader) 750 m_imageLoader.set(new HTMLImageLoader(this)); 751 m_imageLoader->updateFromElement(); 752 if (renderer()) { 753 RenderImage* imageObj = toRenderImage(renderer()); 754 imageObj->setCachedImage(m_imageLoader->image()); 755 756 // If we have no image at all because we have no src attribute, set 757 // image height and width for the alt text instead. 758 if (!m_imageLoader->image() && !imageObj->cachedImage()) 759 imageObj->setImageSizeForAltText(); 760 } 761 } 762} 763 764void HTMLInputElement::detach() 765{ 766 HTMLFormControlElementWithState::detach(); 767 setFormControlValueMatchesRenderer(false); 768} 769 770String HTMLInputElement::altText() const 771{ 772 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen 773 // also heavily discussed by Hixie on bugzilla 774 // note this is intentionally different to HTMLImageElement::altText() 775 String alt = getAttribute(altAttr); 776 // fall back to title attribute 777 if (alt.isNull()) 778 alt = getAttribute(titleAttr); 779 if (alt.isNull()) 780 alt = getAttribute(valueAttr); 781 if (alt.isEmpty()) 782 alt = inputElementAltText(); 783 return alt; 784} 785 786bool HTMLInputElement::isSuccessfulSubmitButton() const 787{ 788 // HTML spec says that buttons must have names to be considered successful. 789 // However, other browsers do not impose this constraint. So we do likewise. 790 return !disabled() && (inputType() == IMAGE || inputType() == SUBMIT); 791} 792 793bool HTMLInputElement::isActivatedSubmit() const 794{ 795 return m_activeSubmit; 796} 797 798void HTMLInputElement::setActivatedSubmit(bool flag) 799{ 800 m_activeSubmit = flag; 801} 802 803bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart) 804{ 805 // image generates its own names, but for other types there is no form data unless there's a name 806 if (name().isEmpty() && inputType() != IMAGE) 807 return false; 808 809 switch (inputType()) { 810 case EMAIL: 811 case HIDDEN: 812 case ISINDEX: 813 case NUMBER: 814 case PASSWORD: 815 case RANGE: 816 case SEARCH: 817 case TELEPHONE: 818 case TEXT: 819 case URL: 820 // always successful 821 encoding.appendData(name(), value()); 822 return true; 823 824 case CHECKBOX: 825 case RADIO: 826 if (checked()) { 827 encoding.appendData(name(), value()); 828 return true; 829 } 830 break; 831 832 case BUTTON: 833 case RESET: 834 // these types of buttons are never successful 835 return false; 836 837 case IMAGE: 838 if (m_activeSubmit) { 839 encoding.appendData(name().isEmpty() ? "x" : (name() + ".x"), m_xPos); 840 encoding.appendData(name().isEmpty() ? "y" : (name() + ".y"), m_yPos); 841 if (!name().isEmpty() && !value().isEmpty()) 842 encoding.appendData(name(), value()); 843 return true; 844 } 845 break; 846 847 case SUBMIT: 848 if (m_activeSubmit) { 849 String enc_str = valueWithDefault(); 850 encoding.appendData(name(), enc_str); 851 return true; 852 } 853 break; 854 855 case FILE: { 856 // Can't submit file on GET. 857 if (!multipart) 858 return false; 859 860 // If no filename at all is entered, return successful but empty. 861 // Null would be more logical, but Netscape posts an empty file. Argh. 862 unsigned numFiles = m_fileList->length(); 863 if (!numFiles) { 864 encoding.appendFile(name(), File::create("")); 865 return true; 866 } 867 868 for (unsigned i = 0; i < numFiles; ++i) 869 encoding.appendFile(name(), m_fileList->item(i)); 870 return true; 871 } 872 } 873 return false; 874} 875 876void HTMLInputElement::reset() 877{ 878 if (storesValueSeparateFromAttribute()) 879 setValue(String()); 880 881 setChecked(m_defaultChecked); 882 m_useDefaultChecked = true; 883} 884 885void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent) 886{ 887 if (checked() == nowChecked) 888 return; 889 890 checkedRadioButtons(this).removeButton(this); 891 892 m_useDefaultChecked = false; 893 m_checked = nowChecked; 894 setNeedsStyleRecalc(); 895 896 checkedRadioButtons(this).addButton(this); 897 898 if (renderer() && renderer()->style()->hasAppearance()) 899 theme()->stateChanged(renderer(), CheckedState); 900 901 // Only send a change event for items in the document (avoid firing during 902 // parsing) and don't send a change event for a radio button that's getting 903 // unchecked to match other browsers. DOM is not a useful standard for this 904 // because it says only to fire change events at "lose focus" time, which is 905 // definitely wrong in practice for these types of elements. 906 if (sendChangeEvent && inDocument() && (inputType() != RADIO || nowChecked)) 907 dispatchFormControlChangeEvent(); 908} 909 910void HTMLInputElement::setIndeterminate(bool _indeterminate) 911{ 912 // Only checkboxes honor indeterminate. 913 if (inputType() != CHECKBOX || indeterminate() == _indeterminate) 914 return; 915 916 m_indeterminate = _indeterminate; 917 918 setNeedsStyleRecalc(); 919 920 if (renderer() && renderer()->style()->hasAppearance()) 921 theme()->stateChanged(renderer(), CheckedState); 922} 923 924int HTMLInputElement::size() const 925{ 926 return m_data.size(); 927} 928 929void HTMLInputElement::copyNonAttributeProperties(const Element* source) 930{ 931 const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source); 932 933 m_data.setValue(sourceElement->m_data.value()); 934 m_checked = sourceElement->m_checked; 935 m_indeterminate = sourceElement->m_indeterminate; 936 937 HTMLFormControlElementWithState::copyNonAttributeProperties(source); 938} 939 940String HTMLInputElement::value() const 941{ 942 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control 943 // but we don't want to break existing websites, who may be relying on being able to get the file name as a value. 944 if (inputType() == FILE) { 945 if (!m_fileList->isEmpty()) 946 return m_fileList->item(0)->fileName(); 947 return String(); 948 } 949 950 String value = m_data.value(); 951 if (value.isNull()) { 952 value = constrainValue(getAttribute(valueAttr)); 953 954 // If no attribute exists, then just use "on" or "" based off the checked() state of the control. 955 if (value.isNull() && (inputType() == CHECKBOX || inputType() == RADIO)) 956 return checked() ? "on" : ""; 957 } 958 959 return value; 960} 961 962String HTMLInputElement::valueWithDefault() const 963{ 964 String v = value(); 965 if (v.isNull()) { 966 switch (inputType()) { 967 case BUTTON: 968 case CHECKBOX: 969 case EMAIL: 970 case FILE: 971 case HIDDEN: 972 case IMAGE: 973 case ISINDEX: 974 case NUMBER: 975 case PASSWORD: 976 case RADIO: 977 case RANGE: 978 case SEARCH: 979 case TELEPHONE: 980 case TEXT: 981 case URL: 982 break; 983 case RESET: 984 v = resetButtonDefaultLabel(); 985 break; 986 case SUBMIT: 987 v = submitButtonDefaultLabel(); 988 break; 989 } 990 } 991 return v; 992} 993 994void HTMLInputElement::setValue(const String& value) 995{ 996 // For security reasons, we don't allow setting the filename, but we do allow clearing it. 997 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't applicable to the file upload control 998 // but we don't want to break existing websites, who may be relying on this method to clear things. 999 if (inputType() == FILE && !value.isEmpty()) 1000 return; 1001 1002 setFormControlValueMatchesRenderer(false); 1003 if (storesValueSeparateFromAttribute()) { 1004 if (inputType() == FILE) 1005 m_fileList->clear(); 1006 else { 1007 m_data.setValue(constrainValue(value)); 1008 if (isTextField()) { 1009 InputElement::updatePlaceholderVisibility(m_data, this, this); 1010 if (inDocument()) 1011 document()->updateStyleIfNeeded(); 1012 } 1013 } 1014 if (renderer()) 1015 renderer()->updateFromElement(); 1016 setNeedsStyleRecalc(); 1017 } else 1018 setAttribute(valueAttr, constrainValue(value)); 1019 1020 if (isTextField()) { 1021 unsigned max = m_data.value().length(); 1022 if (document()->focusedNode() == this) 1023#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 1024 { 1025 // Make sure our UI side textfield changes to match the RenderTextControl 1026 android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, false, value); 1027#endif 1028 InputElement::updateSelectionRange(this, this, max, max); 1029#ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS 1030 } 1031#endif 1032 else 1033 cacheSelection(max, max); 1034 } 1035 InputElement::notifyFormStateChanged(this); 1036} 1037 1038String HTMLInputElement::placeholder() const 1039{ 1040 return getAttribute(placeholderAttr).string(); 1041} 1042 1043void HTMLInputElement::setPlaceholder(const String& value) 1044{ 1045 setAttribute(placeholderAttr, value); 1046} 1047 1048bool HTMLInputElement::searchEventsShouldBeDispatched() const 1049{ 1050 return hasAttribute(incrementalAttr); 1051} 1052 1053void HTMLInputElement::setValueFromRenderer(const String& value) 1054{ 1055 // File upload controls will always use setFileListFromRenderer. 1056 ASSERT(inputType() != FILE); 1057 InputElement::setValueFromRenderer(m_data, this, this, value); 1058} 1059 1060void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths) 1061{ 1062 m_fileList->clear(); 1063 int size = paths.size(); 1064 for (int i = 0; i < size; i++) 1065 m_fileList->append(File::create(paths[i])); 1066 1067 setFormControlValueMatchesRenderer(true); 1068 InputElement::notifyFormStateChanged(this); 1069} 1070 1071bool HTMLInputElement::storesValueSeparateFromAttribute() const 1072{ 1073 switch (inputType()) { 1074 case BUTTON: 1075 case CHECKBOX: 1076 case HIDDEN: 1077 case IMAGE: 1078 case RADIO: 1079 case RESET: 1080 case SUBMIT: 1081 return false; 1082 case EMAIL: 1083 case FILE: 1084 case ISINDEX: 1085 case NUMBER: 1086 case PASSWORD: 1087 case RANGE: 1088 case SEARCH: 1089 case TELEPHONE: 1090 case TEXT: 1091 case URL: 1092 return true; 1093 } 1094 return false; 1095} 1096 1097void* HTMLInputElement::preDispatchEventHandler(Event *evt) 1098{ 1099 // preventDefault or "return false" are used to reverse the automatic checking/selection we do here. 1100 // This result gives us enough info to perform the "undo" in postDispatch of the action we take here. 1101 void* result = 0; 1102 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent() 1103 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1104 if (inputType() == CHECKBOX) { 1105 // As a way to store the state, we return 0 if we were unchecked, 1 if we were checked, and 2 for 1106 // indeterminate. 1107 if (indeterminate()) { 1108 result = (void*)0x2; 1109 setIndeterminate(false); 1110 } else { 1111 if (checked()) 1112 result = (void*)0x1; 1113 setChecked(!checked(), true); 1114 } 1115 } else { 1116 // For radio buttons, store the current selected radio object. 1117 // We really want radio groups to end up in sane states, i.e., to have something checked. 1118 // Therefore if nothing is currently selected, we won't allow this action to be "undone", since 1119 // we want some object in the radio group to actually get selected. 1120 HTMLInputElement* currRadio = checkedRadioButtons(this).checkedButtonForGroup(name()); 1121 if (currRadio) { 1122 // We have a radio button selected that is not us. Cache it in our result field and ref it so 1123 // that it can't be destroyed. 1124 currRadio->ref(); 1125 result = currRadio; 1126 } 1127 setChecked(true, true); 1128 } 1129 } 1130 return result; 1131} 1132 1133void HTMLInputElement::postDispatchEventHandler(Event *evt, void* data) 1134{ 1135 if ((inputType() == CHECKBOX || inputType() == RADIO) && evt->isMouseEvent() 1136 && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1137 if (inputType() == CHECKBOX) { 1138 // Reverse the checking we did in preDispatch. 1139 if (evt->defaultPrevented() || evt->defaultHandled()) { 1140 if (data == (void*)0x2) 1141 setIndeterminate(true); 1142 else 1143 setChecked(data); 1144 } 1145 } else if (data) { 1146 HTMLInputElement* input = static_cast<HTMLInputElement*>(data); 1147 if (evt->defaultPrevented() || evt->defaultHandled()) { 1148 // Restore the original selected radio button if possible. 1149 // Make sure it is still a radio button and only do the restoration if it still 1150 // belongs to our group. 1151 1152 if (input->form() == form() && input->inputType() == RADIO && input->name() == name()) { 1153 // Ok, the old radio button is still in our form and in our group and is still a 1154 // radio button, so it's safe to restore selection to it. 1155 input->setChecked(true); 1156 } 1157 } 1158 input->deref(); 1159 } 1160 1161 // Left clicks on radio buttons and check boxes already performed default actions in preDispatchEventHandler(). 1162 evt->setDefaultHandled(); 1163 } 1164} 1165 1166void HTMLInputElement::defaultEventHandler(Event* evt) 1167{ 1168 // FIXME: It would be better to refactor this for the different types of input element. 1169 // Having them all in one giant function makes this hard to read, and almost all the handling is type-specific. 1170 1171 bool clickDefaultFormButton = false; 1172 1173 if (isTextField() && evt->type() == eventNames().textInputEvent && evt->isTextEvent() && static_cast<TextEvent*>(evt)->data() == "\n") 1174 clickDefaultFormButton = true; 1175 1176 if (inputType() == IMAGE && evt->isMouseEvent() && evt->type() == eventNames().clickEvent) { 1177 // record the mouse position for when we get the DOMActivate event 1178 MouseEvent* me = static_cast<MouseEvent*>(evt); 1179 // FIXME: We could just call offsetX() and offsetY() on the event, 1180 // but that's currently broken, so for now do the computation here. 1181 if (me->isSimulated() || !renderer()) { 1182 m_xPos = 0; 1183 m_yPos = 0; 1184 } else { 1185 // FIXME: This doesn't work correctly with transforms. 1186 // FIXME: pageX/pageY need adjusting for pageZoomFactor(). Use actualPageLocation()? 1187 IntPoint absOffset = roundedIntPoint(renderer()->localToAbsolute()); 1188 m_xPos = me->pageX() - absOffset.x(); 1189 m_yPos = me->pageY() - absOffset.y(); 1190 } 1191 } 1192 1193 if (isTextField() 1194 && evt->type() == eventNames().keydownEvent 1195 && evt->isKeyboardEvent() 1196 && focused() 1197 && document()->frame() 1198 && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { 1199 evt->setDefaultHandled(); 1200 return; 1201 } 1202 1203 if (inputType() == RADIO 1204 && evt->isMouseEvent() 1205 && evt->type() == eventNames().clickEvent 1206 && static_cast<MouseEvent*>(evt)->button() == LeftButton) { 1207 evt->setDefaultHandled(); 1208 return; 1209 } 1210 1211 // Call the base event handler before any of our own event handling for almost all events in text fields. 1212 // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. 1213 bool callBaseClassEarly = isTextField() && !clickDefaultFormButton 1214 && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent); 1215 if (callBaseClassEarly) { 1216 HTMLFormControlElementWithState::defaultEventHandler(evt); 1217 if (evt->defaultHandled()) 1218 return; 1219 } 1220 1221 // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means 1222 // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks 1223 // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element 1224 // must dispatch a DOMActivate event - a click event will not do the job. 1225 if (evt->type() == eventNames().DOMActivateEvent && !disabled()) { 1226 if (inputType() == IMAGE || inputType() == SUBMIT || inputType() == RESET) { 1227 if (!form()) 1228 return; 1229 if (inputType() == RESET) 1230 form()->reset(); 1231 else { 1232 m_activeSubmit = true; 1233 // FIXME: Would be cleaner to get m_xPos and m_yPos out of the underlying mouse 1234 // event (if any) here instead of relying on the variables set above when 1235 // processing the click event. Even better, appendFormData could pass the 1236 // event in, and then we could get rid of m_xPos and m_yPos altogether! 1237 if (!form()->prepareSubmit(evt)) { 1238 m_xPos = 0; 1239 m_yPos = 0; 1240 } 1241 m_activeSubmit = false; 1242 } 1243 } else if (inputType() == FILE && renderer()) 1244 static_cast<RenderFileUploadControl*>(renderer())->click(); 1245 } 1246 1247 // Use key press event here since sending simulated mouse events 1248 // on key down blocks the proper sending of the key press event. 1249 if (evt->type() == eventNames().keypressEvent && evt->isKeyboardEvent()) { 1250 bool clickElement = false; 1251 1252 int charCode = static_cast<KeyboardEvent*>(evt)->charCode(); 1253 1254 if (charCode == '\r') { 1255 switch (inputType()) { 1256 case CHECKBOX: 1257 case EMAIL: 1258 case HIDDEN: 1259 case ISINDEX: 1260 case NUMBER: 1261 case PASSWORD: 1262 case RANGE: 1263 case SEARCH: 1264 case TELEPHONE: 1265 case TEXT: 1266 case URL: 1267 // Simulate mouse click on the default form button for enter for these types of elements. 1268 clickDefaultFormButton = true; 1269 break; 1270 case BUTTON: 1271 case FILE: 1272 case IMAGE: 1273 case RESET: 1274 case SUBMIT: 1275 // Simulate mouse click for enter for these types of elements. 1276 clickElement = true; 1277 break; 1278 case RADIO: 1279 break; // Don't do anything for enter on a radio button. 1280 } 1281 } else if (charCode == ' ') { 1282 switch (inputType()) { 1283 case BUTTON: 1284 case CHECKBOX: 1285 case FILE: 1286 case IMAGE: 1287 case RESET: 1288 case SUBMIT: 1289 case RADIO: 1290 // Prevent scrolling down the page. 1291 evt->setDefaultHandled(); 1292 return; 1293 default: 1294 break; 1295 } 1296 } 1297 1298 if (clickElement) { 1299 dispatchSimulatedClick(evt); 1300 evt->setDefaultHandled(); 1301 return; 1302 } 1303 } 1304 1305 if (evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) { 1306 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); 1307 1308 if (key == "U+0020") { 1309 switch (inputType()) { 1310 case BUTTON: 1311 case CHECKBOX: 1312 case FILE: 1313 case IMAGE: 1314 case RESET: 1315 case SUBMIT: 1316 case RADIO: 1317 setActive(true, true); 1318 // No setDefaultHandled(), because IE dispatches a keypress in this case 1319 // and the caller will only dispatch a keypress if we don't call setDefaultHandled. 1320 return; 1321 default: 1322 break; 1323 } 1324 } 1325 1326// allow enter to change state of radio 1327 if (inputType() == RADIO && (key == "Up" || key == "Down" || key == "Left" || key == "Right")) { 1328 // Left and up mean "previous radio button". 1329 // Right and down mean "next radio button". 1330 // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves 1331 // to the right). Seems strange, but we'll match it. 1332 bool forward = (key == "Down" || key == "Right"); 1333 1334 // We can only stay within the form's children if the form hasn't been demoted to a leaf because 1335 // of malformed HTML. 1336 Node* n = this; 1337 while ((n = (forward ? n->traverseNextNode() : n->traversePreviousNode()))) { 1338 // Once we encounter a form element, we know we're through. 1339 if (n->hasTagName(formTag)) 1340 break; 1341 1342 // Look for more radio buttons. 1343 if (n->hasTagName(inputTag)) { 1344 HTMLInputElement* elt = static_cast<HTMLInputElement*>(n); 1345 if (elt->form() != form()) 1346 break; 1347 if (n->hasTagName(inputTag)) { 1348 HTMLInputElement* inputElt = static_cast<HTMLInputElement*>(n); 1349 if (inputElt->inputType() == RADIO && inputElt->name() == name() && inputElt->isFocusable()) { 1350 inputElt->setChecked(true); 1351 document()->setFocusedNode(inputElt); 1352 inputElt->dispatchSimulatedClick(evt, false, false); 1353 evt->setDefaultHandled(); 1354 break; 1355 } 1356 } 1357 } 1358 } 1359 } 1360 } 1361 1362 if (evt->type() == eventNames().keyupEvent && evt->isKeyboardEvent()) { 1363 bool clickElement = false; 1364 1365 String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); 1366 1367 if (key == "U+0020") { 1368 switch (inputType()) { 1369 case BUTTON: 1370 case CHECKBOX: 1371 case FILE: 1372 case IMAGE: 1373 case RESET: 1374 case SUBMIT: 1375 // Simulate mouse click for spacebar for these types of elements. 1376 // The AppKit already does this for some, but not all, of them. 1377 clickElement = true; 1378 break; 1379 case RADIO: 1380 // If an unselected radio is tabbed into (because the entire group has nothing 1381 // checked, or because of some explicit .focus() call), then allow space to check it. 1382 if (!checked()) 1383 clickElement = true; 1384 break; 1385 case EMAIL: 1386 case HIDDEN: 1387 case ISINDEX: 1388 case NUMBER: 1389 case PASSWORD: 1390 case RANGE: 1391 case SEARCH: 1392 case TELEPHONE: 1393 case TEXT: 1394 case URL: 1395 break; 1396 } 1397 } 1398 1399 if (clickElement) { 1400 if (active()) 1401 dispatchSimulatedClick(evt); 1402 evt->setDefaultHandled(); 1403 return; 1404 } 1405 } 1406 1407 if (clickDefaultFormButton) { 1408 if (isSearchField()) { 1409 addSearchResult(); 1410 onSearch(); 1411 } 1412 // Fire onChange for text fields. 1413 RenderObject* r = renderer(); 1414 if (r && r->isTextField() && toRenderTextControl(r)->isEdited()) { 1415 dispatchFormControlChangeEvent(); 1416 // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it. 1417 r = renderer(); 1418 if (r && r->isTextField()) 1419 toRenderTextControl(r)->setEdited(false); 1420 } 1421 // Form may never have been present, or may have been destroyed by the change event. 1422 if (form()) 1423 form()->submitClick(evt); 1424 evt->setDefaultHandled(); 1425 return; 1426 } 1427 1428 if (evt->isBeforeTextInsertedEvent()) 1429 InputElement::handleBeforeTextInsertedEvent(m_data, this, document(), evt); 1430 1431 if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent)) 1432 static_cast<RenderTextControlSingleLine*>(renderer())->forwardEvent(evt); 1433 1434 if (inputType() == RANGE && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent())) 1435 static_cast<RenderSlider*>(renderer())->forwardEvent(evt); 1436 1437 if (!callBaseClassEarly && !evt->defaultHandled()) 1438 HTMLFormControlElementWithState::defaultEventHandler(evt); 1439} 1440 1441bool HTMLInputElement::isURLAttribute(Attribute *attr) const 1442{ 1443 return (attr->name() == srcAttr); 1444} 1445 1446String HTMLInputElement::defaultValue() const 1447{ 1448 return getAttribute(valueAttr); 1449} 1450 1451void HTMLInputElement::setDefaultValue(const String &value) 1452{ 1453 setAttribute(valueAttr, value); 1454} 1455 1456bool HTMLInputElement::defaultChecked() const 1457{ 1458 return !getAttribute(checkedAttr).isNull(); 1459} 1460 1461void HTMLInputElement::setDefaultChecked(bool defaultChecked) 1462{ 1463 setAttribute(checkedAttr, defaultChecked ? "" : 0); 1464} 1465 1466void HTMLInputElement::setDefaultName(const AtomicString& name) 1467{ 1468 m_data.setName(name); 1469} 1470 1471String HTMLInputElement::accept() const 1472{ 1473 return getAttribute(acceptAttr); 1474} 1475 1476void HTMLInputElement::setAccept(const String &value) 1477{ 1478 setAttribute(acceptAttr, value); 1479} 1480 1481String HTMLInputElement::accessKey() const 1482{ 1483 return getAttribute(accesskeyAttr); 1484} 1485 1486void HTMLInputElement::setAccessKey(const String &value) 1487{ 1488 setAttribute(accesskeyAttr, value); 1489} 1490 1491String HTMLInputElement::align() const 1492{ 1493 return getAttribute(alignAttr); 1494} 1495 1496void HTMLInputElement::setAlign(const String &value) 1497{ 1498 setAttribute(alignAttr, value); 1499} 1500 1501String HTMLInputElement::alt() const 1502{ 1503 return getAttribute(altAttr); 1504} 1505 1506void HTMLInputElement::setAlt(const String &value) 1507{ 1508 setAttribute(altAttr, value); 1509} 1510 1511int HTMLInputElement::maxLength() const 1512{ 1513 return m_data.maxLength(); 1514} 1515 1516void HTMLInputElement::setMaxLength(int _maxLength) 1517{ 1518 setAttribute(maxlengthAttr, String::number(_maxLength)); 1519} 1520 1521bool HTMLInputElement::multiple() const 1522{ 1523 return !getAttribute(multipleAttr).isNull(); 1524} 1525 1526void HTMLInputElement::setMultiple(bool multiple) 1527{ 1528 setAttribute(multipleAttr, multiple ? "" : 0); 1529} 1530 1531void HTMLInputElement::setSize(unsigned _size) 1532{ 1533 setAttribute(sizeAttr, String::number(_size)); 1534} 1535 1536KURL HTMLInputElement::src() const 1537{ 1538 return document()->completeURL(getAttribute(srcAttr)); 1539} 1540 1541void HTMLInputElement::setSrc(const String &value) 1542{ 1543 setAttribute(srcAttr, value); 1544} 1545 1546String HTMLInputElement::useMap() const 1547{ 1548 return getAttribute(usemapAttr); 1549} 1550 1551void HTMLInputElement::setUseMap(const String &value) 1552{ 1553 setAttribute(usemapAttr, value); 1554} 1555 1556void HTMLInputElement::setAutofilled(bool b) 1557{ 1558 if (b == m_autofilled) 1559 return; 1560 1561 m_autofilled = b; 1562 setNeedsStyleRecalc(); 1563} 1564 1565FileList* HTMLInputElement::files() 1566{ 1567 if (inputType() != FILE) 1568 return 0; 1569 return m_fileList.get(); 1570} 1571 1572String HTMLInputElement::constrainValue(const String& proposedValue) const 1573{ 1574 return InputElement::constrainValue(this, proposedValue, m_data.maxLength()); 1575} 1576 1577bool HTMLInputElement::needsActivationCallback() 1578{ 1579 return inputType() == PASSWORD || m_autocomplete == Off; 1580} 1581 1582void HTMLInputElement::registerForActivationCallbackIfNeeded() 1583{ 1584 if (needsActivationCallback()) 1585 document()->registerForDocumentActivationCallbacks(this); 1586} 1587 1588void HTMLInputElement::unregisterForActivationCallbackIfNeeded() 1589{ 1590 if (!needsActivationCallback()) 1591 document()->unregisterForDocumentActivationCallbacks(this); 1592} 1593 1594void HTMLInputElement::cacheSelection(int start, int end) 1595{ 1596 m_data.setCachedSelectionStart(start); 1597 m_data.setCachedSelectionEnd(end); 1598} 1599 1600void HTMLInputElement::addSearchResult() 1601{ 1602 ASSERT(isSearchField()); 1603 if (renderer()) 1604 static_cast<RenderTextControlSingleLine*>(renderer())->addSearchResult(); 1605} 1606 1607void HTMLInputElement::onSearch() 1608{ 1609 ASSERT(isSearchField()); 1610 if (renderer()) 1611 static_cast<RenderTextControlSingleLine*>(renderer())->stopSearchEventTimer(); 1612 dispatchEvent(eventNames().searchEvent, true, false); 1613} 1614 1615VisibleSelection HTMLInputElement::selection() const 1616{ 1617 if (!renderer() || !isTextField() || m_data.cachedSelectionStart() == -1 || m_data.cachedSelectionEnd() == -1) 1618 return VisibleSelection(); 1619 return toRenderTextControl(renderer())->selection(m_data.cachedSelectionStart(), m_data.cachedSelectionEnd()); 1620} 1621 1622void HTMLInputElement::documentDidBecomeActive() 1623{ 1624 ASSERT(needsActivationCallback()); 1625 reset(); 1626} 1627 1628void HTMLInputElement::willMoveToNewOwnerDocument() 1629{ 1630 // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered 1631 if (needsActivationCallback()) 1632 document()->unregisterForDocumentActivationCallbacks(this); 1633 1634 document()->checkedRadioButtons().removeButton(this); 1635 1636 HTMLFormControlElementWithState::willMoveToNewOwnerDocument(); 1637} 1638 1639void HTMLInputElement::didMoveToNewOwnerDocument() 1640{ 1641 registerForActivationCallbackIfNeeded(); 1642 1643 HTMLFormControlElementWithState::didMoveToNewOwnerDocument(); 1644} 1645 1646void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 1647{ 1648 HTMLFormControlElementWithState::addSubresourceAttributeURLs(urls); 1649 1650 addSubresourceURL(urls, src()); 1651} 1652 1653bool HTMLInputElement::willValidate() const 1654{ 1655 // FIXME: This shall check for new WF2 input types too 1656 return HTMLFormControlElementWithState::willValidate() && inputType() != HIDDEN && 1657 inputType() != BUTTON && inputType() != RESET; 1658} 1659 1660bool HTMLInputElement::placeholderShouldBeVisible() const 1661{ 1662 return m_data.placeholderShouldBeVisible(); 1663} 1664 1665} // namespace 1666