diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebCore/html/HTMLOptionElement.cpp | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebCore/html/HTMLOptionElement.cpp')
-rw-r--r-- | Source/WebCore/html/HTMLOptionElement.cpp | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/Source/WebCore/html/HTMLOptionElement.cpp b/Source/WebCore/html/HTMLOptionElement.cpp new file mode 100644 index 000000000..9654d674b --- /dev/null +++ b/Source/WebCore/html/HTMLOptionElement.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Motorola Mobility, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "HTMLOptionElement.h" + +#include "Attribute.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLSelectElement.h" +#include "NodeRenderStyle.h" +#include "NodeRenderingContext.h" +#include "RenderMenuList.h" +#include "ScriptElement.h" +#include "Text.h" +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +using namespace HTMLNames; + +HTMLOptionElement::HTMLOptionElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) + , m_isSelected(false) +{ + ASSERT(hasTagName(optionTag)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOptionElement(optionTag, document, form)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOptionElement(tagName, document, form)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::createForJSConstructor(Document* document, const String& data, const String& value, + bool defaultSelected, bool selected, ExceptionCode& ec) +{ + RefPtr<HTMLOptionElement> element = adoptRef(new HTMLOptionElement(optionTag, document)); + + RefPtr<Text> text = Text::create(document, data.isNull() ? "" : data); + + ec = 0; + element->appendChild(text.release(), ec); + if (ec) + return 0; + + if (!value.isNull()) + element->setValue(value); + if (defaultSelected) + element->setAttribute(selectedAttr, emptyAtom); + element->setSelected(selected); + + return element.release(); +} + +void HTMLOptionElement::attach() +{ + if (parentNode()->renderStyle()) + setRenderStyle(styleForRenderer()); + HTMLFormControlElement::attach(); +} + +void HTMLOptionElement::detach() +{ + m_style.clear(); + HTMLFormControlElement::detach(); +} + +bool HTMLOptionElement::supportsFocus() const +{ + return HTMLElement::supportsFocus(); +} + +bool HTMLOptionElement::isFocusable() const +{ + // Option elements do not have a renderer so we check the renderStyle instead. + return supportsFocus() && renderStyle() && renderStyle()->display() != NONE; +} + +const AtomicString& HTMLOptionElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, option, ("option")); + return option; +} + +String HTMLOptionElement::text() const +{ + Document* document = this->document(); + String text; + + // WinIE does not use the label attribute, so as a quirk, we ignore it. + if (!document->inQuirksMode()) + text = fastGetAttribute(labelAttr); + + // FIXME: The following treats an element with the label attribute set to + // the empty string the same as an element with no label attribute at all. + // Is that correct? If it is, then should the label function work the same way? + if (text.isEmpty()) + text = collectOptionInnerText(); + + // FIXME: Is displayStringModifiedByEncoding helpful here? + // If it's correct here, then isn't it needed in the value and label functions too? + return document->displayStringModifiedByEncoding(text).stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setText(const String &text, ExceptionCode& ec) +{ + // Changing the text causes a recalc of a select's items, which will reset the selected + // index to the first item if the select is single selection with a menu list. We attempt to + // preserve the selected item. + HTMLSelectElement* select = ownerSelectElement(); + bool selectIsMenuList = select && select->usesMenuList(); + int oldSelectedIndex = selectIsMenuList ? select->selectedIndex() : -1; + + // Handle the common special case where there's exactly 1 child node, and it's a text node. + Node* child = firstChild(); + if (child && child->isTextNode() && !child->nextSibling()) + static_cast<Text *>(child)->setData(text, ec); + else { + removeChildren(); + appendChild(Text::create(document(), text), ec); + } + + if (selectIsMenuList && select->selectedIndex() != oldSelectedIndex) + select->setSelectedIndex(oldSelectedIndex); +} + +void HTMLOptionElement::accessKeyAction(bool) +{ + HTMLSelectElement* select = ownerSelectElement(); + if (select) + select->accessKeySetSelectedIndex(index()); +} + +int HTMLOptionElement::index() const +{ + // It would be faster to cache the index, but harder to get it right in all cases. + + HTMLSelectElement* selectElement = ownerSelectElement(); + if (!selectElement) + return 0; + + int optionIndex = 0; + + const Vector<HTMLElement*>& items = selectElement->listItems(); + size_t length = items.size(); + for (size_t i = 0; i < length; ++i) { + if (!items[i]->hasTagName(optionTag)) + continue; + if (items[i] == this) + return optionIndex; + ++optionIndex; + } + + return 0; +} + +void HTMLOptionElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == selectedAttr) { + // FIXME: This doesn't match what the HTML specification says. + // The specification implies that removing the selected attribute or + // changing the value of a selected attribute that is already present + // has no effect on whether the element is selected. Further, it seems + // that we need to do more than just set m_isSelected to select in that + // case; we'd need to do the other work from the setSelected function. + m_isSelected = !attr->isNull(); + } else + HTMLFormControlElement::parseMappedAttribute(attr); +} + +String HTMLOptionElement::value() const +{ + const AtomicString& value = fastGetAttribute(valueAttr); + if (!value.isNull()) + return value; + return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setValue(const String& value) +{ + setAttribute(valueAttr, value); +} + +bool HTMLOptionElement::selected() +{ + if (HTMLSelectElement* select = ownerSelectElement()) + select->updateListItemSelectedStates(); + return m_isSelected; +} + +void HTMLOptionElement::setSelected(bool selected) +{ + if (m_isSelected == selected) + return; + + setSelectedState(selected); + + if (HTMLSelectElement* select = ownerSelectElement()) + select->optionSelectionStateChanged(this, selected); +} + +void HTMLOptionElement::setSelectedState(bool selected) +{ + if (m_isSelected == selected) + return; + + m_isSelected = selected; + setNeedsStyleRecalc(); +} + +void HTMLOptionElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (HTMLSelectElement* select = ownerSelectElement()) + select->optionElementChildrenChanged(); + HTMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +HTMLSelectElement* HTMLOptionElement::ownerSelectElement() const +{ + ContainerNode* select = parentNode(); + while (select && !select->hasTagName(selectTag)) + select = select->parentNode(); + + if (!select) + return 0; + + return toHTMLSelectElement(select); +} + +String HTMLOptionElement::label() const +{ + const AtomicString& label = fastGetAttribute(labelAttr); + if (!label.isNull()) + return label; + return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setLabel(const String& label) +{ + setAttribute(labelAttr, label); +} + +void HTMLOptionElement::setRenderStyle(PassRefPtr<RenderStyle> newStyle) +{ + m_style = newStyle; + if (HTMLSelectElement* select = ownerSelectElement()) { + if (RenderObject* renderer = select->renderer()) + renderer->repaint(); + } +} + +RenderStyle* HTMLOptionElement::nonRendererRenderStyle() const +{ + return m_style.get(); +} + +String HTMLOptionElement::textIndentedToRespectGroupLabel() const +{ + ContainerNode* parent = parentNode(); + if (parent && parent->hasTagName(optgroupTag)) + return " " + text(); + return text(); +} + +bool HTMLOptionElement::disabled() const +{ + return ownElementDisabled() || (parentNode() && static_cast<HTMLFormControlElement*>(parentNode())->disabled()); +} + +void HTMLOptionElement::insertedIntoTree(bool deep) +{ + if (HTMLSelectElement* select = ownerSelectElement()) { + select->setRecalcListItems(); + // Do not call selected() since calling updateListItemSelectedStates() + // at this time won't do the right thing. (Why, exactly?) + // FIXME: Might be better to call this unconditionally, always passing m_isSelected, + // rather than only calling it if we are selected. + if (m_isSelected) + select->optionSelectionStateChanged(this, true); + select->scrollToSelection(); + } + + HTMLFormControlElement::insertedIntoTree(deep); +} + +String HTMLOptionElement::collectOptionInnerText() const +{ + StringBuilder text; + for (Node* node = firstChild(); node; ) { + if (node->isTextNode()) + text.append(node->nodeValue()); + // Text nodes inside script elements are not part of the option text. + if (node->isElementNode() && toScriptElement(toElement(node))) + node = node->traverseNextSibling(this); + else + node = node->traverseNextNode(this); + } + return text.toString(); +} + +#ifndef NDEBUG + +HTMLOptionElement* toHTMLOptionElement(Node* node) +{ + ASSERT(!node || node->hasTagName(optionTag)); + return static_cast<HTMLOptionElement*>(node); +} + +const HTMLOptionElement* toHTMLOptionElement(const Node* node) +{ + ASSERT(!node || node->hasTagName(optionTag)); + return static_cast<const HTMLOptionElement*>(node); +} + +#endif + +} // namespace |