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