1/* 2 * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21#include "config.h" 22#include "InputElement.h" 23 24#include "BeforeTextInsertedEvent.h" 25#include "Chrome.h" 26#include "ChromeClient.h" 27#include "Document.h" 28#include "Event.h" 29#include "EventNames.h" 30#include "Frame.h" 31#include "HTMLInputElement.h" 32#include "HTMLNames.h" 33#include "MappedAttribute.h" 34#include "Page.h" 35#include "RenderTextControlSingleLine.h" 36#include "SelectionController.h" 37#include "TextIterator.h" 38 39#if ENABLE(WML) 40#include "WMLInputElement.h" 41#include "WMLNames.h" 42#endif 43 44namespace WebCore { 45 46using namespace HTMLNames; 47 48// FIXME: According to HTML4, the length attribute's value can be arbitrarily 49// large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things 50// get rather sluggish when a text field has a larger number of characters than 51// this, even when just clicking in the text field. 52const int InputElement::s_maximumLength = 524288; 53const int InputElement::s_defaultSize = 20; 54 55void InputElement::dispatchFocusEvent(InputElement* inputElement, Element* element) 56{ 57 if (!inputElement->isTextField()) 58 return; 59 60 Document* document = element->document(); 61 if (inputElement->isPasswordField() && document->frame()) 62 document->setUseSecureKeyboardEntryWhenActive(true); 63} 64 65void InputElement::dispatchBlurEvent(InputElement* inputElement, Element* element) 66{ 67 if (!inputElement->isTextField()) 68 return; 69 70 Document* document = element->document(); 71 Frame* frame = document->frame(); 72 if (!frame) 73 return; 74 75 if (inputElement->isPasswordField()) 76 document->setUseSecureKeyboardEntryWhenActive(false); 77 78 frame->textFieldDidEndEditing(element); 79} 80 81void InputElement::updateFocusAppearance(InputElementData& data, InputElement* inputElement, Element* element, bool restorePreviousSelection) 82{ 83 ASSERT(inputElement->isTextField()); 84 85 if (!restorePreviousSelection || data.cachedSelectionStart() == -1) 86 inputElement->select(); 87 else 88 // Restore the cached selection. 89 updateSelectionRange(inputElement, element, data.cachedSelectionStart(), data.cachedSelectionEnd()); 90 91 Document* document = element->document(); 92 if (document && document->frame()) 93 document->frame()->revealSelection(); 94} 95 96void InputElement::updateSelectionRange(InputElement* inputElement, Element* element, int start, int end) 97{ 98 if (!inputElement->isTextField()) 99 return; 100 101 element->document()->updateLayoutIgnorePendingStylesheets(); 102 103 if (RenderTextControl* renderer = toRenderTextControl(element->renderer())) 104 renderer->setSelectionRange(start, end); 105} 106 107void InputElement::aboutToUnload(InputElement* inputElement, Element* element) 108{ 109 if (!inputElement->isTextField() || !element->focused()) 110 return; 111 112 Document* document = element->document(); 113 Frame* frame = document->frame(); 114 if (!frame) 115 return; 116 117 frame->textFieldDidEndEditing(element); 118} 119 120void InputElement::setValueFromRenderer(InputElementData& data, InputElement* inputElement, Element* element, const String& value) 121{ 122 // Renderer and our event handler are responsible for sanitizing values. 123 ASSERT_UNUSED(inputElement, value == inputElement->sanitizeValue(value) || inputElement->sanitizeValue(value).isEmpty()); 124 125 // Workaround for bug where trailing \n is included in the result of textContent. 126 // The assert macro above may also be simplified to: value == constrainValue(value) 127 // http://bugs.webkit.org/show_bug.cgi?id=9661 128 if (value == "\n") 129 data.setValue(""); 130 else 131 data.setValue(value); 132 133 element->setFormControlValueMatchesRenderer(true); 134 135 element->dispatchEvent(Event::create(eventNames().inputEvent, true, false)); 136 notifyFormStateChanged(element); 137} 138 139String InputElement::sanitizeValue(const InputElement* inputElement, const String& proposedValue) 140{ 141 return InputElement::sanitizeUserInputValue(inputElement, proposedValue, s_maximumLength); 142} 143 144String InputElement::sanitizeUserInputValue(const InputElement* inputElement, const String& proposedValue, int maxLength) 145{ 146 if (!inputElement->isTextField()) 147 return proposedValue; 148 149 String string = proposedValue; 150 string.replace("\r\n", " "); 151 string.replace('\r', ' '); 152 string.replace('\n', ' '); 153 154 unsigned newLength = string.numCharactersInGraphemeClusters(maxLength); 155 for (unsigned i = 0; i < newLength; ++i) { 156 const UChar current = string[i]; 157 if (current < ' ' && current != '\t') { 158 newLength = i; 159 break; 160 } 161 } 162 return string.left(newLength); 163} 164 165void InputElement::handleBeforeTextInsertedEvent(InputElementData& data, InputElement* inputElement, Element* element, Event* event) 166{ 167 ASSERT(event->isBeforeTextInsertedEvent()); 168 // Make sure that the text to be inserted will not violate the maxLength. 169 170 // We use RenderTextControlSingleLine::text() instead of InputElement::value() 171 // because they can be mismatched by sanitizeValue() in 172 // RenderTextControlSingleLine::subtreeHasChanged() in some cases. 173 unsigned oldLength = toRenderTextControlSingleLine(element->renderer())->text().numGraphemeClusters(); 174 175 // selection() may be a pre-edit text. 176 unsigned selectionLength = plainText(element->document()->frame()->selection()->selection().toNormalizedRange().get()).numGraphemeClusters(); 177 ASSERT(oldLength >= selectionLength); 178 179 // Selected characters will be removed by the next text event. 180 unsigned baseLength = oldLength - selectionLength; 181 unsigned maxLength = static_cast<unsigned>(data.maxLength()); // maxLength() can never be negative. 182 unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0; 183 184 // Truncate the inserted text to avoid violating the maxLength and other constraints. 185 BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event); 186 textEvent->setText(sanitizeUserInputValue(inputElement, textEvent->text(), appendableLength)); 187} 188 189void InputElement::parseSizeAttribute(InputElementData& data, Element* element, MappedAttribute* attribute) 190{ 191 data.setSize(attribute->isNull() ? InputElement::s_defaultSize : attribute->value().toInt()); 192 193 if (RenderObject* renderer = element->renderer()) 194 renderer->setNeedsLayoutAndPrefWidthsRecalc(); 195} 196 197void InputElement::parseMaxLengthAttribute(InputElementData& data, InputElement* inputElement, Element* element, MappedAttribute* attribute) 198{ 199 int maxLength = attribute->isNull() ? InputElement::s_maximumLength : attribute->value().toInt(); 200 if (maxLength <= 0 || maxLength > InputElement::s_maximumLength) 201 maxLength = InputElement::s_maximumLength; 202 203 int oldMaxLength = data.maxLength(); 204 data.setMaxLength(maxLength); 205 206 if (oldMaxLength != maxLength) 207 updateValueIfNeeded(data, inputElement); 208 209 element->setNeedsStyleRecalc(); 210} 211 212void InputElement::updateValueIfNeeded(InputElementData& data, InputElement* inputElement) 213{ 214 String oldValue = data.value(); 215 String newValue = sanitizeValue(inputElement, oldValue); 216 if (newValue != oldValue) 217 inputElement->setValue(newValue); 218} 219 220void InputElement::notifyFormStateChanged(Element* element) 221{ 222 Document* document = element->document(); 223 Frame* frame = document->frame(); 224 if (!frame) 225 return; 226 227 if (Page* page = frame->page()) 228 page->chrome()->client()->formStateDidChange(element); 229} 230 231// InputElementData 232InputElementData::InputElementData() 233 : m_size(InputElement::s_defaultSize) 234 , m_maxLength(InputElement::s_maximumLength) 235 , m_cachedSelectionStart(-1) 236 , m_cachedSelectionEnd(-1) 237{ 238} 239 240const AtomicString& InputElementData::name() const 241{ 242 return m_name.isNull() ? emptyAtom : m_name; 243} 244 245InputElement* toInputElement(Element* element) 246{ 247 if (element->isHTMLElement() && (element->hasTagName(inputTag) || element->hasTagName(isindexTag))) 248 return static_cast<HTMLInputElement*>(element); 249 250#if ENABLE(WML) 251 if (element->isWMLElement() && element->hasTagName(WMLNames::inputTag)) 252 return static_cast<WMLInputElement*>(element); 253#endif 254 255 return 0; 256} 257 258} 259