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 44 namespace WebCore { 45 46 using 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. 52 const int InputElement::s_maximumLength = 524288; 53 const int InputElement::s_defaultSize = 20; 54 55 void 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 65 void 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 81 void 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 96 void 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 107 void 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 120 void 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 139 String InputElement::sanitizeValue(const InputElement* inputElement, const String& proposedValue) 140 { 141 return InputElement::sanitizeUserInputValue(inputElement, proposedValue, s_maximumLength); 142 } 143 144 String 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 165 void 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 189 void 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 197 void 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 212 void 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 220 void 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 232 InputElementData::InputElementData() 233 : m_size(InputElement::s_defaultSize) 234 , m_maxLength(InputElement::s_maximumLength) 235 , m_cachedSelectionStart(-1) 236 , m_cachedSelectionEnd(-1) 237 { 238 } 239 240 const AtomicString& InputElementData::name() const 241 { 242 return m_name.isNull() ? emptyAtom : m_name; 243 } 244 245 InputElement* 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