webengine/osswebengine/WebCore/html/HTMLTextAreaElement.cpp
changeset 0 dd21522fd290
child 47 e1bea15f9a39
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     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 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 "HTMLTextAreaElement.h"
       
    28 
       
    29 #include "Document.h"
       
    30 #include "Event.h"
       
    31 #include "EventNames.h"
       
    32 #include "FocusController.h"
       
    33 #include "FormDataList.h"
       
    34 #include "Frame.h"
       
    35 #include "HTMLNames.h"
       
    36 #include "Page.h"
       
    37 #include "RenderStyle.h"
       
    38 #include "RenderTextControl.h"
       
    39 #include "Selection.h"
       
    40 #include "Text.h"
       
    41 
       
    42 namespace WebCore {
       
    43 
       
    44 using namespace EventNames;
       
    45 using namespace HTMLNames;
       
    46 
       
    47 static const int defaultRows = 2;
       
    48 static const int defaultCols = 20;
       
    49 
       
    50 HTMLTextAreaElement::HTMLTextAreaElement(Document* doc, HTMLFormElement* f)
       
    51     : HTMLFormControlElementWithState(textareaTag, doc, f)
       
    52     , m_rows(defaultRows)
       
    53     , m_cols(defaultCols)
       
    54     , m_wrap(ta_Virtual)
       
    55     , cachedSelStart(-1)
       
    56     , cachedSelEnd(-1)
       
    57 {
       
    58     setValueMatchesRenderer();
       
    59 }
       
    60 
       
    61 const AtomicString& HTMLTextAreaElement::type() const
       
    62 {
       
    63     static const AtomicString textarea("textarea");
       
    64     return textarea;
       
    65 }
       
    66 
       
    67 bool HTMLTextAreaElement::saveState(String& result) const
       
    68 {
       
    69     result = value();
       
    70     return true;
       
    71 }
       
    72 
       
    73 void HTMLTextAreaElement::restoreState(const String& state)
       
    74 {
       
    75     setDefaultValue(state);
       
    76 }
       
    77 
       
    78 int HTMLTextAreaElement::selectionStart()
       
    79 {
       
    80     if (renderer()) {
       
    81         if (document()->focusedNode() != this && cachedSelStart != -1)
       
    82             return cachedSelStart;
       
    83         return static_cast<RenderTextControl *>(renderer())->selectionStart();
       
    84     }
       
    85     return 0;
       
    86 }
       
    87 
       
    88 int HTMLTextAreaElement::selectionEnd()
       
    89 {
       
    90     if (renderer()) {
       
    91         if (document()->focusedNode() != this && cachedSelEnd != -1)
       
    92             return cachedSelEnd;
       
    93         return static_cast<RenderTextControl *>(renderer())->selectionEnd();
       
    94     }
       
    95     return 0;
       
    96 }
       
    97 
       
    98 void HTMLTextAreaElement::setSelectionStart(int start)
       
    99 {
       
   100     if (renderer())
       
   101         static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
       
   102 }
       
   103 
       
   104 void HTMLTextAreaElement::setSelectionEnd(int end)
       
   105 {
       
   106     if (renderer())
       
   107         static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
       
   108 }
       
   109 
       
   110 void HTMLTextAreaElement::select()
       
   111 {
       
   112     if (renderer())
       
   113         static_cast<RenderTextControl *>(renderer())->select();
       
   114 }
       
   115 
       
   116 void HTMLTextAreaElement::setSelectionRange(int start, int end)
       
   117 {
       
   118     if (renderer())
       
   119         static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
       
   120 }
       
   121 
       
   122 void HTMLTextAreaElement::childrenChanged()
       
   123 {
       
   124     setValue(defaultValue());
       
   125 }
       
   126     
       
   127 void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute *attr)
       
   128 {
       
   129     if (attr->name() == rowsAttr) {
       
   130         int rows = attr->value().toInt();
       
   131         if (rows <= 0)
       
   132             rows = defaultRows;
       
   133         if (m_rows != rows) {
       
   134             m_rows = rows;
       
   135             if (renderer())
       
   136                 renderer()->setNeedsLayoutAndPrefWidthsRecalc();
       
   137         }
       
   138     } else if (attr->name() == colsAttr) {
       
   139         int cols = attr->value().toInt();
       
   140         if (cols <= 0)
       
   141             cols = defaultCols;
       
   142         if (m_cols != cols) {
       
   143             m_cols = cols;
       
   144             if (renderer())
       
   145                 renderer()->setNeedsLayoutAndPrefWidthsRecalc();
       
   146         }
       
   147     } else if (attr->name() == wrapAttr) {
       
   148         // virtual / physical is Netscape extension of HTML 3.0, now deprecated
       
   149         // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
       
   150         if (equalIgnoringCase(attr->value(), "virtual") || equalIgnoringCase(attr->value(), "soft"))
       
   151             m_wrap = ta_Virtual;
       
   152         else if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard"))
       
   153             m_wrap = ta_Physical;
       
   154         else if (equalIgnoringCase(attr->value(), "on" ))
       
   155             m_wrap = ta_Physical;
       
   156         else if (equalIgnoringCase(attr->value(), "off"))
       
   157             m_wrap = ta_NoWrap;
       
   158         if (renderer())
       
   159             renderer()->setNeedsLayoutAndPrefWidthsRecalc();
       
   160     } else if (attr->name() == accesskeyAttr) {
       
   161         // ignore for the moment
       
   162     } else if (attr->name() == alignAttr) {
       
   163         // Don't map 'align' attribute.  This matches what Firefox, Opera and IE do.
       
   164         // See http://bugs.webkit.org/show_bug.cgi?id=7075
       
   165     } else if (attr->name() == onfocusAttr)
       
   166         setHTMLEventListener(focusEvent, attr);
       
   167     else if (attr->name() == onblurAttr)
       
   168         setHTMLEventListener(blurEvent, attr);
       
   169     else if (attr->name() == onselectAttr)
       
   170         setHTMLEventListener(selectEvent, attr);
       
   171     else if (attr->name() == onchangeAttr)
       
   172         setHTMLEventListener(changeEvent, attr);
       
   173     else
       
   174         HTMLFormControlElementWithState::parseMappedAttribute(attr);
       
   175 }
       
   176 
       
   177 RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle* style)
       
   178 {
       
   179     return new (arena) RenderTextControl(this, true);
       
   180 }
       
   181 
       
   182 bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool)
       
   183 {
       
   184     if (name().isEmpty())
       
   185         return false;
       
   186         
       
   187     bool hardWrap = renderer() && wrap() == ta_Physical;
       
   188     String v = hardWrap ? static_cast<RenderTextControl*>(renderer())->textWithHardLineBreaks() : value();
       
   189     encoding.appendData(name(), v);
       
   190     return true;
       
   191 }
       
   192 
       
   193 void HTMLTextAreaElement::reset()
       
   194 {
       
   195     setValue(defaultValue());
       
   196 }
       
   197 
       
   198 bool HTMLTextAreaElement::isKeyboardFocusable(KeyboardEvent*) const
       
   199 {
       
   200     // If text areas can be focused, then they should always be keyboard focusable
       
   201     return HTMLFormControlElementWithState::isFocusable();
       
   202 }
       
   203 
       
   204 bool HTMLTextAreaElement::isMouseFocusable() const
       
   205 {
       
   206     return HTMLFormControlElementWithState::isFocusable();
       
   207 }
       
   208 
       
   209 void HTMLTextAreaElement::updateFocusAppearance(bool restorePreviousSelection)
       
   210 {
       
   211     ASSERT(renderer());
       
   212     
       
   213     if (!restorePreviousSelection || cachedSelStart == -1) {
       
   214         // If this is the first focus, set a caret at the beginning of the text.  
       
   215         // This matches some browsers' behavior; see Bugzilla Bug 11746 Comment #15.
       
   216         // http://bugs.webkit.org/show_bug.cgi?id=11746#c15
       
   217         setSelectionRange(0, 0);
       
   218     } else
       
   219         // Restore the cached selection.  This matches other browsers' behavior.
       
   220         setSelectionRange(cachedSelStart, cachedSelEnd); 
       
   221 
       
   222     if (document()->frame())
       
   223         document()->frame()->revealSelection();
       
   224 }
       
   225 
       
   226 void HTMLTextAreaElement::defaultEventHandler(Event *evt)
       
   227 {
       
   228     if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent))
       
   229         static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
       
   230 
       
   231     HTMLFormControlElementWithState::defaultEventHandler(evt);
       
   232 }
       
   233 
       
   234 void HTMLTextAreaElement::rendererWillBeDestroyed()
       
   235 {
       
   236     updateValue();
       
   237 }
       
   238 
       
   239 void HTMLTextAreaElement::updateValue() const
       
   240 {
       
   241     if (!valueMatchesRenderer()) {
       
   242         ASSERT(renderer());
       
   243         m_value = static_cast<RenderTextControl*>(renderer())->text();
       
   244         setValueMatchesRenderer();
       
   245     }
       
   246 }
       
   247 
       
   248 String HTMLTextAreaElement::value() const
       
   249 {
       
   250     updateValue();
       
   251     return m_value;
       
   252 }
       
   253 
       
   254 void HTMLTextAreaElement::setValue(const String& value)
       
   255 {
       
   256     // Code elsewhere normalizes line endings added by the user via the keyboard or pasting.
       
   257     // We must normalize line endings coming from JS.
       
   258     DeprecatedString valueWithNormalizedLineEndings = value.deprecatedString();
       
   259     valueWithNormalizedLineEndings.replace("\r\n", "\n");
       
   260     valueWithNormalizedLineEndings.replace("\r", "\n");
       
   261     
       
   262     m_value = valueWithNormalizedLineEndings;
       
   263     setValueMatchesRenderer();
       
   264     if (inDocument())
       
   265         document()->updateRendering();
       
   266     if (renderer())
       
   267         renderer()->updateFromElement();
       
   268     
       
   269     // Set the caret to the end of the text value.
       
   270     if (document()->focusedNode() == this) {
       
   271         unsigned endOfString = m_value.length();
       
   272         setSelectionRange(endOfString, endOfString);
       
   273     }
       
   274 
       
   275     setChanged();
       
   276 }
       
   277 
       
   278 String HTMLTextAreaElement::defaultValue() const
       
   279 {
       
   280     String val = "";
       
   281 
       
   282     // Since there may be comments, ignore nodes other than text nodes.
       
   283     for (Node* n = firstChild(); n; n = n->nextSibling())
       
   284         if (n->isTextNode())
       
   285             val += static_cast<Text*>(n)->data();
       
   286 
       
   287     // FIXME: We should only drop the first carriage return for the default
       
   288     // value in the original source, not defaultValues set from JS. This code
       
   289     // will do both.
       
   290     if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n')
       
   291         val.remove(0, 2);
       
   292     else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n'))
       
   293         val.remove(0, 1);
       
   294 
       
   295     return val;
       
   296 }
       
   297 
       
   298 void HTMLTextAreaElement::setDefaultValue(const String& defaultValue)
       
   299 {
       
   300     // To preserve comments, remove all the text nodes, then add a single one.
       
   301     Vector<RefPtr<Node> > textNodes;
       
   302     for (Node* n = firstChild(); n; n = n->nextSibling())
       
   303         if (n->isTextNode())
       
   304             textNodes.append(n);
       
   305     ExceptionCode ec = 0;
       
   306     size_t size = textNodes.size();
       
   307     for (size_t i = 0; i < size; ++i)
       
   308         removeChild(textNodes[i].get(), ec);
       
   309     insertBefore(document()->createTextNode(defaultValue), firstChild(), ec);
       
   310     setValue(defaultValue);
       
   311 }
       
   312 
       
   313 void HTMLTextAreaElement::accessKeyAction(bool sendToAnyElement)
       
   314 {
       
   315     focus();
       
   316 }
       
   317 
       
   318 String HTMLTextAreaElement::accessKey() const
       
   319 {
       
   320     return getAttribute(accesskeyAttr);
       
   321 }
       
   322 
       
   323 void HTMLTextAreaElement::setAccessKey(const String& value)
       
   324 {
       
   325     setAttribute(accesskeyAttr, value);
       
   326 }
       
   327 
       
   328 void HTMLTextAreaElement::setCols(int cols)
       
   329 {
       
   330     setAttribute(colsAttr, String::number(cols));
       
   331 }
       
   332 
       
   333 void HTMLTextAreaElement::setRows(int rows)
       
   334 {
       
   335     setAttribute(rowsAttr, String::number(rows));
       
   336 }
       
   337 
       
   338 Selection HTMLTextAreaElement::selection() const
       
   339 {
       
   340     if (!renderer() || cachedSelStart == -1 || cachedSelEnd == -1)
       
   341         return Selection();
       
   342     return static_cast<RenderTextControl*>(renderer())->selection(cachedSelStart, cachedSelEnd);
       
   343 }
       
   344 
       
   345 bool HTMLTextAreaElement::shouldUseInputMethod() const
       
   346 {
       
   347     return true;
       
   348 }
       
   349 
       
   350 } // namespace