HTMLOptionElement.cpp [plain text]
#include "config.h"
#include "HTMLOptionElement.h"
#include "Attribute.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 "RenderTheme.h"
#include "ScriptElement.h"
#include "StyleResolver.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)
: HTMLElement(tagName, document)
, m_disabled(false)
, m_isSelected(false)
{
ASSERT(hasTagName(optionTag));
}
PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(Document* document)
{
return adoptRef(new HTMLOptionElement(optionTag, document));
}
PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(const QualifiedName& tagName, Document* document)
{
return adoptRef(new HTMLOptionElement(tagName, document));
}
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());
HTMLElement::attach();
}
void HTMLOptionElement::detach()
{
m_style.clear();
HTMLElement::detach();
}
bool HTMLOptionElement::supportsFocus() const
{
return HTMLElement::supportsFocus();
}
bool HTMLOptionElement::isFocusable() const
{
return supportsFocus() && renderStyle() && renderStyle()->display() != NONE;
}
String HTMLOptionElement::text() const
{
Document* document = this->document();
String text;
if (!document->inQuirksMode())
text = fastGetAttribute(labelAttr);
if (text.isEmpty())
text = collectOptionInnerText();
return document->displayStringModifiedByEncoding(text).stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace);
}
void HTMLOptionElement::setText(const String &text, ExceptionCode& ec)
{
RefPtr<Node> protectFromMutationEvents(this);
RefPtr<HTMLSelectElement> select = ownerSelectElement();
bool selectIsMenuList = select && select->usesMenuList();
int oldSelectedIndex = selectIsMenuList ? select->selectedIndex() : -1;
Node* child = firstChild();
if (child && child->isTextNode() && !child->nextSibling())
toText(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
{
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::parseAttribute(Attribute* attr)
{
if (attr->name() == disabledAttr) {
bool oldDisabled = m_disabled;
m_disabled = !attr->isNull();
if (oldDisabled != m_disabled) {
setNeedsStyleRecalc();
if (renderer() && renderer()->style()->hasAppearance())
renderer()->theme()->stateChanged(renderer(), EnabledState);
}
} else if (attr->name() == selectedAttr) {
m_isSelected = !attr->isNull();
} else
HTMLElement::parseAttribute(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();
HTMLElement::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() && parentNode()->isHTMLElement() && static_cast<HTMLElement*>(parentNode())->disabled());
}
Node::InsertionNotificationRequest HTMLOptionElement::insertedInto(Node* insertionPoint)
{
if (HTMLSelectElement* select = ownerSelectElement()) {
select->setRecalcListItems();
if (m_isSelected)
select->optionSelectionStateChanged(this, true);
select->scrollToSelection();
}
return HTMLElement::insertedInto(insertionPoint);
}
String HTMLOptionElement::collectOptionInnerText() const
{
StringBuilder text;
for (Node* node = firstChild(); node; ) {
if (node->isTextNode())
text.append(node->nodeValue());
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
}