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 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.
24 #include "html/html_imageimpl.h"
25 #include "html/html_formimpl.h"
26 #include "html/html_documentimpl.h"
28 #include "misc/htmlhashes.h"
29 #include "khtmlview.h"
30 #include "khtml_part.h"
32 #include <kstringhandler.h>
36 #include "rendering/render_image.h"
37 #include "rendering/render_flow.h"
38 #include "css/cssstyleselector.h"
39 #include "css/cssproperties.h"
40 #include "css/cssvalues.h"
41 #include "css/csshelper.h"
42 #include "xml/dom2_eventsimpl.h"
47 #include <qptrstack.h>
49 #include <qpointarray.h>
52 using namespace khtml;
54 //#define INSTRUMENT_LAYOUT_SCHEDULING 1
56 HTMLImageLoader::HTMLImageLoader(ElementImpl* elt)
57 :m_element(elt), m_image(0), m_firedLoad(true), m_imageComplete(true)
61 HTMLImageLoader::~HTMLImageLoader()
65 if (m_element->getDocument())
66 m_element->getDocument()->removeImage(this);
69 void HTMLImageLoader::updateFromElement()
71 // If we're not making renderers for the page, then don't load images. We don't want to slow
72 // down the raw HTML parsing case by loading images we don't intend to display.
73 DocumentImpl* document = element()->getDocument();
74 if (!document || !document->renderer())
78 if (element()->id() == ID_OBJECT)
79 attr = element()->getAttribute(ATTR_DATA);
81 attr = element()->getAttribute(ATTR_SRC);
83 // Treat a lack of src or empty string for src as no image at all.
84 CachedImage* newImage = 0;
86 newImage = element()->getDocument()->docLoader()->requestImage(khtml::parseURL(attr));
88 if (newImage != m_image) {
90 m_imageComplete = false;
91 CachedImage* oldImage = m_image;
96 oldImage->deref(this);
99 khtml::RenderImage *renderer = static_cast<khtml::RenderImage*>(element()->renderer());
101 renderer->resetAnimation();
105 void HTMLImageLoader::dispatchLoadEvent()
109 if (m_image->isErrorImage())
110 element()->dispatchHTMLEvent(EventImpl::ERROR_EVENT, false, false);
112 element()->dispatchHTMLEvent(EventImpl::LOAD_EVENT, false, false);
116 void HTMLImageLoader::notifyFinished(CachedObject* image)
118 m_imageComplete = true;
119 DocumentImpl* document = element()->getDocument();
121 document->dispatchImageLoadEventSoon(this);
122 #ifdef INSTRUMENT_LAYOUT_SCHEDULING
123 if (!document->ownerElement())
124 printf("Image loaded at %d\n", element()->getDocument()->elapsedTime());
127 if (element()->renderer()) {
128 RenderImage* imageObj = static_cast<RenderImage*>(element()->renderer());
129 imageObj->setImage(m_image);
133 // -------------------------------------------------------------------------
135 HTMLImageElementImpl::HTMLImageElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
136 : HTMLElementImpl(doc), m_imageLoader(this), ismap(false), m_form(f)
139 m_form->registerImgElement(this);
142 HTMLImageElementImpl::~HTMLImageElementImpl()
145 m_form->removeImgElement(this);
148 NodeImpl::Id HTMLImageElementImpl::id() const
153 bool HTMLImageElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
166 result = eReplaced; // Shared with embeds and iframes
172 return HTMLElementImpl::mapToEntry(attr, result);
175 void HTMLImageElementImpl::parseMappedAttribute(MappedAttributeImpl *attr)
180 if (m_render) static_cast<RenderImage*>(m_render)->updateAltText();
183 m_imageLoader.updateFromElement();
186 addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
189 addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
192 // border="noborder" -> border="0"
193 if(attr->value().toInt()) {
194 addCSSLength(attr, CSS_PROP_BORDER_WIDTH, attr->value());
195 addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
196 addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
197 addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
198 addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
202 addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
203 addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
206 addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
207 addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
210 addHTMLAlignment(attr);
213 addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
216 if ( attr->value().domString()[0] == '#' )
217 usemap = attr->value();
219 QString url = getDocument()->completeURL( khtml::parseURL( attr->value() ).string() );
220 // ### we remove the part before the anchor and hope
221 // the map is on the same html page....
224 m_isLink = !attr->isNull();
228 case ATTR_ONABORT: // ### add support for this
229 setHTMLEventListener(EventImpl::ABORT_EVENT,
230 getDocument()->createHTMLEventListener(attr->value().string(), this));
233 setHTMLEventListener(EventImpl::ERROR_EVENT,
234 getDocument()->createHTMLEventListener(attr->value().string(), this));
237 setHTMLEventListener(EventImpl::LOAD_EVENT,
238 getDocument()->createHTMLEventListener(attr->value().string(), this));
244 _compositeOperator = attr->value().string();
249 QString newNameAttr = attr->value().string();
250 if (attached() && getDocument()->isHTMLDocument()) {
251 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
252 document->removeNamedImageOrForm(oldNameAttr);
253 document->addNamedImageOrForm(newNameAttr);
255 oldNameAttr = newNameAttr;
260 QString newIdAttr = attr->value().string();
261 if (attached() && getDocument()->isHTMLDocument()) {
262 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
263 document->removeNamedImageOrForm(oldIdAttr);
264 document->addNamedImageOrForm(newIdAttr);
266 oldIdAttr = newIdAttr;
270 HTMLElementImpl::parseMappedAttribute(attr);
274 DOMString HTMLImageElementImpl::altText() const
276 // lets figure out the alt text.. magic stuff
277 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
278 // also heavily discussed by Hixie on bugzilla
279 DOMString alt( getAttribute( ATTR_ALT ) );
280 // fall back to title attribute
282 alt = getAttribute( ATTR_TITLE );
284 if ( alt.isNull() ) {
285 QString p = KURL( getDocument()->completeURL( getAttribute(ATTR_SRC).string() ) ).prettyURL();
287 if ( ( pos = p.findRev( '.' ) ) > 0 )
289 alt = DOMString( KStringHandler::csqueeze( p ) );
296 RenderObject *HTMLImageElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
298 return new (arena) RenderImage(this);
301 void HTMLImageElementImpl::attach()
303 HTMLElementImpl::attach();
306 RenderImage* imageObj = static_cast<RenderImage*>(renderer());
307 imageObj->setImage(m_imageLoader.image());
310 if (getDocument()->isHTMLDocument()) {
311 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
312 document->addNamedImageOrForm(oldIdAttr);
313 document->addNamedImageOrForm(oldNameAttr);
317 void HTMLImageElementImpl::detach()
319 if (getDocument()->isHTMLDocument()) {
320 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
321 document->removeNamedImageOrForm(oldIdAttr);
322 document->removeNamedImageOrForm(oldNameAttr);
325 HTMLElementImpl::detach();
328 long HTMLImageElementImpl::width(bool ignorePendingStylesheets) const
331 // check the attribute first for an explicit pixel value
332 DOM::DOMString attrWidth = getAttribute(ATTR_WIDTH);
334 long width = attrWidth.string().toLong(&ok);
340 DOM::DocumentImpl* docimpl = getDocument();
342 if (ignorePendingStylesheets)
343 docimpl->updateLayoutIgnorePendingStylesheets();
345 docimpl->updateLayout();
352 return m_render->contentWidth();
355 long HTMLImageElementImpl::height(bool ignorePendingStylesheets) const
358 // check the attribute first for an explicit pixel value
359 DOM::DOMString attrHeight = getAttribute(ATTR_HEIGHT);
361 long height = attrHeight.string().toLong(&ok);
367 DOM::DocumentImpl* docimpl = getDocument();
369 if (ignorePendingStylesheets)
370 docimpl->updateLayoutIgnorePendingStylesheets();
372 docimpl->updateLayout();
379 return m_render->contentHeight();
382 QImage HTMLImageElementImpl::currentImage() const
384 RenderImage *r = static_cast<RenderImage*>(renderer());
386 return r->pixmap().convertToImage();
390 bool HTMLImageElementImpl::isURLAttribute(AttributeImpl *attr) const
392 return (attr->id() == ATTR_SRC || (attr->id() == ATTR_USEMAP && attr->value().domString()[0] != '#'));
395 DOMString HTMLImageElementImpl::name() const
397 return getAttribute(ATTR_NAME);
400 void HTMLImageElementImpl::setName(const DOMString &value)
402 setAttribute(ATTR_NAME, value);
405 DOMString HTMLImageElementImpl::align() const
407 return getAttribute(ATTR_ALIGN);
410 void HTMLImageElementImpl::setAlign(const DOMString &value)
412 setAttribute(ATTR_ALIGN, value);
415 DOMString HTMLImageElementImpl::alt() const
417 return getAttribute(ATTR_ALT);
420 void HTMLImageElementImpl::setAlt(const DOMString &value)
422 setAttribute(ATTR_ALT, value);
425 long HTMLImageElementImpl::border() const
427 // ### return value in pixels
428 return getAttribute(ATTR_BORDER).toInt();
431 void HTMLImageElementImpl::setBorder(long value)
433 setAttribute(ATTR_BORDER, QString::number(value));
436 void HTMLImageElementImpl::setHeight(long value)
438 setAttribute(ATTR_HEIGHT, QString::number(value));
441 long HTMLImageElementImpl::hspace() const
443 // ### return actual value
444 return getAttribute(ATTR_HSPACE).toInt();
447 void HTMLImageElementImpl::setHspace(long value)
449 setAttribute(ATTR_HSPACE, QString::number(value));
452 bool HTMLImageElementImpl::isMap() const
454 return !getAttribute(ATTR_ISMAP).isNull();
457 void HTMLImageElementImpl::setIsMap(bool isMap)
459 setAttribute(ATTR_ISMAP, isMap ? "" : 0);
462 DOMString HTMLImageElementImpl::longDesc() const
464 return getAttribute(ATTR_LONGDESC);
467 void HTMLImageElementImpl::setLongDesc(const DOMString &value)
469 setAttribute(ATTR_LONGDESC, value);
472 DOMString HTMLImageElementImpl::src() const
474 return getDocument()->completeURL(getAttribute(ATTR_SRC));
477 void HTMLImageElementImpl::setSrc(const DOMString &value)
479 setAttribute(ATTR_SRC, value);
482 DOMString HTMLImageElementImpl::useMap() const
484 return getAttribute(ATTR_USEMAP);
487 void HTMLImageElementImpl::setUseMap(const DOMString &value)
489 setAttribute(ATTR_USEMAP, value);
492 long HTMLImageElementImpl::vspace() const
494 // ### return actual vspace
495 return getAttribute(ATTR_VSPACE).toInt();
498 void HTMLImageElementImpl::setVspace(long value)
500 setAttribute(ATTR_VSPACE, QString::number(value));
503 void HTMLImageElementImpl::setWidth(long value)
505 setAttribute(ATTR_WIDTH, QString::number(value));
508 long HTMLImageElementImpl::x() const
510 RenderObject *r = renderer();
514 r->absolutePosition(x, y);
518 long HTMLImageElementImpl::y() const
520 RenderObject *r = renderer();
524 r->absolutePosition(x, y);
528 // -------------------------------------------------------------------------
530 HTMLMapElementImpl::HTMLMapElementImpl(DocumentPtr *doc)
531 : HTMLElementImpl(doc)
535 HTMLMapElementImpl::~HTMLMapElementImpl()
538 getDocument()->removeImageMap(this);
541 NodeImpl::Id HTMLMapElementImpl::id() const
547 HTMLMapElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
548 RenderObject::NodeInfo& info)
550 //cout << "map:mapMouseEvent " << endl;
551 //cout << x_ << " " << y_ <<" "<< width_ <<" "<< height_ << endl;
552 QPtrStack<NodeImpl> nodeStack;
554 NodeImpl *current = firstChild();
559 if(nodeStack.isEmpty()) break;
560 current = nodeStack.pop();
561 current = current->nextSibling();
564 if(current->id()==ID_AREA)
566 //cout << "area found " << endl;
567 HTMLAreaElementImpl* area=static_cast<HTMLAreaElementImpl*>(current);
568 if (area->mapMouseEvent(x_,y_,width_,height_, info))
571 NodeImpl *child = current->firstChild();
574 nodeStack.push(current);
579 current = current->nextSibling();
586 void HTMLMapElementImpl::parseMappedAttribute(MappedAttributeImpl *attr)
591 // Must call base class so that hasID bit gets set.
592 HTMLElementImpl::parseMappedAttribute(attr);
593 if (getDocument()->htmlMode() != DocumentImpl::XHtml) break;
596 getDocument()->removeImageMap(this);
597 m_name = attr->value();
598 if (m_name.length() != 0 && m_name[0] == '#')
600 getDocument()->addImageMap(this);
603 HTMLElementImpl::parseMappedAttribute(attr);
607 SharedPtr<HTMLCollectionImpl> HTMLMapElementImpl::areas()
609 return SharedPtr<HTMLCollectionImpl>(new HTMLCollectionImpl(this, HTMLCollectionImpl::MAP_AREAS));
612 DOMString HTMLMapElementImpl::name() const
614 return getAttribute(ATTR_NAME);
617 void HTMLMapElementImpl::setName(const DOMString &value)
619 setAttribute(ATTR_NAME, value);
622 // -------------------------------------------------------------------------
624 HTMLAreaElementImpl::HTMLAreaElementImpl(DocumentPtr *doc)
625 : HTMLAnchorElementImpl(doc)
633 HTMLAreaElementImpl::~HTMLAreaElementImpl()
635 if (m_coords) delete [] m_coords;
638 NodeImpl::Id HTMLAreaElementImpl::id() const
643 void HTMLAreaElementImpl::parseMappedAttribute(MappedAttributeImpl *attr)
648 if ( strcasecmp( attr->value(), "default" ) == 0 )
650 else if ( strcasecmp( attr->value(), "circle" ) == 0 )
652 else if ( strcasecmp( attr->value(), "poly" ) == 0 )
654 else if ( strcasecmp( attr->value(), "rect" ) == 0 )
658 if (m_coords) delete [] m_coords;
659 m_coords = attr->value().toLengthArray(m_coordsLen);
662 m_hasTarget = !attr->isNull();
669 HTMLAnchorElementImpl::parseMappedAttribute(attr);
673 bool HTMLAreaElementImpl::mapMouseEvent(int x_, int y_, int width_, int height_,
674 RenderObject::NodeInfo& info)
677 if (width_ != lastw || height_ != lasth)
679 region=getRegion(width_, height_);
680 lastw=width_; lasth=height_;
682 if (region.contains(QPoint(x_,y_)))
685 info.setInnerNode(this);
686 info.setURLElement(this);
692 QRect HTMLAreaElementImpl::getRect(RenderObject* obj) const
695 obj->absolutePosition(dx, dy);
696 QRegion region = getRegion(lastw,lasth);
697 region.translate(dx, dy);
698 return region.boundingRect();
701 QRegion HTMLAreaElementImpl::getRegion(int width_, int height_) const
707 // added broken HTML support (Dirk): some pages omit the SHAPE
708 // attribute, so we try to guess by looking at the coords count
709 // what the HTML author tried to tell us.
711 // a Poly needs at least 3 points (6 coords), so this is correct
712 if ((m_shape==Poly || m_shape==Unknown) && m_coordsLen > 5) {
713 // make sure its even
714 int len = m_coordsLen >> 1;
715 QPointArray points(len);
716 for (int i = 0; i < len; ++i)
717 points.setPoint(i, m_coords[(i<<1)].minWidth(width_),
718 m_coords[(i<<1)+1].minWidth(height_));
719 region = QRegion(points);
721 else if (m_shape==Circle && m_coordsLen>=3 || m_shape==Unknown && m_coordsLen == 3) {
722 int r = kMin(m_coords[2].minWidth(width_), m_coords[2].minWidth(height_));
723 region = QRegion(m_coords[0].minWidth(width_)-r,
724 m_coords[1].minWidth(height_)-r, 2*r, 2*r,QRegion::Ellipse);
726 else if (m_shape==Rect && m_coordsLen>=4 || m_shape==Unknown && m_coordsLen == 4) {
727 int x0 = m_coords[0].minWidth(width_);
728 int y0 = m_coords[1].minWidth(height_);
729 int x1 = m_coords[2].minWidth(width_);
730 int y1 = m_coords[3].minWidth(height_);
731 region = QRegion(x0,y0,x1-x0,y1-y0);
733 else if (m_shape==Default)
734 region = QRegion(0,0,width_,height_);
736 // return null region
741 DOMString HTMLAreaElementImpl::accessKey() const
743 return getAttribute(ATTR_ACCESSKEY);
746 void HTMLAreaElementImpl::setAccessKey(const DOMString &value)
748 setAttribute(ATTR_ACCESSKEY, value);
751 DOMString HTMLAreaElementImpl::alt() const
753 return getAttribute(ATTR_ALT);
756 void HTMLAreaElementImpl::setAlt(const DOMString &value)
758 setAttribute(ATTR_ALT, value);
761 DOMString HTMLAreaElementImpl::coords() const
763 return getAttribute(ATTR_COORDS);
766 void HTMLAreaElementImpl::setCoords(const DOMString &value)
768 setAttribute(ATTR_COORDS, value);
771 DOMString HTMLAreaElementImpl::href() const
773 return getDocument()->completeURL(getAttribute(ATTR_HREF));
776 void HTMLAreaElementImpl::setHref(const DOMString &value)
778 setAttribute(ATTR_HREF, value);
781 bool HTMLAreaElementImpl::noHref() const
783 return !getAttribute(ATTR_NOHREF).isNull();
786 void HTMLAreaElementImpl::setNoHref(bool noHref)
788 setAttribute(ATTR_NOHREF, noHref ? "" : 0);
791 DOMString HTMLAreaElementImpl::shape() const
793 return getAttribute(ATTR_SHAPE);
796 void HTMLAreaElementImpl::setShape(const DOMString &value)
798 setAttribute(ATTR_SHAPE, value);
801 long HTMLAreaElementImpl::tabIndex() const
803 return getAttribute(ATTR_TABINDEX).toInt();
806 void HTMLAreaElementImpl::setTabIndex(long tabIndex)
808 setAttribute(ATTR_TABINDEX, QString::number(tabIndex));
811 DOMString HTMLAreaElementImpl::target() const
813 return getAttribute(ATTR_TARGET);
816 void HTMLAreaElementImpl::setTarget(const DOMString &value)
818 setAttribute(ATTR_TARGET, value);