1/*
2 * Copyright (C) 2004-2018 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Copyright (C) 2012 Samsung Electronics. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24#include "ImageInputType.h"
25
26#include "CachedImage.h"
27#include "DOMFormData.h"
28#include "HTMLFormElement.h"
29#include "HTMLImageLoader.h"
30#include "HTMLInputElement.h"
31#include "HTMLNames.h"
32#include "HTMLParserIdioms.h"
33#include "InputTypeNames.h"
34#include "MouseEvent.h"
35#include "RenderImage.h"
36#include <wtf/NeverDestroyed.h>
37
38namespace WebCore {
39
40using namespace HTMLNames;
41
42ImageInputType::ImageInputType(HTMLInputElement& element)
43 : BaseButtonInputType(element)
44{
45}
46
47const AtomicString& ImageInputType::formControlType() const
48{
49 return InputTypeNames::image();
50}
51
52bool ImageInputType::isFormDataAppendable() const
53{
54 return true;
55}
56
57bool ImageInputType::appendFormData(DOMFormData& formData, bool) const
58{
59 ASSERT(element());
60 if (!element()->isActivatedSubmit())
61 return false;
62
63 auto& name = element()->name();
64 if (name.isEmpty()) {
65 formData.append("x"_s, String::number(m_clickLocation.x()));
66 formData.append("y"_s, String::number(m_clickLocation.y()));
67 return true;
68 }
69
70 formData.append(makeString(name, ".x"), String::number(m_clickLocation.x()));
71 formData.append(makeString(name, ".y"), String::number(m_clickLocation.y()));
72
73 auto value = element()->value();
74 if (!value.isEmpty())
75 formData.append(name, value);
76
77 return true;
78}
79
80bool ImageInputType::supportsValidation() const
81{
82 return false;
83}
84
85void ImageInputType::handleDOMActivateEvent(Event& event)
86{
87 ASSERT(element());
88 Ref<HTMLInputElement> protectedElement(*element());
89 if (protectedElement->isDisabledFormControl() || !protectedElement->form())
90 return;
91
92 Ref<HTMLFormElement> protectedForm(*protectedElement->form());
93
94 protectedElement->setActivatedSubmit(true);
95
96 m_clickLocation = IntPoint();
97 if (event.underlyingEvent()) {
98 Event& underlyingEvent = *event.underlyingEvent();
99 if (is<MouseEvent>(underlyingEvent)) {
100 MouseEvent& mouseEvent = downcast<MouseEvent>(underlyingEvent);
101 if (!mouseEvent.isSimulated())
102 m_clickLocation = IntPoint(mouseEvent.offsetX(), mouseEvent.offsetY());
103 }
104 }
105
106 // Update layout before processing form actions in case the style changes
107 // the Form or button relationships.
108 protectedElement->document().updateLayoutIgnorePendingStylesheets();
109
110 if (auto currentForm = protectedElement->form())
111 currentForm->prepareForSubmission(event); // Event handlers can run.
112
113 protectedElement->setActivatedSubmit(false);
114 event.setDefaultHandled();
115}
116
117RenderPtr<RenderElement> ImageInputType::createInputRenderer(RenderStyle&& style)
118{
119 ASSERT(element());
120 return createRenderer<RenderImage>(*element(), WTFMove(style));
121}
122
123void ImageInputType::attributeChanged(const QualifiedName& name)
124{
125 if (name == altAttr) {
126 if (auto* element = this->element()) {
127 auto* renderer = element->renderer();
128 if (is<RenderImage>(renderer))
129 downcast<RenderImage>(*renderer).updateAltText();
130 }
131 } else if (name == srcAttr) {
132 if (auto* element = this->element()) {
133 if (element->renderer())
134 element->ensureImageLoader().updateFromElementIgnoringPreviousError();
135 }
136 }
137 BaseButtonInputType::attributeChanged(name);
138}
139
140void ImageInputType::attach()
141{
142 BaseButtonInputType::attach();
143
144 ASSERT(element());
145 HTMLImageLoader& imageLoader = element()->ensureImageLoader();
146 imageLoader.updateFromElement();
147
148 auto* renderer = downcast<RenderImage>(element()->renderer());
149 if (!renderer)
150 return;
151
152 if (imageLoader.hasPendingBeforeLoadEvent())
153 return;
154
155 auto& imageResource = renderer->imageResource();
156 imageResource.setCachedImage(imageLoader.image());
157
158 // If we have no image at all because we have no src attribute, set
159 // image height and width for the alt text instead.
160 if (!imageLoader.image() && !imageResource.cachedImage())
161 renderer->setImageSizeForAltText();
162}
163
164bool ImageInputType::shouldRespectAlignAttribute()
165{
166 return true;
167}
168
169bool ImageInputType::canBeSuccessfulSubmitButton()
170{
171 return true;
172}
173
174bool ImageInputType::isImageButton() const
175{
176 return true;
177}
178
179bool ImageInputType::isEnumeratable()
180{
181 return false;
182}
183
184bool ImageInputType::shouldRespectHeightAndWidthAttributes()
185{
186 return true;
187}
188
189unsigned ImageInputType::height() const
190{
191 ASSERT(element());
192 Ref<HTMLInputElement> element(*this->element());
193
194 element->document().updateLayout();
195
196 if (auto* renderer = element->renderer())
197 return adjustForAbsoluteZoom(downcast<RenderBox>(*renderer).contentHeight(), *renderer);
198
199 // Check the attribute first for an explicit pixel value.
200 if (auto optionalHeight = parseHTMLNonNegativeInteger(element->attributeWithoutSynchronization(heightAttr)))
201 return optionalHeight.value();
202
203 // If the image is available, use its height.
204 auto* imageLoader = element->imageLoader();
205 if (imageLoader && imageLoader->image())
206 return imageLoader->image()->imageSizeForRenderer(element->renderer(), 1).height().toUnsigned();
207
208 return 0;
209}
210
211unsigned ImageInputType::width() const
212{
213 ASSERT(element());
214 Ref<HTMLInputElement> element(*this->element());
215
216 element->document().updateLayout();
217
218 if (auto* renderer = element->renderer())
219 return adjustForAbsoluteZoom(downcast<RenderBox>(*renderer).contentWidth(), *renderer);
220
221 // Check the attribute first for an explicit pixel value.
222 if (auto optionalWidth = parseHTMLNonNegativeInteger(element->attributeWithoutSynchronization(widthAttr)))
223 return optionalWidth.value();
224
225 // If the image is available, use its width.
226 auto* imageLoader = element->imageLoader();
227 if (imageLoader && imageLoader->image())
228 return imageLoader->image()->imageSizeForRenderer(element->renderer(), 1).width().toUnsigned();
229
230 return 0;
231}
232
233} // namespace WebCore
234