|
1 /* |
|
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
|
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
|
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. |
|
5 * Copyright (C) 2010 Google Inc. All rights reserved. |
|
6 * |
|
7 * This library is free software; you can redistribute it and/or |
|
8 * modify it under the terms of the GNU Library General Public |
|
9 * License as published by the Free Software Foundation; either |
|
10 * version 2 of the License, or (at your option) any later version. |
|
11 * |
|
12 * This library is distributed in the hope that it will be useful, |
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
15 * Library General Public License for more details. |
|
16 * |
|
17 * You should have received a copy of the GNU Library General Public License |
|
18 * along with this library; see the file COPYING.LIB. If not, write to |
|
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
20 * Boston, MA 02110-1301, USA. |
|
21 */ |
|
22 |
|
23 #include "config.h" |
|
24 #include "HTMLImageElement.h" |
|
25 |
|
26 #include "Attribute.h" |
|
27 #include "CSSHelper.h" |
|
28 #include "CSSPropertyNames.h" |
|
29 #include "CSSValueKeywords.h" |
|
30 #include "EventNames.h" |
|
31 #include "FrameView.h" |
|
32 #include "HTMLDocument.h" |
|
33 #include "HTMLFormElement.h" |
|
34 #include "HTMLNames.h" |
|
35 #include "RenderImage.h" |
|
36 #include "ScriptEventListener.h" |
|
37 |
|
38 using namespace std; |
|
39 |
|
40 namespace WebCore { |
|
41 |
|
42 using namespace HTMLNames; |
|
43 |
|
44 HTMLImageElement::HTMLImageElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) |
|
45 : HTMLElement(tagName, document) |
|
46 , m_imageLoader(this) |
|
47 , ismap(false) |
|
48 , m_form(form) |
|
49 , m_compositeOperator(CompositeSourceOver) |
|
50 { |
|
51 ASSERT(hasTagName(imgTag)); |
|
52 if (form) |
|
53 form->registerImgElement(this); |
|
54 } |
|
55 |
|
56 PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document* document) |
|
57 { |
|
58 return adoptRef(new HTMLImageElement(imgTag, document)); |
|
59 } |
|
60 |
|
61 PassRefPtr<HTMLImageElement> HTMLImageElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) |
|
62 { |
|
63 return adoptRef(new HTMLImageElement(tagName, document, form)); |
|
64 } |
|
65 |
|
66 HTMLImageElement::~HTMLImageElement() |
|
67 { |
|
68 if (m_form) |
|
69 m_form->removeImgElement(this); |
|
70 } |
|
71 |
|
72 PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document* document, const int* optionalWidth, const int* optionalHeight) |
|
73 { |
|
74 RefPtr<HTMLImageElement> image = adoptRef(new HTMLImageElement(imgTag, document)); |
|
75 if (optionalWidth) |
|
76 image->setWidth(*optionalWidth); |
|
77 if (optionalHeight > 0) |
|
78 image->setHeight(*optionalHeight); |
|
79 return image.release(); |
|
80 } |
|
81 |
|
82 bool HTMLImageElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const |
|
83 { |
|
84 if (attrName == widthAttr || |
|
85 attrName == heightAttr || |
|
86 attrName == vspaceAttr || |
|
87 attrName == hspaceAttr || |
|
88 attrName == valignAttr) { |
|
89 result = eUniversal; |
|
90 return false; |
|
91 } |
|
92 |
|
93 if (attrName == borderAttr || attrName == alignAttr) { |
|
94 result = eReplaced; // Shared with embed and iframe elements. |
|
95 return false; |
|
96 } |
|
97 |
|
98 return HTMLElement::mapToEntry(attrName, result); |
|
99 } |
|
100 |
|
101 void HTMLImageElement::parseMappedAttribute(Attribute* attr) |
|
102 { |
|
103 const QualifiedName& attrName = attr->name(); |
|
104 if (attrName == altAttr) { |
|
105 if (renderer() && renderer()->isImage()) |
|
106 toRenderImage(renderer())->updateAltText(); |
|
107 } else if (attrName == srcAttr) |
|
108 m_imageLoader.updateFromElementIgnoringPreviousError(); |
|
109 else if (attrName == widthAttr) |
|
110 addCSSLength(attr, CSSPropertyWidth, attr->value()); |
|
111 else if (attrName == heightAttr) |
|
112 addCSSLength(attr, CSSPropertyHeight, attr->value()); |
|
113 else if (attrName == borderAttr) { |
|
114 // border="noborder" -> border="0" |
|
115 addCSSLength(attr, CSSPropertyBorderWidth, attr->value().toInt() ? attr->value() : "0"); |
|
116 addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid); |
|
117 addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid); |
|
118 addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid); |
|
119 addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid); |
|
120 } else if (attrName == vspaceAttr) { |
|
121 addCSSLength(attr, CSSPropertyMarginTop, attr->value()); |
|
122 addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); |
|
123 } else if (attrName == hspaceAttr) { |
|
124 addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); |
|
125 addCSSLength(attr, CSSPropertyMarginRight, attr->value()); |
|
126 } else if (attrName == alignAttr) |
|
127 addHTMLAlignment(attr); |
|
128 else if (attrName == valignAttr) |
|
129 addCSSProperty(attr, CSSPropertyVerticalAlign, attr->value()); |
|
130 else if (attrName == usemapAttr) { |
|
131 if (attr->value().string()[0] == '#') |
|
132 usemap = attr->value(); |
|
133 else |
|
134 usemap = document()->completeURL(deprecatedParseURL(attr->value())).string(); |
|
135 setIsLink(!attr->isNull()); |
|
136 } else if (attrName == ismapAttr) |
|
137 ismap = true; |
|
138 else if (attrName == onabortAttr) |
|
139 setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr)); |
|
140 else if (attrName == onloadAttr) |
|
141 setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); |
|
142 else if (attrName == onbeforeloadAttr) |
|
143 setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); |
|
144 else if (attrName == compositeAttr) { |
|
145 if (!parseCompositeOperator(attr->value(), m_compositeOperator)) |
|
146 m_compositeOperator = CompositeSourceOver; |
|
147 } else if (attrName == nameAttr) { |
|
148 const AtomicString& newName = attr->value(); |
|
149 if (inDocument() && document()->isHTMLDocument()) { |
|
150 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); |
|
151 document->removeNamedItem(m_name); |
|
152 document->addNamedItem(newName); |
|
153 } |
|
154 m_name = newName; |
|
155 } else if (isIdAttributeName(attr->name())) { |
|
156 const AtomicString& newId = attr->value(); |
|
157 if (inDocument() && document()->isHTMLDocument()) { |
|
158 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); |
|
159 document->removeExtraNamedItem(m_id); |
|
160 document->addExtraNamedItem(newId); |
|
161 } |
|
162 m_id = newId; |
|
163 // also call superclass |
|
164 HTMLElement::parseMappedAttribute(attr); |
|
165 } else |
|
166 HTMLElement::parseMappedAttribute(attr); |
|
167 } |
|
168 |
|
169 String HTMLImageElement::altText() const |
|
170 { |
|
171 // lets figure out the alt text.. magic stuff |
|
172 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen |
|
173 // also heavily discussed by Hixie on bugzilla |
|
174 String alt = getAttribute(altAttr); |
|
175 // fall back to title attribute |
|
176 if (alt.isNull()) |
|
177 alt = getAttribute(titleAttr); |
|
178 return alt; |
|
179 } |
|
180 |
|
181 RenderObject* HTMLImageElement::createRenderer(RenderArena* arena, RenderStyle* style) |
|
182 { |
|
183 if (style->contentData()) |
|
184 return RenderObject::createObject(this, style); |
|
185 |
|
186 return new (arena) RenderImage(this); |
|
187 } |
|
188 |
|
189 void HTMLImageElement::attach() |
|
190 { |
|
191 HTMLElement::attach(); |
|
192 |
|
193 if (renderer() && renderer()->isImage() && m_imageLoader.haveFiredBeforeLoadEvent()) { |
|
194 RenderImage* imageObj = toRenderImage(renderer()); |
|
195 if (imageObj->hasImage()) |
|
196 return; |
|
197 imageObj->setCachedImage(m_imageLoader.image()); |
|
198 |
|
199 // If we have no image at all because we have no src attribute, set |
|
200 // image height and width for the alt text instead. |
|
201 if (!m_imageLoader.image() && !imageObj->cachedImage()) |
|
202 imageObj->setImageSizeForAltText(); |
|
203 } |
|
204 } |
|
205 |
|
206 void HTMLImageElement::insertedIntoDocument() |
|
207 { |
|
208 if (document()->isHTMLDocument()) { |
|
209 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); |
|
210 document->addNamedItem(m_name); |
|
211 document->addExtraNamedItem(m_id); |
|
212 } |
|
213 |
|
214 // If we have been inserted from a renderer-less document, |
|
215 // our loader may have not fetched the image, so do it now. |
|
216 if (!m_imageLoader.image()) |
|
217 m_imageLoader.updateFromElement(); |
|
218 |
|
219 HTMLElement::insertedIntoDocument(); |
|
220 } |
|
221 |
|
222 void HTMLImageElement::removedFromDocument() |
|
223 { |
|
224 if (document()->isHTMLDocument()) { |
|
225 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); |
|
226 document->removeNamedItem(m_name); |
|
227 document->removeExtraNamedItem(m_id); |
|
228 } |
|
229 |
|
230 HTMLElement::removedFromDocument(); |
|
231 } |
|
232 |
|
233 void HTMLImageElement::insertedIntoTree(bool deep) |
|
234 { |
|
235 if (!m_form) { |
|
236 // m_form can be non-null if it was set in constructor. |
|
237 for (Node* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { |
|
238 if (ancestor->hasTagName(formTag)) { |
|
239 m_form = static_cast<HTMLFormElement*>(ancestor); |
|
240 m_form->registerImgElement(this); |
|
241 break; |
|
242 } |
|
243 } |
|
244 } |
|
245 |
|
246 HTMLElement::insertedIntoTree(deep); |
|
247 } |
|
248 |
|
249 void HTMLImageElement::removedFromTree(bool deep) |
|
250 { |
|
251 if (m_form) |
|
252 m_form->removeImgElement(this); |
|
253 m_form = 0; |
|
254 HTMLElement::removedFromTree(deep); |
|
255 } |
|
256 |
|
257 int HTMLImageElement::width(bool ignorePendingStylesheets) const |
|
258 { |
|
259 if (!renderer()) { |
|
260 // check the attribute first for an explicit pixel value |
|
261 bool ok; |
|
262 int width = getAttribute(widthAttr).toInt(&ok); |
|
263 if (ok) |
|
264 return width; |
|
265 |
|
266 // if the image is available, use its width |
|
267 if (m_imageLoader.image()) { |
|
268 float zoomFactor = document()->view() ? document()->view()->pageZoomFactor() : 1.0f; |
|
269 return m_imageLoader.image()->imageSize(zoomFactor).width(); |
|
270 } |
|
271 } |
|
272 |
|
273 if (ignorePendingStylesheets) |
|
274 document()->updateLayoutIgnorePendingStylesheets(); |
|
275 else |
|
276 document()->updateLayout(); |
|
277 |
|
278 return renderBox() ? renderBox()->contentWidth() : 0; |
|
279 } |
|
280 |
|
281 int HTMLImageElement::height(bool ignorePendingStylesheets) const |
|
282 { |
|
283 if (!renderer()) { |
|
284 // check the attribute first for an explicit pixel value |
|
285 bool ok; |
|
286 int height = getAttribute(heightAttr).toInt(&ok); |
|
287 if (ok) |
|
288 return height; |
|
289 |
|
290 // if the image is available, use its height |
|
291 if (m_imageLoader.image()) { |
|
292 float zoomFactor = document()->view() ? document()->view()->pageZoomFactor() : 1.0f; |
|
293 return m_imageLoader.image()->imageSize(zoomFactor).height(); |
|
294 } |
|
295 } |
|
296 |
|
297 if (ignorePendingStylesheets) |
|
298 document()->updateLayoutIgnorePendingStylesheets(); |
|
299 else |
|
300 document()->updateLayout(); |
|
301 |
|
302 return renderBox() ? renderBox()->contentHeight() : 0; |
|
303 } |
|
304 |
|
305 int HTMLImageElement::naturalWidth() const |
|
306 { |
|
307 if (!m_imageLoader.image()) |
|
308 return 0; |
|
309 |
|
310 return m_imageLoader.image()->imageSize(1.0f).width(); |
|
311 } |
|
312 |
|
313 int HTMLImageElement::naturalHeight() const |
|
314 { |
|
315 if (!m_imageLoader.image()) |
|
316 return 0; |
|
317 |
|
318 return m_imageLoader.image()->imageSize(1.0f).height(); |
|
319 } |
|
320 |
|
321 bool HTMLImageElement::isURLAttribute(Attribute* attr) const |
|
322 { |
|
323 return attr->name() == srcAttr |
|
324 || attr->name() == lowsrcAttr |
|
325 || attr->name() == longdescAttr |
|
326 || (attr->name() == usemapAttr && attr->value().string()[0] != '#'); |
|
327 } |
|
328 |
|
329 const AtomicString& HTMLImageElement::alt() const |
|
330 { |
|
331 return getAttribute(altAttr); |
|
332 } |
|
333 |
|
334 bool HTMLImageElement::draggable() const |
|
335 { |
|
336 // Image elements are draggable by default. |
|
337 return !equalIgnoringCase(getAttribute(draggableAttr), "false"); |
|
338 } |
|
339 |
|
340 void HTMLImageElement::setHeight(int value) |
|
341 { |
|
342 setAttribute(heightAttr, String::number(value)); |
|
343 } |
|
344 |
|
345 KURL HTMLImageElement::src() const |
|
346 { |
|
347 return document()->completeURL(getAttribute(srcAttr)); |
|
348 } |
|
349 |
|
350 void HTMLImageElement::setSrc(const String& value) |
|
351 { |
|
352 setAttribute(srcAttr, value); |
|
353 } |
|
354 |
|
355 void HTMLImageElement::setWidth(int value) |
|
356 { |
|
357 setAttribute(widthAttr, String::number(value)); |
|
358 } |
|
359 |
|
360 int HTMLImageElement::x() const |
|
361 { |
|
362 RenderObject* r = renderer(); |
|
363 if (!r) |
|
364 return 0; |
|
365 |
|
366 // FIXME: This doesn't work correctly with transforms. |
|
367 FloatPoint absPos = r->localToAbsolute(); |
|
368 return absPos.x(); |
|
369 } |
|
370 |
|
371 int HTMLImageElement::y() const |
|
372 { |
|
373 RenderObject* r = renderer(); |
|
374 if (!r) |
|
375 return 0; |
|
376 |
|
377 // FIXME: This doesn't work correctly with transforms. |
|
378 FloatPoint absPos = r->localToAbsolute(); |
|
379 return absPos.y(); |
|
380 } |
|
381 |
|
382 bool HTMLImageElement::complete() const |
|
383 { |
|
384 return m_imageLoader.imageComplete(); |
|
385 } |
|
386 |
|
387 void HTMLImageElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const |
|
388 { |
|
389 HTMLElement::addSubresourceAttributeURLs(urls); |
|
390 |
|
391 addSubresourceURL(urls, src()); |
|
392 // FIXME: What about when the usemap attribute begins with "#"? |
|
393 addSubresourceURL(urls, document()->completeURL(getAttribute(usemapAttr))); |
|
394 } |
|
395 |
|
396 void HTMLImageElement::willMoveToNewOwnerDocument() |
|
397 { |
|
398 m_imageLoader.elementWillMoveToNewOwnerDocument(); |
|
399 HTMLElement::willMoveToNewOwnerDocument(); |
|
400 } |
|
401 |
|
402 } |