• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "core/HTMLNames.h"
25 #include "core/dom/shadow/ElementShadow.h"
26 #include "core/dom/shadow/ShadowRoot.h"
27 #include "core/fileapi/FileList.h"
28 #include "core/html/HTMLInputElement.h"
29 #include "core/rendering/PaintInfo.h"
30 #include "core/rendering/RenderButton.h"
31 #include "core/rendering/RenderTheme.h"
32 #include "platform/fonts/Font.h"
33 #include "platform/graphics/GraphicsContextStateSaver.h"
34 #include "platform/text/PlatformLocale.h"
35 #include "platform/text/TextRun.h"
36 #include <math.h>
37 
38 using namespace std;
39 
40 namespace WebCore {
41 
42 using namespace HTMLNames;
43 
44 const int afterButtonSpacing = 4;
45 const int defaultWidthNumChars = 34;
46 const int buttonShadowHeight = 2;
47 
RenderFileUploadControl(HTMLInputElement * input)48 RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input)
49     : RenderBlockFlow(input)
50     , m_canReceiveDroppedFiles(input->canReceiveDroppedFiles())
51 {
52 }
53 
~RenderFileUploadControl()54 RenderFileUploadControl::~RenderFileUploadControl()
55 {
56 }
57 
updateFromElement()58 void RenderFileUploadControl::updateFromElement()
59 {
60     HTMLInputElement* input = toHTMLInputElement(node());
61     ASSERT(input->isFileUpload());
62 
63     if (HTMLInputElement* button = uploadButton()) {
64         bool newCanReceiveDroppedFilesState = input->canReceiveDroppedFiles();
65         if (m_canReceiveDroppedFiles != newCanReceiveDroppedFilesState) {
66             m_canReceiveDroppedFiles = newCanReceiveDroppedFilesState;
67             button->setActive(newCanReceiveDroppedFilesState);
68         }
69     }
70 
71     // This only supports clearing out the files, but that's OK because for
72     // security reasons that's the only change the DOM is allowed to make.
73     FileList* files = input->files();
74     ASSERT(files);
75     if (files && files->isEmpty())
76         paintInvalidationForWholeRenderer();
77 }
78 
nodeWidth(Node * node)79 static int nodeWidth(Node* node)
80 {
81     return (node && node->renderBox()) ? node->renderBox()->pixelSnappedWidth() : 0;
82 }
83 
maxFilenameWidth() const84 int RenderFileUploadControl::maxFilenameWidth() const
85 {
86     return max(0, contentBoxRect().pixelSnappedWidth() - nodeWidth(uploadButton()) - afterButtonSpacing);
87 }
88 
paintObject(PaintInfo & paintInfo,const LayoutPoint & paintOffset)89 void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
90 {
91     if (style()->visibility() != VISIBLE)
92         return;
93 
94     // Push a clip.
95     GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
96     if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
97         IntRect clipRect = enclosingIntRect(LayoutRect(paintOffset.x() + borderLeft(), paintOffset.y() + borderTop(),
98                          width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight));
99         if (clipRect.isEmpty())
100             return;
101         stateSaver.save();
102         paintInfo.context->clip(clipRect);
103     }
104 
105     if (paintInfo.phase == PaintPhaseForeground) {
106         const String& displayedFilename = fileTextValue();
107         const Font& font = style()->font();
108         TextRun textRun = constructTextRun(this, font, displayedFilename, style(), TextRun::AllowTrailingExpansion, RespectDirection | RespectDirectionOverride);
109         textRun.disableRoundingHacks();
110 
111         // Determine where the filename should be placed
112         LayoutUnit contentLeft = paintOffset.x() + borderLeft() + paddingLeft();
113         HTMLInputElement* button = uploadButton();
114         if (!button)
115             return;
116 
117         LayoutUnit buttonWidth = nodeWidth(button);
118         LayoutUnit buttonAndSpacingWidth = buttonWidth + afterButtonSpacing;
119         float textWidth = font.width(textRun);
120         LayoutUnit textX;
121         if (style()->isLeftToRightDirection())
122             textX = contentLeft + buttonAndSpacingWidth;
123         else
124             textX = contentLeft + contentWidth() - buttonAndSpacingWidth - textWidth;
125 
126         LayoutUnit textY = 0;
127         // We want to match the button's baseline
128         // FIXME: Make this work with transforms.
129         if (RenderButton* buttonRenderer = toRenderButton(button->renderer()))
130             textY = paintOffset.y() + borderTop() + paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
131         else
132             textY = baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine);
133         TextRunPaintInfo textRunPaintInfo(textRun);
134         // FIXME: Shouldn't these offsets be rounded? crbug.com/350474
135         textRunPaintInfo.bounds = FloatRect(textX.toFloat(), textY.toFloat() - style()->fontMetrics().ascent(),
136             textWidth, style()->fontMetrics().height());
137 
138         paintInfo.context->setFillColor(resolveColor(CSSPropertyColor));
139 
140         // Draw the filename
141         paintInfo.context->drawBidiText(font, textRunPaintInfo, IntPoint(roundToInt(textX), roundToInt(textY)));
142     }
143 
144     // Paint the children.
145     RenderBlockFlow::paintObject(paintInfo, paintOffset);
146 }
147 
computeIntrinsicLogicalWidths(LayoutUnit & minLogicalWidth,LayoutUnit & maxLogicalWidth) const148 void RenderFileUploadControl::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const
149 {
150     // Figure out how big the filename space needs to be for a given number of characters
151     // (using "0" as the nominal character).
152     const UChar character = '0';
153     const String characterAsString = String(&character, 1);
154     const Font& font = style()->font();
155     // FIXME: Remove the need for this const_cast by making constructTextRun take a const RenderObject*.
156     RenderFileUploadControl* renderer = const_cast<RenderFileUploadControl*>(this);
157     float minDefaultLabelWidth = defaultWidthNumChars * font.width(constructTextRun(renderer, font, characterAsString, style(), TextRun::AllowTrailingExpansion));
158 
159     const String label = toHTMLInputElement(node())->locale().queryString(blink::WebLocalizedString::FileButtonNoFileSelectedLabel);
160     float defaultLabelWidth = font.width(constructTextRun(renderer, font, label, style(), TextRun::AllowTrailingExpansion));
161     if (HTMLInputElement* button = uploadButton())
162         if (RenderObject* buttonRenderer = button->renderer())
163             defaultLabelWidth += buttonRenderer->maxPreferredLogicalWidth() + afterButtonSpacing;
164     maxLogicalWidth = static_cast<int>(ceilf(max(minDefaultLabelWidth, defaultLabelWidth)));
165 
166     if (!style()->width().isPercent())
167         minLogicalWidth = maxLogicalWidth;
168 }
169 
computePreferredLogicalWidths()170 void RenderFileUploadControl::computePreferredLogicalWidths()
171 {
172     ASSERT(preferredLogicalWidthsDirty());
173 
174     m_minPreferredLogicalWidth = 0;
175     m_maxPreferredLogicalWidth = 0;
176     RenderStyle* styleToUse = style();
177 
178     if (styleToUse->width().isFixed() && styleToUse->width().value() > 0)
179         m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->width().value());
180     else
181         computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
182 
183     if (styleToUse->minWidth().isFixed() && styleToUse->minWidth().value() > 0) {
184         m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value()));
185         m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value()));
186     }
187 
188     if (styleToUse->maxWidth().isFixed()) {
189         m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value()));
190         m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value()));
191     }
192 
193     int toAdd = borderAndPaddingWidth();
194     m_minPreferredLogicalWidth += toAdd;
195     m_maxPreferredLogicalWidth += toAdd;
196 
197     clearPreferredLogicalWidthsDirty();
198 }
199 
positionForPoint(const LayoutPoint &)200 PositionWithAffinity RenderFileUploadControl::positionForPoint(const LayoutPoint&)
201 {
202     return PositionWithAffinity();
203 }
204 
uploadButton() const205 HTMLInputElement* RenderFileUploadControl::uploadButton() const
206 {
207     // FIXME: This should be on HTMLInputElement as an API like innerButtonElement().
208     HTMLInputElement* input = toHTMLInputElement(node());
209     Node* buttonNode = input->userAgentShadowRoot()->firstChild();
210     return isHTMLInputElement(buttonNode) ? toHTMLInputElement(buttonNode) : 0;
211 }
212 
buttonValue()213 String RenderFileUploadControl::buttonValue()
214 {
215     if (HTMLInputElement* button = uploadButton())
216         return button->value();
217 
218     return String();
219 }
220 
fileTextValue() const221 String RenderFileUploadControl::fileTextValue() const
222 {
223     HTMLInputElement* input = toHTMLInputElement(node());
224     ASSERT(input->files());
225     return RenderTheme::theme().fileListNameForWidth(input->locale(), input->files(), style()->font(), maxFilenameWidth());
226 }
227 
228 } // namespace WebCore
229