1/**
2 * Copyright (C) 2005 Apple 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#include "RenderTreeBuilder.h"
31#include "StyleInheritedData.h"
32#include <wtf/IsoMallocInlines.h>
33
34#if PLATFORM(IOS_FAMILY)
35#include "RenderThemeIOS.h"
36#endif
37
38namespace WebCore {
39
40using namespace HTMLNames;
41
42WTF_MAKE_ISO_ALLOCATED_IMPL(RenderButton);
43
44RenderButton::RenderButton(HTMLFormControlElement& element, RenderStyle&& style)
45 : RenderFlexibleBox(element, WTFMove(style))
46{
47}
48
49RenderButton::~RenderButton() = default;
50
51HTMLFormControlElement& RenderButton::formControlElement() const
52{
53 return downcast<HTMLFormControlElement>(nodeForNonAnonymous());
54}
55
56bool RenderButton::canBeSelectionLeaf() const
57{
58 return formControlElement().hasEditableStyle();
59}
60
61bool RenderButton::hasLineIfEmpty() const
62{
63 return is<HTMLInputElement>(formControlElement());
64}
65
66void RenderButton::setInnerRenderer(RenderBlock& innerRenderer)
67{
68 ASSERT(!m_inner.get());
69 m_inner = makeWeakPtr(innerRenderer);
70 updateAnonymousChildStyle(m_inner->mutableStyle());
71}
72
73void RenderButton::updateAnonymousChildStyle(RenderStyle& childStyle) const
74{
75 childStyle.setFlexGrow(1.0f);
76 // min-width: 0; is needed for correct shrinking.
77 childStyle.setMinWidth(Length(0, Fixed));
78 // Use margin:auto instead of align-items:center to get safe centering, i.e.
79 // when the content overflows, treat it the same as align-items: flex-start.
80 childStyle.setMarginTop(Length());
81 childStyle.setMarginBottom(Length());
82 childStyle.setFlexDirection(style().flexDirection());
83 childStyle.setJustifyContent(style().justifyContent());
84 childStyle.setFlexWrap(style().flexWrap());
85 childStyle.setAlignItems(style().alignItems());
86 childStyle.setAlignContent(style().alignContent());
87}
88
89void RenderButton::updateFromElement()
90{
91 // If we're an input element, we may need to change our button text.
92 if (is<HTMLInputElement>(formControlElement())) {
93 HTMLInputElement& input = downcast<HTMLInputElement>(formControlElement());
94 String value = input.valueWithDefault();
95 setText(value);
96 }
97}
98
99void RenderButton::setText(const String& str)
100{
101 if (!m_buttonText && str.isEmpty())
102 return;
103
104 if (!m_buttonText) {
105 auto newButtonText = createRenderer<RenderTextFragment>(document(), str);
106 m_buttonText = makeWeakPtr(*newButtonText);
107 // FIXME: This mutation should go through the normal RenderTreeBuilder path.
108 if (RenderTreeBuilder::current())
109 RenderTreeBuilder::current()->attach(*this, WTFMove(newButtonText));
110 else
111 RenderTreeBuilder(*document().renderView()).attach(*this, WTFMove(newButtonText));
112 return;
113 }
114
115 if (!str.isEmpty()) {
116 m_buttonText->setText(str.impl());
117 return;
118 }
119 if (RenderTreeBuilder::current())
120 RenderTreeBuilder::current()->destroy(*m_buttonText);
121 else
122 RenderTreeBuilder(*document().renderView()).destroy(*m_buttonText);
123}
124
125String RenderButton::text() const
126{
127 if (m_buttonText)
128 return m_buttonText->text();
129 return { };
130}
131
132bool RenderButton::canHaveGeneratedChildren() const
133{
134 // Input elements can't have generated children, but button elements can. We'll
135 // write the code assuming any other button types that might emerge in the future
136 // can also have children.
137 return !is<HTMLInputElement>(formControlElement());
138}
139
140LayoutRect RenderButton::controlClipRect(const LayoutPoint& additionalOffset) const
141{
142 // Clip to the padding box to at least give content the extra padding space.
143 return LayoutRect(additionalOffset.x() + borderLeft(), additionalOffset.y() + borderTop(), width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom());
144}
145
146static int synthesizedBaselineFromContentBox(const RenderBox& box, LineDirectionMode direction)
147{
148 return direction == HorizontalLine ? box.borderTop() + box.paddingTop() + box.contentHeight() : box.borderRight() + box.paddingRight() + box.contentWidth();
149}
150
151int RenderButton::baselinePosition(FontBaseline, bool, LineDirectionMode direction, LinePositionMode) const
152{
153 // We cannot rely on RenderFlexibleBox::baselinePosition() because of flexboxes have some special behavior
154 // regarding baselines that shouldn't apply to buttons.
155 int baseline = firstLineBaseline().valueOr(synthesizedBaselineFromContentBox(*this, direction));
156 int marginAscent = direction == HorizontalLine ? marginTop() : marginRight();
157 return baseline + marginAscent;
158}
159
160#if PLATFORM(IOS_FAMILY)
161void RenderButton::layout()
162{
163 RenderFlexibleBox::layout();
164
165 // FIXME: We should not be adjusting styles during layout. See <rdar://problem/7675493>.
166 RenderThemeIOS::adjustRoundBorderRadius(mutableStyle(), *this);
167}
168#endif
169
170} // namespace WebCore
171