2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
25 #include "html_imageimpl.h"
27 #include "DocLoader.h"
28 #include "EventNames.h"
29 #include "FloatRect.h"
30 #include "HTMLFormElement.h"
31 #include "csshelper.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "HTMLDocument.h"
35 #include "RenderImage.h"
36 #include "HTMLNames.h"
42 using namespace EventNames;
43 using namespace HTMLNames;
45 HTMLImageLoader::HTMLImageLoader(Element* elt)
46 : m_element(elt), m_image(0), m_firedLoad(true), m_imageComplete(true)
50 HTMLImageLoader::~HTMLImageLoader()
54 m_element->document()->removeImage(this);
57 void HTMLImageLoader::setLoadingImage(CachedImage *loadingImage)
60 m_imageComplete = false;
61 m_image = loadingImage;
64 void HTMLImageLoader::updateFromElement()
66 // If we're not making renderers for the page, then don't load images. We don't want to slow
67 // down the raw HTML parsing case by loading images we don't intend to display.
68 Element* elem = element();
69 Document* doc = elem->document();
73 AtomicString attr = elem->getAttribute(elem->hasLocalName(objectTag) ? dataAttr : srcAttr);
75 // Treat a lack of src or empty string for src as no image at all.
76 CachedImage *newImage = 0;
78 newImage = doc->docLoader()->requestImage(parseURL(attr));
80 CachedImage *oldImage = m_image;
81 if (newImage != oldImage) {
82 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
83 if (!doc->ownerElement() && newImage)
84 printf("Image requested at %d\n", doc->elapsedTime());
86 setLoadingImage(newImage);
90 oldImage->deref(this);
93 if (RenderImage* renderer = static_cast<RenderImage*>(elem->renderer()))
94 renderer->resetAnimation();
97 void HTMLImageLoader::dispatchLoadEvent()
99 if (!m_firedLoad && m_image) {
101 element()->dispatchHTMLEvent(m_image->isErrorImage() ? errorEvent : loadEvent, false, false);
105 void HTMLImageLoader::notifyFinished(CachedObject *image)
107 m_imageComplete = true;
108 Element* elem = element();
109 Document* doc = elem->document();
110 doc->dispatchImageLoadEventSoon(this);
111 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
112 if (!doc->ownerElement())
113 printf("Image loaded at %d\n", doc->elapsedTime());
115 if (RenderImage* renderer = static_cast<RenderImage*>(elem->renderer()))
116 renderer->setCachedImage(m_image);
119 // -------------------------------------------------------------------------
121 HTMLImageElement::HTMLImageElement(Document *doc, HTMLFormElement *f)
122 : HTMLElement(imgTag, doc), m_imageLoader(this), ismap(false), m_form(f)
123 , m_compositeOperator(CompositeSourceOver)
126 f->registerImgElement(this);
129 HTMLImageElement::HTMLImageElement(const QualifiedName& tagName, Document *doc)
130 : HTMLElement(tagName, doc), m_imageLoader(this), ismap(false), m_form(0)
131 , m_compositeOperator(CompositeSourceOver)
135 HTMLImageElement::~HTMLImageElement()
138 m_form->removeImgElement(this);
141 bool HTMLImageElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
143 if (attrName == widthAttr ||
144 attrName == heightAttr ||
145 attrName == vspaceAttr ||
146 attrName == hspaceAttr ||
147 attrName == valignAttr) {
152 if (attrName == borderAttr || attrName == alignAttr) {
153 result = eReplaced; // Shared with embed and iframe elements.
157 return HTMLElement::mapToEntry(attrName, result);
160 void HTMLImageElement::parseMappedAttribute(MappedAttribute *attr)
162 const QualifiedName& attrName = attr->name();
163 if (attrName == altAttr) {
165 static_cast<RenderImage*>(renderer())->updateAltText();
166 } else if (attrName == srcAttr)
167 m_imageLoader.updateFromElement();
168 else if (attrName == widthAttr)
169 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
170 else if (attrName == heightAttr)
171 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
172 else if (attrName == borderAttr) {
173 // border="noborder" -> border="0"
174 if(attr->value().toInt()) {
175 addCSSLength(attr, CSS_PROP_BORDER_WIDTH, attr->value());
176 addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
177 addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
178 addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
179 addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
181 } else if (attrName == vspaceAttr) {
182 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
183 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
184 } else if (attrName == hspaceAttr) {
185 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
186 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
187 } else if (attrName == alignAttr)
188 addHTMLAlignment(attr);
189 else if (attrName == valignAttr)
190 addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
191 else if (attrName == usemapAttr) {
192 if (attr->value().domString()[0] == '#')
193 usemap = attr->value();
195 usemap = document()->completeURL(parseURL(attr->value()));
196 m_isLink = !attr->isNull();
197 } else if (attrName == ismapAttr)
199 else if (attrName == onabortAttr)
200 setHTMLEventListener(abortEvent, attr);
201 else if (attrName == onloadAttr)
202 setHTMLEventListener(loadEvent, attr);
203 else if (attrName == compositeAttr) {
204 if (!parseCompositeOperator(attr->value(), m_compositeOperator))
205 m_compositeOperator = CompositeSourceOver;
206 } else if (attrName == nameAttr) {
207 String newNameAttr = attr->value();
208 if (inDocument() && document()->isHTMLDocument()) {
209 HTMLDocument* doc = static_cast<HTMLDocument*>(document());
210 doc->removeNamedItem(oldNameAttr);
211 doc->addNamedItem(newNameAttr);
213 oldNameAttr = newNameAttr;
215 HTMLElement::parseMappedAttribute(attr);
218 String HTMLImageElement::altText() const
220 // lets figure out the alt text.. magic stuff
221 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
222 // also heavily discussed by Hixie on bugzilla
223 String alt = getAttribute(altAttr);
224 // fall back to title attribute
226 alt = getAttribute(titleAttr);
230 RenderObject *HTMLImageElement::createRenderer(RenderArena *arena, RenderStyle *style)
232 return new (arena) RenderImage(this);
235 void HTMLImageElement::attach()
237 HTMLElement::attach();
239 if (RenderImage* imageObj = static_cast<RenderImage*>(renderer()))
240 imageObj->setCachedImage(m_imageLoader.image());
243 void HTMLImageElement::insertedIntoDocument()
245 Document* doc = document();
246 if (doc->isHTMLDocument())
247 static_cast<HTMLDocument*>(doc)->addNamedItem(oldNameAttr);
249 HTMLElement::insertedIntoDocument();
252 void HTMLImageElement::removedFromDocument()
254 Document* doc = document();
255 if (doc->isHTMLDocument())
256 static_cast<HTMLDocument*>(doc)->removeNamedItem(oldNameAttr);
258 HTMLElement::removedFromDocument();
261 int HTMLImageElement::width(bool ignorePendingStylesheets) const
264 // check the attribute first for an explicit pixel value
266 int width = getAttribute(widthAttr).toInt(&ok);
270 // if the image is available, use its width
271 if (m_imageLoader.image())
272 return m_imageLoader.image()->imageSize().width();
275 Document* doc = document();
276 if (ignorePendingStylesheets)
277 doc->updateLayoutIgnorePendingStylesheets();
281 return renderer() ? renderer()->contentWidth() : 0;
284 int HTMLImageElement::height(bool ignorePendingStylesheets) const
287 // check the attribute first for an explicit pixel value
289 int height = getAttribute(heightAttr).toInt(&ok);
293 // if the image is available, use its height
294 if (m_imageLoader.image())
295 return m_imageLoader.image()->imageSize().height();
298 Document* doc = document();
299 if (ignorePendingStylesheets)
300 doc->updateLayoutIgnorePendingStylesheets();
304 return renderer() ? renderer()->contentHeight() : 0;
307 bool HTMLImageElement::isURLAttribute(Attribute *attr) const
309 return attr->name() == srcAttr || (attr->name() == usemapAttr && attr->value().domString()[0] != '#');
312 String HTMLImageElement::name() const
314 return getAttribute(nameAttr);
317 void HTMLImageElement::setName(const String& value)
319 setAttribute(nameAttr, value);
322 String HTMLImageElement::align() const
324 return getAttribute(alignAttr);
327 void HTMLImageElement::setAlign(const String& value)
329 setAttribute(alignAttr, value);
332 String HTMLImageElement::alt() const
334 return getAttribute(altAttr);
337 void HTMLImageElement::setAlt(const String& value)
339 setAttribute(altAttr, value);
342 int HTMLImageElement::border() const
344 // ### return value in pixels
345 return getAttribute(borderAttr).toInt();
348 void HTMLImageElement::setBorder(int value)
350 setAttribute(borderAttr, String::number(value));
353 void HTMLImageElement::setHeight(int value)
355 setAttribute(heightAttr, String::number(value));
358 int HTMLImageElement::hspace() const
360 // ### return actual value
361 return getAttribute(hspaceAttr).toInt();
364 void HTMLImageElement::setHspace(int value)
366 setAttribute(hspaceAttr, String::number(value));
369 bool HTMLImageElement::isMap() const
371 return !getAttribute(ismapAttr).isNull();
374 void HTMLImageElement::setIsMap(bool isMap)
376 setAttribute(ismapAttr, isMap ? "" : 0);
379 String HTMLImageElement::longDesc() const
381 return getAttribute(longdescAttr);
384 void HTMLImageElement::setLongDesc(const String& value)
386 setAttribute(longdescAttr, value);
389 String HTMLImageElement::src() const
391 return document()->completeURL(getAttribute(srcAttr));
394 void HTMLImageElement::setSrc(const String& value)
396 setAttribute(srcAttr, value);
399 String HTMLImageElement::useMap() const
401 return getAttribute(usemapAttr);
404 void HTMLImageElement::setUseMap(const String& value)
406 setAttribute(usemapAttr, value);
409 int HTMLImageElement::vspace() const
411 // ### return actual vspace
412 return getAttribute(vspaceAttr).toInt();
415 void HTMLImageElement::setVspace(int value)
417 setAttribute(vspaceAttr, String::number(value));
420 void HTMLImageElement::setWidth(int value)
422 setAttribute(widthAttr, String::number(value));
425 int HTMLImageElement::x() const
427 RenderObject *r = renderer();
431 r->absolutePosition(x, y);
435 int HTMLImageElement::y() const
437 RenderObject *r = renderer();
441 r->absolutePosition(x, y);
445 bool HTMLImageElement::complete() const
447 return m_imageLoader.imageComplete();
450 // -------------------------------------------------------------------------
452 HTMLMapElement::HTMLMapElement(Document *doc)
453 : HTMLElement(mapTag, doc)
457 HTMLMapElement::~HTMLMapElement()
459 document()->removeImageMap(this);
462 bool HTMLMapElement::checkDTD(const Node* newChild)
464 // FIXME: This seems really odd, allowing only blocks inside map elements.
465 return newChild->hasTagName(areaTag) || newChild->hasTagName(scriptTag) || inBlockTagList(newChild);
468 bool HTMLMapElement::mapMouseEvent(int x, int y, int width, int height, RenderObject::NodeInfo& info)
471 while ((node = node->traverseNextNode(this)))
472 if (node->hasTagName(areaTag))
473 if (static_cast<HTMLAreaElement *>(node)->mapMouseEvent(x, y, width, height, info))
478 void HTMLMapElement::parseMappedAttribute(MappedAttribute *attr)
480 const QualifiedName& attrName = attr->name();
481 if (attrName == idAttr || attrName == nameAttr) {
482 Document* doc = document();
483 if (attrName == idAttr) {
484 // Call base class so that hasID bit gets set.
485 HTMLElement::parseMappedAttribute(attr);
486 if (doc->htmlMode() != Document::XHtml)
489 doc->removeImageMap(this);
490 m_name = attr->value();
491 if (m_name[0] == '#') {
492 String mapName(m_name.domString().copy());
493 mapName.remove(0, 1);
496 doc->addImageMap(this);
498 HTMLElement::parseMappedAttribute(attr);
501 PassRefPtr<HTMLCollection> HTMLMapElement::areas()
503 return new HTMLCollection(this, HTMLCollection::MAP_AREAS);
506 String HTMLMapElement::name() const
508 return getAttribute(nameAttr);
511 void HTMLMapElement::setName(const String& value)
513 setAttribute(nameAttr, value);
516 // -------------------------------------------------------------------------
518 HTMLAreaElement::HTMLAreaElement(Document *doc)
519 : HTMLAnchorElement(areaTag, doc)
528 HTMLAreaElement::~HTMLAreaElement()
533 void HTMLAreaElement::parseMappedAttribute(MappedAttribute *attr)
535 if (attr->name() == shapeAttr) {
536 if (equalIgnoringCase(attr->value(), "default"))
538 else if (equalIgnoringCase(attr->value(), "circle"))
540 else if (equalIgnoringCase(attr->value(), "poly"))
542 else if (equalIgnoringCase(attr->value(), "rect"))
544 } else if (attr->name() == coordsAttr) {
546 m_coords = attr->value().toCoordsArray(m_coordsLen);
547 } else if (attr->name() == targetAttr) {
548 m_hasTarget = !attr->isNull();
549 } else if (attr->name() == altAttr || attr->name() == accesskeyAttr) {
552 HTMLAnchorElement::parseMappedAttribute(attr);
555 bool HTMLAreaElement::mapMouseEvent(int x, int y, int width, int height, RenderObject::NodeInfo& info)
557 if (width != lastw || height != lasth) {
558 region = getRegion(width, height);
563 if (!region.contains(IntPoint(x, y)))
566 info.setInnerNode(this);
567 info.setURLElement(this);
571 IntRect HTMLAreaElement::getRect(RenderObject* obj) const
574 obj->absolutePosition(dx, dy);
575 Path p = getRegion(lastw, lasth);
576 p.translate(IntSize(dx, dy));
577 return enclosingIntRect(p.boundingRect());
580 Path HTMLAreaElement::getRegion(int width, int height) const
585 // If element omits the shape attribute, select shape based on number of coordinates.
586 Shape shape = m_shape;
587 if (shape == Unknown) {
588 if (m_coordsLen == 3)
590 else if (m_coordsLen == 4)
592 else if (m_coordsLen >= 6)
599 if (m_coordsLen >= 6) {
601 int numPoints = m_coordsLen / 2;
602 path.moveTo(FloatPoint(m_coords[0].calcMinValue(width), m_coords[1].calcMinValue(height)));
603 for (int i = 1; i < numPoints; ++i)
604 path.addLineTo(FloatPoint(m_coords[i * 2].calcMinValue(width), m_coords[i * 2 + 1].calcMinValue(height)));
609 if (m_coordsLen >= 3) {
610 Length radius = m_coords[2];
611 int r = min(radius.calcMinValue(width), radius.calcMinValue(height));
612 path.addEllipse(FloatRect(m_coords[0].calcMinValue(width) - r, m_coords[1].calcMinValue(height) - r,
617 if (m_coordsLen >= 4) {
618 int x0 = m_coords[0].calcMinValue(width);
619 int y0 = m_coords[1].calcMinValue(height);
620 int x1 = m_coords[2].calcMinValue(width);
621 int y1 = m_coords[3].calcMinValue(height);
622 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0));
626 path.addRect(FloatRect(0, 0, width, height));
635 String HTMLAreaElement::accessKey() const
637 return getAttribute(accesskeyAttr);
640 void HTMLAreaElement::setAccessKey(const String& value)
642 setAttribute(accesskeyAttr, value);
645 String HTMLAreaElement::alt() const
647 return getAttribute(altAttr);
650 void HTMLAreaElement::setAlt(const String& value)
652 setAttribute(altAttr, value);
655 String HTMLAreaElement::coords() const
657 return getAttribute(coordsAttr);
660 void HTMLAreaElement::setCoords(const String& value)
662 setAttribute(coordsAttr, value);
665 String HTMLAreaElement::href() const
667 return document()->completeURL(getAttribute(hrefAttr));
670 void HTMLAreaElement::setHref(const String& value)
672 setAttribute(hrefAttr, value);
675 bool HTMLAreaElement::noHref() const
677 return !getAttribute(nohrefAttr).isNull();
680 void HTMLAreaElement::setNoHref(bool noHref)
682 setAttribute(nohrefAttr, noHref ? "" : 0);
685 String HTMLAreaElement::shape() const
687 return getAttribute(shapeAttr);
690 void HTMLAreaElement::setShape(const String& value)
692 setAttribute(shapeAttr, value);
695 int HTMLAreaElement::tabIndex() const
697 return getAttribute(tabindexAttr).toInt();
700 void HTMLAreaElement::setTabIndex(int tabIndex)
702 setAttribute(tabindexAttr, String::number(tabIndex));
705 String HTMLAreaElement::target() const
707 return getAttribute(targetAttr);
710 void HTMLAreaElement::setTarget(const String& value)
712 setAttribute(targetAttr, value);