diff options
Diffstat (limited to 'WebCore/html/HTMLTextAreaElement.cpp')
-rw-r--r-- | WebCore/html/HTMLTextAreaElement.cpp | 207 |
1 files changed, 111 insertions, 96 deletions
diff --git a/WebCore/html/HTMLTextAreaElement.cpp b/WebCore/html/HTMLTextAreaElement.cpp index 4277f30..1882fe5 100644 --- a/WebCore/html/HTMLTextAreaElement.cpp +++ b/WebCore/html/HTMLTextAreaElement.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. * (C) 2006 Alexey Proskuryakov (ap@nypop.com) * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) * @@ -40,25 +40,23 @@ #include "Text.h" #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS -#include "FrameView.h" -#include "WebCoreViewBridge.h" +#include "WebViewCore.h" #endif namespace WebCore { -using namespace EventNames; using namespace HTMLNames; static const int defaultRows = 2; static const int defaultCols = 20; -HTMLTextAreaElement::HTMLTextAreaElement(Document* doc, HTMLFormElement* f) - : HTMLFormControlElementWithState(textareaTag, doc, f) +HTMLTextAreaElement::HTMLTextAreaElement(Document* document, HTMLFormElement* form) + : HTMLFormControlElementWithState(textareaTag, document, form) , m_rows(defaultRows) , m_cols(defaultCols) - , m_wrap(ta_Virtual) - , cachedSelStart(-1) - , cachedSelEnd(-1) + , m_wrap(SoftWrap) + , m_cachedSelectionStart(-1) + , m_cachedSelectionEnd(-1) { setValueMatchesRenderer(); } @@ -82,55 +80,57 @@ void HTMLTextAreaElement::restoreState(const String& state) int HTMLTextAreaElement::selectionStart() { - if (renderer()) { - if (document()->focusedNode() != this && cachedSelStart != -1) - return cachedSelStart; - return static_cast<RenderTextControl *>(renderer())->selectionStart(); - } - return 0; + if (!renderer()) + return 0; + if (document()->focusedNode() != this && m_cachedSelectionStart >= 0) + return m_cachedSelectionStart; + return static_cast<RenderTextControl*>(renderer())->selectionStart(); } int HTMLTextAreaElement::selectionEnd() { - if (renderer()) { - if (document()->focusedNode() != this && cachedSelEnd != -1) - return cachedSelEnd; - return static_cast<RenderTextControl *>(renderer())->selectionEnd(); - } - return 0; + if (!renderer()) + return 0; + if (document()->focusedNode() != this && m_cachedSelectionEnd >= 0) + return m_cachedSelectionEnd; + return static_cast<RenderTextControl*>(renderer())->selectionEnd(); } void HTMLTextAreaElement::setSelectionStart(int start) { - if (renderer()) - static_cast<RenderTextControl*>(renderer())->setSelectionStart(start); + if (!renderer()) + return; + static_cast<RenderTextControl*>(renderer())->setSelectionStart(start); } void HTMLTextAreaElement::setSelectionEnd(int end) { - if (renderer()) - static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end); + if (!renderer()) + return; + static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end); } void HTMLTextAreaElement::select() { - if (renderer()) - static_cast<RenderTextControl *>(renderer())->select(); + if (!renderer()) + return; + static_cast<RenderTextControl*>(renderer())->select(); } void HTMLTextAreaElement::setSelectionRange(int start, int end) { - if (renderer()) - static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end); + if (!renderer()) + return; + static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end); } -void HTMLTextAreaElement::childrenChanged(bool changedByParser) +void HTMLTextAreaElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) { setValue(defaultValue()); - HTMLElement::childrenChanged(changedByParser); + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); } -void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute *attr) +void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute* attr) { if (attr->name() == rowsAttr) { int rows = attr->value().toInt(); @@ -151,36 +151,38 @@ void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute *attr) renderer()->setNeedsLayoutAndPrefWidthsRecalc(); } } else if (attr->name() == wrapAttr) { - // virtual / physical is Netscape extension of HTML 3.0, now deprecated - // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4 - if (equalIgnoringCase(attr->value(), "virtual") || equalIgnoringCase(attr->value(), "soft")) - m_wrap = ta_Virtual; - else if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard")) - m_wrap = ta_Physical; - else if (equalIgnoringCase(attr->value(), "on" )) - m_wrap = ta_Physical; + // The virtual/physical values were a Netscape extension of HTML 3.0, now deprecated. + // The soft/hard /off values are a recommendation for HTML 4 extension by IE and NS 4. + WrapMethod wrap; + if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard") || equalIgnoringCase(attr->value(), "on")) + wrap = HardWrap; else if (equalIgnoringCase(attr->value(), "off")) - m_wrap = ta_NoWrap; - if (renderer()) - renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + wrap = NoWrap; + else + wrap = SoftWrap; + if (wrap != m_wrap) { + m_wrap = wrap; + if (renderer()) + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } } else if (attr->name() == accesskeyAttr) { // ignore for the moment } else if (attr->name() == alignAttr) { // Don't map 'align' attribute. This matches what Firefox, Opera and IE do. // See http://bugs.webkit.org/show_bug.cgi?id=7075 } else if (attr->name() == onfocusAttr) - setHTMLEventListener(focusEvent, attr); + setInlineEventListenerForTypeAndAttribute(eventNames().focusEvent, attr); else if (attr->name() == onblurAttr) - setHTMLEventListener(blurEvent, attr); + setInlineEventListenerForTypeAndAttribute(eventNames().blurEvent, attr); else if (attr->name() == onselectAttr) - setHTMLEventListener(selectEvent, attr); + setInlineEventListenerForTypeAndAttribute(eventNames().selectEvent, attr); else if (attr->name() == onchangeAttr) - setHTMLEventListener(changeEvent, attr); + setInlineEventListenerForTypeAndAttribute(eventNames().changeEvent, attr); else HTMLFormControlElementWithState::parseMappedAttribute(attr); } -RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle* style) +RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle*) { return new (arena) RenderTextControl(this, true); } @@ -189,10 +191,12 @@ bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool) { if (name().isEmpty()) return false; - - bool hardWrap = renderer() && wrap() == ta_Physical; - String v = hardWrap ? static_cast<RenderTextControl*>(renderer())->textWithHardLineBreaks() : value(); - encoding.appendData(name(), v); + + // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer. + // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. + RenderTextControl* control = static_cast<RenderTextControl*>(renderer()); + const String& text = (m_wrap == HardWrap && control) ? control->textWithHardLineBreaks() : value(); + encoding.appendData(name(), text); return true; } @@ -203,42 +207,43 @@ void HTMLTextAreaElement::reset() bool HTMLTextAreaElement::isKeyboardFocusable(KeyboardEvent*) const { - // If text areas can be focused, then they should always be keyboard focusable - return HTMLFormControlElementWithState::isFocusable(); + // If a given text area can be focused at all, then it will always be keyboard focusable. + return isFocusable(); } bool HTMLTextAreaElement::isMouseFocusable() const { - return HTMLFormControlElementWithState::isFocusable(); + return isFocusable(); } void HTMLTextAreaElement::updateFocusAppearance(bool restorePreviousSelection) { ASSERT(renderer()); - if (!restorePreviousSelection || cachedSelStart == -1) { + if (!restorePreviousSelection || m_cachedSelectionStart < 0) { // If this is the first focus, set a caret at the beginning of the text. - // This matches some browsers' behavior; see Bugzilla Bug 11746 Comment #15. + // This matches some browsers' behavior; see bug 11746 Comment #15. // http://bugs.webkit.org/show_bug.cgi?id=11746#c15 setSelectionRange(0, 0); #ifdef ANDROID_SELECT_TEXT_AREAS // We need to select the entire text to match the platform text field. select(); #endif - } else + } else { // Restore the cached selection. This matches other browsers' behavior. - setSelectionRange(cachedSelStart, cachedSelEnd); + setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd); + } if (document()->frame()) document()->frame()->revealSelection(); } -void HTMLTextAreaElement::defaultEventHandler(Event *evt) +void HTMLTextAreaElement::defaultEventHandler(Event* event) { - if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent)) - static_cast<RenderTextControl*>(renderer())->forwardEvent(evt); + if (renderer() && (event->isMouseEvent() || event->isDragEvent() || event->isWheelEvent() || event->type() == eventNames().blurEvent)) + static_cast<RenderTextControl*>(renderer())->forwardEvent(event); - HTMLFormControlElementWithState::defaultEventHandler(evt); + HTMLFormControlElementWithState::defaultEventHandler(event); } void HTMLTextAreaElement::rendererWillBeDestroyed() @@ -248,11 +253,12 @@ void HTMLTextAreaElement::rendererWillBeDestroyed() void HTMLTextAreaElement::updateValue() const { - if (!valueMatchesRenderer()) { - ASSERT(renderer()); - m_value = static_cast<RenderTextControl*>(renderer())->text(); - setValueMatchesRenderer(); - } + if (valueMatchesRenderer()) + return; + + ASSERT(renderer()); + m_value = static_cast<RenderTextControl*>(renderer())->text(); + setValueMatchesRenderer(); } String HTMLTextAreaElement::value() const @@ -265,24 +271,21 @@ void HTMLTextAreaElement::setValue(const String& value) { // Code elsewhere normalizes line endings added by the user via the keyboard or pasting. // We must normalize line endings coming from JS. - DeprecatedString valueWithNormalizedLineEndings = value.deprecatedString(); - valueWithNormalizedLineEndings.replace("\r\n", "\n"); - valueWithNormalizedLineEndings.replace("\r", "\n"); - - m_value = valueWithNormalizedLineEndings; + m_value = value; + m_value.replace("\r\n", "\n"); + m_value.replace('\r', '\n'); + setValueMatchesRenderer(); if (inDocument()) document()->updateRendering(); if (renderer()) renderer()->updateFromElement(); - // Set the caret to the end of the text value. if (document()->focusedNode() == this) { #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS // Make sure our UI side textfield changes to match the RenderTextControl - WebCoreViewBridge* viewImpl = document()->frame()->view()->getWebCoreViewBridge(); - viewImpl->updateTextfield(this, false, value); + android::WebViewCore::getWebViewCore(document()->view())->updateTextfield(this, false, value); #endif unsigned endOfString = m_value.length(); setSelectionRange(endOfString, endOfString); @@ -293,45 +296,57 @@ void HTMLTextAreaElement::setValue(const String& value) String HTMLTextAreaElement::defaultValue() const { - String val = ""; + String value = ""; // Since there may be comments, ignore nodes other than text nodes. - for (Node* n = firstChild(); n; n = n->nextSibling()) + for (Node* n = firstChild(); n; n = n->nextSibling()) { if (n->isTextNode()) - val += static_cast<Text*>(n)->data(); + value += static_cast<Text*>(n)->data(); + } - // FIXME: We should only drop the first carriage return for the default - // value in the original source, not defaultValues set from JS. This code - // will do both. - if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n') - val.remove(0, 2); - else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n')) - val.remove(0, 1); + UChar firstCharacter = value[0]; + if (firstCharacter == '\r' && value[1] == '\n') + value.remove(0, 2); + else if (firstCharacter == '\r' || firstCharacter == '\n') + value.remove(0, 1); - return val; + return value; } void HTMLTextAreaElement::setDefaultValue(const String& defaultValue) { - // To preserve comments, remove all the text nodes, then add a single one. + // To preserve comments, remove only the text nodes, then add a single text node. + Vector<RefPtr<Node> > textNodes; - for (Node* n = firstChild(); n; n = n->nextSibling()) + for (Node* n = firstChild(); n; n = n->nextSibling()) { if (n->isTextNode()) textNodes.append(n); - ExceptionCode ec = 0; + } + ExceptionCode ec; size_t size = textNodes.size(); for (size_t i = 0; i < size; ++i) removeChild(textNodes[i].get(), ec); - insertBefore(document()->createTextNode(defaultValue), firstChild(), ec); - setValue(defaultValue); + + // Normalize line endings. + // Add an extra line break if the string starts with one, since + // the code to read default values from the DOM strips the leading one. + String value = defaultValue; + value.replace("\r\n", "\n"); + value.replace('\r', '\n'); + if (value[0] == '\n') + value = "\n" + value; + + insertBefore(document()->createTextNode(value), firstChild(), ec); + + setValue(value); } -void HTMLTextAreaElement::accessKeyAction(bool sendToAnyElement) +void HTMLTextAreaElement::accessKeyAction(bool) { focus(); } -String HTMLTextAreaElement::accessKey() const +const AtomicString& HTMLTextAreaElement::accessKey() const { return getAttribute(accesskeyAttr); } @@ -353,9 +368,9 @@ void HTMLTextAreaElement::setRows(int rows) Selection HTMLTextAreaElement::selection() const { - if (!renderer() || cachedSelStart == -1 || cachedSelEnd == -1) + if (!renderer() || m_cachedSelectionStart < 0 || m_cachedSelectionEnd < 0) return Selection(); - return static_cast<RenderTextControl*>(renderer())->selection(cachedSelStart, cachedSelEnd); + return static_cast<RenderTextControl*>(renderer())->selection(m_cachedSelectionStart, m_cachedSelectionEnd); } bool HTMLTextAreaElement::shouldUseInputMethod() const |