1 /**
2 * Copyright (C) 2005 Apple Computer, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21 #include "config.h"
22 #include "RenderButton.h"
23
24 #include "Document.h"
25 #include "GraphicsContext.h"
26 #include "HTMLInputElement.h"
27 #include "HTMLNames.h"
28 #include "RenderTextFragment.h"
29 #include "RenderTheme.h"
30
31 #if ENABLE(WML)
32 #include "WMLDoElement.h"
33 #include "WMLNames.h"
34 #endif
35
36 namespace WebCore {
37
38 using namespace HTMLNames;
39
RenderButton(Node * node)40 RenderButton::RenderButton(Node* node)
41 : RenderFlexibleBox(node)
42 , m_buttonText(0)
43 , m_inner(0)
44 , m_default(false)
45 {
46 }
47
~RenderButton()48 RenderButton::~RenderButton()
49 {
50 }
51
addChild(RenderObject * newChild,RenderObject * beforeChild)52 void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild)
53 {
54 if (!m_inner) {
55 // Create an anonymous block.
56 ASSERT(!firstChild());
57 bool isFlexibleBox = style()->display() == BOX || style()->display() == INLINE_BOX;
58 m_inner = createAnonymousBlock(isFlexibleBox);
59 setupInnerStyle(m_inner->style());
60 RenderFlexibleBox::addChild(m_inner);
61 }
62
63 m_inner->addChild(newChild, beforeChild);
64 }
65
removeChild(RenderObject * oldChild)66 void RenderButton::removeChild(RenderObject* oldChild)
67 {
68 if (oldChild == m_inner || !m_inner) {
69 RenderFlexibleBox::removeChild(oldChild);
70 m_inner = 0;
71 } else
72 m_inner->removeChild(oldChild);
73 }
74
styleWillChange(StyleDifference diff,const RenderStyle * newStyle)75 void RenderButton::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
76 {
77 if (m_inner) {
78 // RenderBlock::setStyle is going to apply a new style to the inner block, which
79 // will have the initial box flex value, 0. The current value is 1, because we set
80 // it right below. Here we change it back to 0 to avoid getting a spurious layout hint
81 // because of the difference.
82 m_inner->style()->setBoxFlex(0);
83 }
84 RenderBlock::styleWillChange(diff, newStyle);
85 }
86
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)87 void RenderButton::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
88 {
89 RenderBlock::styleDidChange(diff, oldStyle);
90
91 if (m_buttonText)
92 m_buttonText->setStyle(style());
93 if (m_inner) // RenderBlock handled updating the anonymous block's style.
94 setupInnerStyle(m_inner->style());
95
96 if (!m_default && theme()->isDefault(this)) {
97 if (!m_timer)
98 m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired));
99 m_timer->startRepeating(0.03);
100 m_default = true;
101 } else if (m_default && !theme()->isDefault(this)) {
102 m_default = false;
103 m_timer.clear();
104 }
105 }
106
setupInnerStyle(RenderStyle * innerStyle)107 void RenderButton::setupInnerStyle(RenderStyle* innerStyle)
108 {
109 ASSERT(innerStyle->refCount() == 1);
110 // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is
111 // safe to modify.
112 innerStyle->setBoxFlex(1.0f);
113 innerStyle->setBoxOrient(style()->boxOrient());
114 }
115
updateFromElement()116 void RenderButton::updateFromElement()
117 {
118 // If we're an input element, we may need to change our button text.
119 if (node()->hasTagName(inputTag)) {
120 HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
121 String value = input->valueWithDefault();
122 setText(value);
123 }
124
125
126 #if ENABLE(WML)
127 else if (node()->hasTagName(WMLNames::doTag)) {
128 WMLDoElement* doElement = static_cast<WMLDoElement*>(node());
129
130 String value = doElement->label();
131 if (value.isEmpty())
132 value = doElement->name();
133
134 setText(value);
135 }
136 #endif
137 }
138
canHaveChildren() const139 bool RenderButton::canHaveChildren() const
140 {
141 // Input elements can't have children, but button elements can. We'll
142 // write the code assuming any other button types that might emerge in the future
143 // can also have children.
144 return !node()->hasTagName(inputTag);
145 }
146
setText(const String & str)147 void RenderButton::setText(const String& str)
148 {
149 if (str.isEmpty()) {
150 if (m_buttonText) {
151 m_buttonText->destroy();
152 m_buttonText = 0;
153 }
154 } else {
155 if (m_buttonText)
156 m_buttonText->setText(str.impl());
157 else {
158 m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl());
159 m_buttonText->setStyle(style());
160 addChild(m_buttonText);
161 }
162 }
163 }
164
text() const165 String RenderButton::text() const
166 {
167 return m_buttonText ? m_buttonText->text() : 0;
168 }
169
updateBeforeAfterContent(PseudoId type)170 void RenderButton::updateBeforeAfterContent(PseudoId type)
171 {
172 if (m_inner)
173 m_inner->children()->updateBeforeAfterContent(m_inner, type, this);
174 else
175 children()->updateBeforeAfterContent(this, type);
176 }
177
controlClipRect(int tx,int ty) const178 IntRect RenderButton::controlClipRect(int tx, int ty) const
179 {
180 // Clip to the padding box to at least give content the extra padding space.
181 return IntRect(tx + borderLeft(), ty + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom());
182 }
183
timerFired(Timer<RenderButton> *)184 void RenderButton::timerFired(Timer<RenderButton>*)
185 {
186 // FIXME Bug 25110: Ideally we would stop our timer when our Document
187 // enters the page cache. But we currently have no way of being notified
188 // when that happens, so we'll just ignore the timer firing as long as
189 // we're in the cache.
190 if (document()->inPageCache())
191 return;
192
193 repaint();
194 }
195
196 } // namespace WebCore
197