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, 2008, 2010 Apple Inc. All rights reserved.
6 *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB.  If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "HTMLOptGroupElement.h"
27
28#include "Document.h"
29#include "HTMLNames.h"
30#include "HTMLSelectElement.h"
31#include "RenderMenuList.h"
32#include "NodeRenderStyle.h"
33#include "NodeRenderingContext.h"
34#include "StyleResolver.h"
35#include <wtf/StdLibExtras.h>
36
37namespace WebCore {
38
39using namespace HTMLNames;
40
41inline HTMLOptGroupElement::HTMLOptGroupElement(const QualifiedName& tagName, Document* document)
42    : HTMLElement(tagName, document)
43{
44    ASSERT(hasTagName(optgroupTag));
45    setHasCustomStyleCallbacks();
46}
47
48PassRefPtr<HTMLOptGroupElement> HTMLOptGroupElement::create(const QualifiedName& tagName, Document* document)
49{
50    return adoptRef(new HTMLOptGroupElement(tagName, document));
51}
52
53bool HTMLOptGroupElement::isDisabledFormControl() const
54{
55    return fastHasAttribute(disabledAttr);
56}
57
58bool HTMLOptGroupElement::supportsFocus() const
59{
60    return HTMLElement::supportsFocus();
61}
62
63bool HTMLOptGroupElement::isFocusable() const
64{
65    // Optgroup elements do not have a renderer so we check the renderStyle instead.
66    return supportsFocus() && renderStyle() && renderStyle()->display() != NONE;
67}
68
69const AtomicString& HTMLOptGroupElement::formControlType() const
70{
71    DEFINE_STATIC_LOCAL(const AtomicString, optgroup, ("optgroup", AtomicString::ConstructFromLiteral));
72    return optgroup;
73}
74
75void HTMLOptGroupElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta)
76{
77    recalcSelectOptions();
78    HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
79}
80
81void HTMLOptGroupElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
82{
83    HTMLElement::parseAttribute(name, value);
84    recalcSelectOptions();
85
86    if (name == disabledAttr)
87        didAffectSelector(AffectedSelectorDisabled | AffectedSelectorEnabled);
88}
89
90void HTMLOptGroupElement::recalcSelectOptions()
91{
92    ContainerNode* select = parentNode();
93    while (select && !select->hasTagName(selectTag))
94        select = select->parentNode();
95    if (select)
96        toHTMLSelectElement(select)->setRecalcListItems();
97}
98
99void HTMLOptGroupElement::attach(const AttachContext& context)
100{
101    HTMLElement::attach(context);
102    // If after attaching nothing called styleForRenderer() on this node we
103    // manually cache the value. This happens if our parent doesn't have a
104    // renderer like <optgroup> or if it doesn't allow children like <select>.
105    if (!m_style && parentNode()->renderStyle())
106        updateNonRenderStyle();
107}
108
109void HTMLOptGroupElement::detach(const AttachContext& context)
110{
111    m_style.clear();
112    HTMLElement::detach(context);
113}
114
115void HTMLOptGroupElement::updateNonRenderStyle()
116{
117    m_style = document()->ensureStyleResolver()->styleForElement(this);
118}
119
120RenderStyle* HTMLOptGroupElement::nonRendererStyle() const
121{
122    return m_style.get();
123}
124
125PassRefPtr<RenderStyle> HTMLOptGroupElement::customStyleForRenderer()
126{
127    // styleForRenderer is called whenever a new style should be associated
128    // with an Element so now is a good time to update our cached style.
129    updateNonRenderStyle();
130    return m_style;
131}
132
133String HTMLOptGroupElement::groupLabelText() const
134{
135    String itemText = document()->displayStringModifiedByEncoding(getAttribute(labelAttr));
136
137    // In WinIE, leading and trailing whitespace is ignored in options and optgroups. We match this behavior.
138    itemText = itemText.stripWhiteSpace();
139    // We want to collapse our whitespace too.  This will match other browsers.
140    itemText = itemText.simplifyWhiteSpace();
141
142    return itemText;
143}
144
145HTMLSelectElement* HTMLOptGroupElement::ownerSelectElement() const
146{
147    ContainerNode* select = parentNode();
148    while (select && !select->hasTagName(selectTag))
149        select = select->parentNode();
150
151    if (!select)
152       return 0;
153
154    return toHTMLSelectElement(select);
155}
156
157void HTMLOptGroupElement::accessKeyAction(bool)
158{
159    HTMLSelectElement* select = ownerSelectElement();
160    // send to the parent to bring focus to the list box
161    if (select && !select->focused())
162        select->accessKeyAction(false);
163}
164
165} // namespace
166