blob: 7228e4b35144fd7819f060dccd8e97ec6fa3cdb5 [file] [log] [blame]
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +01001/*
2 * Copyright (C) 2006, 2007, 2012 Apple Inc. All rights reserved.
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 "core/rendering/RenderFileUploadControl.h"
23
24#include <math.h>
25#include "HTMLNames.h"
Torne (Richard Coles)e5249552013-05-15 11:35:13 +010026#include "core/dom/shadow/ElementShadow.h"
27#include "core/dom/shadow/ShadowRoot.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010028#include "core/editing/VisiblePosition.h"
29#include "core/fileapi/FileList.h"
30#include "core/html/HTMLInputElement.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010031#include "core/platform/graphics/Font.h"
Torne (Richard Coles)81a51572013-05-13 16:52:28 +010032#include "core/platform/graphics/GraphicsContextStateSaver.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010033#include "core/platform/graphics/TextRun.h"
34#include "core/rendering/PaintInfo.h"
35#include "core/rendering/RenderButton.h"
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010036#include "core/rendering/RenderTheme.h"
37
38using namespace std;
39
40namespace WebCore {
41
42using namespace HTMLNames;
43
44const int afterButtonSpacing = 4;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010045const int defaultWidthNumChars = 34;
46const int buttonShadowHeight = 2;
47
48RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input)
49 : RenderBlock(input)
50 , m_canReceiveDroppedFiles(input->canReceiveDroppedFiles())
51{
52}
53
54RenderFileUploadControl::~RenderFileUploadControl()
55{
56}
57
58bool RenderFileUploadControl::canBeReplacedWithInlineRunIn() const
59{
60 return false;
61}
62
63void RenderFileUploadControl::updateFromElement()
64{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010065 HTMLInputElement* input = toHTMLInputElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010066 ASSERT(input->isFileUpload());
67
68 if (HTMLInputElement* button = uploadButton()) {
69 bool newCanReceiveDroppedFilesState = input->canReceiveDroppedFiles();
70 if (m_canReceiveDroppedFiles != newCanReceiveDroppedFilesState) {
71 m_canReceiveDroppedFiles = newCanReceiveDroppedFilesState;
72 button->setActive(newCanReceiveDroppedFilesState);
73 }
74 }
75
76 // This only supports clearing out the files, but that's OK because for
77 // security reasons that's the only change the DOM is allowed to make.
78 FileList* files = input->files();
79 ASSERT(files);
80 if (files && files->isEmpty())
81 repaint();
82}
83
84static int nodeWidth(Node* node)
85{
86 return (node && node->renderBox()) ? node->renderBox()->pixelSnappedWidth() : 0;
87}
88
89int RenderFileUploadControl::maxFilenameWidth() const
90{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +010091 HTMLInputElement* input = toHTMLInputElement(node());
Ben Murdoch83750172013-07-24 10:36:59 +010092 return max(0, contentBoxRect().pixelSnappedWidth() - nodeWidth(uploadButton()) - afterButtonSpacing);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +010093}
94
95void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
96{
97 if (style()->visibility() != VISIBLE)
98 return;
Ben Murdoch02772c62013-07-26 10:21:05 +010099
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100100 // Push a clip.
101 GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
102 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
103 IntRect clipRect = enclosingIntRect(LayoutRect(paintOffset.x() + borderLeft(), paintOffset.y() + borderTop(),
104 width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight));
105 if (clipRect.isEmpty())
106 return;
107 stateSaver.save();
108 paintInfo.context->clip(clipRect);
109 }
110
111 if (paintInfo.phase == PaintPhaseForeground) {
112 const String& displayedFilename = fileTextValue();
113 const Font& font = style()->font();
114 TextRun textRun = constructTextRun(this, font, displayedFilename, style(), TextRun::AllowTrailingExpansion, RespectDirection | RespectDirectionOverride);
115 textRun.disableRoundingHacks();
116
117 // Determine where the filename should be placed
118 LayoutUnit contentLeft = paintOffset.x() + borderLeft() + paddingLeft();
119 HTMLInputElement* button = uploadButton();
120 if (!button)
121 return;
122
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100123 HTMLInputElement* input = toHTMLInputElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100124 LayoutUnit buttonWidth = nodeWidth(button);
Ben Murdoch83750172013-07-24 10:36:59 +0100125 LayoutUnit buttonAndSpacingWidth = buttonWidth + afterButtonSpacing;
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100126 float textWidth = font.width(textRun);
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100127 LayoutUnit textX;
128 if (style()->isLeftToRightDirection())
Ben Murdoch83750172013-07-24 10:36:59 +0100129 textX = contentLeft + buttonAndSpacingWidth;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100130 else
Ben Murdoch83750172013-07-24 10:36:59 +0100131 textX = contentLeft + contentWidth() - buttonAndSpacingWidth - textWidth;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100132
133 LayoutUnit textY = 0;
134 // We want to match the button's baseline
135 // FIXME: Make this work with transforms.
136 if (RenderButton* buttonRenderer = toRenderButton(button->renderer()))
137 textY = paintOffset.y() + borderTop() + paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
138 else
139 textY = baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100140 TextRunPaintInfo textRunPaintInfo(textRun);
141 textRunPaintInfo.bounds = FloatRect(textX,
142 textY - style()->fontMetrics().ascent(),
143 textWidth,
144 style()->fontMetrics().height());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100145
Ben Murdoch7757ec22013-07-23 11:17:36 +0100146 paintInfo.context->setFillColor(resolveColor(CSSPropertyColor));
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100147
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100148 // Draw the filename
Torne (Richard Coles)93ac45c2013-05-29 14:40:20 +0100149 paintInfo.context->drawBidiText(font, textRunPaintInfo, IntPoint(roundToInt(textX), roundToInt(textY)));
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100150 }
151
152 // Paint the children.
153 RenderBlock::paintObject(paintInfo, paintOffset);
154}
155
156void RenderFileUploadControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
157{
158 // Figure out how big the filename space needs to be for a given number of characters
159 // (using "0" as the nominal character).
160 const UChar character = '0';
161 const String characterAsString = String(&character, 1);
162 const Font& font = style()->font();
163 // FIXME: Remove the need for this const_cast by making constructTextRun take a const RenderObject*.
164 RenderFileUploadControl* renderer = const_cast<RenderFileUploadControl*>(this);
165 float minDefaultLabelWidth = defaultWidthNumChars * font.width(constructTextRun(renderer, font, characterAsString, style(), TextRun::AllowTrailingExpansion));
166
Torne (Richard Coles)521d96e2013-06-19 11:58:24 +0100167 const String label = theme()->fileListDefaultLabel(toHTMLInputElement(node())->multiple());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100168 float defaultLabelWidth = font.width(constructTextRun(renderer, font, label, style(), TextRun::AllowTrailingExpansion));
169 if (HTMLInputElement* button = uploadButton())
170 if (RenderObject* buttonRenderer = button->renderer())
171 defaultLabelWidth += buttonRenderer->maxPreferredLogicalWidth() + afterButtonSpacing;
172 maxLogicalWidth = static_cast<int>(ceilf(max(minDefaultLabelWidth, defaultLabelWidth)));
173
174 if (!style()->width().isPercent())
175 minLogicalWidth = maxLogicalWidth;
176}
177
178void RenderFileUploadControl::computePreferredLogicalWidths()
179{
180 ASSERT(preferredLogicalWidthsDirty());
181
182 m_minPreferredLogicalWidth = 0;
183 m_maxPreferredLogicalWidth = 0;
184
185 if (style()->width().isFixed() && style()->width().value() > 0)
186 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(style()->width().value());
187 else
188 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
189
190 if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
191 m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
192 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->minWidth().value()));
193 }
194
195 if (style()->maxWidth().isFixed()) {
196 m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
197 m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(style()->maxWidth().value()));
198 }
199
200 int toAdd = borderAndPaddingWidth();
201 m_minPreferredLogicalWidth += toAdd;
202 m_maxPreferredLogicalWidth += toAdd;
203
204 setPreferredLogicalWidthsDirty(false);
205}
206
207VisiblePosition RenderFileUploadControl::positionForPoint(const LayoutPoint&)
208{
209 return VisiblePosition();
210}
211
212HTMLInputElement* RenderFileUploadControl::uploadButton() const
213{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100214 HTMLInputElement* input = toHTMLInputElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100215
216 ASSERT(input->shadow());
217
218 Node* buttonNode = input->shadow()->oldestShadowRoot()->firstChild();
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100219 return buttonNode && buttonNode->isHTMLElement() && buttonNode->hasTagName(inputTag) ? toHTMLInputElement(buttonNode) : 0;
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100220}
221
222String RenderFileUploadControl::buttonValue()
223{
224 if (HTMLInputElement* button = uploadButton())
225 return button->value();
Ben Murdoch02772c62013-07-26 10:21:05 +0100226
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100227 return String();
228}
229
230String RenderFileUploadControl::fileTextValue() const
231{
Torne (Richard Coles)5267f702013-06-11 10:57:24 +0100232 HTMLInputElement* input = toHTMLInputElement(node());
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100233 ASSERT(input->files());
234 return theme()->fileListNameForWidth(input->files(), style()->font(), maxFilenameWidth(), input->multiple());
235}
Ben Murdoch02772c62013-07-26 10:21:05 +0100236
Torne (Richard Coles)53e740f2013-05-09 18:38:43 +0100237} // namespace WebCore